 暗号化スクリプトと、暗号文がおいてある。鍵も置いてあるので、読みながら順番に元に戻すコードを書いて復元する。
import sysfrom Crypto.Cipher import AESimport 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}