bugs_bunny ctf 2k17 Writeup

この大会は2017/7/30 4:00(JST)~2017/7/31 4:00(JST)に開催されました。
今回もチームで参戦。結果は2921点で420チーム中12位でした。
自分で解けた問題をWriteupとして書いておきます。

Not found (Misc 1)

IRCのページからログインしてみる。

[04:19] -ChanServ- [#bugsbunnyctf] Bugs_Bunny{Th1s_1s_0ur_fl4g_f0rm4t}
Bugs_Bunny{Th1s_1s_0ur_fl4g_f0rm4t}

Locked PDF (Misc 40)

pdfcrackでパスワードを解析する。
辞書ファイルは https://wiki.skullsecurity.org/index.php?title=Passwords にあるものを使う。

$ pdfcrack -f Patricia.pdf -w dict/rockyou.txt 

PDF version 1.7
Security Handler: Standard
V: 4
R: 4
P: -516
Length: 128
Encrypted Metadata: False
FileID: 104c940ca5734c71b2e5fa3b26414f5e
U: 857e6a5a82ab5bc8fc9f9d4dfe5ba381684523c72e38fdcf6ec068ae127dc499
O: 7337f340e3f15670a5a5bc1f8a217a0714538ebf23a1a4a15206a4c0c0adff51
Average Speed: 34524.2 w/s. Current Word: 'belky'
Average Speed: 34157.8 w/s. Current Word: 'puba23'
found user-password: 'g00skie'

見つかったパスワード g00skie でPDFを開くと、薄い字だがフラグが見つかった。

Bugs_Bunny{Pdf_Cr4Ck1nG_1s_Ea5y}

Crypto-15 (Crypto 15)

シーザー暗号と推定し、http://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherでフラグ部分を復号する。ROT1で復号できた。

Bugs_Bunny{C35aR_3NC0D3_4R3_N0T_S3CuR3_AT_4LL}

Crypto-20 (Crypto 20)

Brainf*ck言語。オンラインツール https://sange.fi/esoteric/brainfuck/impl/interp/i.html で実行すると、フラグが表示された。

Bugs_Bunny{Br41N_Fu**}

Scy way (Crypto 45)

タイトルからスキュタレー暗号を推測。http://www.dcode.fr/scytale-cipherブルートフォース
ターン数が2のときに、ISHOULDLEARNMORECIPHERとなり、英文として読める。

Bugs_Bunny{ISHOULDLEARNMORECIPHER}

Crypto-50 (Crypto 50)

かなり長いBase64文字列データ。Base64デコードを繰り返せば良さそう。

with open('enc.txt', 'r') as f:
    data = f.read()

for i in range(50):
    print 'Round %d' % (i+1)
    data = data.decode('base64')
    print data

36回デコードすると、フラグを得ることができた。

Bugs_Bunny{N0T_H4Rd_4T_4ll}

Baby RSA (Crypto 55)

nをfactordbで素因数分解する。

n = 2165121523231 * 9456131321351327

enc.txtの中身は複数行の数値。それぞれ復号した後、ASCIIコードとして文字に置き換える。

n = 20473673450356553867543177537
e = 17
p = 2165121523231
q = 9456131321351327

a = (p - 1) * (q - 1)

with open('enc.txt', 'r') as f:
    c_list = f.readlines()

flag = ''
for c_str in c_list:
    c = int(c_str.strip())

    x = 0
    while True:
        if (a * x + 1) % e == 0:
            d = (a * x + 1) / e
            break
        x = x + 1

    m = pow(c, d, n)
    flag += chr(m)

print flag
Bugs_Bunny{Baby_RSA_Its_Cool_Lik3_school_haHAha}

RSA2 (Crypto 80)

eが非常に大きいため、Wiener's attackで復号する。

from fractions import Fraction

def egcd(a, b):
    x,y, u,v = 0,1, 1,0
    while a != 0:
        q, r = b//a, b%a
        m, n = x-u*q, y-v*q
        b,a, x,y, u,v = a,r, u,v, m,n
        gcd = b
    return gcd, x, y

def decrypt(p, q, e, c):
    n = p * q
    phi = (p - 1) * (q - 1)
    gcd, a, b = egcd(e, phi)
    d = a
    pt = pow(c, d, n)
    return hex(pt)[2:-1].decode('hex')

def continued_fractions(n,e):
    cf = [0]
    while e != 0:
        cf.append(int(n/e))
        N = n
        n = e
        e = N%e
    return cf

def calcKD(cf):
    kd = list()
    for i in range(1,len(cf)+1):
        tmp = Fraction(0)
        for j in cf[1:i][::-1]:
            tmp = 1/(tmp+j)
        kd.append((tmp.numerator,tmp.denominator))
    return kd

def int_sqrt(n):
    def f(prev):
        while True:
            m = (prev + n/prev)/2
            if m >= prev:
                return prev
            prev = m
    return f(n)

def calcPQ(a,b):
    if a*a < 4*b or a < 0:
        return None
    c = int_sqrt(a*a-4*b)
    p = (a + c) /2
    q = (a - c) /2
    if p + q == a and p * q == b:
        return (p,q)
    else:
        return None

def wiener(n,e):
    kd = calcKD(continued_fractions(n,e))
    for (k,d) in kd:
        if k == 0:
            continue
        if (e*d-1) % k != 0:
            continue
        phin = (e*d-1) / k
        if phin >= n:
            continue
        ans = calcPQ(n-phin+1,n)
        if ans is None:
            continue
        return (ans[0],ans[1])

c = 0x217c8bf9b45601267624c3b1ba89ae93d04c8fae32dc15496262f36f48d06c0dc9e178a77b77a33708dcbe1fcd55ea9eb636fe5684c2f0f08df3389f47b36a128636671eba300491c829ed1e252b1bb4dbb3b93bc46d98a10bb5d55347752052ab45e143fd46799be1d06ac3ff7e8b1eb181dfbba8dfac3910202fd0b9a25befe
e = 266524484526673326121255015126836087453426858655909092116029065652649301962338744664679734617977550306567819672969837450223062478394149960243362563995235387971047857994699247277712682103161537347874310994510059329875060868679654080020041070975648626636209785889112656335054840517934593236597457100751820027783
n = 412460203584740978970185080155274765823237615982150661072746604041385717906706098256415230390148737678989448404730885157667896599397615737297545930957425615121654272472589331747646564634264520011009284080299605233265170506809736069720838542498970453928922703911186788239628906189362646418960560442406497717567

p, q = wiener(n, e)

flag = decrypt(p, q, e, c)
print flag
Bugs_Bunny{Baby_Its_Cool_Lik3_school_haHAha}

Nothing here (Web 5)

ソースのコメントにこう書いてある。

QnVnc19CdW5ueXs1MjljNDI5YWJkZTIxNzFkMGEyNTU4NDQ3MmFmODIxN30K

Base64デコードする。

$ echo QnVnc19CdW5ueXs1MjljNDI5YWJkZTIxNzFkMGEyNTU4NDQ3MmFmODIxN30K | base64 -d
Bugs_Bunny{529c429abde2171d0a25584472af8217}
Bugs_Bunny{529c429abde2171d0a25584472af8217}

Web100 (Web 100)

HTMLソースはスクリプトのみで、以下の通り。

<script type="text/javascript">
var generate = function(string) {

    function RT(lValue, iShiftBits) {
        return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
    }

    function AU(lX, lY) {
        var lX4, lY4, lX8, lY8, lResult;
        lX8 = (lX & 0x80000000);
        lY8 = (lY & 0x80000000);
        lX4 = (lX & 0x40000000);
        lY4 = (lY & 0x40000000);
        lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
        if (lX4 & lY4) {
            return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
        }
        if (lX4 | lY4) {
            if (lResult & 0x40000000) {
                return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
            } else {
                return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
            }
        } else {
            return (lResult ^ lX8 ^ lY8);
        }
    }
    function F(x, y, z) { return (x & y) | ((~x) & z); }
    function G(x, y, z) { return (x & z) | (y & (~z)); }
    function H(x, y, z) { return (x ^ y ^ z); }
    function I(x, y, z) { return (y ^ (x | (~z))); }

    function FF(a, b, c, d, x, s, ac) {
        a = AU(a, AU(AU(F(b, c, d), x), ac));
        return AU(RT(a, s), b);
    };

    function GG(a, b, c, d, x, s, ac) {
        a = AU(a, AU(AU(G(b, c, d), x), ac));
        return AU(RT(a, s), b);
    };

    function HH(a, b, c, d, x, s, ac) {
        a = AU(a, AU(AU(H(b, c, d), x), ac));
        return AU(RT(a, s), b);
    };

    function II(a, b, c, d, x, s, ac) {
        a = AU(a, AU(AU(I(b, c, d), x), ac));
        return AU(RT(a, s), b);
    };

    function CTWA(bytes) {
        var lWordCount;
        var lMessageLength = bytes.length;
        var lNumberOfWords_temp1 = lMessageLength + 8;
        var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
        var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
        var lWordArray = Array(lNumberOfWords - 1);
        var lBytePosition = 0;
        var lByteCount = 0;
        while (lByteCount < lMessageLength) {
            lWordCount = (lByteCount - (lByteCount % 4)) / 4;
            lBytePosition = (lByteCount % 4) * 8;
            lWordArray[lWordCount] = (lWordArray[lWordCount] | (bytes[lByteCount] << lBytePosition));
            lByteCount++;
        }
        lWordCount = (lByteCount - (lByteCount % 4)) / 4;
        lBytePosition = (lByteCount % 4) * 8;
        lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
        lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
        lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
        return lWordArray;
    };

    function WordToHex(lValue) {
        var WordToHexValue = "", WordToHexValue_temp = "", lByte, lCount;
        for (lCount = 0; lCount <= 3; lCount++) {
            lByte = (lValue >>> (lCount * 8)) & 255;
            WordToHexValue_temp = "0" + lByte.toString(16);
            WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2);
        }
        return WordToHexValue;
    };

    function Utf8Encode(string) {
        string = string.replace(/\r\n/g, "\n");
        var result = Array();

        for (var n = 0; n < string.length; n++) {

            var c = string.charCodeAt(n);

            if (c < 128) {
                result.push(c);
            }
            else if ((c > 127) && (c < 2048)) {
                result.push((c >> 6) | 192);
                result.push((c & 63) | 128);
            }
            else {
                result.push((c >> 12) | 224);
                result.push(((c >> 6) & 63) | 128);
                result.push((c & 63) | 128);
            }
        }
        return result;
    };

    var x = Array();
    var k, AA, BB, CC, DD, a, b, c, d;
    var S11 = 7, S12 = 12, S13 = 17, S14 = 22;
    var S21 = 5, S22 = 9, S23 = 14, S24 = 20;
    var S31 = 4, S32 = 11, S33 = 16, S34 = 23;
    var S41 = 6, S42 = 10, S43 = 15, S44 = 21;

    var bytes = Utf8Encode(string);
    x = CTWA(bytes);

    a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;

    for (k = 0; k < x.length; k += 16) {
        AA = a; BB = b; CC = c; DD = d;
        a = FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
        d = FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
        c = FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
        b = FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
        a = FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
        d = FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
        c = FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
        b = FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
        a = FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
        d = FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
        c = FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
        b = FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
        a = FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
        d = FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
        c = FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
        b = FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
        a = GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
        d = GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
        c = GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
        b = GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
        a = GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
        d = GG(d, a, b, c, x[k + 10], S22, 0x2441453);
        c = GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
        b = GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
        a = GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
        d = GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
        c = GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
        b = GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
        a = GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
        d = GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
        c = GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
        b = GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
        a = HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
        d = HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
        c = HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
        b = HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
        a = HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
        d = HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
        c = HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
        b = HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
        a = HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
        d = HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
        c = HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
        b = HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
        a = HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
        d = HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
        c = HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
        b = HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
        a = II(a, b, c, d, x[k + 0], S41, 0xF4292244);
        d = II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
        c = II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
        b = II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
        a = II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
        d = II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
        c = II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
        b = II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
        a = II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
        d = II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
        c = II(c, d, a, b, x[k + 6], S43, 0xA3014314);
        b = II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
        a = II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
        d = II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
        c = II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
        b = II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
        a = AU(a, AA);
        b = AU(b, BB);
        c = AU(c, CC);
        d = AU(d, DD);
    }

    var temp = WordToHex(a) + WordToHex(b) + WordToHex(c) + WordToHex(d);

    return temp.toLowerCase();
}
__seceret = '622b010e27e3f82d0f4e2e69a3785a395767c7a39599aea7114553448239eb41cab90bfecd4a8a0881d0a8128f27c483';
var _=__=___='';
for (var i = 0; i < __seceret.length; i+=3) {
   _+=__seceret[i+0]; 
   __+=__seceret[i+1];
   ___+=__seceret[i+2];
}
var h = prompt("Please enter your passowrd");
if(generate(h[11]+h[8]+h[1]+h[0]+h[9]+h[4]+h[13])==_&&generate(h[15]+h[10]+h[3]+h[5]+h[6])==__&&generate(h[16]+h[12]+h[14]+h[2]+h[7])==___){
    alert('your flag is Bugs_Bunny{'+h+'}');
}else{
    alert('I\'m sorry my son it\' not easy');
}
</script>

Chromeデベロッパーツールを使いながら、変数の値を確認する。

_ = "6b07fd4ea837c39e1542e1bbca01a224"
__ = "20ee80e63596799a1543bc9fd88d8878"
___ = "21232f297a57a5a743894a0e4a801fc3"

またgenerateの動作を確認する。

generate('a')
→"0cc175b9c0f1b6a831c399e269772661"
generate('b')
→"92eb5ffee6ae2fec3ad71c777531578f"
generate('c')
→"4a8a08f09d37b73795649038408b5f33"

この結果からgenerateはmd5を算出していると考えられる。フラグを表示させる条件を考えると、以下のようになっている必要がある。

h[11]+h[8]+h[1]+h[0]+h[9]+h[4]+h[13] = "6b07fd4ea837c39e1542e1bbca01a224" のMD5逆変換
h[15]+h[10]+h[3]+h[5]+h[6] = "20ee80e63596799a1543bc9fd88d8878" のMD5逆変換
h[16]+h[12]+h[14]+h[2]+h[7] = "21232f297a57a5a743894a0e4a801fc3" のMD5逆変換

つまり以下のようになる。

h[11]+h[8]+h[1]+h[0]+h[9]+h[4]+h[13] = 'tunisia'
h[15]+h[10]+h[3]+h[5]+h[6] = 'bunny'
h[16]+h[12]+h[14]+h[2]+h[7] = 'admin'

このことから h = 'inininynusutdamba' であることがわかる。

Bugs_Bunny{inininynusutdamba}

Stego10 (Steganography 10)

EXIFのCommentにフラグあり。

Bugs_Bunny{0258c4a75fc36076b41d02df8074372b}

For25 (Forensics 25)

xxdの情報が書いてあるので、次のコマンドでバイナリに戻す。

$ xxd -r hex > hex.zip

zip解凍するとhex.pngが展開され、フラグが書かれていた。
f:id:satou-y:20170801200957p:plain

Bugs_Bunny{Y0u_D1D_1T_W3ll}

UNKOWN file !! (Forensics 30)

pngファイルのバイナリが逆転しているので、逆にして画像にする。

with open('UNKOWN', 'rb') as f:
    data = f.read()

with open('flag.png', 'wb') as f:
    f.write(data[::-1])

結果のPNGファイルを見ると、フラグが逆に書いてある。
f:id:satou-y:20170801201335p:plain

Bugs_Bunny{E4Sy_T4Sk_F0R_H4X0r_L1KeS_Y0u}

Lost data (Forensics 50)

バイナリエディタでファイルを見てみると、flag.arjの文字列が含まれていることがわかる。このファイルはARJ形式の圧縮ファイルが壊れているものであると推定する。ARJ形式の圧縮ファイルは先頭2バイトが60 EAであると決められているので、先頭2バイトを 60 EA に変更する。この変更後の圧縮ファイルを解凍すると、flag.pngが入っていて、フラグが書かれていた。
f:id:satou-y:20170801201826p:plain

Bugs_Bunny{r3m3mb3r_4ll_w4ys_t0_ch3ck_h34d3r_f1l3}

Give me the Flag ! (Forensics 85)

旗の画像の中にQRコードの破片の画像が紛れ込んでいる。
QRコードを組み立て、読み込む。

== 34Sy_P4SSW0Rd_H4X0r ==

読み込んだ文字列をパスワードとして、flag.zipを解凍する。展開したReadmeの中に2進数8桁の文字列がスペース区切りでたくさん書いてある。これをASCIIコードとして読む。

with open('Readme', 'r') as f:
    data = f.read()

codes = data.split(' ')
flag = ''
for code in codes:
    if code != '':
        flag += chr(int(code, 2))

print flag
Bugs_Bunny{2b97263beb70d0f659bdb93cc5291d0a}

ZERO-ONE ! (Programation 45)

ZEROを0、ONEを1に変換し、2進数をASCIIコードとして読む。BASE64エンコード文字列になるので、デコードする。

with open('progTask.txt', 'r') as f:
    data = f.read()

data = data.replace('ZERO ', '0')
data = data.replace('ONE ', '1')

b64_flag = ''
for i in range(0, len(data), 8):
    b64_flag += chr(int(data[i:i+8], 2))

flag = b64_flag.decode('base64')
print flag
Bugs_Bunny{05fe8238cfee1e5f04b65339bea4fed2}

Capital (Programation 80)

$ nc 34.253.165.46 11223
Hi, do YOU love math ?!?!
Level 1.: Alaska
Juneau
Great, keep it up
Level 2.: x + 4 = 1119
1115
Great, keep it up

州都を答える問題か方程式を解く問題かどちからかが出題される。州都は対応表を作って、州に対応する州都を取得し、方程式は四則演算の左側がxである前提で計算し、答えていく。

import socket
import re

capitals={"Washington":"Olympia","Oregon":"Salem",\
    "California":"Sacramento","Ohio":"Columbus",\
    "Nebraska":"Lincoln","Colorado":"Denver",\
    "Michigan":"Lansing","Massachusetts":"Boston",\
    "Florida":"Tallahassee","Texas":"Austin",\
    "Oklahoma":"Oklahoma City","Hawaii":"Honolulu",\
    "Alaska":"Juneau","Utah":"Salt Lake City",\
    "New Mexico":"Santa Fe","North Dakota":"Bismarck",\
    "South Dakota":"Pierre","West Virginia":"Charleston",\
    "Virginia":"Richmond","New Jersey":"Trenton",\
    "Minnesota":"Saint Paul","Illinois":"Springfield",\
    "Indiana":"Indianapolis","Kentucky":"Frankfort",\
    "Tennessee":"Nashville","Georgia":"Atlanta",\
    "Alabama":"Montgomery","Mississippi":"Jackson",\
    "North Carolina":"Raleigh","South Carolina":"Columbia",\
    "Maine":"Augusta","Vermont":"Montpelier",\
    "New Hampshire":"Concord","Connecticut":"Hartford",\
    "Rhode Island":"Providence","Wyoming":"Cheyenne",\
    "Montana":"Helena","Kansas":"Topeka",\
    "Iowa":"Des Moines","Pennsylvania":"Harrisburg",\
    "Maryland":"Annapolis","Missouri":"Jefferson City",\
    "Arizona":"Phoenix","Nevada":"Carson City",\
    "New York":"Albany","Wisconsin":"Madison",\
    "Delaware":"Dover","Idaho":"Boise",\
    "Arkansas":"Little Rock","Louisiana":"Baton Rouge"
}

pattern = 'Level (.+)\.: (.+)'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('34.253.165.46', 11223))

data = s.recv(256)
print data

for i in range(1, 10000):
    data = s.recv(256)
    print data
    m = re.match(pattern, data)
    q = m.group(2)
    if capitals.has_key(q):
        ans = capitals[q]
    else:
        ope = q.split(' ')[1]
        val1 = int(q.split(' ')[2])
        val2 = int(q.split(' ')[4])
        if ope == '+':
            ans = str(val2 - val1)
        elif ope == '-':
            ans = str(val2 + val1)
        elif ope == '*':
            ans = str(val2 / val1)
        elif ope == '/':
            ans = str(val2 * val1)
        else:
            ans = ''
    print ans
    s.sendall(ans + '\n')
    data = s.recv(256)
    print data

500回正解すると、フラグが表示された。

Bugs_Bunny{M4TH_LO0k!_HarD_But_s0_EA5Y}