---
# System prepended metadata

title: upside-down writeup

---

# upside-down writeup

TSGCTF 2023 のWeb問題。
久々に解けたので、WriteUp書く。
Cryptoも解けそうだったけど、ぷろぐらみんぐちからが足りなかった。

## index.html
どうやら、POSTリクエストでJSONを送信しているようだ。

![image.png](https://hackmd.io/_uploads/Hyoz8TE7a.png)

## main.mjs
受け取る部分。JSONを受け取って解釈してる。
![image.png](https://hackmd.io/_uploads/B1KwUTEQT.png)

string.lengthを比べて、Arrayの中身を見ているようだ。
![image.png](https://hackmd.io/_uploads/H1gcI64m6.png)

## JavaScriptにおけるJSONの取り扱いについてみる。

[JSON の操作](https://developer.mozilla.org/ja/docs/Learn/JavaScript/Objects/JSON)のページにもある通り、
![image.png](https://hackmd.io/_uploads/r1QjJC47p.png)
で表すことができる。

そのため`string.length`と、`string[i]`、`string[string.length -i -1]`はそれぞれJSONの要素を指定することができる形を取っているので、stringではなくJSONを送るものであると推測して作成する。

この時点で確定しているPayloadは
```jsonld
{"length":"1000"}
```

## Arrayについてみる。
`const i of Array(string.length).keys()`のiに入る値は`string.length`の配列の長さによる。
[Array - mdn web docs](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array)

`string.length`はJSONの値なので、配列としての長さは存在せず、`const i`には`0`がはいる。
~~はいるというか、入った。dockerで動かして、return で`i`を返させたらそうだった。~~
`i=0`なので、
```javascript
string[i] = string[0]
string[string.length -i -1] = string[999]
```
となる。ここまででできるPayloadは
```jsonld
{"length":"1000","0":hogehoge,"999":hugahuga}
```
## ifの条件に合わないように調整する。
回避しなきゃいけない条件は合計で3つ。
最終的に`return null`でないといけないため、すべて避ける必要がある。
```javascript
if (string.length < 1000) {
    return 'too short';
}
```
1つ目はstring.lengthが1000以上であること。これは`"length":"1000"`で回避できている。

2つ目と3つ目はこれ。
```javascript
if (original !== reverse || typeof original !== 'string') {
	return 'not palindrome';
}
```
2つ目は回文チェック（だったもの）で、`original`と`reverse`が同じである必要がある。
`original`は`string[i]`を、`reverse`は`string[string.length-i-1]`を指しているため、JSON内の`0`と`999`の値を同一にすることで回避できる。
3つ目は、`original`が`string`であること。
これは、JSONの値が色々な型を取ることができることを活かして、`string`になるようにおいてあげる。具体的には`"`でくくってあげたらそれで良い。
これら3つの条件を満たしたものが以下のJSONになる。
```jsonld
{"length": "1000","0": "1000","999":"1000"}
```

## 送信しておわり！！！
今まで忘れられていた`index.html`はここでいきる。
```htmlembedded=
const res = await fetch('/', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({palindrome}),
});
```

`{palindrome}`は見慣れない書き方だけど、[ここに色々書いてあった](https://sbfl.net/blog/2019/08/26/javascript-bracketed-variables/)。分割代入という方法のようだ。
[JSON.stringify()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)は`{palindrome}`に代入された値をJSONに変換しているとみることができる。
（JSON内の変数名"palindrome"がどこから来ているのかが全く分からなかったので、わかる方教えてください。）
つまり、先のJSONと組み合わせるとこうなる。
```jsonld
{"palindrome": {"length": "1000","0": "1000","999":"1000"}}
```
![image.png](https://hackmd.io/_uploads/Bynm5A4Q6.png)
ただ、Web上から送信するとその値がダブルクォーテーションで囲まれているため、送信後も文字列として扱われてしまう。
JSONで扱ってほしいので、Web上から送信するのではなくPostmanなどで直接リクエストを送る。![image.png](https://hackmd.io/_uploads/rJCOqCNmT.png)

お疲れさまでした。