Rating:

暗号化スクリプトと、暗号文がおいてある。鍵も置いてあるので、読みながら順番に元に戻すコードを書いて復元する。

import sys
from Crypto.Cipher import AES
import base64


def encrypt(key, text):
s = ''
for i in range(len(text)):
s += chr((((ord(text[i]) - 0x20) + (ord(key[i % len(key)]) - 0x20)) % (0x7e - 0x20 + 1)) + 0x20)
return s

def decrypt(key, text):
s = ''
for i in range(len(text)):
s += chr((((ord(text[i]) - 0x20) - (ord(key[i % len(key)]) - 0x20) + (0x7e-0x20+1)) % (0x7e - 0x20 + 1)) + 0x20)

return s


key1 = "SECCON"
key2 = "seccon2019"
#text = sys.argv[1]

#enc1 = encrypt(key1, text)
cipher = AES.new(key2 + chr(0x00) * (16 - (len(key2) % 16)), AES.MODE_ECB)
#p = 16 - (len(enc1) % 16)
#enc2 = cipher.encrypt(enc1 + chr(p) * p)
#print(base64.b64encode(enc2).decode('ascii'))

crypted='FyRyZNBO2MG6ncd3hEkC/yeYKUseI/CxYoZiIeV2fe/Jmtwx+WbWmU1gtMX9m905'
crypt_raw=base64.b64decode(crypted)
enc1 = cipher.decrypt(crypt_raw)

enc1 = enc1[:-5]

print decrypt(key1, enc1)
1

SECCON{Success_Decryption_Yeah_Yeah_SECCON}
Option-Cmd-U

繋ぐと、指定した URL に接続し、取得した HTML を表示してくれる。ソースコードも公開されている。フラグは /flag.php にあるが、こちらは内部ネットワークからしか見れないとのこと。Docker で構成され、 http://nginx/ に繋げばよさそうな雰囲気が漂っているが、 IP アドレス確認で nginx と一致するとはじかれる仕様になっている。

当該ソースコードには、以下のような部分があった。


$url = filter_input(INPUT_GET, 'url');
$parsed_url = parse_url($url);
if($parsed_url["scheme"] !== "http"){
// only http: should be allowed.
echo 'URL should start with http!';
} else if (gethostbyname(idn_to_ascii($parsed_url["host"], 0, INTL_IDNA_VARIANT_UTS46)) === gethostbyname("nginx")) {
// local access to nginx from php-fpm should be blocked.
echo 'Oops, are you a robot or an attacker?';
} else {
// file_get_contents needs idn_to_ascii(): https://stackoverflow.com/questions/40663425/
highlight_string(file_get_contents(idn_to_ascii($url, 0, INTL_IDNA_VARIANT_UTS46),
false,
stream_context_create(array(
'http' => array(
'follow_location' => false,
'timeout' => 2
)
))));
}

ここで、 idn_to_ascii という処理を挟んでいるが、ドメイン部をチェックする部分まではよかったものの、その後 file_get_contents で中身を取得する際に URL 全体に対してこの処理をかけてしまっていることに気付く。/や:も生き残るようなので、「正しく URL を解釈した場合にはホスト名が nginx にならず」「URL 全体を idn_to_ascii してしまったら、 nginx につながる URL になる」ような URL を渡すことになる。

全角文字をつけると、ピリオドで区切られた範囲で最初に xn-- が、最後に全角文字を変換した文字が入るので、 http://a:.b@あnginx/ などとするとxn-- は @ の前のパスワード部に、全角文字を変換したものは / のあとになるので、ホスト名をだますことまではできたが、 flag.php のピリオドが邪魔でなかなかうまくいかなかった。

試行錯誤の結果 http://a:hb.@¡nginx:80./flag.php が http://a:hb.xn--@nginx:80-qja./flag.php に変換させるところまではいけたが、 PHP の file_get_contents はポート番号は先頭が数字ならそこだけ解釈するものの、 5 文字以下でないと形式として認識しないようで、うまくいっていなかった。( ¡ は調べた限り idn_to_ascii が返還対象にする中では一番若い Unicode 番号を持っており、これ以上の短縮は厳しいように思えた)

これをチャットに貼ってしばらく悩んでいたところ、チームメンバーが http://a:.@✊nginx:80.:/flag.php なら通るということに気付き、フラグが得たようだ。変換後は http://a:.xn--@nginx:80-5s4f.:/flag.php となり、ポート番号らしき箇所が二つあるが、 PHP はそういうことは気にしないようだ。
1

SECCON{what_a_easy_bypass_314208thg0n423g}

Original writeup (https://blog.nhiroki.net/2019/10/20/seccon-2019-qual-write-up).