今開催されてるTAMUCTFにsecure coding って新ジャンルが出題されてました。この出題方法なら防御問題としていいかもしれません。
— kazkitictf (@kazkiti_ctf) 2018年2月19日
ということで、試しに解いてみた。大学の学生向けのコンテストを外にもう公開している(?)からか、見た問題はどれもわりと簡単。コンテストは終了したけれど、しばらくは問題を公開したままらしい。
システム
運営がGitLabサーバーを動かしていて、各問題ではリポジトリのURLが与えられる。forkして脆弱性を修正してpushすると、CIが走る。正常系が動作することと、脆弱性が無いことが自動でチェックされ、パスすれば、フラグが表示される。競技プログラミングっぽい。
nginx
nginxの設定ファイル。
root /;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
try_files $uri $uri/ =404;
index /usr/share/nginx/html/index.html;
autoindex on;
}
/
を公開しているw
root /usr/share/nginx/html;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
}
で通った。
gigem{f1x1N_conF1g5_0533cfc}
sql
ログインフォーム。
// Ensure admin will always be the first record, though really unnecessary
$sql = "SELECT * FROM Users WHERE User='$user' AND Password='$pass' ORDER BY ID";
プリペアドステートメントを使うべき。ただ、全体的に書き換える必要がある。エスケープをするにしても自分で実装はせずに用意された関数を使うべき。しかし、mysql_real_escape_stringはPHP7で削除された。仕方がないので、自分で'
を''
に書き換えたら通った。
// Ensure admin will always be the first record, though really unnecessary
$sql = "SELECT * FROM Users WHERE User='".str_replace("'","''",$user)."' AND Password='".str_replace("'","''",$pass)."' ORDER BY ID";
gigem{cAn_y0U_sQL_TH3_Pr0bL3m?_9f431b}
maze
express製のサーバー。
app.get('/*', function(request, response){
console.log('request starting...');
var filePath = __dirname + '/..' +request.url;
if (filePath == __dirname)
filePath = __dirname + '/../public/index.html';
var extname = path.extname(filePath);
これで、ディレクトリトラバーサルができるようになっていたので、
var filePath = __dirname + '/../public' +path.normalize(request.url);
に直した。
gigem{LifES_4_m4ze_37c83f}
shell-plugin
PythonのSimpleXMLRPCServerで、OSのユーザーを追加するスクリプト。
def add_user_func(name, password):
os.system("./add-user.sh " + name + " " + password)
この部分はsubprocess
を使えば良いけれど、add-user.sh
の中身は、
#!/bin/bash
mkdir /home/$1
useradd -G ctf-users --home /home/$1 -s /bin/bash $1
chown $1:$1 /home/$1
echo -e "$2\n$2" | passwd $1
シェルスクリプトでのエスケープなんてどうするのだろう? ということで、英数字以外は弾くようにしたら通った。
def add_user_func(name, password):
name = re.sub(r'\W', '', name)
password = re.sub(r'\W', '', password)
os.system("./add-user.sh " + name + " " + password)
gigem{gH0s7_in_7h3_Sh3ll_fb63a0}
感想
たしかに、「守るためのコンテスト」感はある。SQLインジェクションとか他のコンテストで攻撃するのは慣れていても、「じゃあ守ってみて」と言われると戸惑いがあって、面白い。
とはいえ、これで正しい守り方が学べるかというと、上のような雑な回答でも通ってしまうという問題がある。mazeはまだディレクトリトラバーサルができるし、shell-pluginもユーザーが入力したもの違うnameとpasswordを渡してしまっているのは良くない。password*|>?+$|!('"-
というパスワードがpassword
になるので辞書攻撃で破られる。
この方式だと、テストを通るようなプログラムを一から実装するという最終手段があるので、一定以上に難しい問題は作れない。
防御側を問題にするのは難しそうだ。