SwampCTF 2018 Writeup

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

Welcome! (MISC 50)

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

flag{w3lc0m3_to_th3_Sw4mp}

Locked Dungeon (CRYPTO 500)

$ nc chal1.swampctf.com 1450
a
3093544369609e4c2f5f26df2aa78919037bd1387ce3892b819b684df891444b901e13b8930c546a9a0adcf1a9833d47
aa
3093544369609e4c2f5f26df2aa78919037bd1387ce3892b819b684df891444b45c8cd48c8290ab80e019654ddcc5e7e
aaa
3093544369609e4c2f5f26df2aa78919037bd1387ce3892b819b684df891444bde20f2cf2d966c306ef7b9c354aa27c9
aaaa
3093544369609e4c2f5f26df2aa78919037bd1387ce3892b819b684df891444b86bb631359f4eda8ad02f48a1bd939ae
aaaaa
3093544369609e4c2f5f26df2aa78919037bd1387ce3892b819b684df891444b4e12b2f258ca77a4d0bff5c44de3095dea6098e410f2e26f0f5e5ee117982362

16バイトずつ、ブロック単位で区切ると以下のようなイメージ。

flag{xxxxxxxxxxx
xxxxxxxxxxxxxxxx
xxxxxxxxxx}#####

flag{xxxxxxxxxxx
xxxxxxxxxxxxxxxx
xxxxxxxxxx0#####

フラグを後ろから1文字ずつ、暗号結果が同じものを探り当てる。

import socket
import string

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('chal1.swampctf.com', 1450))

pad_str = '#####'
#print pad_str
s.sendall(pad_str + '\n')
data = s.recv(256)
#print data
correct = data.strip()[:96]

flag = ''
for i in range(0x1900):
    for c in string.printable:
        try_str = c + flag + pad_str
        print try_str
        s.sendall(try_str + '\n')
        data = s.recv(256)
        #print data
        try_enc = data.strip()
        if try_enc == correct:
            flag = c + flag
            break
    if len(flag) == (48 - len(pad_str)):
        break

print flag
flag{rem3mber_the_pic_of_tux_aes_3ncrypted}

Orb of Light 1: Secret (CRYPTO 500)

以下の通り、英大文字以外を使っていない英大文字に置換する。

\xe2\x80\x99 -> '
2 -> B
9 -> C
a -> D
d -> F
g -> H
f -> I
i -> J
h -> K
l -> M
o -> O
n -> P
r -> Q
t -> U
z -> V

これをquipqiupにかけると、復号できる。

DOMINION OF SHADOWS AND UNUSUAL THINGS

"TOXIC DOMAIN OF DARK ILLUSION
LAND THAT SHROUDS LOATHING LIGHT
BOUNDARY OF WORLDS UNKNOWN"

LAND OF SHADOWS, A TWILIGHT DOMAIN THAT IS A DARK MIRROR, OR COPY, OF OUR WORLD. A POINT OF BLIGHT AND CORROSION, A DOMAIN OUT OF SYNC, A LAND WITH HORRORS RIGHT BY YOU AND YOU DON'T KNOW.

ARTISANS OF A THAUMATURGICAL KIND DO NOT USUALLY CROSS INTO LANDS OF SHADOW FOR IN SUCH DOMAINS AN UNKNOWN HORROR IS SAID TO LURK.  ARRIVAL ALWAYS BRINGS ABOUT DISSOLUTION OF ASPIRATIONS FOR SHADOWS QUICKLY SWALLOW SOULS OF LIGHT IN A MYSTIC IMPOSSIBILITY.  A SOLITARY AUGURY WAS PROOF: KINGDOMS WILL FALL AS CONJURATION OF SHADOW GLOOMS MIDDAY WITH DARK MALICIOUS FOG, A LUMINOUS CHARM WILL CAST A RAY THAT AGAIN ALIGNS OUR WORLD.

ここからチームのメンバが解いてくれた。文字の置換のマッピングが関係していたらしい。復号したアルファベット順に並べると、フラグになる。

A=f
B=l
C=a
D=g
F=S
G=T
H=R
I=A
J=N
K=G
L=E
M=2
N=t
O=h
P=i
Q=n
R=9
S=z
T=W
U=o
V=r
W=L
X=d
Y=Z
flag{STRANGE2thin9zWorLdZ}

Securinets CTF Quals 2018 Writeup

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

IRC (Misc 50)

IRCタグから#ctf_securinetsチャネルに入る。

Welcome to CTF Securinets Channel :D Flag{W3Lc0m3_T0_CTFSecurinets_Quals_2018}
Flag{W3Lc0m3_T0_CTFSecurinets_Quals_2018}

Easy (Reverse Engineering 100)

$ strings easy | grep Flag
Flag{This_Is_A_Hidden_Flag!!}
Flag{This_Is_A_Hidden_Flag!!}

looser (Crypto 150)

キー1文字でXORしていそう。PNGに復号することを想定して復号する。

with open('flag.png.crypt', 'rb') as f:
    c = f.read()

key = ord(c[0]) ^ 0x89

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

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

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

Flag{Hopefully_headers_are_constants}

The worst RSA Joke (Crypto 350)

公開鍵と暗号ファイルが与えられている。まず、公開鍵を見てみる。

$ openssl rsa -pubin -text < public.pem
Public-Key: (2049 bit)
Modulus:
    01:21:3b:05:7d:6f:de:bc:45:27:b5:b7:42:1a:c1:
    e9:3c:cf:fe:a7:6c:4f:08:74:99:1c:f3:fa:cf:0e:
    ab:9e:f6:46:a2:18:39:82:0b:4d:65:3a:91:6b:68:
    f2:58:e9:d3:f7:c2:ba:05:4e:4a:f0:5f:3e:4d:61:
    b3:de:77:2d:2a:d2:33:8b:0e:ae:6e:a0:8f:20:76:
    e4:e4:b0:8a:b2:09:20:fa:68:85:e6:c4:4d:ec:1b:
    ee:28:a8:76:53:c7:4b:cb:9e:9f:12:94:de:8a:48:
    bd:61:46:52:a3:d6:59:df:ce:7b:89:44:61:0f:25:
    bf:af:93:6e:9a:54:16:7c:4d:22:7d:16:d3:2e:65:
    ea:45:8b:69:d6:0d:ec:d7:fa:03:4c:1b:3b:d8:62:
    71:71:64:e7:78:5e:b0:6d:cc:5b:88:ba:a2:62:e4:
    31:20:e5:46:65:c0:cb:cb:3e:ad:51:b0:a0:08:19:
    b4:e9:1d:48:72:d3:fb:e7:72:4e:03:ab:71:bc:af:
    8b:b8:4c:74:de:c9:6a:ad:fc:b1:86:53:a8:f0:53:
    93:d6:66:06:99:23:bc:7b:9b:31:36:3d:6d:6d:9b:
    45:9f:46:db:5b:af:96:f8:40:4a:af:1a:83:1f:0d:
    b8:aa:d7:d9:3a:42:56:e8:15:6b:2b:70:75:7a:01:
    20:93
Exponent: 65537 (0x10001)
writing RSA key
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEBITsFfW/evEUntbdCGsHp
PM/+p2xPCHSZHPP6zw6rnvZGohg5ggtNZTqRa2jyWOnT98K6BU5K8F8+TWGz3nct
KtIziw6ubqCPIHbk5LCKsgkg+miF5sRN7BvuKKh2U8dLy56fEpTeiki9YUZSo9ZZ
3857iURhDyW/r5NumlQWfE0ifRbTLmXqRYtp1g3s1/oDTBs72GJxcWTneF6wbcxb
iLqiYuQxIOVGZcDLyz6tUbCgCBm06R1IctP753JOA6txvK+LuEx03slqrfyxhlOo
8FOT1mYGmSO8e5sxNj1tbZtFn0bbW6+W+EBKrxqDHw24qtfZOkJW6BVrK3B1egEg
kwIDAQAB
-----END PUBLIC KEY-----
n = 0x01213b057d6fdebc4527b5b7421ac1e93ccffea76c4f0874991cf3facf0eab9ef646a21839820b4d653a916b68f258e9d3f7c2ba054e4af05f3e4d61b3de772d2ad2338b0eae6ea08f2076e4e4b08ab20920fa6885e6c44dec1bee28a87653c74bcb9e9f1294de8a48bd614652a3d659dfce7b8944610f25bfaf936e9a54167c4d227d16d32e65ea458b69d60decd7fa034c1b3bd862717164e7785eb06dcc5b88baa262e43120e54665c0cbcb3ead51b0a00819b4e91d4872d3fbe7724e03ab71bcaf8bb84c74dec96aadfcb18653a8f05393d666069923bc7b9b31363d6d6d9b459f46db5baf96f8404aaf1a831f0db8aad7d93a4256e8156b2b70757a012093
  = 36511974694593690272644354864534934200522045623187752384823594729275730789191680514905027084950729514148679507005058047146031869995768765034876499069680202424692876360895377987654388335335689563844006584610187090074654410815638237841872991488680663410743679302763304922852173164820002226109196761249018548478251723505481964749218302723593776180246783117481280725673997940309717028451914887375722437833384305529884261542397905220138488012276363046571670926597766521674838665982314321651508118240682649024780239598188845543243957916954287138820155843952556411235376764737602711594439293285811102883736229645274092478611

nを素因数分解してみる。

$ python -m primefac 36511974694593690272644354864534934200522045623187752384823594729275730789191680514905027084950729514148679507005058047146031869995768765034876499069680202424692876360895377987654388335335689563844006584610187090074654410815638237841872991488680663410743679302763304922852173164820002226109196761249018548478251723505481964749218302723593776180246783117481280725673997940309717028451914887375722437833384305529884261542397905220138488012276363046571670926597766521674838665982314321651508118240682649024780239598188845543243957916954287138820155843952556411235376764737602711594439293285811102883736229645274092478611
36511974694593690272644354864534934200522045623187752384823594729275730789191680514905027084950729514148679507005058047146031869995768765034876499069680202424692876360895377987654388335335689563844006584610187090074654410815638237841872991488680663410743679302763304922852173164820002226109196761249018548478251723505481964749218302723593776180246783117481280725673997940309717028451914887375722437833384305529884261542397905220138488012276363046571670926597766521674838665982314321651508118240682649024780239598188845543243957916954287138820155843952556411235376764737602711594439293285811102883736229645274092478611: 36511974694593690272644354864534934200522045623187752384823594729275730789191680514905027084950729514148679507005058047146031869995768765034876499069680202424692876360895377987654388335335689563844006584610187090074654410815638237841872991488680663410743679302763304922852173164820002226109196761249018548478251723505481964749218302723593776180246783117481280725673997940309717028451914887375722437833384305529884261542397905220138488012276363046571670926597766521674838665982314321651508118240682649024780239598188845543243957916954287138820155843952556411235376764737602711594439293285811102883736229645274092478611

nが素数のようだ。RSA暗号の仕組みを考える。

c = m^e % n
 ↓
c = m^e % (素数 * n)

この場合は復号結果が複数出るが、文字として読み取れるものをブルートフォースで探す。

from Crypto.PublicKey import RSA

def is_prime(d):
    if d < 2:
        return False
    if d == 2:
        return True
    if d % 2 == 0:
        return False

    a = 3
    while a ** 2 <= d:
        if d % a == 0:
            return False
        a = a + 2

    return True

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

    return True

with open('RSA_Worst_Joke/public.pem', 'r') as f:
    pub_data = f.read()

pubkey = RSA.importKey(pub_data)
p = pubkey.n
e = pubkey.e

with open('RSA_Worst_Joke/flag.enc', 'r') as f:
    c = int(f.read().decode('base64').encode('hex'), 16)

for q in range(50):
    if is_prime(q):
        n = p * q

        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(c, d, n)

        hex_flag = '%x' % m
        if len(hex_flag) % 2 == 1:
            hex_flag = '0' + hex_flag
        flag = hex_flag.decode('hex')
        if is_printable(flag):
            print 'q =', q
            print flag

実行結果は以下の通り。

q = 47
The empire secret system has been exposed ! The top secret flag is : Flag{S1nGL3_PR1m3_M0duLUs_ATT4cK_TaK3d_D0wn_RSA_T0_A_Sym3tr1c_ALg0r1thm}
Flag{S1nGL3_PR1m3_M0duLUs_ATT4cK_TaK3d_D0wn_RSA_T0_A_Sym3tr1c_ALg0r1thm}

Improve the quality (Crypto 800)

decription.txtには以下のように記載されている。

Hello Every one,
We didn't know what to do, so we are asking for your help.

A friend of us sent us the following text:

I used an elliptic curve encrytion for the first time.
The only thing that i kown about elliptic curve is that a number K must always be hidden.
so i made multiple encryption to send some information.

Here is all the informations about the elliptic curve that i used excep the K number.

The elliptic curve is : 
y^2 = x^3 + A*x + B
A = 658974
Sorry i forget the B :/ , I just remember that it's most significant number is  6

As an order of a finite field must be a prime power, i used p = 962280654317 (FiniteField(p)).
as a starter point, i used the generator G for this elliptic curve: (518459267012 : 339109212996 : 1)
and each time i reuse it to encrypt again

let my secret message be K .
for exemple I divided my K to 2 elements k1 and k2
then Q1 is k1*G
and Q2 is k2*G

here are the Qi that i got:

[(656055339629 : 670956206845 : 1), 
(714432985374 : 30697818482 : 1), 
(519532969453 : 833497145865 : 1), 
(606806384185 : 353033449641 : 1), 
(370553209582 : 211121736115 : 1), 
(95617246846 : 666814491609 : 1), 
(474872055371 : 795112698430 : 1), 
(249845085299 : 222352033875 : 1), 
(850954431245 : 810446463695 : 1), 
(188731559428 : 877002121896 : 1), 
(168665615402 : 464872506873 : 1), 
(26722558561 : 269217869309 : 1), 
(16403346294 : 478534963882 : 1), 
(539749282946 : 332444159141 : 1), 
(932295517649 : 23439478940 : 1), 
(765194933041 : 920187938377 : 1), 
(853124087439 : 845601917928 : 1), 
(246454416048 : 212483699689 : 1), 
(312547608490 : 688107262695 : 1), 
(43261158649 : 439444472742 : 1), 
(320785434805 : 477080449838 : 1), 
(741706320740 : 672809544395 : 1), 
(361762297756 : 858805805323 : 1), 
(782235980044 : 600673464737 : 1), 
(69196762074 : 327427680437 : 1), 
(876001563166 : 573218279075 : 1), 
(117946101727 : 954797129239 : 1), 
(771781111553 : 314018907599 : 1), 
(579549799021 : 322325160055 : 1), 
(857081196493 : 464260539273 : 1), 
(852938568103 : 429083796488 : 1), 
(850954431245 : 810446463695 : 1), 
(55203632714 : 255470537391 : 1), 
(600464434215 : 605840305721 : 1), 
(620532163623 : 575613893944 : 1), 
(215810002861 : 481354983411 : 1), 
(538481263994 : 666638294130 : 1), 
(528666082457 : 895034116069 : 1), 
(296218553972 : 899557390183 : 1), 
(428618251485 : 445768511836 : 1), 
(632412058600 : 685699421425 : 1), 
(634041855232 : 495546745721 : 1), 
(570481762204 : 252944477333 : 1), 
(760959783781 : 435626456209 : 1)]
y^2 = x^3 + a*x + b mod n 
→b = y^2 - x^3 - a*x mod n

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

a -> A
b -> B
n -> p
p = 962280654317
A = 658974
G = (518459267012, 339109212996)

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

Bの値はわかったので、Pohlig–Hellman algorithmを使って、k1から順番に求める。その値を見ながらメッセージになるようASCIIコード(10進数)として2桁ずつ読み込む。
コードは以下の通り。

# solve.sage
p = 962280654317
A = 658974
B = 618
G = (518459267012, 339109212996)
Q = [(656055339629, 670956206845), 
(714432985374, 30697818482), 
(519532969453, 833497145865), 
(606806384185, 353033449641), 
(370553209582, 211121736115), 
(95617246846, 666814491609), 
(474872055371, 795112698430), 
(249845085299, 222352033875), 
(850954431245, 810446463695), 
(188731559428, 877002121896), 
(168665615402, 464872506873), 
(26722558561, 269217869309), 
(16403346294, 478534963882), 
(539749282946, 332444159141), 
(932295517649, 23439478940), 
(765194933041, 920187938377), 
(853124087439, 845601917928), 
(246454416048, 212483699689), 
(312547608490, 688107262695), 
(43261158649, 439444472742), 
(320785434805, 477080449838), 
(741706320740, 672809544395), 
(361762297756, 858805805323), 
(782235980044, 600673464737), 
(69196762074, 327427680437), 
(876001563166, 573218279075), 
(117946101727, 954797129239), 
(771781111553, 314018907599), 
(579549799021, 322325160055), 
(857081196493, 464260539273), 
(852938568103, 429083796488), 
(850954431245, 810446463695), 
(55203632714, 255470537391), 
(600464434215, 605840305721), 
(620532163623, 575613893944), 
(215810002861, 481354983411), 
(538481263994, 666638294130), 
(528666082457, 895034116069), 
(296218553972, 899557390183), 
(428618251485, 445768511836), 
(632412058600, 685699421425), 
(634041855232, 495546745721), 
(570481762204, 252944477333), 
(760959783781, 435626456209)]

F = FiniteField(p)
E = EllipticCurve(F, [A,B])
G = E.point(G)

factors, exponents = zip(*factor(E.order()))
primes = [factors[i] ^ exponents[i] for i in range(len(factors))]

str_num = ''
for i in range(len(Q)):
    dlogs = []
    for fac in primes:
        t = int(G.order()) / int(fac)
        dlog = discrete_log(t*E.point(Q[i]), t*G, operation='+')
        dlogs += [dlog]

    k = crt(dlogs, primes)
    print k
    #print k * G == E.point(Q[i])
    str_num += str(k)

msg = ''
for i in range(0, len(str_num), 2):
    msg += chr(int(str_num[i:i+2]))

print msg

実行結果は以下の通り。

CONVERT THIS TO LOWER CASE FIRST :
THIS IMAGE CONTAINS THE FLAG, TRY TO GET IT
THE SUBMITTED FLAG MUST BE IN THIS FORMAT: 
FLAG-EC[WHAT YOU'LL FIND IN THE IMAGE]
IMAGE URL:
HTTP://CRYPTO.CTFSECURINETS.COM/1/STEG-PART.PNG

上記で大文字、小文字が関係するところは小文字にする。
http://crypto.ctfsecurinets.com/1/steg-part.pngのファイルを見たら、フラグが書いてあった。
f:id:satou-y:20180402213759p:plain

flag-ec[EC_St!e-g1(a)no]

VolgaCTF 2018 Quals Writeup

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

Nonsense (crypto 200)

ECDSAの問題だが、ほぼ数学の問題。xの値を求めればよい。

state = seed
state = (a * state + b) % m
k = state
(r, s) = signature.sign(message, k)
state = (a * state + b) % m
k = state
(r, s) = signature.sign(message, k)
   :

h1 = int(hashlib.md5(m1).hexdigest(), 16)
h2 = int(hashlib.md5(m2).hexdigest(), 16)
r1 = pow(g, k1, p) % q
s1 = int((x * r1 + h1) * invert(k1, q) % q)
r2 = pow(g, k2, p) % q
s2 = int((x * r2 + h2) * invert(k2, q) % q)
k2 = a * k1 + b (mod m)

m = qになっていることを使い、mod q を前提に式を変形していく。

s1 * k1 = x * r1 + h1
s2 * k2 = x * r2 + h2
    ↓
s1 * k1 = x * r1 + h1
s2 * (a * k1 + b) = x * r2 + h2
    ↓
s1 * s2 * k1 * a = x * r1 * s2 * a + h1 * s2 * a
s1 * s2 * k1 * a + s1 * s2 * b = x * r2 * s1 + h2 * s1
    ↓
s1 * s2 * b = x * (r2 * s1 - r1 * s2 * a) + (h2 * s1 - h1 * s2 * a)
    ↓
x = (s1 * s2 * b - (h2 * s1 - h1 * s2 * a)) * invert(r2 * s1 - r1 * s2 * a, q)

コードにすると、以下の通り。

import hashlib
import gmpy2

q = 1118817215266473099401489299835945027713635248219
a = 3437776292996777467976657547577967657547
b = 828669865469592426262363475477574643634

r1 = 1030409245884476193717141088285092765299686864672
s1 = 830067187231135666416948244755306407163838542785
r2 = 403903893160663712713225718481237860747338118174
s2 = 803753330562964683180744246754284061126230157465
msg1 = 'VolgaCTF{nKpV/dmkBeQ0n9Mz0g9eGQ==}'
msg2 = 'VolgaCTF{KtetaQ4YT8PhTL3O4vsfDg==}'

h1 = int(hashlib.md5(msg1).hexdigest(), 16)
h2 = int(hashlib.md5(msg2).hexdigest(), 16)

x = ((s1 * s2 * b - (h2 * s1 - h1 * s2 * a)) * gmpy2.invert(r2 * s1 - r1 * s2 * a, q)) % q
flag = '%x' % x
print flag
9d529e2da84117fe72a1770a79cec6ece4065212

angstromCTF 2018 Writeup

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

IRC (MISC 10)

freenodeで#angstromctfチャネルに入る。

11:04 *topic : Need help from an admin? All of the channel operators are admins. | You didn't forget your password, login is currently down! All will be well soon. | IRC flag: actf{irc} | ?ngstromCTF 2018 will start sometime soon PM EDT :: https://angstromctf.com/ | Everything is dead sorry...but soon it shall be reborn
actf{irc}

WARMUP (CRYPTO 10)

暗号はパッと見た感じシーザー暗号。

myjd{ij_fkwizq}

しかし復号できず。問題文の「it's a fine cipher.」からAffine暗号と推測。https://www.dcode.fr/affine-cipherブルートフォースで復号。

A=19,B=12	ACTFITBEGIN
actf{it_begins}

BACK TO BASE-ICS (CRYPTO 20)

Part 1: 011000010110001101110100011001100111101100110000011011100110010101011111011101000111011100110000010111110110011000110000
Part 2:	165 162 137 145 151 147 150 164 137 163 151 170 164 63 63 
Part 3: 6e5f7468317274797477305f733178
Part 4: dHlmMHVyX25vX20wcmV9

それぞれエンコードしているものを復号し、結合する。

Part1: 2進数
Part2: 8進数
Part3: hexエンコード
Part4: Base64エンコード
e1 = '011000010110001101110100011001100111101100110000011011100110010101011111011101000111011100110000010111110110011000110000'

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

e2 = '165 162 137 145 151 147 150 164 137 163 151 170 164 63 63 '
e2 = e2.split(' ')

flag2 = ''
for i in range(len(e2) - 1):
    flag2 += chr(int(e2[i], 8))

e3 = '6e5f7468317274797477305f733178'
flag3 = e3.decode('hex')

e4 = 'dHlmMHVyX25vX20wcmV9'
flag4 = e4.decode('base64')

flag = flag1 + flag2 + flag3 + flag4
print flag
actf{0ne_tw0_f0ur_eight_sixt33n_th1rtytw0_s1xtyf0ur_no_m0re}

XOR (CRYPTO 40)

XORキーは1文字のようなので、平文がactfで始まることを前提に復号する。

with open('ciphertext.txt', 'r') as f:
    data = f.read().strip().decode('hex')

key = ord(data[0]) ^ ord('a')

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

print flag
actf{hope_you_used_a_script}

INTRO TO RSA (CRYPTO 50)

p, q, e, cがわかっているので、そのまま復号する。

p = 169524110085046954319747170465105648233168702937955683889447853815898670069828343980818367807171215202643149176857117014826791242142210124521380573480143683660195568906553119683192470329413953411905742074448392816913467035316596822218317488903257069007949137629543010054246885909276872349326142152285347048927
q = 170780128973387404254550233211898468299200117082734909936129463191969072080198908267381169837578188594808676174446856901962451707859231958269401958672950141944679827844646158659922175597068183903642473161665782065958249304202759597168259072368123700040163659262941978786363797334903233540121308223989457248267
e = 65537
c = 4531850464036745618300770366164614386495084945985129111541252641569745463086472656370005978297267807299415858324820149933137259813719550825795569865301790252501254180057121806754411506817019631341846094836070057184169015820234429382145019281935017707994070217705460907511942438972962653164287761695982230728969508370400854478181107445003385579261993625770566932506870421547033934140554009090766102575218045185956824020910463996496543098753308927618692783836021742365910050093343747616861660744940014683025321538719970946739880943167282065095406465354971096477229669290277771547093476011147370441338501427786766482964
n = p * q

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(c, d, n)

flag = ('%x' % m).decode('hex')
print flag
actf{rsa_is_reallllly_fun!!!!!!}

OFB (CRYPTO 120)

4の倍数の長さになるよう\x00を付ける。その後4バイトずつの配列にする。
4バイト単位で暗号化しているが、PNGのフォーマットを前提にx, a, cがわかる。

x1 = a * x0 + c mod m
x2 = a * x1 + c mod m
x3 = a * x2 + c mod m

x2 - x1 = a * (x1 - x0) mod m
x3 - x1 = a * (x2 - x0) mod m

a = (x2 - x1) * modinv(x1 - x0, m)
c = x1 - (x0 * a) mod m

これで4バイト単位にxor鍵が割り出せるので、復号できる。

import struct

def lcg(m, a, c, x):
    return (a * x + c) % m

def egcd(a, b):
   if a == 0:
       return (b, 0, 1)
   else:
       g, y, x = egcd(b % a, a)
       return (g, x - (b // a) * y, y)

m = pow(2, 32)

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

PNG_HEAD = '\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0d'

x = []
for i in range(0, len(PNG_HEAD), 4):
    iXor = int(ciphertext[i:i+4].encode('hex'), 16)
    iPlain = int(PNG_HEAD[i:i+4].encode('hex'), 16)
    x.append(iXor ^ iPlain)

x1 = x[2] - x[1]
alpha = x[1] - x[0]

g1, p1, q1 = egcd(alpha, m)

if g1 == 1:
    mod_inv = p1 % m
elif g1 == -1:
    mod_inv = - p1 % m
else:
    modinv = 0
a = (x1 * mod_inv) % m

c = (x[1] - x[0] * a) % m

flag = ''
x = x[0]
for i in range(0, len(ciphertext), 4):
    flag += struct.pack('>I', x ^ struct.unpack('>I', ciphertext[i:i+4])[0])
    x = lcg(m, a, c, x)

while True:
    if flag[-1] == '\x00':
        flag = flag[:-1]
    else:
        break

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

復号したPNG画像にフラグが書いてあった。
f:id:satou-y:20180326201114p:plain

actf{pad_rng}

SSH (CRYPTO 150)

公開鍵をPEM形式に変換し、内容を確認する。

$ ssh-keygen -f id_rsa.pub -e -m PKCS8 > id_rsa.pub.pem
$ openssl rsa -pubin -text < id_rsa.pub.pem
Public-Key: (2048 bit)
Modulus:
    00:be:5d:95:8b:09:b2:29:1c:f0:bd:36:51:1c:91:
    55:e2:29:f8:ae:8d:cc:ae:e1:55:2c:96:69:b8:1b:
    53:2a:36:3b:4f:34:76:94:12:86:7c:cf:92:cb:40:
    ad:de:f5:20:0a:05:dc:de:f0:9c:8d:a3:09:82:fa:
    54:13:d9:52:f4:e7:db:3d:a7:39:51:9f:ab:77:d5:
    74:de:52:36:6c:96:03:ac:e8:87:c0:cb:f3:2c:52:
    47:ce:c1:42:28:e8:a7:2a:a5:25:67:99:e5:4c:40:
    f3:a2:2d:46:42:cd:af:5e:0d:d0:77:33:11:58:e7:
    d8:4d:ba:87:56:d5:31:a4:bb:4d:2b:a3:e7:9c:29:
    97:2f:27:eb:8d:0b:f9:df:81:e2:e9:cd:a2:3b:0d:
    de:ad:23:c0:0a:ae:bf:a0:f5:38:32:77:a2:21:77:
    72:9a:9c:b5:ee:58:c4:70:19:b6:cb:32:2d:7f:b9:
    a4:1d:f3:a2:d5:62:df:d2:02:f9:06:3b:5e:5e:50:
    42:cf:ef:6d:dc:fe:41:23:28:67:e1:c1:22:a8:dc:
    c1:8c:e5:1e:fb:b8:cc:5f:9b:c0:f3:29:6f:10:91:
    ca:30:10:ed:85:12:73:d4:ca:40:67:57:53:da:89:
    6a:e5:fc:fa:01:59:3a:7c:84:d5:18:c5:03:c0:ae:
    e5:81
Exponent: 65537 (0x10001)
writing RSA key
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvl2ViwmyKRzwvTZRHJFV
4in4ro3MruFVLJZpuBtTKjY7TzR2lBKGfM+Sy0Ct3vUgCgXc3vCcjaMJgvpUE9lS
9OfbPac5UZ+rd9V03lI2bJYDrOiHwMvzLFJHzsFCKOinKqUlZ5nlTEDzoi1GQs2v
Xg3QdzMRWOfYTbqHVtUxpLtNK6PnnCmXLyfrjQv534Hi6c2iOw3erSPACq6/oPU4
MneiIXdympy17ljEcBm2yzItf7mkHfOi1WLf0gL5BjteXlBCz+9t3P5BIyhn4cEi
qNzBjOUe+7jMX5vA8ylvEJHKMBDthRJz1MpAZ1dT2olq5fz6AVk6fITVGMUDwK7l
gQIDAQAB
-----END PUBLIC KEY-----
n = 0x00be5d958b09b2291cf0bd36511c9155e229f8ae8dccaee1552c9669b81b532a363b4f34769412867ccf92cb40addef5200a05dcdef09c8da30982fa5413d952f4e7db3da739519fab77d574de52366c9603ace887c0cbf32c5247cec14228e8a72aa5256799e54c40f3a22d4642cdaf5e0dd077331158e7d84dba8756d531a4bb4d2ba3e79c29972f27eb8d0bf9df81e2e9cda23b0ddead23c00aaebfa0f5383277a22177729a9cb5ee58c47019b6cb322d7fb9a41df3a2d562dfd202f9063b5e5e5042cfef6ddcfe41232867e1c122a8dcc18ce51efbb8cc5f9bc0f3296f1091ca3010ed851273d4ca40675753da896ae5fcfa01593a7c84d518c503c0aee581

秘密鍵は大部分がマスクされている。与えられた情報から秘密鍵を復元することができれば、フラグが得られそう。
https://tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pemを参考

RSAPrivateKey ::= SEQUENCE {
  version           Version,
  modulus           INTEGER,  -- n
  publicExponent    INTEGER,  -- e
  privateExponent   INTEGER,  -- d
  prime1            INTEGER,  -- p
  prime2            INTEGER,  -- q
  exponent1         INTEGER,  -- d mod (p-1)
  exponent2         INTEGER,  -- d mod (q-1)
  coefficient       INTEGER,  -- (inverse of q) mod p
  otherPrimeInfos   OtherPrimeInfos OPTIONAL
}

バイナリで構成を見てみる。

version         0x0000 - 0x0006 (30 82 04 A3 02 01 00)	7
modulus         0x0007 - 0x010b (02 82 01 01)		261(257)
publicExponent  0x010c - 0x0110 (02 03) 010001		5(3)
privateExponent 0x0111 - 0x0214 (02 82 01 00)		260(256)
prime1          0x0215 - 0x0298 (02 81 81)		132(129)
prime2          0x0299 - 0x031c (02 81 81)		132(129)
exponent1       0x031d - 0x03a0 (02 81 81)		132(129)
exponent2       0x03a1 - 0x0423 (02 81 80)		131(128)
coefficient     0x0424 - 0x04a6 (02 81 80)		131(128)

最初の624バイトが不明。625バイト目以降144バイト分がわかっている。prime1の後半とprime2の前半しかわからない。

prime1: 625-665(下41バイトのみ判明)
prime2: 669-768(上100バイトのみ判明)

片方の素数の上位ビット半分以上が判明しているため、Coppersmith's Attackを行い、素数を割り出す。

# solve.sage
n = 0x00be5d958b09b2291cf0bd36511c9155e229f8ae8dccaee1552c9669b81b532a363b4f34769412867ccf92cb40addef5200a05dcdef09c8da30982fa5413d952f4e7db3da739519fab77d574de52366c9603ace887c0cbf32c5247cec14228e8a72aa5256799e54c40f3a22d4642cdaf5e0dd077331158e7d84dba8756d531a4bb4d2ba3e79c29972f27eb8d0bf9df81e2e9cda23b0ddead23c00aaebfa0f5383277a22177729a9cb5ee58c47019b6cb322d7fb9a41df3a2d562dfd202f9063b5e5e5042cfef6ddcfe41232867e1c122a8dcc18ce51efbb8cc5f9bc0f3296f1091ca3010ed851273d4ca40675753da896ae5fcfa01593a7c84d518c503c0aee581
e = 65537

priv_part = '''
YC2/ZTbmSZFL9t5Em+ic2ayw0nNUSI6XO7+3tcT9TABzh94t9YLhiDcCgYEA0LFZ
OUTgvmnWAkwGSo/6huQOu/7VmsM7OBdFntgotOJXALXFqCeT2PMXyWVc9/6ObUZj
z9LQUlT6mnzYwFrX4mPPOTY5nvCyjepQlSDA7w49yaRhXKCFRHmEieeFJqzrZoQG
'''

der = priv_part.decode('base64')
q_pre = der[44:].encode('hex')

kbits = 29 * 8
q_bar = int(q_pre + '0' * (29 * 2), 16)

PR.<x> = PolynomialRing(Zmod(n))
f = x + q_bar

x0 = f.small_roots(X=2^kbits, beta=0.3)[0]
q = x0 + q_bar
print q

この結果から、p, qは以下の通りとわかる。

q = 146549045227354172989110651205310632574067392372993711861623526130946979713469625772410411197033006823693645223316947254702263484103463390279967082549781844195965622071604103417650285219901124684816890970225510506869249766243257198851929421990982433654502509027141033118789478594390852225097012248592818058247
p = 163982139712862418555526520701188853132435025142588334581109287244065993155706892302449577647543501705795708316995057467949529976798218410513821698432879399377481736303821213515873396833696040164615779257072417113571942677691455176550027493593024586313878742269786944829130979576180492741390980797445630429239

秘密鍵を生成する。

$ rsatool.py -f PEM -o privkey.pem -p 163982139712862418555526520701188853132435025142588334581109287244065993155706892302449577647543501705795708316995057467949529976798218410513821698432879399377481736303821213515873396833696040164615779257072417113571942677691455176550027493593024586313878742269786944829130979576180492741390980797445630429239 -q 146549045227354172989110651205310632574067392372993711861623526130946979713469625772410411197033006823693645223316947254702263484103463390279967082549781844195965622071604103417650285219901124684816890970225510506869249766243257198851929421990982433654502509027141033118789478594390852225097012248592818058247
Using (p, q) to initialise RSA instance

n =
be5d958b09b2291cf0bd36511c9155e229f8ae8dccaee1552c9669b81b532a363b4f34769412867c
cf92cb40addef5200a05dcdef09c8da30982fa5413d952f4e7db3da739519fab77d574de52366c96
03ace887c0cbf32c5247cec14228e8a72aa5256799e54c40f3a22d4642cdaf5e0dd077331158e7d8
4dba8756d531a4bb4d2ba3e79c29972f27eb8d0bf9df81e2e9cda23b0ddead23c00aaebfa0f53832
77a22177729a9cb5ee58c47019b6cb322d7fb9a41df3a2d562dfd202f9063b5e5e5042cfef6ddcfe
41232867e1c122a8dcc18ce51efbb8cc5f9bc0f3296f1091ca3010ed851273d4ca40675753da896a
e5fcfa01593a7c84d518c503c0aee581

e = 65537 (0x10001)

d =
620075bb457b95d4d34ee586ae6957c87e090b7beeb2dd486712ec4c1ead1adf1e7b712bd6a10ee1
744f431a0228f512d076223617b2d0ebed3aa3bae3190faf0b2a003c75b2c2bb988ea882c7da42de
9bf7c922122c2cfd5542a87b2f9f35ded18281962b51338780a5ae1f2cc70d1023967db729a8167b
71d0a45a1c99590f3c4bfa59f6cb99808d1b8c4e5c0ca7feeec7f2c88f6cee0a343be1f1217c23c2
ec0e779740374cffda319dc8797a0b010a58b0ad79e33adc3ca088dcbf4fdb68da954c49b3aa693f
0e0545d6845e3413d720a2df5c98158e4b45d2bd6e2ea08672db20644ea9aec7c192c1087b970564
cd929d43e5ea32f1c1e19a266adb84ed

p =
e984b0820151d7ed6a86569073ea12e64d36fa33bc360082cd1e87ad0618f6dd6f0dbac674170c3d
11f1e8ea1208c48ee3c6313f7703138fc2d2dc8e94fdd4a427bd92a420de8a025d0423e80351acaa
2c92bfe5e11729602dbf6536e649914bf6de449be89cd9acb0d27354488e973bbfb7b5c4fd4c0073
87de2df582e18837

q =
d0b1593944e0be69d6024c064a8ffa86e40ebbfed59ac33b3817459ed828b4e25700b5c5a82793d8
f317c9655cf7fe8e6d4663cfd2d05254fa9a7cd8c05ad7e263cf3936399ef0b28dea509520c0ef0e
3dc9a4615ca08544798489e78526aceb668406cb284ebe0ee0120defe7405ffe7d76fb6bb469183c
b262822c9b6f3407

Saving PEM as privkey.pem

この秘密鍵SSH接続する。

$ ssh -i privkey.pem ctf@web.angstromctf.com -p 3004
The authenticity of host '[web.angstromctf.com]:3004 ([162.243.30.173]:3004)' can't be established.
ECDSA key fingerprint is 85:61:bc:f6:51:7c:c9:44:ec:b3:bd:e3:f6:fc:d0:d6.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[web.angstromctf.com]:3004,[162.243.30.173]:3004' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 16.04.4 LTS (GNU/Linux 4.4.0-116-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage
actf{ssh_keys_not_broken_enough}
Connection to web.angstromctf.com closed.
actf{ssh_keys_not_broken_enough}

BackdoorCTF 2018 Writeup

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

AWESOME-MIX1 (crypto 75)

$ nc 51.15.73.163 8082 
Welcome to admin's music portal.
To verify that you are the owner of this service
send the public key which will verify the following signature :

Message   -> super important information for admin only

Signature -> 43d04e6f9e22904576d5b2db818dcb82d88e733fb50a000ad5183d786f8136e9e2fdf11fe44825d63f7e94b90ddeddb66725a751086357edd6a62401cdbac16c08f801153475bd73f5dfb301007ef070cbac1390df9beb66b693025f822a8851672547d09f7c16466f07712b45d0d71b6559d272adc622d6a70d138f8e63fa64

コードを見ると、以下のような感じ。
s: Signatureの10進数値
e: 65537
n: ???
m: Messageのsha1(d5a8c16ad32b7e74ce624a17a1b3b2427c263389)
pad(m): 1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003021300906052b0e03021a05000414d5a8c16ad32b7e74ce624a17a1b3b2427c263389

pow(s, e, n) == pad(m)
になるようn, eを指定する。

eが小さいほうが計算が早い。3を指定してみる。

import socket
import re
from Crypto.Hash import SHA

def PKCS1_pad(data):
    asn1 = '003021300906052b0e03021a05000414'
    ans = asn1 + data
    n = len(ans)
    padding = '0001' + 'f' * (1024/4 - n - 4)
    return int((padding + ans), 16)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('51.15.73.163', 8082))

data = s.recv(1024)
print data

pattern = 'Signature -> (.+)'
m = re.search(pattern, data)
sig = int(m.group(1), 16)

e = 3

message = 'super important information for admin only'
h = SHA.new(message)
m = h.hexdigest()
pad = PKCS1_pad(m)

n = pow(sig, e) - pad
print n
s.sendall(str(n) + '\n')
print e
s.sendall(str(e) + '\n')

data = s.recv(2048)
print data

実行結果は以下の通り。

Welcome to admin's music portal.
To verify that you are the owner of this service
send the public key which will verify the following signature :

Message   -> super important information for admin only

Signature -> 9db600326a7bfb115481fe078bd12420e91e25d2f63c576253bbda1d240bace75202e5ba5f2fefe9a924258fb47075c5f09880ce0b93bb36e3bba09a69a97de1882fb839363b772e2977c79ba6ff5e67e9e1269f801d6ace9b47171428d917b9cf44e004a242465c821e1aafbeec6077a30c4c47d04ea652e12003df01aba4a0


1358351756754439312376868517955764942617308500821039660391447768219172433407484471720434149398588844051224479476097704653260474521147742197752337211618966969538754558752563625400109949008415884153605726213293179622115349501037957509039084994777112650517856558748051951763910727845519040438532345138108852221060969539008224431649734657173412735827457060186339830910314705536016907064167093349446851226543539586673303923129991164372535641355318540931749183979990122857942574477002602542887612625641000776776563335089371858750840107955118115215185306002698984648597764979075369578200879233516411633397568715792727319942793246729625042550167470345283544356104574756696443518372751117534027952587102670844179898224868761719628957044212603094793130289162603581972325228525246424445218647529216591533294284328042930489863126762952371168966851776626727260187444811963991442153194089236603252815872488778433467803642501454097130474615
3
Enter n:Enter e:

I'm an alligator,
I'm a mama-papa coming for you
I'm the space invader,
I'll be a rock-and-rolling bitch for you

Keep your mouth shut,
You're squawking like a pink monkey bird
And I'm busting up my brains for the words

Keep your 'lectric eye on me babe
Put your ray gun to my head
Press your space face close to mine, love
Freak out in a moonage daydream oh yeah!

Don't fake it baby,
Lay the real thing on me
The church of man, love
Is such a holy place to be

Make me baby,
Make me know you really care
Make me jump into the air

Keep your 'lectric eye on me babe
Put your ray gun to my head
Press your space face close to mine, love
Freak out in a moonage daydream oh yeah!

Keep your 'lectric eye on me babe
Put your ray gun to my head
Press your space face close to mine, love
Freak out in a moonage daydream oh

Keep your 'lectric eye on me babe
Put your ray gun to my head
Press your space face close to mine, love
Freak out in a moonage daydream oh yeah

Freak out, far out, in out

CTF{cryp70_5ur3_15_w13rd}
$ echo -n CTF{cryp70_5ur3_15_w13rd} | sha256sum
93b7d01e2d60c22e5cc3033a86bc9ed577208139581bbae48397b2d656f285d6  -
93b7d01e2d60c22e5cc3033a86bc9ed577208139581bbae48397b2d656f285d6

BIT-LEAKER (crypto 150)

$ nc 51.15.73.163 8083
Welcome to BACKDOORCTF17

PublicKey:

N = 132125452490525272868512719086793824829543883912463086176670664838620633007182240166809947589097680911394793693935584719583603365100626341570568318241861642033202935437181211455076573287843223083918457652758292409212221952378430472710490094002780866599756159336468032017382896815282341434169411799424271491121

e = 65537

c = 12379713349101636227902832624035022346592359493481761589607599941163359935793354396977060791877242742663032926986094100616765497355165937197518287465298505258856776800007343292387728699031072357239640226196638363477668914730912679200629657016016233437304130727476028235554577343009967662591789799781285410844

temp_c =123
 l = 8638
temp_c =456789
 l = 2082
temp_c: 入力
temp_m = pow(temp_c, key.d, key.n)

指定した暗号に対して、復号結果の情報の一部を以下の情報として表示させる。

l = str(((temp_m&5) * random.randint(1,10000))%(2*(random.randint(1,10000))))

ランダムの数値を使っているが、以下のことがわかる。

[復号数値&5] * [ランダムA] % (2 * ランダムB)
・この結果が奇数のときは必ず復号数値は奇数
・この結果が偶数のときは復号数値は偶奇どちらも可能性がある。
 ⇒10回くらい試して、すべて偶数の場合偶数と判断する。
  1回でも奇数の場合があった場合は奇数を判断する。

以上の情報から、LSB decryption oracle attackで復号する。

from fractions import Fraction
import socket
import re

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

def lsb_oracle(s, cipher):
    for i in range(10):
        data = recvuntil(s, 'temp_c =')
        print data + str(cipher)
        s.sendall(str(cipher) + '\n')
        data = recvuntil(s, '\n')
        print data

        l = int(data.split(' = ')[1])
        if l % 2 == 1:
            return 1
    return 0

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('51.15.73.163', 8083))
data = recvuntil(s, '\n').strip()
print data
data = recvuntil(s, '\n').strip()
print data
data = recvuntil(s, '\n').strip()
print data
data = recvuntil(s, '\n').strip()
print data
data = recvuntil(s, '\n').strip()
print data
N = int(data.split(' = ')[1])
data = recvuntil(s, '\n').strip()
print data
data = recvuntil(s, '\n').strip()
print data
e = int(data.split(' = ')[1])
data = recvuntil(s, '\n').strip()
print data
data = recvuntil(s, '\n').strip()
print data
c = int(data.split(' = ')[1])

bounds = [0, Fraction(N)]

i = 0
m = 0
while True:
    print 'Round %d' % (i+1)
    i += 1

    c2 = (c * pow(2, e, N)) % N
    lsb = lsb_oracle(s, 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 = ('%x' % m).decode('hex')
print flag

実行結果は以下の通り。

    :
Round 1024
temp_c =35787795981286419742136296604317346853455644236933262220366195275020097573849339965175921268712745735906219041017373711598867167379282050083120679226952404156408554223787296491985019760199325109854563160312420870232970499884525855365692632209687348163871549869814935790064329250398905518396441921378974751032
 l = 1675

0
CTF{r1v35t_5h4m1r_4dl3m4n_cr4ck1ng_15_fun_:)}
$ echo -n CTF{r1v35t_5h4m1r_4dl3m4n_cr4ck1ng_15_fun_:\)} | sha256sum
744dc1a4f946892bba87b6dd7b4bf001cc80cf5d4f1141b0274b148de87b8d2c  -
744dc1a4f946892bba87b6dd7b4bf001cc80cf5d4f1141b0274b148de87b8d2c

b00t2root '18 Writeup

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

RSA-2 (Crypto 200)

2組の平文の差と暗号文がわかっているので、Franklin-Reiter Related Message Attackで復号する。

# solve.sage
from hashlib import sha256

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 = 114725527397185618184017233206819193913174443780510744606142335459665478168081417742295326326458510125306461590118257162988125409459000413629137879229803717947627133370343339582895822944017711093729671794212087753322731071609302218014807365556283824229308384059742494244873283137838666434755861643308137132991

e = 12289

c1 = 87410813732157727701928184577314318681587457726095432638836338681211650253979034474596959990411435773763619929643745595561018045828590610328140736165000754846648327232298646701600080979346670157972588491030528441191645554122003288005262778737855011793921980764813901447954145380508985385929190951189001811183

c2 = 13405530225102142120310029551994876557837309021095393214835868463875586021351445304878372433515568058695315045246405214821866800311993984428311760617739453096525618753380012567374424357402936368910846206438489444245873338816383688500972351617863313954864557810634214028546221991063291427532826592675208605764

pad1 = int('qwerty'.encode('hex'), 16)
pad2 = int('asdfgh'.encode('hex'), 16)
diff = pad1 - pad2
m = (related_message_attack(c2, c1, diff, e, n) - pad2) >> (8 * 6)
flag = ('%x' % m).decode('hex')
print flag
b00t2root{M4ny_pa7hs_up_th3_m0unt4in_4ll_l3ading_t0_th3_s4me_plac3}

hlea (Crypto 200)

Hash Length Extention Attackの問題。

hash(SALT + 'a') = 既知ハッシュ
hash(SALT + 'a' + '???' + 'administrator') 

HashPumpを使って、'???'とハッシュ値を取得、提示することができれば、フラグが得られる。

import socket
import re
import subprocess

def encHex(s):
    enc = ''
    xcode = ''
    for i in range(len(s)):
        if s[i] == '\\' and len(xcode) == 0:
            xcode += s[i]
        elif s[i] == 'x' and len(xcode) == 1:
            xcode += s[i]
        elif len(xcode) == 2:
            xcode += s[i]
        elif len(xcode) == 3:
            xcode += s[i]
            enc += xcode[2:]
            xcode = ''
        else:
            enc += '%02x' % ord(s[i])
    return enc

cmd_format = 'hashpump -s %s -d a -k %d -a administrator'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('18.188.130.86', 1223))

for j in range(2):
    data = s.recv(1024)
    print data

choice = '1'
print choice
s.sendall(choice + '\n')
data = s.recv(1024)
print data

username = 'a'
print username
s.sendall(username + '\n')
data = s.recv(1024)
print data
pattern = 'Here\'s your token\:(.+)'
m = re.search(pattern, data, re.DOTALL)
h = m.group(1).replace('\n', '').split(':')[1]
data = s.recv(1024)
print data

for i in range(1, 32):
    print 'SALT Length: %d' % i
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('18.188.130.86', 1223))

    for j in range(2):
        data = s.recv(1024)
        print data

    choice = '2'
    print choice
    s.sendall(choice + '\n')
    data = s.recv(1024)
    print data

    cmd = cmd_format % (h, i)
    print cmd
    ret = subprocess.check_output(cmd.split(' '))
    print ret
    try_ans = ret.split('\n')

    token = encHex(try_ans[1])
    token += ':' + try_ans[0]
    print token
    s.sendall(token + '\n')
    data = s.recv(1024)
    print data

    if 'Invalid Token' not in data:
        data = s.recv(1024)
        print data
        break

実行結果は以下の通り。

     :
SALT Length: 15
What do you want to do?

[1] Register
[2] Login
Enter your choice: 
2
Enter your token: 
hashpump -s d346d2179993563cfc0cc81ff7d31f627ca9cce5645ea5b8d0b6588d975f0396 -d a -k 15 -a administrator
30dea6e09040845ef0474fe8b15490f2afc5e36a4e14a808c065e9b054b996d2
a\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80administrator

6180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008061646d696e6973747261746f72:30dea6e09040845ef0474fe8b15490f2afc5e36a4e14a808c065e9b054b996d2
Welcome a��administrator!

Here's the flag: b00t2root{51mpl3_h45h_l3ng7h_3x73n510n_4774ck}
Closing connection.
b00t2root{51mpl3_h45h_l3ng7h_3x73n510n_4774ck}

unZipitCat (Forensics 150)

ZIPの既知平文攻撃。

$ zipinfo ZIPIT.zip 
Archive:  ZIPIT.zip   1201088 bytes   5 files
-rw-r--r--  3.0 unx       47 TX stor 16-Mar-18 17:00 gg
-rw-rw-r--  3.0 unx  1023704 BX defN 13-Mar-18 23:04 catgif.gif
-rw-rw-r--  3.0 unx   186442 BX defN 13-Mar-18 23:05 minion.gif
-rw-rw-r--  3.0 unx    10342 BX defN 13-Mar-18 23:06 TaPz.gif
-rw-r--r--  3.0 unx        5 TX stor 16-Mar-18 17:01 flag
5 files, 1220540 bytes uncompressed, 1200218 bytes compressed:  1.7%
$ ls -l am.gif
-rwxrwxrwx 1 root root 1023704  317 18:26 am.gif
$ zip am.zip ./am.gif
  adding: am.gif (deflated 1%)
$ ./pkcrack-1.2.2/src/pkcrack -C ZIPIT.zip -c catgif.gif -p am.gif -P am.zip -d decrypted.zip
          :
Stage 2 completed. Starting zipdecrypt on Sat Mar 17 18:59:11 2018
Decrypting gg (373132b4e5a73cb6227ad66b)... OK!
Decrypting catgif.gif (f9fac3f69bc4ecbde55f609c)... OK!
Decrypting minion.gif (275d6236c096e1f8116f749c)... OK!
Decrypting TaPz.gif (a7c27d9a7f06f41ad7e5849c)... OK!
Decrypting flag (e65781eb8c4ddbbbbc1ce26b)... OK!
Finished on Sat Mar 17 18:59:11 2018
$ unzip decrypted.zip 
Archive:  decrypted.zip
 extracting: gg                      
  inflating: catgif.gif              
  inflating: minion.gif              
  inflating: TaPz.gif                
replace flag? [y]es, [n]o, [A]ll, [N]one, [r]ename: y
 extracting: flag
$ cat gg
b00t2root{1t_only_requ1r3s_a_k3y_noth1ng_els3}
b00t2root{1t_only_requ1r3s_a_k3y_noth1ng_els3}

Thousand Milliard (Misc 120)

SEEDを元に最初のランダムな値を求め、そこから四則演算や今の値を答えていく。

import socket
import re
import random

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(('18.188.130.86', 1000))

for i in range(8):
    data = recvuntil(s, '\n')
    print data
    if i == 2:
        pattern = 'SEED: (.+)\n'
        m = re.search(pattern, data)
        SEED = int(m.group(1))

random.seed(SEED)
now = random.randint(0, 1000000000000)

for i in range(600):
    print 'Round %d' % (i+1)

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

    first = data.split(' ')[0]
    op = data.split(' ')[2]
    num = data.split(' ')[4]
    num_sub = data.split(' ')[1]

    ans = ''
    if first == 'What':
        ans = str(now)
    elif first == 'Subtract':
        ans = str(now - int(num_sub))
    elif op == 'added':
        ans = str(now + int(num))
    elif op == 'multiplied':
        ans = str(now * int(num))
    elif op == 'divided':
        ans = str(now / int(num))

    print ans
    s.sendall(ans + '\n')
    now = int(ans)

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

実行結果は以下の通り。

   :
Round 600
Subtract 4960 from your number

739060326684744025972495958689
You did it
Here is your reward


b00t2root{h0pe_y0u_g0t_this_3asily}
b00t2root{h0pe_y0u_g0t_this_3asily}

N1CTF 2018 Writeup

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

baby_N1ES (Crypto)

カスタマイズしたDESのような処理。keyはわかっているので、encrypt関数を参考にdecrypt関数を作って、復号すればよい。
次のようにN1ES.pyにコードを追加する。

      :
class N1ES:
      :
    def encrypt(self, plaintext):
        if (len(plaintext) % 16 != 0 or isinstance(plaintext, bytes) == False):
            raise Exception("plaintext must be a multiple of 16 in length")
        res = ''
        for i in range(len(plaintext) / 16):
            block = plaintext[i * 16:(i + 1) * 16]
            L = block[:8]
            R = block[8:]
            for round_cnt in range(32):
                L, R = R, (round_add(L, self.Kn[round_cnt]))
            L, R = R, L
            res += L + R
        return res

## 以下、追加 ##
    def decrypt(self, ciphertext):
        if (len(ciphertext) % 16 != 0 or isinstance(ciphertext, bytes) == False):
            raise Exception("ciphertext must be a multiple of 16 in length")
        res = ''
        for i in range(len(ciphertext) / 16):
            block = ciphertext[i * 16:(i + 1) * 16]
            L = block[:8]
            R = block[8:]
            for round_cnt in range(32):
                L, R = (round_add(R, self.Kn[round_cnt])), L
            L, R = R, L
            res += L + R
        return res

あとは以下のコードで復号する。

from N1ES import N1ES
import base64

key = 'wxy191iss00000000000cute'
n1es = N1ES(key)
cipher = base64.b64decode('HRlgC2ReHW1/WRk2DikfNBo1dl1XZBJrRR9qECMNOjNHDktBJSxcI1hZIz07YjVx')
flag = n1es.decrypt(cipher)
print flag
N1CTF{F3istel_n3tw0rk_c4n_b3_ea5i1y_s0lv3d_/--/}

easy_fs (Crypto)

$ nc 47.52.195.203 2333
==============================
   Welcome to Nu1L Easy FS    
==============================
   1. File list               
   2. Read encrypted file     
   3. Custom encryption       
   4. Quit                    
==============================
   Input your choice:         
==============================
1
total 8
-rwxr-xr-x 1 root root  16 Mar 10 17:28 a_small_file
-rwxr-xr-x 1 root root 216 Mar 10 17:28 flag
==============================
   Welcome to Nu1L Easy FS    
==============================
   1. File list               
   2. Read encrypted file     
   3. Custom encryption       
   4. Quit                    
==============================
   Input your choice:         
==============================
2
Input filename:
flag
Now generating N......
N = 0xb93c631540d83109f523e342985fd282748b01b8c2a4d324f2cd7d507addd33b3844d2972a4258de132d277291cde1bb89862efec2ad040fb15fc68f92ca313a833f889851bca2eaed462813dc4cc9a02598872b7538e231a3f0598150a58e9106a3dba1efe87eff119c2185f0b0c20eed78aaab5c8c4e5fff5087e264391b45938d2a315212afa028b49b2a7c898da4d4cac558b20e8b43093b4ff0d1b531298ae4e346633d2751b703063de6b0af346023a01441055500c5d7d417e77bc9864fb20c6280e8d141752d8d37a2602fc0b275dd85bae7e403eb4631f60c8f3d8b574d52d41bb4080bc582e59fff701bd151d7f8777c8b0ce7d1df988f89d2769
Please input your E: (Hexadecimal, without '0x/0X', 1 - 16 digits)
3
e = 0x3
Success!
C = 0x9d0dccdfe8ff3778b578f4f872e4ad10c33fae97b292d19435b6f577426dc09187593e00a5d81d415a4e849e3721040a10381a7111ac36eadbbddb1983938f8f49d6055153badb5caa2587391510a1118591abd7ccd6b493edf6f413ed7411811e4a7fa48b161b9cda96f8b64b853912d509502203462b
You can just read once :)
$ nc 47.52.195.203 2333
==============================
   Welcome to Nu1L Easy FS    
==============================
   1. File list               
   2. Read encrypted file     
   3. Custom encryption       
   4. Quit                    
==============================
   Input your choice:         
==============================
2
Input filename:
flag
Now generating N......
N = 0x6bfd9bbd41dc934f85f0ae0df7ff303999fd13a4071010d3b6ad2677da0bea82906b9fe8c2966278c6f21c8531aa5ace28982ef0cfdaa2326c5a5a77e52e26798b2d9d143320dfafeb754de59c96e472e3583e49774c37b7d620d95f1f2a741097c17b17863432a49ae7c785d4b5514378cbc4298e1d79fbd0c8d8ce787df4442088b28ea2f875197b822b1573b97a31ec76eb2fbc8adc04c7417e9ba6504f2847d8eaf7d617d6b2db6fdf7efb32c3cc45100fb94d96629abaaf1fbf0bc234988d259d18e2a301d6572a52ed6bb1913594b89f5eb98bc447d9cb7f75e66d70838527b793af01bb19f46fffc9885486e06a7e9410aea6fb52caaa18a083ea142d
Please input your E: (Hexadecimal, without '0x/0X', 1 - 16 digits)
3
e = 0x3
Invalid E, use 0x10001
Success!
C = 0x850ae708ae14dba31e70876be5cdb33456c1235c9b5b019b98678a2b3e5fbc4149e9110606287e5b70fcfbbde13d70d767caf1a1504066e9da0e1b9cf459d561f1fbd9cd56e4407800735de9bd67b28d4b6a884a1de6b6e486715cad0828414bde3cf41010c604d9c0f81ceec98f6da1c61503eac8788e05e786d6f1f0fa776c53cad4b6221afb587366fa9e5ab0403545df67eb71fed057dd5e6e8382a0b15d431c9c29c3b5bed5dbce659f58d94983d73b35ade95f8858de98408c40f135cd688ff78e2e8fe20d14eb7d916d67405906f033438a2203c678fdf97cb51f15c36ce9b8a5969d98b2f979fbcec2579624a93042f425487eb0839dbd440200d49

flagファイルの内容をRSA暗号したデータを入手できる。nはランダムで、eは指定できるが同じ値でも受け付けてもらえないことがある。
Custom encryptionで有効なeを確認しながら、有効なeに対応する暗号化データをe種類の情報が得られるまで収集する。
eが3の場合は、3個収集できれば、Hastad's broadcast attackで復号できる。

[1回目]
==============================
   Welcome to Nu1L Easy FS    
==============================
   1. File list               
   2. Read encrypted file     
   3. Custom encryption       
   4. Quit                    
==============================
   Input your choice:         
==============================
2
Input filename:
flag
N = 0x6ec96a53b50bb87fb6ebccc68640d18ba0c5b7798319ba551971d2444867f85b819524cf686271d07c08b0c6daff51485fed221dada9abf50b76c1b21578d9114eb6e8b04439f08567d848d103657f817b3cc9f0f56d6aaa865fdec5f3364d4e973a615d3c678c46d24ef6a5092467af5282d759ac85ab6ddc86f7940538013a8ac2a75b8f55d218d676b22e0233e75626c794cd5df64b3ca8e4d761f596d717641060a070ec85451321f862d8b6e264798656159acebf68ef653455550ed9d73e8dd6e322610ff6fe554945adde3df0e934e5d59c365226238c54b96dea088cac8ef748634160ae6807a6672628926c81a8975450e27337306f88e4e9ff8c85
Please input your E: (Hexadecimal, without '0x/0X', 1 - 16 digits)
3
e = 0x3
Success!
C = 0x3a7bdc206e63785573ef49ca4e4bf812237f2cdc0e0204978ab2d6d4e6faa3bbbaea937608bbf000f3f4fbdd1a6a9d690015072c75e1daba85cc3bb8e1ff1428161e67213b104e7ac9435bfcb504a733fcb89df0af34c7c73c4df2bf1c97e3e56bee4f3f1dd52fcda4f40a5e33c5486135afaaa1f5032ea59bd71517b8b692e512322cdd2ba03df5b41f107939d5f361862b58fd2973a296b0c4c9bf095e181746b69d0da6ec625a00e740fe6e3dcdd83e76f0a9511a868832aa536aa0866debba92369a491d240939956266239a3e242f3d25a3415ddcccbd47c4ccc1eb376062b19e4018a29991f870786e7195eba1bd0d1cf8790832deff6f9fa5eaa49489

[2回目]
==============================
   Welcome to Nu1L Easy FS    
==============================
   1. File list               
   2. Read encrypted file     
   3. Custom encryption       
   4. Quit                    
==============================
   Input your choice:         
==============================
2
Input filename:
flag
N = 0x34becd5b321d34414e7b13d9499f29a00bfeda5dcaaeadaf27de31a8afb45c303ef0e6734575d106973189de4ffdd5646eed5a90814c5f5b40bbf504e4ee4dfa50e6c99a80cb166fe7612756951a3edefe5d61c461938001c65bbf89adae4f16d4ac7ec4890b7899cea9ed284add7500af5a41b436e7314a90c2ce763264f3ada70a747663bb5a7b0648672103b1bf1fb93cf9f66bc3d262f2cd53b5be6f7cb2b1d44f9768faf53c7a9cfedaad46958a71278ede34c7dfb4a0c4b51a2e2526e333d4a2da11d6f0bb97ec41982046684204ec8b4903bbc92f7d9736d82b4d388b8624d9b9ba1569ecdd818c0ccbaa868d892a012f65b332ab4ed7d002ec505fcd
Please input your E: (Hexadecimal, without '0x/0X', 1 - 16 digits)
3
e = 0x3
Success!
C = 0xe1cb3e348dcf16e66ed1ec2fc9c91289aaf532187807608983a53211cadf1080e3d7ed54fe5fa8e2dacdd6c101c0d02bcba1a69c0f14d1c80fd880e7480c2695087570e16e07bdf6216578354701a7b729c1cba93f06e0dfe3e4df60db9dcd688290ea90abca0864595fe69c7c3454781db4e150fe411374a9f9d57f1bca655148bc3fac531ffb49b5008f7465242ce8eaec4b0e9641f0455ba9e634f8ffb6a12be2099c926d8b7e47d496ddbd5c34ede8c0cf116575cc5e646bc68762da3d1b3e01db463fe1f5dbcf66e250b949b5e46aeb4d14214dfeefc0f47b9f8124fb7f600003f267e6ffd09d1c5c59f82540e4acbd25e756e6999c16c897675775076

[3回目]
==============================
   Welcome to Nu1L Easy FS    
==============================
   1. File list               
   2. Read encrypted file     
   3. Custom encryption       
   4. Quit                    
==============================
   Input your choice:         
==============================
2
Input filename:
flag
N = 0x1506b0cb9d5eed45f8a1ebd9721f43214d857fdb21980b21e6c031e48a1194d520c9fd6b48adc1a1d12a1708b81ff7d4ee77d06e21567cc3bf0bc52036ef65fa96ae3efb871d32db854d0db286b7fdd7013cb7d71873a0a8e379fa1d38a385179cc0eaa55b617236d993c39ea4b4995eb7b5fcfa06c8d50ed3c245d7a72d57b8817f2cef8ad57b5c71482c14c210b3ace47539f109d117015f6c8bb82bd8dcbfe4985d5fd0bc7e1cf58275dc628538b3d64ad5f30404072858fe6298380795814e95ec4abb83ba4ece47f2f3a35bbe66b5eb76208540cac03f7c71d17ab37fa466f0cfc5514c06bc98e5982a19d8a6d57ad5107a9e1dc24b119f9ca852744569
Please input your E: (Hexadecimal, without '0x/0X', 1 - 16 digits)
3
e = 0x3
Success!
C = 0x132e7f056cdd5e4f980ca06a29ba16697672a78ead81040ca452d071189e8917757cc33ff40738d4bddcbfea638c4e1ea61c6606ed5430085257a0aa2c17fa7d5ffc4987868374b5c427457ae235e7c692c27629a7039a750781619ac3534a3d4cfcf76b6cec65c9a05668382a8fa998db0796bd735bd3fc9bee82b1e31d23f4e415cbb75fcf3dc957684e619e38467b28b1d33881c17da9fe94577dccdf7aae4a7d05de3888adc4fefff07bb3fd06e317ecee61261919a46019a129c1b22dc21c600931e84efd59acdb45393cdcdb9649d2d5c64bd3878b9a0a3afabec1e4237fcf91f495b44effeeeb0453bae965943e5882863044581cf65576ff3150d850

以上の情報から復号する。

N1 = 0x6ec96a53b50bb87fb6ebccc68640d18ba0c5b7798319ba551971d2444867f85b819524cf686271d07c08b0c6daff51485fed221dada9abf50b76c1b21578d9114eb6e8b04439f08567d848d103657f817b3cc9f0f56d6aaa865fdec5f3364d4e973a615d3c678c46d24ef6a5092467af5282d759ac85ab6ddc86f7940538013a8ac2a75b8f55d218d676b22e0233e75626c794cd5df64b3ca8e4d761f596d717641060a070ec85451321f862d8b6e264798656159acebf68ef653455550ed9d73e8dd6e322610ff6fe554945adde3df0e934e5d59c365226238c54b96dea088cac8ef748634160ae6807a6672628926c81a8975450e27337306f88e4e9ff8c85
C1 = 0x3a7bdc206e63785573ef49ca4e4bf812237f2cdc0e0204978ab2d6d4e6faa3bbbaea937608bbf000f3f4fbdd1a6a9d690015072c75e1daba85cc3bb8e1ff1428161e67213b104e7ac9435bfcb504a733fcb89df0af34c7c73c4df2bf1c97e3e56bee4f3f1dd52fcda4f40a5e33c5486135afaaa1f5032ea59bd71517b8b692e512322cdd2ba03df5b41f107939d5f361862b58fd2973a296b0c4c9bf095e181746b69d0da6ec625a00e740fe6e3dcdd83e76f0a9511a868832aa536aa0866debba92369a491d240939956266239a3e242f3d25a3415ddcccbd47c4ccc1eb376062b19e4018a29991f870786e7195eba1bd0d1cf8790832deff6f9fa5eaa49489

N2 = 0x34becd5b321d34414e7b13d9499f29a00bfeda5dcaaeadaf27de31a8afb45c303ef0e6734575d106973189de4ffdd5646eed5a90814c5f5b40bbf504e4ee4dfa50e6c99a80cb166fe7612756951a3edefe5d61c461938001c65bbf89adae4f16d4ac7ec4890b7899cea9ed284add7500af5a41b436e7314a90c2ce763264f3ada70a747663bb5a7b0648672103b1bf1fb93cf9f66bc3d262f2cd53b5be6f7cb2b1d44f9768faf53c7a9cfedaad46958a71278ede34c7dfb4a0c4b51a2e2526e333d4a2da11d6f0bb97ec41982046684204ec8b4903bbc92f7d9736d82b4d388b8624d9b9ba1569ecdd818c0ccbaa868d892a012f65b332ab4ed7d002ec505fcd
C2 = 0xe1cb3e348dcf16e66ed1ec2fc9c91289aaf532187807608983a53211cadf1080e3d7ed54fe5fa8e2dacdd6c101c0d02bcba1a69c0f14d1c80fd880e7480c2695087570e16e07bdf6216578354701a7b729c1cba93f06e0dfe3e4df60db9dcd688290ea90abca0864595fe69c7c3454781db4e150fe411374a9f9d57f1bca655148bc3fac531ffb49b5008f7465242ce8eaec4b0e9641f0455ba9e634f8ffb6a12be2099c926d8b7e47d496ddbd5c34ede8c0cf116575cc5e646bc68762da3d1b3e01db463fe1f5dbcf66e250b949b5e46aeb4d14214dfeefc0f47b9f8124fb7f600003f267e6ffd09d1c5c59f82540e4acbd25e756e6999c16c897675775076

N3 = 0x1506b0cb9d5eed45f8a1ebd9721f43214d857fdb21980b21e6c031e48a1194d520c9fd6b48adc1a1d12a1708b81ff7d4ee77d06e21567cc3bf0bc52036ef65fa96ae3efb871d32db854d0db286b7fdd7013cb7d71873a0a8e379fa1d38a385179cc0eaa55b617236d993c39ea4b4995eb7b5fcfa06c8d50ed3c245d7a72d57b8817f2cef8ad57b5c71482c14c210b3ace47539f109d117015f6c8bb82bd8dcbfe4985d5fd0bc7e1cf58275dc628538b3d64ad5f30404072858fe6298380795814e95ec4abb83ba4ece47f2f3a35bbe66b5eb76208540cac03f7c71d17ab37fa466f0cfc5514c06bc98e5982a19d8a6d57ad5107a9e1dc24b119f9ca852744569
C3 = 0x132e7f056cdd5e4f980ca06a29ba16697672a78ead81040ca452d071189e8917757cc33ff40738d4bddcbfea638c4e1ea61c6606ed5430085257a0aa2c17fa7d5ffc4987868374b5c427457ae235e7c692c27629a7039a750781619ac3534a3d4cfcf76b6cec65c9a05668382a8fa998db0796bd735bd3fc9bee82b1e31d23f4e415cbb75fcf3dc957684e619e38467b28b1d33881c17da9fe94577dccdf7aae4a7d05de3888adc4fefff07bb3fd06e317ecee61261919a46019a129c1b22dc21c600931e84efd59acdb45393cdcdb9649d2d5c64bd3878b9a0a3afabec1e4237fcf91f495b44effeeeb0453bae965943e5882863044581cf65576ff3150d850

import functools
import itertools

def chinese_remainder(n, a):
    sum = 0
    prod = functools.reduce(lambda a, b: a*b, n)
    for n_i, a_i in zip(n, a):
        p = prod // n_i
        sum += a_i * mul_inv(p, n_i) * p
    return sum % prod
 
def mul_inv(a, b):
    b0 = b
    x0, x1 = 0, 1
    if b == 1: return 1
    while a > 1:
        q = a // b
        a, b = b, a%b
        x0, x1 = x1 - q * x0, x0
    if x1 < 0: x1 += b0
    return x1

def inv_pow(c, e):
    low = -1
    high = c+1
    while low + 1 < high:
        m = (low + high) // 2
        p = pow(m, e)
        if p < c:
            low = m
        else:
            high = m
    m = high
    assert pow(m, e) == c
    return m
 
N = [N1, N2, N3]
C = [C1, C2, C3]
e = len(N)
a = chinese_remainder(N, C)
for n, c in zip(N, C):
    assert a % n == c
m = inv_pow(a, e)
flag = ('%x' % m).decode('hex')
print flag

実行結果は以下の通り。

Particular applications of the Coppersmith method for attacking RSA include cases when the public exponent e is small or when partial knowledge of the secret key is available. N1CTF{A_sm4ll_l34k_l3ad5_t0_l4rge_br34k}
N1CTF{A_sm4ll_l34k_l3ad5_t0_l4rge_br34k}

rsa_padding (Crypto)

まず以下のコードで暗号化のコードを得る。

import socket
import re
import itertools
import string
import hashlib

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('47.75.39.249', 23333))

data = s.recv(256)
print data

pattern = 'sha256\(\"(.+)\"\+str\)\.hexdigest\(\)\.startswith\(\"(.+)\"\)'
m = re.search(pattern, data)
pre_str = m.group(1)
pre_hash = m.group(2)

for c in itertools.product(string.digits + string.letters, repeat=4):
    tail_str = ''.join(c)
    if hashlib.sha256(pre_str + tail_str).hexdigest().startswith(pre_hash):
        break

print tail_str
s.sendall(tail_str + '\n')

data = s.recv(1024)
data += s.recv(1024)
data += s.recv(1024)
print data
print '1'
s.sendall('1\n')
data = s.recv(4096)
print data

以下のコードでサーバが動作していることがわかる。

#!/usr/bin/env python3
# -*- coding=utf-8 -*-

from Crypto.Util.number import getPrime, GCD, bytes_to_long
from hashlib import sha256
import random
import signal
import sys, os

signal.alarm(20)

m = b"xxxxxxxxxxxxxx"
n = 21727106551797231400330796721401157037131178503238742210927927256416073956351568958100038047053002307191569558524956627892618119799679572039939819410371609015002302388267502253326720505214690802942662248282638776986759094777991439524946955458393011802700815763494042802326575866088840712980094975335414387283865492939790773300256234946983831571957038601270911425008907130353723909371646714722730577923843205527739734035515152341673364211058969041089741946974118237091455770042750971424415176552479618605177552145594339271192853653120859740022742221562438237923294609436512995857399568803043924319953346241964071252941
e = 3

def welcom():
    batch = """
 _   _      __ _         _____ _______ ______
| \ | |    /_ | |       / ____|__   __|  ____|
|  \| |_   _| | |      | |       | |  | |__
| . ` | | | | | |      | |       | |  |  __|
| |\  | |_| | | |____  | |____   | |  | |
|_| \_|\__,_|_|______|  \_____|  |_|  |_|

_|_|_|      _|_|_|    _|_|          _|_|_|    _|_|    _|      _|  _|_|_|_|
_|    _|  _|        _|    _|      _|        _|    _|  _|_|  _|_|  _|
_|_|_|      _|_|    _|_|_|_|      _|  _|_|  _|_|_|_|  _|  _|  _|  _|_|_|
_|    _|        _|  _|    _|      _|    _|  _|    _|  _|      _|  _|
_|    _|  _|_|_|    _|    _|        _|_|_|  _|    _|  _|      _|  _|_|_|_|      
"""
    print(batch)

def proof():
    strings = "abcdefghijklmnopqrstuvwxyzWOERFJASKL"
    prefix = "".join(random.sample(strings, 6))
    starwith = str(random.randint(10000, 99999))
    pf = """
sha256("%s"+str).hexdigest().startswith("%s") == True
Please give me str
"""%(prefix, starwith)
    print(pf)
    s = input().strip()
    if sha256((prefix+s).encode()).hexdigest().startswith(starwith):
        return True
    else:
        return False

def cmd():
    help = """
1. get code
2. get flag
Please tell me, what you want?
"""
    while True:
        print(help)
        c = input().strip()
        if c == "1":
            return True
        elif c == "2":
            return False
        else:
            print("Enter Error!")

def main():
    if not proof():
        print("Check Failed!")
        return
    welcom()
    if cmd():
        f = open("file.py")
        print(f.read())
        return
    mm = bytes_to_long(m)
    assert pow(mm, e) != pow(mm, e, n)
    sys.stdout.write("Please give me a padding: ")
    padding = input().strip()
    padding = int(sha256(padding.encode()).hexdigest(),16)
    c = pow(mm+padding, e, n)
    print("Your Ciphertext is: %s"%c)

if __name__ == '__main__':
    main()

このコードを見ると、入力した文字列をsha256したものをプラスして暗号化している。
2パターンのプラスした値とその暗号のペアを得ることができれば、Franklin-Reiter Related Message Attackで復号できる。
まず2パターンのデータを得る。

import socket
import re
import itertools
import string
import hashlib

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('47.75.39.249', 23333))

data = s.recv(256)
print data

pattern = 'sha256\(\"(.+)\"\+str\)\.hexdigest\(\)\.startswith\(\"(.+)\"\)'
m = re.search(pattern, data)
pre_str = m.group(1)
pre_hash = m.group(2)

for c in itertools.product(string.digits + string.letters, repeat=4):
    tail_str = ''.join(c)
    if hashlib.sha256(pre_str + tail_str).hexdigest().startswith(pre_hash):
        break

print tail_str
s.sendall(tail_str + '\n')

data = s.recv(1024)
data += s.recv(1024)
data += s.recv(1024)
print data
print '2'
s.sendall('2\n')
data = s.recv(1024)
print data

pad = 'a'
print pad
s.sendall(pad + '\n')
data = s.recv(1024)
print data

上記はaを入力したパターンで、以下の結果が得られた。

Your Ciphertext is: 14550589053226237723784378782911157204367764723816660214902465302717810822172021499672797688101690836227536656522669877557513678726451567934772958540881870505027812672770632131417158771747207631406190598426624499318100261365041342574487198815092211749558434576684546969401968650344265478567760472921329074234371969691478468223496149493810842447332952014841777151128845522350571280750495062964803524353826424837916218613026269361845916011417547716655151014908270156235727462738771351489212657321400055033617868608092797200321917477463676573100374585542816545631299684842142462655847321565714756762304545470196198340385

同様にbの場合は以下の結果が得られた。

Your Ciphertext is: 14550589053226237723784378782911157204367764723812418468791010699229875830463647355158844707705886546430710672657478975121784614197819889919141286816518375155221497149366869190177475417434888910395438707022979956329590154012896676989382415396402741552931485301047383627371895227749949128440632032518188813954527856952846604591394762443469965587269478029933983601737740826745184166883892469527983987217466171262168882709572986062075929388484286172431551877578026734690125212518284473430174129218378709797148961525512754353241835286245103580265565835556405824029469560545138200760717522255129021253354200673658556765641

この情報から復号する。

# related_message_attack.sage
from hashlib import sha256

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 = 21727106551797231400330796721401157037131178503238742210927927256416073956351568958100038047053002307191569558524956627892618119799679572039939819410371609015002302388267502253326720505214690802942662248282638776986759094777991439524946955458393011802700815763494042802326575866088840712980094975335414387283865492939790773300256234946983831571957038601270911425008907130353723909371646714722730577923843205527739734035515152341673364211058969041089741946974118237091455770042750971424415176552479618605177552145594339271192853653120859740022742221562438237923294609436512995857399568803043924319953346241964071252941
c1 = 14550589053226237723784378782911157204367764723816660214902465302717810822172021499672797688101690836227536656522669877557513678726451567934772958540881870505027812672770632131417158771747207631406190598426624499318100261365041342574487198815092211749558434576684546969401968650344265478567760472921329074234371969691478468223496149493810842447332952014841777151128845522350571280750495062964803524353826424837916218613026269361845916011417547716655151014908270156235727462738771351489212657321400055033617868608092797200321917477463676573100374585542816545631299684842142462655847321565714756762304545470196198340385
c2 = 14550589053226237723784378782911157204367764723812418468791010699229875830463647355158844707705886546430710672657478975121784614197819889919141286816518375155221497149366869190177475417434888910395438707022979956329590154012896676989382415396402741552931485301047383627371895227749949128440632032518188813954527856952846604591394762443469965587269478029933983601737740826745184166883892469527983987217466171262168882709572986062075929388484286172431551877578026734690125212518284473430174129218378709797148961525512754353241835286245103580265565835556405824029469560545138200760717522255129021253354200673658556765641
e = 3

h1 = int(sha256('a').hexdigest(), 16)
h2 = int(sha256('b').hexdigest(), 16)
diff = h2 - h1
m = related_message_attack(c1, c2, diff, e, n) - h1
flag = ('%x' % m).decode('hex')
print flag

実行結果は以下の通り。

Welcom to Nu1L CTF, Congratulations, You get flag, and flag is N1CTF{f7efbf4e5f5ef78ca1fb9c8f5eb02635}
N1CTF{f7efbf4e5f5ef78ca1fb9c8f5eb02635}