Pragyan CTF 2018 Writeup

この大会は2018/3/2 16:30(JST)~2018/3/4 16:30(JST)に開催されました。
今回もチームで参戦。結果は2800点で606チーム中11位でした。
自分で解けた問題をWriteupとして書いておきます。

Improper encryption (Crypto 100)

M1に対する平文の中の sAldnJfpUGlciN の位置、アルファベットだけでXOR暗号の復号できる場所を探す。

import string

M1 = '2d142303073d05392c3d3e273c2a1a211f082b280d2d0e332025380301352a122a151c3a342e362d2723171904011c0c0c292b3d0122063e2e1e2a08102a2d3d0b2e102123141c280f0c373d2b380d3d0301301f2d281935233239330f3a102b123b0d2a'
M2 = '29190f1b18390707093a290b2d0b113f37332533333a0c3d1a29160a2f100a1d0034323b1b2e1225252500182531391d13260c21211019242d0a0a123b362d232d3a3a0a083c0e363c183a032b332d252c5637252d047b522a1e462a2a2909081705033d'
M3 = '15350a23053f04272a2f12113a2137202a3022081616090c32162b0b32333413161725072508391f3c36192e1e1e37030b361a2a26163337130628020b34352d1a2e143d3f0a1b1d13012a19223c2b1f0c172b3406033808061d1a2306133011080e1839'

m1 = M1.decode('hex')
m2 = M2.decode('hex')
m3 = M3.decode('hex')

pw = 'sAldnJfpUGlciN'

def isAlp(s):
    for c in s:
        if c not in string.letters:
            return False
    return True

def decrypt(s, key):
    dec = ''
    for i in range(len(s)):
        code = ord(s[i]) ^ ord(key[i%len(key)])
        dec += chr(code)
    return dec

keys = []
for i in range(len(m1) - len(pw) + 1):
    key = ''
    shift = i % len(pw)
    for j in range(len(pw)):
        code = ord(m1[i+j]) ^ ord(pw[j])
        key += chr(code)
    if isAlp(key):
        if shift == 0:
            keys.append(key)
        else:
            keys.append(key[-shift:] + key[:len(pw)-shift])

for key in keys:
    r1 = decrypt(m1, key)
    r2 = decrypt(m2, key)
    r3 = decrypt(m3, key)
    idx = r1.find(pw)
    if idx % 14 == 0:
        print '*** %s ***' % key
        print r1
        print r2
        print r3

実行すると、ある71-84バイト部分が復号できる。

*** oichYwMHXzobYQ ***
B}@k^JHqtGQEe{uH|`r_@eVIOGaRn\IzsbQrlTYO~rxpgiE{AasGn@_oAwI`I]`uSTCzEsAldnJfpUGlciNBAz]zEt{W@IKjbC
FplsANJOQ@FitZ~VT[|D~rTGuKO[@yiuYCsCT}G|toqFY`j^nT[Nr@uBcizbA`ku@UhQma__pctf{u_C4ntBm:sibrSfjNTlT3
z\iK\HIorU}scpXIIX{[^Qv]trZ]ZW{O`hO}rV}egvG}vntF~BPItjf|oKjRCxeBT{_f[ttpisnotsecureij[`_jWk^i_sQ_wP

71-84バイト部分はそれぞれ以下のようになっている。
M1: sAldnJfpUGlciN
M2: a__pctf{u_C4nt
M3: ttpisnotsecure

M3の一部の平文14バイトは繰り返すことを考慮する。
85-98バイト部分もM3の復号が ttpisnotsecure になるようM2も復号する。

import string

M1 = '2d142303073d05392c3d3e273c2a1a211f082b280d2d0e332025380301352a122a151c3a342e362d2723171904011c0c0c292b3d0122063e2e1e2a08102a2d3d0b2e102123141c280f0c373d2b380d3d0301301f2d281935233239330f3a102b123b0d2a'
M2 = '29190f1b18390707093a290b2d0b113f37332533333a0c3d1a29160a2f100a1d0034323b1b2e1225252500182531391d13260c21211019242d0a0a123b362d232d3a3a0a083c0e363c183a032b332d252c5637252d047b522a1e462a2a2909081705033d'
M3 = '15350a23053f04272a2f12113a2137202a3022081616090c32162b0b32333413161725072508391f3c36192e1e1e37030b361a2a26163337130628020b34352d1a2e143d3f0a1b1d13012a19223c2b1f0c172b3406033808061d1a2306133011080e1839'

m1 = M1.decode('hex')
m2 = M2.decode('hex')
m3 = M3.decode('hex')

r3_parts = 'ttpisnotsecure'

def xor_str(s1, s2):
    key = ''
    for i in range(len(s1)):
        code = ord(s1[i]) ^ ord(s2[i])
        key += chr(code)
    return key

key1 = xor_str(r3_parts, m3[70:84])
key2 = xor_str(r3_parts, m3[84:98])

m_part1 = [m1[70:84],  m2[70:84], m3[70:84]]
r_part1 = []
for i in range(3):
    r_part1.append(xor_str(m_part1[i], key1))

m_part2 = [m1[84:98],  m2[84:98], m3[84:98]]
r_part2 = []
for i in range(3):
    r_part2.append(xor_str(m_part2[i], key2))

for i in range(3):
    print r_part1[i] + r_part2[i]

実行結果は以下の通り。

  :
sAldnJfpUGlciN__QTVALdzLCOhP
a__pctf{u_C4nt_s33_m3}__Zlmn
ttpisnotsecurettpisnotsecure
pctf{u_C4nt_s33_m3}

Quite an EC task (Crypto 200)

楕円曲線暗号なので、以下が成り立つ。
>
y^2 = x^3 + a*x + b mod n
→b = y^2 - x^3 - a*x mod n

この場合は以下の文字になるので、まずはBを求める。

a -> A
b -> B
n -> M
M = 93556643250795678718734474880013829509320385402690660619699653921022012489089
A = 66001598144012865876674115570268990806314506711104521036747533612798434904785
P = (56027910981442853390816693056740903416379421186644480759538594137486160388926, 65533262933617146434438829354623658858649726233622196512439589744498050226926)

x = P[0]
y = P[1]
B = (pow(y, 2) - pow(x, 3) - A * x) % M
print 'B =', B

実行すると、Bの値がわかる。

B = 25255205054024371783896605039267101837972419055969636393425590261926131199030

Bの値はわかったので、Pohlig–Hellman algorithmを使って、nを求める。

# solve.sage
M = 93556643250795678718734474880013829509320385402690660619699653921022012489089
A = 66001598144012865876674115570268990806314506711104521036747533612798434904785
B = 25255205054024371783896605039267101837972419055969636393425590261926131199030
P = (56027910981442853390816693056740903416379421186644480759538594137486160388926, 65533262933617146434438829354623658858649726233622196512439589744498050226926)
Q = (61124499720410964164289905006830679547191538609778446060514645905829507254103, 2595146854028317060979753545310334521407008629091560515441729386088057610440)

F = FiniteField(M)
E = EllipticCurve(F, [A,B])
P = E.point(P)
Q = E.point(Q)
factors, exponents = zip(*factor(E.order()))
primes = [factors[i] ^ exponents[i] for i in range(len(factors))][:-2]
dlogs = []
for fac in primes:
    t = int(P.order()) / int(fac)
    dlog = discrete_log(t*Q, t*P, operation='+')
    dlogs += [dlog]

n = crt(dlogs,primes)
print 'n =', n

実行した結果、nがわかった。

152977126447386808276536247114

Rivest, Shamir and Aldeman’s quest (Crypto 200)

$ nc 128.199.224.175 34000
Enter encrypted message (in HEX) :- 10

Decrypted message :- 
In HEX :- 
5954815972c69c0428f17ae1aeb74c32f2c23d075b9958718cf5169ab359fedd1c9af4fbdf73fe52155bcdca461db65a2685628f079c55d96f6cd13e9ccb87e87f9183ca8a032fe4b0bfe4d120d7f5824bba874da9a31df1a638124bbd10b7e7a543ca547a0af84c6a78fc3c77da25a16e37a957d2c2c211610adb75033b00b7

In ASCII :- 
The decrypted message is not a valid ASCII text.

$ nc 128.199.224.175 34000
Enter encrypted message (in HEX) :- 1000

Decrypted message :- 
In HEX :- 
5954815972c69c0428f17ae1aeb74c32f2c23d075b9958718cf5169ab359fedd1c9af4fbdf73fe52155bcdca461db65a2685628f079c55d96f6cd13e9ccb87e87f9183ca8a032fe4b0bfe4d120d7f5824bba874da9a31df1a638124bbd10b7e7a543ca547a0af84c6a78fc3c77da25a16e37a957d2c2c211610adb75033b00b7

In ASCII :- 
The decrypted message is not a valid ASCII text.

面倒なことに入力、出力のHEXコードはリトルエンディアン。このことに注意しながら、LSB decryption oracle attackで復号する。

from fractions import Fraction
import socket
import re

def i2hex(i):
    h = '%0256x' % i
    h = h[::-1]
    rev_i = ''
    for j in range(0, len(h), 2):
        s = h[j+1] + h[j]
        rev_i += s
    return rev_i

def hex2i(h):
    h = h[::-1]
    rev_i = ''
    for j in range(0, len(h), 2):
        s = h[j+1] + h[j]
        rev_i += s
    return int(rev_i, 16)

def lsb_oracle(cipher):
    cipher = i2hex(cipher)
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('128.199.224.175', 34000))
    data = s.recv(512)
    #print data + cipher
    s.sendall(cipher + '\n')
    data = s.recv(512)
    #print data
    pattern = 'In HEX :- \n(.+)\n\nIn'
    m = re.search(pattern, data, re.DOTALL)
    p = hex2i(m.group(1).strip())
    return p % 2

c = hex2i('a0b75ef5dfd9ab0de9aa8c36a9c5aa28a924128a62826740ac3986efac69e2b85fc0df803e80da04c5f803726689e5f3134178de3cb203f6aebca22b376f7205d93a7224aca82cbbdc382200a1749fee095dfebe2aaabf99b622e4343bf5423cf6527433e26273e67d576157bf65a9258f613be9ad88d7b50350a89e676ae462')
n = 0x00b961cd4db6cca2c5ec44d1e669e52b850574a5575c093aa740d223a7b42a48ed3d478ac3e910c793d29fda13f23cecd00bd0acbdcdb70ab1f6d5e9821b85153f3981f207cf5fa20fcdf5e4e432b1d3fbb3b012d7d270400d5c67c99abaeb2ff3c08e5b29c807b1243a297387ff06443c0977dbf2f284a948d45c1696eba459bf
e = 65537

bounds = [0, Fraction(n)]

i = 0
m = 0
while True:
    print i
    i += 1

    c2 = (c * pow(2, e, n)) % n
    lsb = lsb_oracle(c2)
    if lsb == 1:
        bounds[0] = sum(bounds)/2
    else:
        bounds[1] = sum(bounds)/2
    diff = bounds[1] - bounds[0]
    diff = diff.numerator / diff.denominator
    print diff
    if diff == 0:
        m = bounds[1].numerator / bounds[1].denominator
        break
    c = c2

flag = i2hex(m).decode('hex')
print flag
pctf{13xtb0Ok^Rs4+is`vUln3r4b1e,p4D~i1}

NeverLAN CTF 2018 Writeup

この大会は2018/2/24 4:00(JST)~2018/2/27 9:00(JST)に開催されました。
今回もチームで参戦。結果は6973点で816チーム中15位でした。
自分で解けた問題をWriteupとして書いておきます。

basic math (Scripting 10)

合計値を求めるだけ。

with open('some_numbers.txt', 'r') as f:
    lines = f.readlines()

sum = 0
for line in lines:
    sum += int(line.strip())

print sum
49562942146280612

more basic math (Scripting 50)

合計値を求めるだけ。

with open('some_more_numbers.txt', 'r') as f:
    lines = f.readlines()

sum = 0
for line in lines:
    sum += int(line.strip())

print sum
50123971501856573397

even more basic math with some junk (Scripting 100)

数値以外が入ってくることがあることに気を付け、合計値を出す。

with open('even_more_numbers_with_some_mild_inconveniences.txt', 'r') as f:
    lines = f.readlines()

sum = 0
for line in lines:
    line = line.strip().replace(',', ' ')
    nums = line.split(' ')
    for num in nums:
        if num.isdigit():
            sum += int(num)

print sum
34659711530484678082

Encoding != Hashing (Passwords 100)

NetworkMinorでpcapファイルを開くと、Credentialsタブでフラグを確認できる。
f:id:satou-y:20180304193022p:plain

flag{help-me-obiwan}

Zip Attack (Passwords 100)

添付ファイルの構成から明らかにZIPの既知平文攻撃で解ける問題。

$ zipinfo encrypted.zip 
Archive:  encrypted.zip   186312 bytes   3 files
drwxr-xr-x  3.0 unx        0 bx stor 23-Feb-18 10:53 supersecretstuff/
-rw-r--r--  3.0 unx       34 TX stor 23-Feb-18 10:53 supersecretstuff/flag.txt
-rw-r--r--  3.0 unx   220680 BX defX 23-Feb-18 10:48 supersecretstuff/sw-iphone-wallpaper-first-order.jpg
3 files, 220714 bytes uncompressed, 185662 bytes compressed:  15.9%
$ unzip known-file.zip 
Archive:  known-file.zip
  inflating: sw-iphone-wallpaper-first-order.jpg
$ ./pkcrack-1.2.2/src/pkcrack -C encrypted.zip -c supersecretstuff/sw-iphone-wallpaper-first-order.jpg -p sw-iphone-wallpaper-first-order.jpg -P known-file.zip -d decrypted.zip
Files read. Starting stage 1 on Sat Feb 24 11:10:01 2018
Generating 1st generation of possible key2_185639 values...done.
Found 4194304 possible key2-values.
Now we're trying to reduce these...
                          :
Finished on Sat Feb 24 11:10:36 2018
$ unzip decrypted.zip
Archive:  decrypted.zip
   creating: supersecretstuff/
 extracting: supersecretstuff/flag.txt  
  inflating: supersecretstuff/sw-iphone-wallpaper-first-order.jpg  
$ cat supersecretstuff/flag.txt
flag{plaintext-attacks-are-cool!}
flag{plaintext-attacks-are-cool!}

The WIFI Network (Passwords 200)

WIFIのキーを見つける問題。

$ aircrack-ng -w dict/rockyou.txt neverlan.cap 
Opening neverlan.cap
Read 15 packets.

   #  BSSID              ESSID                     Encryption

   1  C6:3D:C7:A2:18:A4  neverlan                  WPA (1 handshake)

Choosing first network as target.

Opening neverlan.cap
Reading packets, please wait...

                                 Aircrack-ng 1.1


                   [01:49:35] 3065940 keys tested (783.26 k/s)


                           KEY FOUND! [ obiwan17 ]


      Master Key     : A9 9B 5F 42 6D 12 57 4E 72 86 3C 47 15 C1 49 1F 
                       FD EB 45 47 EB 2C 6C 37 F0 10 B2 BC 93 21 76 45 

      Transient Key  : 4F E2 99 2C F5 05 67 28 D7 AD 50 58 C6 54 69 E1 
                       33 96 6D 92 5B 65 13 80 91 37 CB 4D 6A 5E 45 4A 
                       78 36 F8 6A 1C 89 30 86 1F D9 D4 5C 57 22 29 F1 
                       53 CA 97 3E FA 8C D6 A7 96 AB DA F9 7D 36 DD 8B 

      EAPOL HMAC     : 7F E2 C8 CE E1 4D 48 05 D4 D7 97 1B C8 C1 3A 86
obiwan17

Neo's Recon (Recon 200)

NeverLAN Neoで検索すると、https://neverlanctf.com/Neoというページがある。
ソースを見ると、右下の位置にsubmitのinputがあり、hidden属性もある。

<form method="post" action="ZD.php">
	<input type="submit" name="submit" value="submit" class="alt" style="position:fixed;bottom:0;right:0;padding:0;z-index:99999;background:transparent;">
	<input type="hidden" name="riddle" maxlength="3" minlength="3" value="">
	<!-- 
		Finding this form is half the challenge. Now can you solve the riddle?
		Edit the input field above to solve the challenge.
			type:text; maxlength: 3; minlength: 3;  
			solve -> riddle: To grow your mental perception and increase your cognizance.  Never let him stop growing. Never let him die; 
	-->
</form>

mental perception cognizance で検索すると ken というあやしい人名がある。

$ curl https://www.neverlanctf.com/ZD.php -d 'riddle=ken'
<!DOCTYPE html>
<html lang="en" itemscope itemtype="http://schema.org/WebPage">
    <head>
        <meta name="robots" content="noindex, nofollow">
        <meta name="Author" content="Neo">
    </head>
    <body>
        <style>body{text-align:center;}</style>
        <h1>Congratulations, You Solved It</h1>
        <p><!--Throughout this event you may have noticed (or didn't notice) that we occationally personified the word ken to symbolize the strive for education.<br/>--> He[Ken] is used as a representation of the constant growth of mental perception. His friendship is your search for knowledge and his death your final attempt to reach new boundries.<br/> To never let Ken die you must never stop trying to learn, never give up on knowledge.<br/> Finding Ken is to find the joy in education. Learn to love learning and your ken will never stop growing.<br/> This event was created to help those who have not yet found their ken realize how important knowledge is.<br/>Even if computer programming or scripting is not your thing, I would hope you continue your search to find something you love to learn about.</p>
        <br/>
        <br/>
        <br/>
        <p>Ken, as described by dictionary.com, is "knowledge, understanding, or cognizance; mental perception"</p>
        <h5>ok, here's you flag{dat_ken_though}</h5>
        <!--<h5>For more fun, try the challenges on my cohorts' pages, you might even learn some more about them</h5>-->
    </body>
</html>
flag{dat_ken_though}

Purvesta's Recon (Recon 200)

rap songsのことが書いてある。SoundCloudを調べてみると、https://soundcloud.com/purvesta がある。
さらにhttps://soundcloud.com/purvesta/the_gettysburg_address_rapを見てみると、Base64文字列らしきものがある。
f:id:satou-y:20180304194818p:plain

$ echo Wm14aFozdE5NWGhVUUhCbFh6RnVYM1JvTTE5dFFHc3hibWMvZlE9PQ== | base64 -d
ZmxhZ3tNMXhUQHBlXzFuX3RoM19tQGsxbmc/fQ==
$ echo ZmxhZ3tNMXhUQHBlXzFuX3RoM19tQGsxbmc/fQ== | base64 -d
flag{M1xT@pe_1n_th3_m@k1ng?}
flag{M1xT@pe_1n_th3_m@k1ng?}

Zesty's challenge (Recon 200)

https://live.neverlanctf.com/robots.txtにアクセスする。

user-agent: *
disallow: zestyschallenge.php

https://live.neverlanctf.com/zestyschallenge.phpにアクセスする。それからそのページにあるリンクhttps://www.youtube.com/watch?v=dhebl9oD5Lcにアクセスしてみる。flagで検索すると、フラグが見つかった。
f:id:satou-y:20180304195316p:plain

flag{10684524746ba936b43a82d84385dcf5}

Story Time! (Cryptography 200)

一文字ずつ半角英字に置き換える。quipqiupで調整しながら、復号する。

CAPTAI KIDD USED THE GOLD BUG CIPHER TO HIDE THE LOCATIO OF HIS TREASURE I A STORY WRITTE BY EDGAR ALLA POE I FLAG IS PIRATESANDDAGGERS

なぜか英文としてはNが抜けている箇所があるが、フラグはわかった。

PIRATESANDDAGGERS

Picture Words (Cryptography 200)

イメージの換字式暗号。各イメージをアルファベットに置き換える。

ABCDEFGHID
JKLFMGCNEO
IJPQQBCLFF
QEIDEQCPOF
RSTEQBPUVI
DJPQQBCGCP
HCERGPJIEK
IUQTLCEAFL
QBPQBFTEPD
OAFLOE

一列にしてquipqiupにかける。復号結果は以下の通り。

WHEN SOLVING PROBLEMS DIG AT THE ROOTS INSTEAD OF JUST HACKING AT THE LEAVES FLAG IS PICTURES WORTH A THOUSAND WORDS
PICTURESWORTHATHOUSANDWORDS

How much can you throw on a Caesar salad? (Cryptography 300)

steghideによる秘匿を疑い、適当にパスフレーズを入れる。パスフレーズはneverlanctf。

$ steghide extract -sf O_SO_Curious.jpeg -p neverlanctf
wrote extracted data to "WhyS0CuR1o5.txt".

抽出したファイルのデータをBase64デコードする。次に2進表記のコードになるので、ASCIIコードとして読む。Ceaser暗号とみて、7シフトする。

import string

def rot7(s):
    str = ''
    for i in range(len(s)):
        if s[i] not in string.letters:
            str += s[i]
        else:
            code = ord(s[i]) - 7
            if s[i] in string.uppercase:
                if code < ord('A'):
                    code += 26
            else:
                if code < ord('a'):
                    code += 26
            str += chr(code)
    return str

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

code_str = data.decode('base64')
codes = code_str.split(' ')

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

flag = rot7(dec)
print flag

この結果、以下のメッセージになる。

Experience is the Teacher of all things

これを言ったのはJulius Caesar。どうやらこの名前をラテン語表記にして答える必要があるらしい。
https://en.wikipedia.org/wiki/Gaius_Julius_Caesar_(name)で調べる。

flag{GAIUS_JULIUS_CAESAR}×
flag{GAIVS_IVLIVS_CAESAR}×
flag{GAIUS_IULIUS_CAESAR}×

なかなかフラグが通らない。
「Julius Caesar's name」の節の最後の段落にこう書いてある。

The name of the dictator Julius Caesar—Latin script: CAIVS IVLIVS CAESAR—was often extended by the official filiation Gai filius ("son of Gaius"), rendered as Gaius Iulius Gai filius Caesar.
flag{CAIVS_IVLIVS_CAESAR}

That's a big file (Cryptography 300)

巨大なBase64文字列。ひたすらデコードする。

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

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

print data

59回デコードすると、フラグが得られた。

flag{persistant_are_we?}

TAMUctf 18 Writeup

この大会は2018/2/17 9:00(JST)~2018/2/26 9:00(JST)に開催されました。
今回もチームで参戦。結果は6284点で1272チーム中11位でした。
自分で解けた問題をWriteupとして書いておきます。

XORbytes (Crypto 100)

バイナリの途中にある「QB1g3l4B5uzPjjD4」がXORキーと推定。復号してみる。

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

key = 'QB1g3l4B5uzPjjD4'

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

with open('out', 'wb') as f:
    f.write(dec)

復号するとELFファイルになる。

$ ./out
Congrats! You found the flag!
GigEm{NibblerEatsNibbles}
GigEm{NibblerEatsNibbles}

00_intrusion (Scenario - NotSoAwesomeInc 25)

このカテゴリはいろんなシナリオに沿って、ログなどから解析し、答えていく。
1.systeminfo.txtのOS Nameからわかる。

windows

2.1.と同様

2008

3.systeminfo.txtのOS Versionからわかる。

sp1

4.ipconfig.txtのHost Nameからわかる。

www

5.ipconfig.txtのPrimary Dns Suffixからわかる。

yes

6.5.と同様

awesomeinc.local

7.ipconfig.txtのリストからわかる。

10.0.0.11,192.168.1.11

8.nmap.txtの情報からわかる。

3389,80

9.netsh_allprofiles.txtのFirewall Policyからわかる。

yes

10.9.と同様

no

01_logs (Scenario - NotSoAwesomeInc 50)

Excelでスペース区切りでフィルタしながら解く。
1.IPアドレスの種類は10.0.0.xでxは以下で全部。

136,138,36,37,39,42,43,45,85,87,89

2.IPアドレスでフィルタし、UserAgentの種類が2個以上のもの。

10.0.0.136

3.favicon.icorobots.txt以外へのアクセスで、HTTPステータスコードが200以外のもの。

10.0.0.136

4.アクセス先をソートするとわかりやすい。最上位のディレクトリまたはファイルは以下の通り。

.git,.src,.svn,favicon.ico,git,logo.png,robots.txt,src,svn

02_analysis (Scenario - NotSoAwesomeInc 75)

gitの解析。
1.wwwroot/.git/logs/HEADを見る。

7

2.wwwroot/.git/logs/HEADを見ても、Bob Robertsがcommitしているから、yesのような気がするが、noで通った。理由がわからない。

no

3.wwwroot/.git/logs/HEADを見ると、以下のログがある。

94071fee6ea1707480f5e202442e60 Bob Roberts  1511560992 -0900	commit: Removing creds to make security "people" happy...
yes

4.3.で見たログからcredentialsがあったことがわかる。

yes

5.3.で見たログからはssh keysは見つからない。

null

6.wwwroot/.git/logs/HEADの内容から確認する。

[wwwroot/.git/logs/HEADの先頭]
0000000000000000000000000000000000000000 f34f90c6da1d9d3e6292c987e728b0a6b05021b5 Bob Roberts  1511560279 -0900	commit (initial): Added under construction page and creds for the box...no one is going to look at this private repo...

[wwwroot/.git/objects/f3/4f90c6da1d9d3e6292c987e728b0a6b05021b5 の解析]
$ python -c 'import zlib; print zlib.decompress(open("4f90c6da1d9d3e6292c987e728b0a6b05021b5").read())'
commit 278tree 926f5c6f981775aa52351b7d65d2e05ee5da4f55
author Mike Moss  1511560279 -0900
committer Mike Moss  1511560279 -0900

Added under construction page and creds for the box...no one is going to look at this private repo...

[wwwroot/.git/objects/92/6f5c6f981775aa52351b7d65d2e05ee5da4f55 の解析]
$ python -c 'import zlib; print zlib.decompress(open("6f5c6f981775aa52351b7d65d2e05ee5da4f55").read())' | xxd -g 1
0000000: 74 72 65 65 20 37 31 00 31 30 30 36 34 34 20 63  tree 71.100644 c
0000010: 72 65 64 73 00 a4 cb 02 11 70 68 4c 4b 8b 99 30  reds.....phLK..0
0000020: 05 99 ea e0 4d ce 76 5a ef 31 30 30 36 34 34 20  ....M.vZ.100644 
0000030: 69 6e 64 65 78 2e 68 74 6d 6c 00 82 4a 38 37 3c  index.html..J87<
0000040: b7 c2 8c 2f 32 59 8a 2f 1b b3 2d c5 29 7d e4 0a  .../2Y./..-.)}..

[wwwroot/.git/objects/a4/cb021170684c4b8b99300599eae04dce765aef の解析]
$ python -c 'import zlib; print zlib.decompress(open("cb021170684c4b8b99300599eae04dce765aef").read())'
blob 71username: webdev
password: 5aA789842c89b6650$6b6a5602Cb^8717bf6c7e47D!
webdev:5aA789842c89b6650$6b6a5602Cb^8717bf6c7e47D!

03_forensics (Scenario - NotSoAwesomeInc 100)

イベントログの解析。
1.securityのイベントログを見ても、11/26/2017-01:30:00以降のエラーレベルの情報にログインに関するものはない。

yes

2.フィルターでイベントID:4624(ログオン)として、時刻でソートする。以下の時刻でログインしている。

11/26/2017-01:31:11

3.2.で見つかったログにIPアドレスが記載されている。

10.0.0.136

05_backdoor(Scenario - NotSoAwesomeInc 150)

netstatの情報が与えられている。
1.以下のデータがある。

  TCP    10.0.0.11:49196        10.0.0.136:4444        ESTABLISHED     1008
 [rs[1].exe]
  TCP    10.0.0.11:49197        10.0.0.136:4444        ESTABLISHED     2940
 [YwmkM.exe]
  TCP    10.0.0.11:49198        10.0.0.136:4444        ESTABLISHED     1900
 [notepad.exe]
  TCP    10.0.0.11:49199        10.0.0.136:4444        ESTABLISHED     2088
 [scvhost.exe]

このことから以下が答え。

outbound

2.1で確認した内容から以下が答え。

10.0.0.136

3.1で確認した内容から以下が答え。

4444

4.1で確認した内容から以下が答え。

YwmkM.exe,notepad.exe,rs[1].exe,scvhost.exe

5.1で確認した内容から以下が答え。

1008,1900,2088,2940

06_persistence (Scenario - NotSoAwesomeInc 175)

レジストリファイルやタスク情報が与えられている。
1.RunAsなどに関係するファイル名はない。

no

2.1.で確認できなかった。

null

3.svchost.exeがスケジュールにある。

yes

4.svchost.exeのタスク名は以下の通り。

SysCheck2

5.svchost.exeのフルパスは以下の通り。

c:\windows\system32\scvhost.exe

6.svchost.exeのRun As Userは以下の通り。

www\administrator

Xiomara 2018 Writeup

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

Giveaway (Cryptography 150)

eが小さいのでcのe乗根で復号する。

import gmpy

e = 3
c = 2039130155866184490894181588949291569587424373754875837330412835527276040280846677481047284126316137541961805207979583672570357348995401556991229785828117383170279052532972654304372432603436204862621797

m = gmpy.root(c, e)[0]
flag = ('%x' % m).decode('hex')
print flag
xiomara{4y3_4y3_cryp70_6uy!}

He Moron (Cryptography 200)

AES ECBモードで、16バイトごろにmb配列にセット。

平文1ブロック目暗号化
平文2ブロック目と暗号1ブロック目とのxorを暗号化
:
CBC-MACと同様

2つの異なる平文から同じ値のCBC-MACが得られれば良さそう。

[平文1ブロック目]                       --(暗号化)--> [暗号文1ブロック目]
[平文2ブロック目] ^ [暗号文1ブロック目] --(暗号化)--> [暗号文2ブロック目]
[平文3ブロック目] ^ [暗号文2ブロック目] --(暗号化)--> [暗号文3ブロック目]
・平文16バイト(P1)の暗号(C1)
・平文16バイト(P1)+(C1)と平文16バイト(P1)のXOR

上記の2つは同じになることを使って、プログラムにする。

import socket

def hex_xor(h1, h2):
    i_xor = int(h1, 16) ^ int(h2, 16)
    h_xor = '%032x' % i_xor
    return h_xor

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('103.5.112.91', 8912))

data = s.recv(256)
print data + '0'
s.sendall('0\n')
data = s.recv(256)
print data

plain1 = '1234567890abcdef1234567890abcdef'
print plain1
s.sendall(plain1 + '\n')
data = s.recv(256)
print data

cipher1 = data.split('\n')[2].strip()
plain2 = plain1 + hex_xor(cipher1, plain1)
s.sendall('\n')
data = s.recv(256)
print data

print plain1
s.sendall(plain1 + '\n')
data = s.recv(256)
print data

print plain2
s.sendall(plain2 + '\n')
data = s.recv(256)
print data
xiomara{1_b0w_d0wn_70_y0u!}

xiomara captcha (Misc 100)

$ nc 103.5.112.91 1340
  Human or Not??
We are giving you 20 base64 encodings of jpg images.
You just need to tell me if it is a human or not.
print 1 if there is a human and 0 if there is no human!

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

写真に写っているのが人なら1と答え、それ以外なら0と答える必要がある。
写真から人かどうか機械的に判別をするのが難しそうなので、
サンプル写真をできるだけ集め、その画像ファイルのハッシュ値と比較する方式をとることにする。
サンプルは何回か集めた結果、人と判別できるものが50種類あった。それを考慮して、プログラムにする。

import socket
import hashlib
import time

human_jpg_md5_list = [
'6863a9ade7e58f52c0747027b7c8d392',
'4afd4607850d0d2d267b4b547a8435e3',
'4d82e61e128b9ff9f993849fbd74c46d',
'a8393bfd69739a97fad157bf61d415b5',
'9a2a8533ce34336176e4140eba8fd381',
'6f84674ba94d0c8ab6010f7c62b35884',
'25cc0428cb8416d080f1a9fc18ccdb81',
'd72c0dfafd095139487500b629385299',
'6934fda5b2d1ff9f45e6240f2997265f',
'2e63782bafbea49855c774a739e238f3',
'e695646ffc9144b0f51d9092e10e9436',
'dffc3c055ee12724e01d752359c0bf09',
'df5e7dd4c3740fa50645611edb9fdbee',
'b19f7bbd9b3678f5ebb6c37211f5732c',
'72ca08509d53891eb99edcb2ac622fe2',
'20f402e662572b0a9c6420be7ee3e1e4',
'eb2a2f10d931e1cf3ad1ba0ab733853c',
'b38361beaab4684d478e2348716f9457',
'f010ccdec6c9b6e45893788023f692e6',
'42dbfdf829012d70c3ffd9a03ccceeea',
'a76b155a44af0d5583aa2097d9f3905c',
'1908d32ff5909ec1cf2d2cfe7a9cf516',
'6d8bd3db81c6d625fc56ce566fe42ef3',
'08870773d7240eb0b7498f0c49f0031e',
'f407d8193250e410640e97a7299d8409',
'167e3e339ef92beb526fa8cd15b9f5a6',
'7dcc93d5c4b71b47dde1868e21fc4da5',
'f24917dee99fedbf06f9887a399c42d0',
'cb528cb595c64c5fd17c7b93a822478e',
'55f1c073b1352dbb1e2c19a5ae8d5261',
'e24596a53799d4aab69844035bfd6a15',
'c559f9245e64688875182e12ac0c04c7',
'1ae5d052d9d4a0b346048858b2a71ba9',
'402e5ff5a56aa21f1d59981bc560f304',
'9f34362cf2f6be3437d4bbb8c45298e7',
'50c8fb9ef41ff37629fa08421d81add7',
'c0937d200114e99a4ecb4545eeb3b876',
'17994c526245fcd88e36661a1631fcfe',
'9635780e05a85d509aae0fb3224e3b32',
'fa38da5b696d754a1309d1ca5fe14a61',
'17ef7dc609a3b616710545e96420dd62',
'49f4a9d0cee09f11abae0d87ad22c472',
'61fad7f415af7e9894c3cab32c5bfab2',
'e40d00cfff549edf8fb8da014e642a20',
'daae5ec44284d5f887229c8680a2ac9c',
'7bd219ec3bca257e52aee398fbef8714',
'4972d90fd3cc967f10a6ebb57906108f',
'211d4285118b755d3f734a70e2703072',
'9a07cd24b7af6fb1a3cc49c6c6cc5064',
'ec8fe40b9f0f4f8a94abe8f9b58f433b'
]

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('103.5.112.91', 1340))

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

for i in range(64):
    print 'Round %d' % i

    data = recvuntil(s, '\n\n')

    jpg = data.decode('base64')
    if hashlib.md5(jpg).hexdigest() in human_jpg_md5_list:
        ans = '1'
    else:
        ans = '0'
    print ans
    s.sendall(ans + '\n')

data = recvuntil(s, '\n')
print data
xiomara{you_just_processed_an_image}

Freemasonry (Forensics 100)

IENDチャンクの後ろに以下のメッセージがある。

some one has changed the checksum and hidden the secret flag

IHDRチャンクにある画像の高さを高くすると、下の方にフラグが現れる。
f:id:satou-y:20180228222528p:plain

xiomara{480_is_the_new_4k_:P}

Dig_Deep (Forensics 150)

FTK Imagerで開く。

[root]
  - PxNc
    - TvNkzcm
      - UdfV
        - BVoAp
          - yHkWSGaL
            - bBYy
              - IxANKI
                - hgugRa
                  - fvgXjIbXg
                    - zz

上記パスの配下に.git, .git.zipが削除ファイルとしてあるので、エクスポートする。
オブジェクトの情報を確認しながら、フラグが記載されているものを探す。

$ cd .git
$ xxd -g 1 index
0000000: 44 49 52 43 00 00 00 02 00 00 00 02 5a 7c 5b d4  DIRC........Z|[.
0000010: 2e 40 89 30 5a 7c 5b d4 2e 40 89 30 00 00 08 01  .@.0Z|[..@.0....
0000020: 00 48 2e e9 00 00 81 a4 00 00 03 e8 00 00 03 e8  .H..............
0000030: 00 00 00 0a 5f 93 42 a6 7f 13 e2 65 75 02 20 f1  ...._.B....eu. .
0000040: ae 7b 66 4b 6d 5c 36 62 00 09 52 45 41 44 4d 45  .{fKm\6b..README
0000050: 2e 6d 64 00 5a 7c 5f db 29 71 0d 7e 5a 7c 5f db  .md.Z|_.)q.~Z|_.
0000060: 29 71 0d 7e 00 00 08 01 00 48 2e ed 00 00 81 a4  )q.~.....H......
0000070: 00 00 03 e8 00 00 03 e8 00 00 00 93 07 50 11 5e  .............P.^
0000080: f6 bc 6c 3e cd 58 3f 07 4a 7d 16 58 63 42 60 18  ..l>.X?.J}.XcB`.
0000090: 00 08 66 6c 61 67 2e 74 78 74 00 00 54 52 45 45  ..flag.txt..TREE
00000a0: 00 00 00 19 00 32 20 30 0a c0 35 17 cd e2 ec d6  .....2 0..5.....
00000b0: 73 47 c3 90 ae 29 a4 4b 4d f1 91 c8 1a 9f 24 ec  sG...).KM.....$.
00000c0: 74 37 8c da 94 d7 f6 90 5b 05 42 ad a7 f9 6e 3e  t7......[.B...n>
00000d0: cd                                               .

$ python -c 'import zlib; print zlib.decompress(open("objects/07/50115ef6bc6c3ecd583f074a7d165863426018").read())'
blob 147oh [REDACTED]

Well screw you !!!


probably u could try some cool tool to find flag

hahahahahahahhahah

sorry no flag for u !!!!!!!!!!!!!!!!!111

$ cat logs/refs/heads/master 
0000000000000000000000000000000000000000 75f9b8f5cd2efc6e9ec39f834542e9787d0c9930 SANS Forensics <sansforensics@siftworkstation.(none)> 1518099412 +0000	clone: from https://github.com/NaveenEzio/dig_deep.git
75f9b8f5cd2efc6e9ec39f834542e9787d0c9930 631728713203c7aea5515bfc7643ac90a09be2a2 lolzzz <fakemail@xiomara.com> 1518099964 +0000	commit: Intial commit
631728713203c7aea5515bfc7643ac90a09be2a2 dde06e5ce71936f03f751ec906f5223fb74fe43e lolzzz <fakemail@xiomara.com> 1518100110 +0000	commit: WIP creating the flag
dde06e5ce71936f03f751ec906f5223fb74fe43e 3505d2ddc47c53207331d54edd126f0a515f5ddc lolzzz <fakemail@xiomara.com> 1518100308 +0000	commit: Still 2 weeks left
3505d2ddc47c53207331d54edd126f0a515f5ddc 2c2196abb8a69cc74605a7f24cb8aafe59412968 lolzzz <fakemail@xiomara.com> 1518100365 +0000	commit: Still 1 week left
2c2196abb8a69cc74605a7f24cb8aafe59412968 1096fc8afd2b30c4b3318cc69f531f49b1f77441 lolzzz <fakemail@xiomara.com> 1518100447 +0000	commit: Still 1 week left

$ python -c 'import zlib; print zlib.decompress(open("objects/dd/e06e5ce71936f03f751ec906f5223fb74fe43e").read())'
commit 228tree b368768b6c7a63fae65df3f665842b6852efd7a9
parent 631728713203c7aea5515bfc7643ac90a09be2a2
author lolzzz <fakemail@xiomara.com> 1518100110 +0000
committer lolzzz <fakemail@xiomara.com> 1518100110 +0000

WIP creating the flag

$ python -c 'import zlib; print zlib.decompress(open("objects/b3/68768b6c7a63fae65df3f665842b6852efd7a9").read())' | xxd -g 1
0000000: 74 72 65 65 20 37 33 00 31 30 30 36 34 34 20 52  tree 73.100644 R
0000010: 45 41 44 4d 45 2e 6d 64 00 5f 93 42 a6 7f 13 e2  EADME.md._.B....
0000020: 65 75 02 20 f1 ae 7b 66 4b 6d 5c 36 62 31 30 30  eu. ..{fKm\6b100
0000030: 36 34 34 20 66 6c 61 67 2e 74 78 74 00 80 18 26  644 flag.txt...&
0000040: ed b4 11 74 7a 61 b3 d7 78 25 10 a9 f0 6c 24 6d  ...tza..x%...l$m
0000050: e0 0a                                            ..

$ python -c 'import zlib; print zlib.decompress(open("objects/80/1826edb411747a61b3d7782510a9f06c246de0").read())'
blob 81xiomara{} well this is our flag format

oh still only few days left !!!!!!!!!!!!

$ python -c 'import zlib; print zlib.decompress(open("objects/63/1728713203c7aea5515bfc7643ac90a09be2a2").read())'
commit 220tree 8d5cf9cbad1a828a1fc3b03d34e42097ebd31342
parent 75f9b8f5cd2efc6e9ec39f834542e9787d0c9930
author lolzzz <fakemail@xiomara.com> 1518099964 +0000
committer lolzzz <fakemail@xiomara.com> 1518099964 +0000

Intial commit

$ python -c 'import zlib; print zlib.decompress(open("objects/8d/5cf9cbad1a828a1fc3b03d34e42097ebd31342").read())' | xxd -g 1
0000000: 74 72 65 65 20 37 33 00 31 30 30 36 34 34 20 52  tree 73.100644 R
0000010: 45 41 44 4d 45 2e 6d 64 00 5f 93 42 a6 7f 13 e2  EADME.md._.B....
0000020: 65 75 02 20 f1 ae 7b 66 4b 6d 5c 36 62 31 30 30  eu. ..{fKm\6b100
0000030: 36 34 34 20 66 6c 61 67 2e 74 78 74 00 b7 6c 29  644 flag.txt..l)
0000040: 32 ff 8b 34 54 de d7 59 ed 44 1a 5a 91 1e 66 5c  2..4T..Y.D.Z..f\
0000050: 46 0a                                            F.

$ python -c 'import zlib; print zlib.decompress(open("objects/b7/6c2932ff8b3454ded759ed441a5a911e665c46").read())'
blob 76xiomara{} well this is our flag format

but have to frame some cool flag :)

$ python -c 'import zlib; print zlib.decompress(open("objects/35/05d2ddc47c53207331d54edd126f0a515f5ddc").read())'
commit 225tree df33643c65c175e109232b95284ec5c0fa7aab1a
parent dde06e5ce71936f03f751ec906f5223fb74fe43e
author lolzzz <fakemail@xiomara.com> 1518100308 +0000
committer lolzzz <fakemail@xiomara.com> 1518100308 +0000

Still 2 weeks left

$ python -c 'import zlib; print zlib.decompress(open("objects/df/33643c65c175e109232b95284ec5c0fa7aab1a").read())' | xxd -g 1
0000000: 74 72 65 65 20 37 33 00 31 30 30 36 34 34 20 52  tree 73.100644 R
0000010: 45 41 44 4d 45 2e 6d 64 00 5f 93 42 a6 7f 13 e2  EADME.md._.B....
0000020: 65 75 02 20 f1 ae 7b 66 4b 6d 5c 36 62 31 30 30  eu. ..{fKm\6b100
0000030: 36 34 34 20 66 6c 61 67 2e 74 78 74 00 ed c4 11  644 flag.txt....
0000040: b5 9e ad 90 8c ba 9b 34 0a b2 18 9a d1 86 94 45  .......4.......E
0000050: 92 0a                                            ..

$ python -c 'import zlib; print zlib.decompress(open("objects/ed/c411b59ead908cba9b340ab2189ad186944592").read())'
blob 61xiomara{wow_autopsy_&_git_is_cool}

Finally this is the flag
xiomara{wow_autopsy_&_git_is_cool}

EasyCTF IV Writeup

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

Hello, world! (Intro 10)

Hello, World!と表示させるプログラムを書けばよい。

print 'Hello, world!'

Linux (Intro 10)

$ ls -a                                                                                                    
.  ..  .bash_logout  .bashrc  .cache  .cloud-locale-test.skip  .flag  .profile
$ cat .flag                                                                                                
easyctf{i_know_how_2_find_hidden_files!}

隠しファイルにフラグが書いてあった。

easyctf{i_know_how_2_find_hidden_files!}

The Oldest Trick in the Book (Intro 10)

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

easyctf{w3lc0m3_70_345yc7f_fd2e53}

Web (Intro 10)

HTMLのソースにフラグが書かれている。

easyctf{hidden_from_the_masses_072e33}

Soupreme Encoder(Cryptography 20)

16進コードをデコードするだけ

>>> '68657869745f6d6174655f3139353961643733346539386132386561363734'.decode('hex')
'hexit_mate_1959ad734e98a28ea674'
easyctf{hexit_mate_1959ad734e98a28ea674}

Netcat (Intro 20)

ncで接続するだけ。

$ nc c1.easyctf.com 12481
enter your player key: 1357494912
thanks! here's your key: easyctf{hello_there!_EdaB38f2DD98ae99}
easyctf{hello_there!_EdaB38f2DD98ae99}

Hashing (Miscellaneous 20)

ファイルのsha512を計算すればよい。

$ sha512sum ecd758d450f4158ef2276ab80aa038ca6761c7f2e1a78374c359ca1c629ded53_image.png 
c5b8c1b2173c66d0752e6f66a970a0cce42c966ef44cdb2e89b4440eb8a16b6efda199d5af9ce2e40854616005ceb41a3503fbc5c87a5bafa61a084ab80435b0  ecd758d450f4158ef2276ab80aa038ca6761c7f2e1a78374c359ca1c629ded53_image.png
easyctf{c5b8c1b2173c66d0752e6f66a970a0cce42c966ef44cdb2e89b4440eb8a16b6efda199d5af9ce2e40854616005ceb41a3503fbc5c87a5bafa61a084ab80435b0}

Exclusive (Programming 20)

2つの入力値のXORを出力するプログラムを書けばよい。

ab = raw_input()
a = int(ab.split(' ')[0])
b = int(ab.split(' ')[1])
print a ^ b

Look At Flag (Forensics 30)

$ file 4680b45d33184b3e3ad99338907d1fe7dfec8ddd4b43ac71da69154ce9a6035c_flag.txt 
4680b45d33184b3e3ad99338907d1fe7dfec8ddd4b43ac71da69154ce9a6035c_flag.txt: PNG image data, 332 x 44, 8-bit/color RGBA, non-interlaced

拡張子をpngにして開く。
f:id:satou-y:20180227204557p:plain

easyctf{FLaaaGGGGGg}

EzSteg (Forensics 30)

jpgファイルのFF D9 より後ろにフラグが入っている。

easyctf{l00k_at_fil3_sigS}

Taking Input (Programming 30)

Hello, <入力文字列>! と出力するプログラムを書けばよい。

name = raw_input()
print 'Hello, %s!' % name

Over and Over (Programming 30)

Over and Over and ... and Overと入力した値の数だけOverを出現させるようプログラムを書けばよい。

n = input()

op = ''
for i in range(n):
    op += 'over'
    if i != n - 1:
        op += ' and '

print op

Teaching Old Tricks New Dogs (Programming 40)

入力した値の数だけ、入力文字列の各文字をシフトするようプログラムを書けばよい。

rot = input()
enc = raw_input()

dec = ''
for i in range(len(enc)):
    if enc[i] == ' ':
        dec += ' '
    else:
        code = ord(enc[i]) - rot
        if code < ord('a'):
            code += 26
        dec += chr(code)

print dec

hexedit (Reverse Engineering 50)

stringsで調べるだけ。

$ strings hexedit | grep easyctf
easyctf{d93a506e}
easyctf{d93a506e}

Substitute (Cryptography 50)

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

YO! NICE?OWLOFSOUP JUST MADE A NEW FLAG FOR THE CTF AND IS TOTALLY PROUD OF ITS INGENUITY. THIS IS ALSO THE SECOND PRO?LEM EVER MADE FOR EASYCTF. HERE: EASYCTF{THIS_IS_AN_EASY_FLAG_TO_GUESS} USE CAPITAL LETTERS.

きちんと復号しきれていないが、フラグの部分はわかる。

EASYCTF{THIS_IS_AN_EASY_FLAG_TO_GUESS}

Markov's Bees (Linux 50)

探すべきファイルは非常に多い。grepで探す。

$ grep easyctf -rl ./                                                                 
./c/e/i/bee913.txt
$ cat ./c/e/i/bee913.txt | grep easyctf                                               
easyctf{grepping_stale_memes_is_fun}
easyctf{grepping_stale_memes_is_fun}

xor (Cryptography 50)

1つのキーで総当たりでXORし、印字可能な文字列だけになり、フラグの形式になるものを探す。

with open('8763dea5b0f7b29b0324f0a53bf9b5cccdd9762ffdb1376596b285588c461278_xor.txt', 'rb') as f:
    data = f.read()

for key in range(256):
    flag = ''
    ng = False
    for i in range(len(data)):
        code = ord(data[i]) ^ key
        if code < 32 or code > 126:
            ng = True
            break
        else:
            flag += chr(code)
    if ng == False:
        if 'easyctf' in flag or 'EASYCTF' in flag:
            print str(key) + ':' + flag

実行結果は以下の通り。

15:easyctf{nfzkkcwopxfwrklydozaoqfji}
47:EASYCTF[NFZKKCWOPXFWRKLYDOZAOQFJI]
easyctf{nfzkkcwopxfwrklydozaoqfji}

Subset Counting (Programming 55)

入力の数だけ数字が提示され、部分和が入力の値になる組み合わせの数を出力するプログラムを書けばよい。

import itertools

NS = raw_input()
N = int(NS.split(' ')[0])
S = int(NS.split(' ')[1])

elems = map(int, raw_input().split(' '))

combs = []
for n in range(1, N + 1):
    for c  in itertools.combinations(elems, n):
        if sum(c) == S:
            combs.append(c)

print len(combs)

In Plain Sight (Web 70)

DNS SPYでこのドメインを検索。https://dnsspy.io/scan/blockingthesky.comを見ると、TXTレコードにフラグが書いてある。
f:id:satou-y:20180227210137p:plain

easyctf{betcha_wish_you_could_have_used_ANY}

My Letter (Forensics 80)

docxを解凍すると、template.pngにフラグが書いてある。
f:id:satou-y:20180227210300p:plain

easyctf{r3j3ct3d_4nd_d3jected}

Nosource, Jr. (Web 80)

ソースを見る。

var flag = 'Fg4GCRoHCQ4TFh0IBxENAE4qEgwHMBsfDiwJRQImHV8GQAwBDEYvV11BCA==';

これと鍵とでXORして復号する必要がある。復号するとeasyctf{になることを想定し、XOR鍵を見つける。
keyがsoupysouとなっているので、soupyの繰り返しで使えば良さそう。

b64_enc = 'Fg4GCRoHCQ4TFh0IBxENAE4qEgwHMBsfDiwJRQImHV8GQAwBDEYvV11BCA=='

enc = b64_enc.decode('base64')

flag_head = 'easyctf{'

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

print key
## soupysou -> key = soupy
key = key[:5]

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

print flag
easyctf{congrats!_but_now_f0r_n0s0urc3_...}

Zippity (Miscellaneous 80)

$ nc c1.easyctf.com 12483
+======================================================================+
| Welcome to Zippy! We love US zip codes, so we'll be asking you some  |
| simple facts about them, based on the 2010 Census. Only the          |
| brightest zip-code fanatics among you will be able to succeed!       |
| You'll have 30 seconds to answer 50 questions correctly.             |
+======================================================================+

3... 2... 1...  Go!

Round  1 / 50
  What is the longitude (degrees) of the zip code 12463? 

https://www.census.gov/geo/maps-data/data/gazetteer2010.htmlからデータファイルを取ってきて、該当する情報を答えていく。

import socket
import re

def search(rows, zipcode):
    fnd_record = ''
    for row in rows:
        if row[:5] == zipcode:
            fnd_record = row.strip()
            break
    return fnd_record

def getLatitude(row):
    return row.split('\t')[7].strip()

def getLongitude(row):
    return row.split('\t')[8].strip()

def getLandArea(row):
    return row.split('\t')[3].strip()

def getWaterArea(row):
    return row.split('\t')[4].strip()

with open('Gaz_zcta_national.txt', 'r') as f:
    lines = f.readlines()[1:]

pattern1 = 'What is the (.+) \(degrees\) of the zip code (.+)\?'
pattern2 = 'What is the (.+) area \(m\^2\) of the zip code (.+)\?'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('c1.easyctf.com', 12483))

data = s.recv(1024)
data += s.recv(1024)
data += s.recv(1024)
print data

for i in range(50):
    data = s.recv(1024)
    print data

    m = re.search(pattern1, data)
    if m is None:
        m = re.search(pattern2, data)
    attr = m.group(1)
    code = m.group(2)

    area = search(lines, code)
    ans = ''
    if attr == 'latitude':
        ans = getLatitude(area)
    elif attr == 'longitude':
        ans = getLongitude(area)
    elif attr == 'land':
        ans = getLandArea(area)
    else:
        ans = getWaterArea(area)
    print ans
    s.sendall(str(ans) + '\n')

data = s.recv(1024)
print data
easyctf{hope_you_liked_parsing_tsvs!}

Keyed Xor (Cryptography 100)

先頭がeasyctf{になることからkeyの先頭を割り出す。

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

flag_head = 'easyctf{'

key = ''
for i in range(len(flag_head)):
    code = ord(flag_head[i]) ^ ord(data[i])
    key += chr(code)

print key

この結果 tambouri とわかる。wordsから探すと、tambourineのようだ。もう一つはブルートフォースで探す。

import string

def checkFlag(s):
    for i in range(len(s)):
        if ord(s[i]) < 32 or ord(s[i]) > 126:
            return False

    if '{' in s[8:-1]:
        return False
    elif '}' in s[8:-1]:
        return False

    if s[i] != '}':
        return False

    return True

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

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

key1 = 'tambourine'

for word in words:
    key2 = word.strip()
    key = key1 + key2

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

実行結果は以下の通り。

easyctf{flagflagflagflagxeavjtwkmumukhbeqsjrfytkkpsezflnfilrwchgowsfmgqmok}
easyctf{flagljac|qbpflagxeavjtwkgsmqquarqsjrfytkkpsep`lj|toewchgowsfmgqmem}
easyctf{fllldjackqhmflagxeavjtz`osmqfukoqsjrfytkkp~nx`ljktexwchgowsfmg|fmm}
easyctf{flgonjakitagflagxeavjtqcesmydpbeqsjrfytkkpumr`lbiqlrwchgowsfmgwegm}
easyctf{flllnjakmvngflagxeavjtz`esmy`rmeqsjrfytkkp~nr`lbmscrwchgowsfmg|fgm}
easyctf{fl`pxlapalhmflagxeavjtv|sumblhkoqsjrfytkkprrdflyaiexwchgowsfmgpzqk}
easyctf{flflkhanglemflagxeavjtp``qm|jhfoqsjrfytkkptnwblggihxwchgowsfmgvfbo}
easyctf{flag~lac|qbpflagxeavjtwkuumqquarqsjrfytkkpsebflj|toewchgowsfmgqmwk}

この中で英文字だけで構成されているものが一番フラグっぽい。

easyctf{flagflagflagflagxeavjtwkmumukhbeqsjrfytkkpsezflnfilrwchgowsfmgqmok}

Zipperoni (Miscellaneous 160)

bigin.zipを解凍すると、以下のファイルが展開される。

・filename.txt
・hash.txt
・pattern.txt

filename.txtの内容は次の解凍対象のファイル名、pattern.txtの内容は解凍する際のパスワードのフォーマット(0:数字、A:英大文字、a:英小文字)、hash.txtの内容は解凍する際のパスワードのsha1となっていることを前提にプログラムにする。

import zipfile
import itertools
import string
import hashlib

def applyText(text, cat, c, cnt):
    appText = text
    for i in range(cnt):
        appText = appText.replace(cat, c[i], 1)
    return appText

DIR = 'zip_files/'

for i in range(100):
    print 'Round %d' % (i+1)
    if i == 0:
        fname = DIR + 'begin.zip'
        pw = 'coolkarni'
    else:
        with open('filename.txt', 'r') as f:
            fname = f.read().strip()
        with open('hash.txt', 'r') as f:
            h = f.read().strip()
        with open('pattern.txt', 'r') as f:
            ptn = f.read().strip()

        ptn = ptn.replace('0', '#')
        ptn = ptn.replace('A', '$')
        ptn = ptn.replace('a', '%')

        cnt_0 = ptn.count('#')
        cnt_A = ptn.count('$')
        cnt_a = ptn.count('%')
        for c0 in itertools.product(string.digits, repeat=cnt_0):
            for cA in itertools.product(string.uppercase, repeat=cnt_A):
                for ca in itertools.product(string.lowercase, repeat=cnt_a):
                    text = ptn
                    text = applyText(text, '#', c0, cnt_0)
                    text = applyText(text, '$', cA, cnt_A)
                    text = applyText(text, '%', ca, cnt_a)
                    if hashlib.sha1(text).hexdigest() == h:
                        pw = text
                        print pw
                        break

    zf = zipfile.ZipFile(fname, 'r')
    zf.setpassword(pw)
    zf.extractall('.')
    zf.close()

100個目のファイルを解凍すると、flag.txtが展開され、フラグが記載されている。

easyctf{you_must_REALLY_luv_zip_files_by_now!}

RSA_v (Cryptography 200)

RSA暗号であることから、以下が言える。

pow(m, e1, n) = c1
pow(c1, e2, n) = c2
pow(c2, e3, n) = c3
pow(c3, e4, n) = c4
pow(c4, e5, n) = c
→pow(m, e1*e2*e3*e4*e5, n) = c

Wiener's attackで復号する。

from fractions import Fraction

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

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

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

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

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

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

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

n = 9247606623523847772698953161616455664821867183571218056970099751301682205123115716089486799837447397925308887976775994817175994945760278197527909621793469
e1 = 11
e2 = 41
e3 = 67623079903
e4 = 5161910578063
e5 = 175238643578591220695210061216092361657427152135258210375005373467710731238260448371371798471959129039441888531548193154205671
c = 7117565509436551004326380884878672285722722211683863300406979545670706419248965442464045826652880670654603049188012705474321735863639519103720255725251120

e = e1 * e2 * e3 * e4 * e5
p, q = wiener(n, e)

flag = decrypt(p, q, e, c)
print flag
easyctf{keblftftzibatdsqmqotemmty}

Hidden Key (Crypto 250)

2 * d + phi(n) = A とする。
A * e = 2 * d * e + e * phi(n) mod phi(n)
→ A * e = 2 mod phi(n)
→ phi(n) = A * e - 2 と考えられる。

これを元に復号のコードを書く。

from gmpy2 import *

n = 26710688765026267138148264204920703883007904874128562799028393547554949765922542277895189518670065560334868811558510266979231934353002179757403268531715067062010281686471508752161203685149228268075021781933099554658431559853466187242611729894977296432641592421613434814603355905619394581849422912654259602509865122156934015070617275449499084493067258800113716929834658774822670480345848547269860867133466260711123975552273716095623892922027240248317967859377410150566076391929436484714414414857035650003266909701614148602709844136551594429588283632818636865431390365833016164255279255802323640923522353818320309748633
e = 65537
c = 6155261872379721610545844543378067292233506464929342294811835612600388915322721422713773519949082615325444696280961405812555399866766226739563377342138484240116517586749074812783395471895199325930180026213789355825092956319857260136986497376123985897638747818201019539905731342033491297584936188106793446672492960854016060922453033593011340995980680523319744413970189205384835729579064694334213775169787018937106526040708918216192341791478828953861227176828698102696958311016012460528271599429418317475682934164425649660020076980302878536164096467035358023176811709964428755690089778737252553786028779140848957236412

A = 53253460112850178018596037315460717011178095833855475112480704910365516369607568535550013776774464901421710311058761745335312922569418356218042111675709203846016562029353499192439190013411789736533843417778394708497184559463716663220686541213932946373526645238458498554186241197186952940378859218658633568565865387359341323473553517547778383854342692530241379613507904160499942231159638220574403240144802192330430882869766628877772376237739254238967605943185500475437018419456964233958276405329507956177859421308456900776164204921100052945746833393254101379336114249596223790020916083908846829231724794651160737011674

phi = A * e - 2
d = invert(e, phi)

m = pow(c, d, n)

flag = ('%x' % m).decode('hex')
print flag
easyctf{lbrqfhlyy8fu2bxu3z}

nullcom HackIM 2018 Writeup

この大会は2018/2/10 7:00(JST)~2018/2/11 15:00(JST)に開催されました。
今回もチームで参戦。結果は700点で248チーム中76位でした。
自分で解けた問題をWriteupとして書いておきます。

What did he said? (Crypto 300)

暗号ファイルと、2つの公開鍵recovered_1.keyとrecovered_2.keyが与えられている。この公開鍵のn, eの値を確認する。

from Crypto.PublicKey import RSA

with open('What_did_he_said\\recovered_1.key', 'r') as f:
    pub_data1 = f.read()

with open('What_did_he_said\\recovered_2.key', 'r') as f:
    pub_data2 = f.read()

pubkey1 = RSA.importKey(pub_data1)
print pubkey1.n
print pubkey1.e

pubkey2 = RSA.importKey(pub_data2)
print pubkey2.n
print pubkey2.e

この結果は以下の通り。

[recovered_1.key]
267655291201323217581766648921840701061
65537
[recovered_2.key]
307896566740839738127153373769666872203
65537

factordbで素因数分解する。

267655291201323217581766648921840701061 = 
14673311234908966559 * 18240960538242393179

307896566740839738127153373769666872203 =
16879405341365159057 * 18240960538242393179

どちらかの鍵で復号できるはず。

with open('What_did_he_said\\encrypt.txt', 'rb') as f:
    c = int(f.read().encode('hex'), 16)

p = 16879405341365159057
q = 18240960538242393179
n = p * q
e = 65537

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

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

m = pow(c, d, n)
flag = ('%x' % m).decode('hex')
print flag

結果2つ目の鍵で復号できた。

BabaSaidJaiJugad
hackim18{'BabaSaidJaiJugad'}

Jailbreak (Crypto 200)

CBCモードなので、以下のようになる。

[平文1ブロック目] ^ IV                  --(暗号化)--> [暗号文1ブロック目]
[平文2ブロック目] ^ [暗号文1ブロック目] --(暗号化)--> [暗号文2ブロック目]
[平文3ブロック目] ^ [暗号文2ブロック目] --(暗号化)--> [暗号文3ブロック目]

鍵を調整しながら、後ろのブロックから復号していく。IVの前半7バイトがID+IDの形式の16進表記として、ブルートフォースで名前が小文字のASCIIコードのみで復号できるものを洗い出す。

from hashlib import md5
from Crypto.Cipher import AES
import string

def unpad(s):
    return s[0:-ord(s[-1])]

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

def chk_name(s):
    for i in range(len(s)):
        if s[i] not in string.lowercase:
            return False
    return True

encoded = 'U2FsdGVkX1+1KcLc+WlP8rcjdSP8DnOx/W1h+lww6rGCUVH4ghAuhSs+Xs9ShwJNEFlJ4IWDoG00T4LnAqIMrsY9EODHGc7Jv/Rn1lC/h7k='

encrypted = encoded.decode('base64')

salt = encrypted[8:16]
secret = 'jailbreak123'
key = md5(secret + salt).digest()
cipher = encrypted[16:]

aes = AES.new(key, AES.MODE_ECB)

xor4 = aes.decrypt(cipher[48:64])
plain4 = str_xor(xor4, cipher[32:48])
plain4 = unpad(plain4)

xor3 = aes.decrypt(cipher[32:48])
plain3 = str_xor(xor3, cipher[16:32])

xor2 = aes.decrypt(cipher[16:32])
plain2 = str_xor(xor2, cipher[:16])

xor1 = aes.decrypt(cipher[:16])
print xor1 + plain2 + plain3 + plain4

for i in range(10000000):
    id = '%07d' % i
    iv = id + id
    name = str_xor(iv.decode('hex'), xor1[:7])
    if chk_name(name):
        print id
        print name

以下上記コードの実行結果で、復号の途中と、IDと名前の組み合わせ。

1uwK;prison_term=3yrs;about=speaker at nullcon 2018;

8060624
spmysqo
8060625
spmisqn
8061624
sqmysao
8061625
sqmisan
8071624
samyrao
8071625
samiran
8160624
rpmycqo
8160625
rpmicqn
8161624
rqmycao
8161625
rqmican
8171624
ramybao
8171625
ramiban
9060624
cpmxsqo
9060625
cpmhsqn
9061624
cqmxsao
9061625
cqmhsan
9071624
camxrao
9071625
camhran
9160624
bpmxcqo
9160625
bpmhcqn
9161624
bqmxcao
9161625
bqmhcan
9171624
bamxbao
9171625
bamhban

nullcon 2018のスピーカーということで、https://nullcon.net/website/goa-2018/about-speakers.phpを調べると、SAMIRAN GHATAKという人がいる。

hackim18{'8071625'}

Harekaze CTF 2018 Writeup

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

welcome flag (WarmUp 10)

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

HarekazeCTF{Welcome to the Harekaze CTF!}

easy problem (Warmup 30)

ROT13でデコードすると、フラグが得られた。

HarekazeCTF{Hello, world!}

gacha (Crypto 100)

平文にWINが含まれる暗号文を選択すると勝ちで、30回以上の勝負で9割以上の成績を残すと、フラグが得られる。暗号化した結果が合うものを選択するようにすればよい。

from Crypto.Util.number import *
import socket
import re

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

pattern1 = '1\. \((.+)\)'
pattern2 = '2\. \((.+)\)'
pattern3 = '3\. \((.+)\)'

e = 65537
m = 6289644257982517902

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

while True:
    data = s.recv(4096)
    print data
    if 'you got the flag' in data:
        break

    n = []
    c = []
    mo = re.search(pattern1, data)
    n.append(int(mo.group(1).split(', ')[0][2:], 16))
    c.append(int(mo.group(1).split(', ')[2][2:], 16))
    mo = re.search(pattern2, data)
    n.append(int(mo.group(1).split(', ')[0][2:], 16))
    c.append(int(mo.group(1).split(', ')[2][2:], 16))
    mo = re.search(pattern3, data)
    n.append(int(mo.group(1).split(', ')[0][2:], 16))
    c.append(int(mo.group(1).split(', ')[2][2:], 16))

    ans = ''
    for i in range(3):
        if pow(m, e, n[i]) == c[i]:
            ans = str(i+1)
            break
    print ans
    s.sendall(ans + '\n')

    for i in range(6):
        data = recvuntil(s, '\n')
        print data

実行すると、フラグが得られる。

  :
実行結果は以下の通り。
        :
[*] ROUND 30
    🔒 1. (0x96a3fa4a676e46330d8bd14eed7a027d1c53b76e45d492bc00a9d11661f30050059cf60cfe91e69be4ed8b944eed56881bfcd15a07e1da8d0314deb591f8b5e24ccc7a1dda4b318b73d8e9eefac36f2c9185bf013abbdca1fad1b561aeab71de3ccd3656bd6b222a77e2fe1d130499c1d76a1ac6e92767d36b8a31c3ec022fc4057b79414420c27ef087fa9a122f2db2390b299296df20883a4e2a22c551f53a55561daa5bcf27716fd4334cfa43235a124432aaf07e89ee86afedf2c96adf1dfb26ea2ac4ec16fc1dba2db8138400abf97b874dc6508b4be57ffef56fb3fd1b99866843ef03b27cec343e5f6d63758b3a324fc474dd6e781523ff73b984556b, 65537, 0x5dc261a7fbaec5e1a13a39732f9af50f219f3a650814597196ae3f34c5bfa6b1be85ae537faeef7651fc1355fb2f196fa661e0a71fd074fa07c179994d5716bb429ee92ac05dc15d92a29499b8fe673df24aa89143135a816374ff6c582b1a20beb5f42b0549c012f342ba3917bb25f3c115ecc9e8b7524002a2e6e7f683bcc2717accc7fec3a6bfc1cd1054c4623124d42a4681a3550a206abb4723c76e8572b20cabce849a1f49f24cb24a1d8994a8a16490bed3dbccae5240d5dc94e826111b5df6ca684311e70956ed9fe3a461734c60e35f92088030cfab0d760b8a6358ca425c0be23d17fd163c4492446da04749081db33682f6654b603267f6f683a8)
    🔒 2. (0x9a5834c5981f16c100a84e6b08987d38204a341c5264adb9db968b843b6d29c7f8e54b9d5147f8fd95836cd61192611cfd7c244047c1d80bcdcb77203650989d9b2efbaf45da875cc6932546d87804fe1cecfad358eda31e2e62d7b3711e7769a0bcd3071fd990977262a1635c8836a965be7098fdac49313503324a7f52ee9ad0922f2d43fbf26d556dc8f4eae649190e2c5a24f0a7f6bb0a9210cbf330c7d79a9632f4779935c2960738bbff563f9b12857dcba95f6b1bf961bd8f5b5f330170132edb9d071df84f93f104ce3d7fc1813ddcda2de0ce882e2dc8940e0131e43ca62cf619ee5b74edd56691c9dd0d0587c246d81d6b0793739d7adb9596a2db, 65537, 0x7107ba3b20b50d93ef351014d1087ba251557c644cbbfd8cb64534367a471e20bc253f9397ac9f8fc23d8f7f4795f221e0082bff23c5e9be2c2aa527f0731d55ce18451ef2fcaa0bd2dda95c99baa8759400eea19cae7ee5175900fbf1ff70b20b7ed391f4458179e788d7021f4f1dd8c7299395b3cc41296333db9b2fe874381f4a0c3bd4056c411177b793a77debc396f77f1dc2b8782872e2d574319f3b32ce8b4ff76cf63d1e9a53dfab7947ffcf3ceea6aef7f53069c21468d8da0e4ef3b9cd4515fc898bdcd4a1427c6fa53ce9279d62d66f6cfc124b262548457eb711a61e0b7a9f62bddc622f244d0a9833d2ca6ef3c2456794ef87fb65f00572ae73)
    🔒 3. (0xa9377bfd02709b87cc9de8fd4e89b8925efe2ffadf18bc3e7965e96205718dad7d38aafbfc0129b8c6569fa8c06d7856a8bf0cfe2a4692e429a5d8857e65287fa3e498798f0666210623199c469fa242fb4dbe9edcef83c2623e70ef768a40321dbae77c5f06d5e856304e16c00762cc5d32bf405b0c125c4b77984e5cf0002837eea1e511908a3635e896d10aed950febfaa099087121756c88d09fda56ecac463db0024b13b54adf23094b52560d1c515688cb41fc8613de18164b7b1cd23697284a9c9690e2e446321eb3acb6ec4196922905b5d7ac3ac724a0aff46a370a40e7168a52efe5c3a7247b059b5916edbb7c725a5225ecdc4ae56660cef20c47, 65537, 0x9433ce34e7fe6e982a815554b152dfba7af14474aea8e2b6b54879ed9b72cdd61efdf8b2243d80cd7327dc304ab13235d4d03b5935b6f1d3e89f1a8dae7b2fa5e87078c286d25c233bbb5743d902855f19a2613818b6551b6bc7cd64d639372d487a192015480a2ca1338cae785df45896669300a32a61988c89c5af92406382af0b6990639a3a2c0d646e0f93c53674ccf164dabedc944fb6a6ebe7935b675586903dabb524801ca2463df4390f688df21612872ca8fa1ae3cf96c816aad09bdbfb04aaaab7ecdb9896e73055510f6d05c959b09ae05bac03ecbc244522d1ab9e9abeb6ac75294136e5b853663fdb869366d6f32ec4c5ddd81045a2792aa582)
[*] select:
>>> 
1
[*] result: WIN 💎

[+] you win 🎉

[*] here are the keys. please ensure that there is no cheating

    🔑 1. 7895995954412064234361798895691268139582514896439045906129410925393920562153770222588810056914159075033981223919158124552006716651188659496199051457286190320692090859788279809860208035893962232678891200034782425161636490486995090748934880560073837212118638861333586572283933749245331568754318155522087116610021719508377831385350502689285698213185323894228949011226089297813776139714178286314631550772569765746889813752314738483697158870342757880124044833572231066140815227624186195450340081288776962154163704962751174334544476745520922480332531682876418638778610050132359898020869921388700867032839447642396387916993

    🔑 2. 2871627322414446708739745407118402039320610534821637431875001264463975811751992475697957271173851890530416811405844414754092177877469323661265530941221479080980729838562469207272013132745402277102897808493558717580357314008162012284741604313539737024956744030148499434498239196119467724831745487309200048671876163687694984163513452981701506817302776063871922940228534343535114758434028528047115159167791857750787657828444280964181607895537940726693850558030502638037006053235518046927831325815085574066001263880008741752758330520257823295627563408765106708309024086304411923544230046304150169111392609526309091497273

    🔑 3. 4261114175761760556575469170514289305600825279273163390030649634102139374435136757831881552789765575472583792758210792969407840385039637423639005401443714928974149994817795380600031070795405605052235196867895358524894145361839581737987794740475045542297047737773980218646790236328911021370281112922925530542045171181448919011772705698091998520968768119957847461409564121043489635305341398474273372414636652903223459247379405576070241085884391735632148389893011321168254894606485639292403631944531222049369388592286103077814749344046044547383837687022513408530946652002143569207061842709239013253655479969969127409969

[+] you got the flag 🏁: HarekazeCTF{92f4187adbbafd3c592bfdfa8689de3be26b770d}
HarekazeCTF{92f4187adbbafd3c592bfdfa8689de3be26b770d}

Fight (Crypto 100)

gen_seedはまとも計算すると時間がかかる。ただやっていることはn-1までの互いに素となる数字の数を求めることなので、置き換える。seedがわかるので、そのまま乱数を計算すれば、XORの鍵もわかるので、復号できる。

import random
import base64
import sympy as sym

def xor(msg, key):
    return bytes([ch1^ch2 for ch1, ch2 in zip(msg, key)])

b64enc = '7XDZk9F4ZI5WpcFOfej3Dbau3yc1kxUgqmRCPMkzgyYFGjsRJF9aMaLHyDU='
enc = base64.b64decode(b64enc)

s = 1
for p in b"Enjoy_HarekazeCTF!!":
  s *= p
seed = sym.totient(s)
random.seed(str(seed).rstrip("0"))

key = bytes([random.randint(0,255) for _ in range(100)])

print(xor(enc, key))
HarekazeCTF{3ul3rrrrrrrrr_t0000000t1nt!!!!!}

Round and Round (Crypto 200)

p1 = pow(p-1, 0, p) + pow(p-1, 1, p) + pow(p-1, 2, p) + ... + pow(p-1, q-1, p)
q1 = pow(q-1, 0, q) + pow(q-1, 1, q) + pow(q-1, 2, q) + ... + pow(q-1, p-1, q)

まともに計算しても時間がかかる。いろいろ試したところ、以下の規則があることに気づいた。

pow(p-1, 奇数, p) = p-1
pow(p-1, 偶数, p) = 1

また、p, qは素数なので奇数。以上を踏まえ式を書き換える。

p1 = (p-1)*((q-1)/2) + (q-1)/2 + 1
   = p*(q-1)/2 + 1
q1 = (q-1)*((p-1)/2) + (p-1)/2 + 1
   = q*(p-1)/2 + 1
      ↓
(p1-1)*2 = p*(q-1) = (p-1)*(q-1) + (q-1)
(q1-1)*2 = q*(p-1) = (p-1)*(q-1) + (p-1)

(p1-1)*2 - (q1-1)*2 = q - p ; diffと置く
         = (p-1) * (p+diff-1) + (p-1)

この2次方程式を解くことができれば、pが求められ、そこからqも求められる。あとは復号するだけ。コードは以下の通り。

from sympy import *

enc = 15507598298834817042463704681892573844935925207353671096676527782423920390858333417805014479686241766827314370902570869063203100704151010294869728739155779685640415815492312661653450808873691693721178331336833872996692091804443257630828512131569609704473214724551132715672202671586891813211353984388741035474991608860773895778988812691240069777435969326282770350038882767450912160134013566175657336041694882842590560100668789803775001750959648059363722039014522592751510672658328196379883088392541680852726136345115484827400366869810045573176782889745710174383221427352027041590910694360773085666697865554456816396551
p1 = 14606124773267989759790608461455191496412830491696356154942727371283685352374696106605522295947073718389291445222948819019827919548861779448943538887273671755720708995173224464135442610773913398114765000584117906488005860577777765761976598659759965848699728860137999472734199231263583504465555230926206555745572068651194660027408008664437845821585312159573051601404228506302601502000674242923654458940017954149007122396560597908895703129094329414813271877228441216708678152764783888299324278380566426363579192681667090193538271960774609959694372731502799584057204257039655016058403786035676376493785696595207371994520
q1 = 14606124773267989759790608461455191496412830491696356154942727371283685352374696106605522295947073718389291445222948819019827919548861779448943538887273671755720708995173224464135442610773913398114765000584117906488005860577777765761976598659759965848699728860137999472734199231263583504465555230926206555745568763680874120108583912617489933976894172558366109559645634758298286470207143481537561897150407972412540709976696855267154744423609260252738825337344339874487812781362826063927023814123654794249583090654283919689841700775405866650720124813397785666726161029434903581762204459888078943696756054152989895680616

e = (1<<16) + 1

# q - p
diff = (p1 - 1) * 2 - (q1 - 1) * 2

# (q1 - 1) * 2
val = (q1 - 1) * 2

x = Symbol('x')
sol = solve((x - 1) * (x + diff - 1) + (x - 1) - val, x)
p = int(sol[1])
q = p + diff

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

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

m = pow(enc, d, p * q)

flag = ('%x' % m).decode('hex')
print(flag)
HarekazeCTF{d1d_y0u_7ry_b1n4ry_se4rch?}