KSNCTF #3 Crawling Cipher

# #3 Crawling Cipher
以下の常設CTFの問題です。

https://ksnctf.sweetduet.info/

## 無理すぎる

**(」・ω・)」うー!(/・ω・)/にゃー!encode**って知ってますか？

https://sanya.sweetduet.info/unyaencode/

これがキーとなります。

## とりあえず色々試してみる

![スクリーンショット 2025-04-24 232853.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4055936/0ee1ff18-1422-4146-a5fb-b0f0ece2a9a8.png)

とりあえず色々送信してみたが、何も起こらない。

consoleを見てみると
![スクリーンショット 2025-04-24 233012.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4055936/10f0dca5-aa12-4b37-a628-828f06a85452.png)

**jquery**が正しく作動してない！
これは意図的なのか時の流れのせい(13年前に作成された問題)なのかわかりません。

---

とりあえずソースコードを取得して、自分の環境で書き替えます。
```html:変更
http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
                            ↓
https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js                        
```

`http`を`https`に変えて、もう一度色々送信すると動作するようになりました。

![スクリーンショット 2025-04-24 234147.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4055936/2a1e4a04-998e-4aba-83c3-f19ff9d40cd2.png)

---

ソースコードを見てみてみると、**(」・ω・)」うー!(/・ω・)/にゃー!**が大量発生していました。
![スクリーンショット 2025-04-24 234437.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4055936/4a8cc01b-ce3b-4bc6-83a1-3b8e5eb49a7b.png)

これらは実行することができるそうなので、`console.log`で出力してみました。
```
/ᆞωᆞ/ -0 /ᆞωᆞ/ 1 2 3 4 5 6 7 8 9 '' 'true' 'false' '[object Object]' 'undefined' -0 'a' 'b' 'c' 'd' 'e' 'f' 'n' 'o' 'r' 's' 't' 'u' '/""ω""//\\\\ω\\\\/' '"' '\\' '\\u' '\\u00' 'constructor' 'return"\\u0024\\u0028\\u0066\\u0075\\u006e\\u0063\\u0074\\u0069\\u006f\\u006e\\u0028\\u0029\\u007b\\u0024\\u0028\\u0022\\u0066\\u006f\\u0072\\u006d\\u0022\\u0029\\u002e\\u0073\\u0075\\u0062\\u006d\\u0069\\u0074\\u0028\\u0066\\u0075\\u006e\\u0063\\u0074\\u0069\\u006f\\u006e\\u0028\\u0029\\u007b\\u0076\\u0061\\u0072\\u0020\\u0074\\u003d\\u0024\\u0028\\u0027\\u0069\\u006e\\u0070\\u0075\\u0074\\u005b\\u0074\\u0079\\u0070\\u0065\\u003d\\u0022\\u0074\\u0065\\u0078\\u0074\\u0022\\u005d\\u0027\\u0029\\u002e\\u0076\\u0061\\u006c\\u0028\\u0029\\u003b\\u0076\\u0061\\u0072\\u0020\\u0070\\u003d\\u0041\\u0072\\u0072\\u0061\\u0079\\u0028\\u0037\\u0030\\u002c\\u0031\\u0035\\u0032\\u002c\\u0031\\u0039\\u0035\\u002c\\u0032\\u0038\\u0034\\u002c\\u0034\\u0037\\u0035\\u002c\\u0036\\u0031\\u0032\\u002c\\u0037\\u0039\\u0031\\u002c\\u0038\\u0039\\u0036\\u002c\\u0038\\u0031\\u0030\\u002c\\u0038\\u0035\\u0030\\u002c\\u0037\\u0033\\u0037\\u002c\\u0031\\u0033\\u0033\\u0032\\u002c\\u0031\\u0034\\u0036\\u0039\\u002c\\u0031\\u0031\\u0032\\u0030\\u002c\\u0031\\u0034\\u0037\\u0030\\u002c\\u0038\\u0033\\u0032\\u002c\\u0031\\u0037\\u0038\\u0035\\u002c\\u0032\\u0031\\u0039\\u0036\\u002c\\u0031\\u0035\\u0032\\u0030\\u002c\\u0031\\u0034\\u0038\\u0030\\u002c\\u0031\\u0034\\u0034\\u0039\\u0029\\u003b\\u0076\\u0061\\u0072\\u0020\\u0066\\u003d\\u0066\\u0061\\u006c\\u0073\\u0065\\u003b\\u0069\\u0066\\u0028\\u0070\\u002e\\u006c\\u0065\\u006e\\u0067\\u0074\\u0068\\u003d\\u003d\\u0074\\u002e\\u006c\\u0065\\u006e\\u0067\\u0074\\u0068\\u0029\\u007b\\u0066\\u003d\\u0074\\u0072\\u0075\\u0065\\u003b\\u0066\\u006f\\u0072\\u0028\\u0076\\u0061\\u0072\\u0020\\u0069\\u003d\\u0030\\u003b\\u0069\\u003c\\u0070\\u002e\\u006c\\u0065\\u006e\\u0067\\u0074\\u0068\\u003b\\u0069\\u002b\\u002b\\u0029\\u0069\\u0066\\u0028\\u0074\\u002e\\u0063\\u0068\\u0061\\u0072\\u0043\\u006f\\u0064\\u0065\\u0041\\u0074\\u0028\\u0069\\u0029\\u002a\\u0028\\u0069\\u002b\\u0031\\u0029\\u0021\\u003d\\u0070\\u005b\\u0069\\u005d\\u0029\\u0066\\u003d\\u0066\\u0061\\u006c\\u0073\\u0065\\u003b\\u0069\\u0066\\u0028\\u0066\\u0029\\u0061\\u006c\\u0065\\u0072\\u0074\\u0028\\u0022\\u0028\\u300d\\u30fb\\u03c9\\u30fb\\u0029\\u300d\\u3046\\u30fc\\u0021\\u0028\\u002f\\u30fb\\u03c9\\u30fb\\u0029\\u002f\\u306b\\u3083\\u30fc\\u0021\\u0022\\u0029\\u003b\\u007d\\u0069\\u0066\\u0028\\u0021\\u0066\\u0029\\u0061\\u006c\\u0065\\u0072\\u0074\\u0028\\u0022\\u004e\\u006f\\u0022\\u0029\\u003b\\u0072\\u0065\\u0074\\u0075\\u0072\\u006e\\u0020\\u0066\\u0061\\u006c\\u0073\\u0065\\u003b\\u007d\\u0029\\u003b\\u007d\\u0029\\u003b"' '' ƒ Function() { [native code] } -0 `$(function(){$("form").submit(function(){var t=$('input[type="text"]').val();var p=Array(70,152,195,284,475,612,791,896,810,850,737,1332,1469,1120,1470,832,1785,2196,1520,1480,1449);var f=false;if(p.length==t.length){f=true;for(var i=0;i<p.length;i++)if(t.charCodeAt(i)*(i+1)!=p[i])f=false;if(f)alert("(」・ω・)」うー!(/・ω・)/にゃー!");}if(!f)alert("No");return false;});});` undefined -0 1 2 3 4 5 6 7 8 9 -0
```
`return"\\u0024\\u0028\\u0066\\・・・`と文字コードらしき16進数が続いていたので変換プログラムを書きました。

```python
strings = input("Unicodeに変換したい16進数を入力してください:")
split = input("区切り文字を入力してください:")

def anotherWord(a):
    if a == "Y":
        return int(input("何文字文切り取る必要がありますか？:"))
        
    elif a != "n" and a != "Y":
        anotherWord(input("文字コードの先頭に他の文字が存在しますか？:[Y/n]"))
    else:
        return 0

rm = anotherWord(input("文字コードの先頭に他の文字が存在しますか？:[Y/n]"))

s = strings.split(split)
print(s)

result = "以下が変換された文字です:"

for i in range(len(s)):
    result += (chr(int(s[i][rm:],16)))
print(result)
```
汎用性高めるために無駄な入力を増やしました。自分の知識のみでコードを書いたので、改善できるところはたくさんあると思いますが目を瞑ってください。

実行すると以下の結果が出てきました。
```javascript
以下が変換された文字です:
$(function(){$("form").submit(function(){var t=$('input[type="text"]').val();var p=Array(70,152,195,284,475,612,791,896,810,850,737,1332,1469,1120,1470,832,1785,2196,1520,1480,1449);var f=false;if(p.length==t.length){f=true;for(var i=0;i<p.length;i++)if(t.charCodeAt(i)*(i+1)!=p[i])f=false;if(f)alert("(」・ω・)」うー!(/・ω・)/にゃー!");}if(!f)alert("No");return false;});});
```
まさかのすでに`console.log`で出てきてました…

---

インデントなどを加えて、見やすくしましょう。

```javascript
$(function () {
     $("form").submit(function () {
         var t = $('input[type="text"]').val(); 
         var p = Array(70, 152, 195, 284, 475, 612, 791, 896, 810, 850, 737, 1332, 1469, 1120, 1470, 832, 1785, 2196, 1520, 1480, 1449); 
         var f = false; 
         if (p.length == t.length) { 
            f = true; 
            for (var i = 0; i < p.length; i++)
                if (t.charCodeAt(i) * (i + 1) != p[i])
                     f = false; 
                    if (f) 
                        alert("(」・ω・)」うー!(/・ω・)/にゃー!");
         } 
         if (!f) 
            alert("No"); 
        return false; 
    }); 
});
```
このコードからわかるのは
- `t`は入力する文字
- `p.length == t.length`にならない問答無用でと`alert("No")`
    - `p.length = 21`である
    - 入力する文字(t)の長さは21文字
- `t.charCodeAt(i) * (i + 1) != p[i]`を満たすtを入力する必要がある
    - `t.charCodeAt(i)`はi文字目の文字コードを表します

これらを満たすtを導きましょう!

```python
p= [70, 152, 195, 284, 475, 612, 791, 896, 810, 850, 737, 1332, 1469, 1120, 1470, 832, 1785, 2196, 1520, 1480, 1449]

t = "これを入力すればflagをゲットできます:"
for i in range(len(p)):
    tmp = int(p[i]/(i+1))
    t += chr(tmp)
print(t)
```

実行結果は`FLAG_hogehoge`でした

![スクリーンショット 2025-04-25 002232.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4055936/bf6cc803-2223-4c61-9391-f548684df6b7.png)
実際に入力してみると、これで良さそうですね。

# まとめ
うにゃー！は絶対わかりません。
世代の問題ですか？