# HW3 PWN CTF writeup R11922138 CTF Account: eric070021 ### 1. how2know This challenge use seccomp to ban almost every syscall except exit and exit_group. Although I can run any shell code, I can't use any syscall to leak the data in the program. I decide to use a side channel attack to leak the data in the program bit by bit. The logic of my shell code is to first grab a byte and assess its bits from rightmost to leftmost. If the current bit is 0. I'll immediately close the connection; Otherwise, if the current bit is 1, I'll run a large loop with a mult operation in it, and close the connection after the loop end. Then, I can easily distinguish 0 and 1 by measuring the connection time. The last thing to do is to find the address of FLAG. Since this program has ASLR, the address of the flag array is different every time. In the main function, there's a code "read(fd, flag, 0x30);" which shows the address of the flag array. It uses the rip register plus an offset to get the address of the flag array, and I notice that the offset is the same every time I run the program!!! The program runs my shell code as calling a function, so I can get the rip of main by the return address on the stack (at rsp at the beginning), adding an offset, and I can get the address of the flag array. With the address, I can use my shell code to read the flag out bit by bit. Below is my shell code, reg_offset is the byte I want to read and bit is the bit I want to read. ```python= payload = asm(''' mov rbx, QWORD PTR ds:[rsp]; add rbx, 0x2c64; mov al, BYTE PTR ds:[rbx+''' + str(reg_offset) + ''']; xor r11, r11; shr al, ''' + str(bit) +'''; shl al, 7; shr al, 7; imul rax, 0x40000000 loop_start: cmp rax, r11; je loop_finished; inc r11; imul ebx, 0x13; jmp loop_start; loop_finished: ''') ``` ### 2. rop++ This program has an obvious buffer overflow on the stack, and the canary is off so I can put anything I want on the stack. But, the NX is open so I can't execute my shell code on the stack. Fortunately, pie is off so that I can use rop just as the challenge name implies. I found the address of the data section and some gadgets below. The critical part is how to get the address of the string "/bin/sh". I use strings and rop_gadget to find but there's no "/bin/sh" in this program, so I need to make it myself. I decide to write it to the data section since "/bin/sh\x00" is exactly 8 bytes so it can be put in a register. I move the data section address to rax, move "/bin/sh\x00" to rdi, and use mov_rax_rdi gadget to move data in rdi to the address of rax. Now, the address of "/bin/sh\x00" is the same as the address of the data section. I can set up the register need to call execve("/bin/sh") and run my rop chain to get the shell. ```python= data_sec = 0x4c50e0 mov_rax_rdi = 0x48d6a4 # mov qword ptr [rax], rdi ; pop rbx ; ret ret = 0x43cd10 # xor rax, rax ; ret pop_rdi_ret = 0x401e3f # pop rdi ; ret pop_rsi_ret = 0x409e6e # pop rsi ; ret pop_rdx_ret = 0x47ed0b # pop rdx ; pop rbx ; ret pop_rax_ret = 0x447b27 # pop rax ; ret syscall_ret = 0x414506 # syscall ; ret ROP = flat( pop_rax_ret, data_sec, pop_rdi_ret, "/bin/sh\x00", mov_rax_rdi, 0, pop_rdi_ret, data_sec, pop_rsi_ret, 0, pop_rdx_ret, 0, 0, pop_rax_ret, 0x3b, syscall_ret ) ``` ### 3. babyums (flag1) The FLAG is at the password of admin, which is the users[0]. First, since ASLR is open, I need to leak the address of heap. Following is my operations, add user1, edit user1, add user2, edit user2, delete user1, delete user2. Now, the first 8 bytes of the name field (tcache fd) of user2 will be the address of user1 chuck. Use a show operation then the address will be printed. I use this leaked address to add an offset and I get the address of the admin's password. Next, since this program has explicit UAF and buffer overflow. I construct a fake chunk that rewrites the data pointer of user2 to the address of the admin's password. Then, call edit user1, send my fake chuck, and successfully rewrite the chuck of user2. A single show operation can then show the admin's password in the data of user2. Below is my fake chunk, admin_pwd is the address of the admin's password. ```python= fake_chunk = flat( b'AAAAAAAA', b'AAAAAAAA', b'AAAAAAAA', b'AAAAAAAA', b'AAAAAAA\x00', 0x31, b'AAAAAAAA', b'AAAAAAA\x00', b'AAAAAAAA', b'AAAAAAAA', admin_pwd ) ``` ### 4. babyums (flag2) The FLAG this time is in the server, so I need to get a shell. The strategy is the same as the lab babynote. The different parts are the user struct has name and password this time, and there is already user0 (admin). My operations flow is as follows, edit user0 with data size 0x418 (to put in unsorted bun after free), add user1, edit user1, add user2, delete user0. The data of user0 will be the address of libc now, so I use show to leak the libc address. Use this libc address to add some offset to get libc base address and further get freehook address and system address. Next, I call edit 1 and send string "/bin/sh\x00" following a fake chunk to rewrite user1 name to "/bin/sh\x00" and rewrite user2 data address to free hook address. So, editing user2 now is the same as modifying function in free hook. I then call edit user2 and send the address of system to it, delete user1 to trigger free hook, and I successfully get a shell. Below is the string and fake chunk. ```python= binsh = b'/bin/sh\x00'.ljust(0x10, b'B') fake_chunk = flat( 0, 0x31, b'CCCCCCCC', b'CCCCCCCC', b'CCCCCCCC', b'CCCCCCCC', free_hook ) ``` ### 5. miniums This program doesn't contain flag so I need to get a shell, but I can't use the before method to leak libc address since this time data is a FILE structure, I can't leak libc address from it. I first try to leak heap address. My operations flows are add user0, edit user0, add user1, edit user1, delete user1, delete user0, show. The flow exploits tcache fd to leak the user1 chuck address at the name of user0. But, some strange things happened. I found that my tcache chain is broken. I then check the source code and find out the reason. The show operation has fread in it, and since I have already free user0, most of the values in user0's FILE structure will be clear to 0. Unfortunately, there's a segment of code in fread that check if the _IO_buf_base and _IO_save_base are both NULL, the flag will be **and** with **~0x100**, and the flag is also the fd of tcache. So, that's why the tcache chain is broken. Below is the code segment in _IO_file_xsgetn. ![](https://i.imgur.com/hi4Kh84.png) I change my operation flow to add user0, edit user0, add user1, delete user1, delete user0, show. Although fread in show operation will still change flag (fd) of user0, there's only one element in tcache 0x1e1 subbin so it won't be a big problem. I successfully leak the address of user1 chuck. Next, I need to leak libc address. When freeing user0, fclose will free the buffer used by fwrite, and the buffer size is 0x1000. It will first be put in unsorted bin and after fread in show operation, it'll be put in the large bin. I add an offset on the user0 chuck address to get the address of the fd of this large bin. Now, I need to read the data at this address. I exploit aar of FILE struction. The method is as follows. I call edit user0 with size 0x1d8. It will malloc the buffer that's the same as user0's FILE structure since user0 has been freed and the size I announce is the same as FILE structure size. I write my fake chuck in it to hijack fwrite. There's a fwrite in edit user0, so after I rewrite the FILE structure, fwrite will write libc address to stdout. ```python= fake_chunk = flat( 0xfbad0800, 0, libc_addr, 0, libc_addr, libc_addr + 0x8, 0, 0, 0, 0, 0, 0, 0, 0, 1, ) ``` I got the address of libc base, free hook , and system. Next thing I need to do is to write system address to free hook. I exploit aaw of FILE structure. First I want to write "/bin/sh\x00" to user3. My operations flow is as follows, add user2, add user3, edit user2, delete user2, edit user2 with fake chunk, show. ```python= fake_chunk = flat( 0xfbad0800, 0, 0, 0, user1_data, 0, 0, user1_data, user1_data + 0x400, 0, 0, 0, 0, 0, 0, ) r.send(fake_chunk) ``` The target of aaw is fread, but before executing fread it will execute a fwrite when the second time I edit user2. The flag cannot be 0xfbad0000 as teaching in the course. It needs to set up _IO_CURRENTLY_PUTTING to avoid falling into the below if condition in fread, since it will modify our read, write pointer. This if condition is in _IO_new_file_overflow. ![](https://i.imgur.com/Dr5ext2.png) One thing to notice is that user2 chuck address is the same as freed user0 chuck address, so the show operation will call hijacked fread twice. I need to send 0x200*2 data to avoid blocking at IO. ```python= # show print(r.recvuntil(b'> ').decode()) r.sendline(b'4') r.send(b'/bin/sh\x00' + b'\x00'*504) r.send(b'\x00'*512) ``` The last thing to do is to write free hook. The procedure is the same as writing "/bin/sh\x00". I add a user4 to do it. But, this time it will call fread 3 times, since user0, user2. user4 chucks are the same. One single show will trigger hijacked fread 3 times. I need to send 0x200*3 to prevent blocking at IO. After writing free hook, I delete user3 and I successfully get a shell.