この大会は2017/4/1 7:00(JST)~2017/4/2 7:00(JST)に開催されました。
今回もチームで参戦。結果は950点で916チーム中38位でした。
自分が得点した問題は1問も無く、本当に残念!
April Fools' GTF 2017 Writeup
この大会は2017/4/1 6:00(JST)~2017/4/1 18:00(JST)に開催されました。
今回もチームで参戦。結果は1243点で278チーム中42位でした。
自分で解けた問題をWriteupとして書いておきます。
Welcome!! (Misc 500)
問題は「あなたのパスワードを入力してください(今日はエイプリルフールです.)」。
答えは何でもよかったらしいが、こう入れてみた。
password
O-mikuji (Guess 100, -500)
問題は「フラグは数字です.」とだけ。
それっぽい数字を選んだら間違いでフラグ2を取って、-500点になってしまった。URLに含まれている問題番号が正解のようだった。
6
Thank You! (Misc 9)
アンケートに答えるだけ。
AFCTF{thank_you_for_wasting_your_time}
Securinets CTF Quals 2017 Writeup
この大会は2017/3/26 2:00(JST)~2017/3/27 2:00(JST)に開催されました。
今回もチームで参戦。結果は1500点で78チーム中8位でした。
自分で解けた問題をWriteupとして書いておきます。
Fix It, Or May Be Not? (Binary 50)
SSHでログインし、damagedファイルを調べる。
DemBruiser@vps389317:~$ xxd damaged | head 00000000: 454c 463e 4000 0000 0000 4000 0000 0000 ELF>@.....@..... 00000010: 0000 981a 0000 0000 0000 0000 0000 4000 ..............@. 00000020: 3800 0040 001f 001c 0006 0000 0005 0000 8..@............ 00000030: 0040 0000 0000 0000 0040 0040 0000 0000 .@.......@.@.... 00000040: 0040 0040 0000 0000 00f8 0100 0000 0000 .@.@............ 00000050: 00f8 0100 0000 0000 0008 0000 0000 0000 ................ 00000060: 0003 0000 0004 0000 0038 0200 0000 0000 .........8...... 00000070: 0038 0240 0000 0000 0038 0240 0000 0000 .8.@.....8.@.... 00000080: 001c 0000 0000 0000 001c 0000 0000 0000 ................ 00000090: 0001 0000 0000 0000 0001 0000 0005 0000 ................
ELFとしては先頭に1バイト「7f」が足りない。
DemBruiser@vps389317:~$ strings damaged ELF>@ /lib64/ld-linux-x86-64.so.2 vX#&I s7"6cW libc.so.6 __stack_chk_fail rand __libc_start_main snprintf __gmon_start__ GLIBC_2.2.5 GLIBC_2.4 UH-H AWAVA AUATL []A\A]A^A_ yeah it's a boring program ;*3$" NDc2ZjZmNjQ1ZjRhNmY2MjVmNDI3Mjc1Njg1Zjc0Njg2NTVmNzc2ZjcyNzM3NDVmNjk3MzVmNzM3\nNDY5NmM2YzVmNzQ2ZjVmNjM2ZjZkNjU=\n ★あやしい文字列発見! GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4)5.4.020160609 crtstuff.c __JCR_LIST__ deregister_tm_clones __do_global_dtors_aux completed.7585 __do_global_dtors_aux_fini_array_entry frame_dummy __frame_dummy_init_array_entry damaged.c __FRAME_END__ __JCR_END__ __init_array_end _DYNAMIC __init_array_start __GNU_EH_FRAME_HDR _GLOBAL_OFFSET_TABLE_ __libc_csu_fini _ITM_deregisterTMCloneTable do_other_stuff _edata __stack_chk_fail@@GLIBC_2.4 snprintf@@GLIBC_2.2.5 __libc_start_main@@GLIBC_2.2.5 __data_start __gmon_start__ __dso_handle _IO_stdin_used __libc_csu_init __bss_start main _Jv_RegisterClasses __TMC_END__ _ITM_registerTMCloneTable do_stuff rand@@GLIBC_2.2.5 .symtab .strtab .shstrtab .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.versio waaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa DemBruiser@vps389317:~$ echo NDc2ZjZmNjQ1ZjRhNmY2MjVmNDI3Mjc1Njg1Zjc0Njg2NTVmNzc2ZjcyNzM3NDVmNjk3MzVmNzM3NDY5NmM2YzVmNzQ2ZjVmNjM2ZjZkNjU= | base64 -d 476f6f645f4a6f625f427275685f7468655f776f7273745f69735f7374696c6c5f746f5f636f6d65 DemBruiser@vps389317:~$ echo 476f6f645f4a6f625f427275685f7468655f776f7273745f69735f7374696c6c5f746f5f636f6d65 | xxd -r -p Good_Job_Bruh_the_worst_is_still_to_come
Good_Job_Bruh_the_worst_is_still_to_come
VolgaCTF 2017 Quals Writeup
この大会は2017/3/25 0:00(JST)~2017/3/27 0:00(JST)に開催されました。
今回もチームで参戦。結果は1650点で1024チーム中26位でした。
自分で解けた問題をWriteupとして書いておきます。
VC (crypto 50)
2つのノイズのような画像が与えられている。2つの画像の差がある部分を黒にしてみる。
from PIL import Image img_a = Image.open('A.png').convert('RGB') img_b = Image.open('B.png').convert('RGB') w, h = img_a.size img_o = Image.new('RGB', (w, h), (255, 255, 255)) for y in range(0, h): for x in range(0, w): r1, g1, b1 = img_a.getpixel((x, y)) r2, g2, b2 = img_b.getpixel((x, y)) if r1 == r2 and g1 == g2 and b1 == b2: img_o.putpixel((x, y), (255, 255, 255)) else: img_o.putpixel((x, y), (0, 0, 0)) img_o.save('flag.png')
フラグが表示された。
VolgaCTF{Classic_secret_sharing_scheme}
Curved (crypto 200)
$ nc curved.quals.2017.volgactf.ru 8786 Solve a puzzle: find an x such that 26 last bits of SHA1(x) are set, len(x)==29 and x[:24]=='875378c8c528de3c569e29a6'
最初にこの問題を解く必要がある。その後、ECDSAの問題となる。
添付のスクリプトを見てみると、以下のことがわかる。
・サーバOS上でls、dir、cd、catコマンド、また抜けるためにexitやleaveというコマンドが使える。 ・ただし、該当するシグネチャと合わせて、チェックを通らないと実行できない。 ・exitとleaveのシグネチャは添付されており、rは同じ値になっている。
cat flagのシグネチャを作ることができれば、フラグを奪取できそうだ。
rが同じで、シグネチャが分かっているものが2組あれば、秘密鍵を導き出すことができる。
e1 = int(hashlib.sha512('exit').hexdigest(), 16) e2 = int(hashlib.sha512('leave').hexdigest(), 16) z1 = e1 >> 512 - nのビット長 z2 = e2 >> 512 - nのビット長 k = ((z1 - z2) % n) * invert(((s1 - s2) % n), n) privatekey = (((((s1 * k) % n) - z1) % n) * invert(r1, n)) % n
秘密鍵がわかれば、どのコマンドでも添付のソースコード記載の通り、シグネチャを作成できる。プログラムは以下の通り。
#!/usr/bin/env python import socket import re import string import itertools import hashlib from gmpy2 import invert, bit_length def import_public_key(file): with open(file, 'r') as f: data = f.read() d = data.split('\n') QAx = int(d[0]) QAy = int(d[1]) return QAx, QAy def import_cmd_signature(cmd): file = '{0}.sig'.format(cmd) with open(file, 'r') as f: data = f.read() d = data.split('\n') (r, s) = (int(d[0]), int(d[1])) return r, s class EllipticCurve(object): def __init__(self, a, b, p, n): self.a = a self.b = b self.p = p self.n = n self.discriminant = -16 * (4 * a * a * a + 27 * b * b) if not self.isSmooth(): raise Exception("The curve %s is not smooth!" % self) def isSmooth(self): return self.discriminant != 0 def testPoint(self, x, y, p): return (y ** 2) % p == (x ** 3 + self.a * x + self.b) % p def __str__(self): return 'y^2 = x^3 + %Gx + %G (mod %G)' % (self.a, self.b, self.p) def __eq__(self, other): return (self.a, self.b, self.p) == (other.a, other.b, other.p) class Point(object): def __init__(self, curve, x, y): self.curve = curve self.x = x self.y = y if not curve.testPoint(x, y, curve.p): raise Exception("The point %s is not on the given curve %s" % (self, curve)) def __neg__(self): return Point(self.curve, self.x, -self.y) def __add__(self, Q): if isinstance(Q, Ideal): return self x_1, y_1, x_2, y_2 = self.x, self.y, Q.x, Q.y if (x_1, y_1) == (x_2, y_2): if y_1 == 0: return Ideal(self.curve) s = (3 * x_1 * x_1 + self.curve.a) * int(invert(2 * y_1, self.curve.p)) else: if x_1 == x_2: return Ideal(self.curve) s = (y_2 - y_1) * int(invert(x_2 - x_1, self.curve.p)) x_3 = (s * s - x_2 - x_1) % self.curve.p y_3 = (s * (x_3 - x_1) + y_1) % self.curve.p return Point(self.curve, x_3, self.curve.p - y_3) def __sub__(self, Q): return self + -Q def __mul__(self, n): if not (isinstance(n, int) or isinstance(n, long)): raise Exception("Can't scale a point by something which isn't an int!") else: if n < 0: return -self * -n if n == 0: return Ideal(self.curve) else: Q = self R = self if n & 1 == 1 else Ideal(self.curve) i = 2 while i <= n: Q = Q + Q if n & i == i: R = Q + R i = i << 1 return R def __rmul__(self, n): return self * n class Ideal(Point): def __init__(self, curve): self.curve = curve def __str__(self): return "Ideal" def __neg__(self): return self def __add__(self, Q): return Q def __mul__(self, n): if not isinstance(n, int): raise Exception("Can't scale a point by something which isn't an int!") else: return self s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('curved.quals.2017.volgactf.ru', 8786)) data = s.recv(1024) print data pattern = 'x\[:24\]==\'(.+)\'' m = re.search(pattern, data) x_head = m.group(1) for c in itertools.product(string.printable, repeat=5): x = x_head + ''.join(c) if int(hashlib.sha1(x).hexdigest(), 16) & 0x3ffffff == 0x3ffffff: break print x s.sendall(x + '\n') data = s.recv(1024) print data p = 39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319 n = 39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643 a = -3 b = int('b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef', 16) NIST384 = EllipticCurve(a, b, p, n) Gx = int('aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7', 16) Gy = int('3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f', 16) G = Point(NIST384, Gx, Gy) QA = import_public_key('key.public') QA = Point(NIST384, QA[0], QA[1]) r1, s1 = import_cmd_signature('exit') r2, s2 = import_cmd_signature('leave') e1 = int(hashlib.sha512('exit').hexdigest(), 16) e2 = int(hashlib.sha512('leave').hexdigest(), 16) Ln = bit_length(n) z1 = e1 >> (512 - Ln) z2 = e2 >> (512 - Ln) k = int(((z1 - z2) % n) * invert(((s1 - s2) % n), n)) priv = int((((((s1 * k) % n) - z1) % n) * invert(r1, n)) % n) cmd = 'cat flag' e3 = int(hashlib.sha512(cmd).hexdigest(), 16) z3 = e3 >> (512 - Ln) xy = k * G r3 = xy.x % n s3 = int(invert(k, n) * (z3 + r3 * priv) % n) ans = str(r3) + ' ' + str(s3) + ' ' + cmd print ans s.sendall(ans + '\n') data = s.recv(1024) print data
実行すると、フラグが表示された。
VolgaCTF{N0nce_1s_me@nt_to_be_used_0n1y_Once}
DoubleS1405 CTF Writeup
この大会は2017/3/25 9:00(JST)~2017/3/26 21:00(JST)に開催されました。
今回もチームで参戦。結果は351点で249チーム中17位でした。
自分で解けた問題をWriteupとして書いておきます。
Mic Check (Misc 1)
問題に記載してあるフラグを解凍するだけ。"flag{" と"}"は不要。
W3lc0me_T0_DoubleS1405_CTF!
Easy_Crypt (Crypto 50)
Vigenere暗号の問題。
オンラインツールhttps://www.guballa.de/vigenere-solverで復号してみると、次のような文章になっていた。
the vigenere cipher is a method of encrypting alphabetic text by using a series of interwoven caesar ciphers based on the letters of a keyword. it is a form of polyalphabetic substitution. flag is hello_vigenere
hello_vigenere
RSA (Crypto 150)
RSA暗号の問題。
$ nc 203.251.182.94 4000 enc : 524088215288945245117812840274234074214259867311424793488872316310812706883024313867933264975831745029054523235181695352586709977208196363681301896057064415274078567043967757970801714625738226262293721554006107104236101335115913713660091583447557282002619134700442555482433678036710317021694260495168589479860504255753735295684993178728329372305366606635533145127946686273344730509844775300122864925154558295745613688004661516962622482022264505375004460809348994246393125753388290161988082612174365368309952527753924526213766797054879514133711596108047008600960292092135854237304270147666380464744930028252443649819646184914979910712331358503515497366868391163434331196745240129334051449430357442795848755908012895351467558418431211464970160313835649205450891868176931866059746834981469604038607844868902540578729908708475089242253961021291237310436998767128596611554846948529983930986516799540905413759161964474942642639146239239417647305400198714676910394008062192060561130474884192131791415273201113911520884357202482483443690793539529640607301455522584558165763889673353291204724196936406524674532675658355516720800608675712275967358182807215410869264526079077702286496666298128451842286840129878459991344435756626173655145826593 e : 65537 p : 32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521954285833276606292740174507176908054077273016103644389803261062635470374515595892199454891155463898488297024308700957247533881208055894474582694028535079545281620566442541400114261729854235365927395115457109476960042332821732358509197923144094801013581965651112146928918286923938064987973879624251895591220179 q : 32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521990685772174514834944123086486002362345153147580453526134037171595087108668773961917317502849945855689432886442889958513294157709640362363734479327004391952407569596153273880472331909250263593691635107321048666489395316204775782962517724272901158130972610802371589601746375325078943967095960733617174538141999
enc, e, p, qがわかり、enc以外は固定になっている。そのまま復号処理をし、hexデコードやbase64デコードで正しい答えになるよう調整する。
import socket import re s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('203.251.182.94', 4000)) data = s.recv(4096) print data pattern = 'enc : (.+)\ne : (.+)\np : (.+)\nq : (.+)' m = re.search(pattern, data) c = int(m.group(1)) e = int(m.group(2)) p = int(m.group(3)) q = int(m.group(4)) n = p * q a = (p - 1) * (q - 1) x = 0 while True: if (a * x + 1) % e == 0: d = (a * x + 1) / e break x = x + 1 m = pow(c, d, n) ans = ('%x' % m).decode('hex').decode('base64') ans = ('%x' % int(ans)).decode('hex') print ans s.sendall(ans) data = s.recv(4096) print data
実行結果は以下の通り。
enc : 943323076663715024141647584331931588504059538634678245280425831973796289347224969397647303454937690940952859232063793733404967720884450831759307523716676374906805977677219485079131899110251690387052195431428481983362303508265715568364926129160708402287993207521665278959090563863929142276264132832262357813131882224629836702376792898930239886372171786808974048556564459127393421203100595023420176207941095860228262228123206815043761469110111545721812944012788553149575087398528786583978437383550919700413294635921293905268700691986832657511626585629156289537911662116698677989724589794181878086199351516800977257909086655330248139060177962291764419430342443557667861917686953473395731524558266643177815366887860803117307072934899459045728454706856296407965798284164935167469656739110378295698864119610217189620023320017254576611161419886036093861180781769495008397890178322624327006809008967677442747797792584907829610673111299004805651549603121773706860385941777176103149272425054779791817810966316678169201560717394130219015631420356160614120412760419002013131762642338218434316378193883589201195199752227857462248942614326911643627127945748100365516522106686625206332033784564337959925495322684860188773445710912068399610295000708 e : 65537 p : 32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521954285833276606292740174507176908054077273016103644389803261062635470374515595892199454891155463898488297024308700957247533881208055894474582694028535079545281620566442541400114261729854235365927395115457109476960042332821732358509197923144094801013581965651112146928918286923938064987973879624251895591220179 q : 32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521990685772174514834944123086486002362345153147580453526134037171595087108668773961917317502849945855689432886442889958513294157709640362363734479327004391952407569596153273880472331909250263593691635107321048666489395316204775782962517724272901158130972610802371589601746375325078943967095960733617174538141999 49U6KMTW0P2m3baysLHcH4WKUjS4WBrk flag{Y34hh_RSA_1S_S0_E4sy}
Y34hh_RSA_1S_S0_E4sy
Cute(Forensic 50)
$ file file file: Matroska data
字幕が隠れていそうなので、ffmpegでテキスト化する。
$ ffmpeg -i file -map 0:s:0 file-subs.srt : (省略)
file-subs.srtの内容は以下の通り。
: 80 00:00:10,517 --> 00:00:10,538 eh~!@#~!!#! 81 00:00:10,539 --> 00:00:10,539 f 82 00:00:10,539 --> 00:00:10,539 l 83 00:00:10,539 --> 00:00:10,539 a 84 00:00:10,539 --> 00:00:10,539 g 85 00:00:10,539 --> 00:00:10,539 { 86 00:00:10,539 --> 00:00:10,539 v 87 00:00:10,539 --> 00:00:10,539 3 88 00:00:10,539 --> 00:00:10,539 r 89 00:00:10,539 --> 00:00:10,539 _ 90 00:00:10,539 --> 00:00:10,539 c 91 00:00:10,539 --> 00:00:10,539 u 92 00:00:10,539 --> 00:00:10,539 t 93 00:00:10,539 --> 00:00:10,539 3 94 00:00:10,539 --> 00:00:10,539 _ 95 00:00:10,539 --> 00:00:10,539 p 96 00:00:10,539 --> 00:00:10,539 4 97 00:00:10,539 --> 00:00:10,539 R 98 00:00:10,539 --> 00:00:10,539 r 99 00:00:10,539 --> 00:00:10,539 0 100 00:00:10,539 --> 00:00:10,539 t 101 00:00:10,539 --> 00:00:10,539 _ 102 00:00:10,539 --> 00:00:10,539 1 103 00:00:10,539 --> 00:00:10,539 2 104 00:00:10,539 --> 00:00:10,539 n 105 00:00:10,539 --> 00:00:10,539 t 106 00:00:10,539 --> 00:00:10,539 _ 107 00:00:10,539 --> 00:00:10,539 ! 108 00:00:10,539 --> 00:00:10,539 t 109 00:00:10,539 --> 00:00:10,539 ? 110 00:00:10,539 --> 00:00:10,539 } 111 00:00:10,539 --> 00:00:10,560 eh~!@#~!!#!@ :
これを見ると、最後の方にフラグが隠れている。
flag{v3r_cut3_p4Rr0t_12nt_!t?}
v3r_cut3_p4Rr0t_12nt_!t?
WhiteHat Challenge 02 Writeup
この大会は2017/3/25 18:00(JST)~2017/3/26 2:00(JST)に開催されました。
今回もチームで参戦。結果は80点で81チーム中20位でした。
自分で解けた問題をWriteupとして書いておきます。
Crypto001 (Cryptography 20)
n(modulus)をFermat法で素因数分解し、復号する。
def isqrt(n): x = n y = (x + n // x) // 2 while y < x: x = y y = (x + n // x) // 2 return x def fermat(n): x = isqrt(n) + 1 y = isqrt(x * x - n) while True: w = x * x - n - y * y if w == 0: break elif w > 0: y += 1 else: x += 1 return x - y, x + y c = 0x42e08bb01f0cb9ba959ddcb771eb641afe87b775adfee9e02f9941d6a0f127ce3e4c794f81661295a3f6f26b5bf5b97fb244c49fe6bcebd202c337d18da4c37520461872c9e0f4d735b45f03db80288d8a4b1e2e9c8350860c0bb7bc4e461cff6e2827720450c189ff7946a48ecc234a1ad8107ed1baf3d32d4b5d678a9972e768f4f8fc854ba03c36d9f67ea1ff058c3e5390c5a3ce16e7803d6f70d2514b977911427ac92694065e1bf4f51791d0cd1e0426ced501e125fe7a03d1514b0c41dfb4c36b8c12128a84ec8bd7776089fd5918315fe4fa01a9021897f48d77ea4719f46f91ed2f07ea2cbe57214cddbccafabc2d04881ccc02cab2e6a12f9e4291 n = 22538226557359593572346358546448534316502374936010448386594814842974948185264245943532391888185780963684478526253798386491706856055721928437429885241499953118882150091346829521458455087101372254666403540557260438634164283165429965553504233799473121743076739557259202565372789362450321949620402058911471334565735242774625841491381660958586223360222338739572747101258430902493093450151421824669407116051626021283885808048507068886635263867243474018292757027989902382327148650784375980240979112130712113839597585132552988013197038652273781471554107489186108861150933302755959031058580590411316776473912540458121516196981 e = 65537 p, q = fermat(n) a = (p - 1) * (q - 1) x = 0 while True: if (a * x + 1) % e == 0: d = (a * x + 1) / e break x = x + 1 m = pow(c, d, n) flag = ('%x' % m).decode('hex') print flag
実行すると、次の文字列になる。
flag is close_primes_is_bad
$ echo -n close_primes_is_bad | sha1sum 0732e65c5c2769ffb5ea0e408f1bf13ae6288767 -
WhiteHat{0732e65c5c2769ffb5ea0e408f1bf13ae6288767}
Crypto002 (Cryptography 15)
逆算し、keyについてブルートフォースで元に戻す。
import string L2I = dict(zip(string.uppercase+string.lowercase+string.digits,range(64))) I2L = dict(zip(range(64),string.uppercase+string.lowercase+string.digits)) with open('ciphertext.txt', 'r') as f: ciphertext = f.read() for key in range(64): b64text = '' for c in ciphertext: if L2I.has_key(c): index = (L2I[c] - key) % 64 if I2L.has_key(index): b64text += I2L[index] else: break else: break try: flag = b64text.decode('base64') print str(key) + ': ' + flag except: pass
この実行結果は以下の通り。
0: 」P08ャ杠枡・]・1枷4l%・f・l2ヲh8ォZ 8: む(ヲ0薯 }ラ:}肅呑;・)}・O」辣蓍・*・0韓 9: ~ヘ・・求ノyニモヲ礁禸∬yヤ・逃・ヲ}ル驕ユ・ネァ 10: zスヲ{qョ?・カクuテe脅ケ|qァuトェGテ}トeyノィ}ナョずf 11: vュewamヂGqヲwqウ$・xxafqエiCs$yエ$uケgyオm~ィ% 12: r・sQ,Qm・m「繝・tQ%m、(?b縉」縡ゥ&u・,z嶺 13: n後o@・@ナi・i弔怪p@臺鍋;R「q討m伜q罷v・ 14: j|「k0ェw0・uエeB{yオl0」eΘ7Bamヂi愛m━rwb 15: flag is Caesar with base32 is exciting! 16: b\ c(o]U2]a゚sY3d!]c$/!゚eb゚ah"ed(jV 21: N ロNホ綮ホI鋹喟畯ホワI゚ミ啣哺ンQ綏 22: I埇Jセ「VセシDE YZOセ妣・タYMYI廴「Q 23: E鷓FョaRョ{@緻@・V輙KョZ@ーH・DHM・ 26: 9ケ・}曦}ク4イィ4マUJカゥ?}・タ・酋<タU8ナ・チ暸エV 27: 5ゥU6m]Bmw0「g0ソFヲh;mV0ーY8ー4オW8ア]=、 28: 1・2]>]6,・,ョモB・7], 31: %hQ&,Y2,s ac ~6ed+,R U・($tS(pY-c 32: !X".2Q"mマ2U#'o・マ$nマ d$`)Rミ 35: 'M・!・ _=&$`・>Q・>3O?U" .ン・-ヒ#/フヒ"ロ ・: ・ 41: 42: 43: EMigW「X 44: 45: ・テ?ヒ 46: 鑼る8忌8、轗皮晦 47: 臈A・I・c緡S縻 T・B縵Eア: ・ 輛C・I・ 56: テメ8タ・ フ・セロ セ銷ミ゚ナ・セ・県顆ツ・ニ・ ヒワ 59: キ。uエe}タeWイェGイキ4トョHケevイクy€w4コク4カスwコケ}ソャ5 60: ウ・ーU<シUョ・ョヲ・オU5ョィ8|fァュ6カゥ<サ幺 61: ッ€D觶Dユェ嫁ェ夢シ哉アD憐xVイイ隆ョ懾イ傴キ教 62: ォpイィ4コエ4幡y┬・ク}・4ウヲ・tFqョ?ェ原ョ絢ウ{r 63: ァ`q、$yー$S「iC「v0エmDゥ$r「wup60ェw0ヲ|sェxyッk1
keyが15のときに文になっている。
flag is Caesar with base32 is exciting!
$ echo -n "Caesar with base32 is exciting!" | sha1sum f4637ac70451282471bb368a7566c063b87b1b24 -
WhiteHat{f4637ac70451282471bb368a7566c063b87b1b24}
Web001 (Web Security 15)
Click meをクリックすると、This is a javascript challengeと表示される。
ソースにJavaScriptがあるところを探す。
hello.jsに次のように書いてある。
function sayHello() { alert ("This is a javascript challenge"); showFlag=String.fromCharCode(118,97,114,32,101,110,99,111,61,39,39,59,13,10,118,97,114,32,101,110,99,111,50,61,49,50,54,59,13,10,118,97,114,32,101,110,99,111,51,61,51,51,59,13,10,118,97,114,32,99,107,61,100,111,99,117,109,101,110,116,46,85,82,76,46,115,117,98,115,116,114,40,100,111,99,117,109,101,110,116,46,85,82,76,46,105,110,100,101,120,79,102,40,39,61,39,41,41,59,13,10,32,13,10,32,13,10,102,111,114,40,105,61,49,59,105,60,49,50,50,59,105,43,43,41,13,10,123,13,10,101,110,99,111,61,101,110,99,111,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,105,44,48,41,59,13,10,125,13,10,32,13,10,102,117,110,99,116,105,111,110,32,101,110,99,111,95,40,120,41,13,10,123,13,10,114,101,116,117,114,110,32,101,110,99,111,46,99,104,97,114,67,111,100,101,65,116,40,120,41,59,13,10,125,13,10,32,13,10,105,102,40,99,107,61,61,34,61,34,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,95,40,50,52,48,41,41,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,95,40,50,50,48,41,41,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,95,40,50,51,50,41,41,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,95,40,49,57,50,41,41,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,95,40,50,50,54,41,41,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,95,40,50,48,48,41,41,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,95,40,50,48,52,41,41,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,95,40,50,50,50,45,50,41,41,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,95,40,49,57,56,41,41,43,34,126,126,126,126,126,126,34,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,50,41,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,51,41,41,13,10,123,13,10,97,108,101,114,116,40,34,80,97,115,115,119,111,114,100,32,105,115,32,34,43,99,107,46,114,101,112,108,97,99,101,40,34,61,34,44,34,34,41,41,59,13,10,125,13,10); eval(showFlag); }
showFlag=String.fromCharCode(118,97,114,32,101,110,99,111,61,39,39,59,13,10,118,97,114,32,101,110,99,111,50,61,49,50,54,59,13,10,118,97,114,32,101,110,99,111,51,61,51,51,59,13,10,118,97,114,32,99,107,61,100,111,99,117,109,101,110,116,46,85,82,76,46,115,117,98,115,116,114,40,100,111,99,117,109,101,110,116,46,85,82,76,46,105,110,100,101,120,79,102,40,39,61,39,41,41,59,13,10,32,13,10,32,13,10,102,111,114,40,105,61,49,59,105,60,49,50,50,59,105,43,43,41,13,10,123,13,10,101,110,99,111,61,101,110,99,111,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,105,44,48,41,59,13,10,125,13,10,32,13,10,102,117,110,99,116,105,111,110,32,101,110,99,111,95,40,120,41,13,10,123,13,10,114,101,116,117,114,110,32,101,110,99,111,46,99,104,97,114,67,111,100,101,65,116,40,120,41,59,13,10,125,13,10,32,13,10,105,102,40,99,107,61,61,34,61,34,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,95,40,50,52,48,41,41,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,95,40,50,50,48,41,41,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,95,40,50,51,50,41,41,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,95,40,49,57,50,41,41,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,95,40,50,50,54,41,41,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,95,40,50,48,48,41,41,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,95,40,50,48,52,41,41,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,95,40,50,50,50,45,50,41,41,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,95,40,49,57,56,41,41,43,34,126,126,126,126,126,126,34,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,50,41,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,101,110,99,111,51,41,41,13,10,123,13,10,97,108,101,114,116,40,34,80,97,115,115,119,111,114,100,32,105,115,32,34,43,99,107,46,114,101,112,108,97,99,101,40,34,61,34,44,34,34,41,41,59,13,10,125,13,10);
また、次のようにJavaScriptのコードになった。
"var enco=''; var enco2=126; var enco3=33; var ck=document.URL.substr(document.URL.indexOf('=')); for(i=1;i<122;i++) { enco=enco+String.fromCharCode(i,0); } function enco_(x) { return enco.charCodeAt(x); } if(ck=="="+String.fromCharCode(enco_(240))+String.fromCharCode(enco_(220))+String.fromCharCode(enco_(232))+String.fromCharCode(enco_(192))+String.fromCharCode(enco_(226))+String.fromCharCode(enco_(200))+String.fromCharCode(enco_(204))+String.fromCharCode(enco_(222-2))+String.fromCharCode(enco_(198))+"~~~~~~"+String.fromCharCode(enco2)+String.fromCharCode(enco3)) { alert("Password is "+ck.replace("=","")); } "
再び以下を実行する。
for(i=1;i<122;i++) { enco=enco+String.fromCharCode(i,0); } function enco_(x) { return enco.charCodeAt(x); } "="+String.fromCharCode(enco_(240))+String.fromCharCode(enco_(220))+String.fromCharCode(enco_(232))+String.fromCharCode(enco_(192))+String.fromCharCode(enco_(226))+String.fromCharCode(enco_(200))+String.fromCharCode(enco_(204))+String.fromCharCode(enco_(222-2))+String.fromCharCode(enco_(198))+"~~~~~~"+String.fromCharCode(enco2)+String.fromCharCode(enco3)
この結果、次のようになった。
"=youaregod~~~~~~~!"
Passwordは"="を""に置換しているので、"youaregod~~~~~~~!"がフラグ
$ echo -n "youaregod~~~~~~~!" | sha1sum 1c0e74d5f61b6c3901a277bdd490ad070265f027 -
WhiteHat{1c0e74d5f61b6c3901a277bdd490ad070265f027}
0CTF 2017 Quals Writeup
この大会は2017/3/18 9:00(JST)~2017/3/20 9:00(JST)に開催されました。
今回もチームで参戦。結果は120点で908チーム中179位でした。
自分で解けた問題をWriteupとして書いておきます。
Welcome (Misc)
IRCでfrreenodeの#0ctf2017に入ると、フラグを見ることができる。
09:02 *topic : Welcome to 0ctf 2017! https://ctf.0ops.net (flag{Welcome_to_0CTF_2017})
flag{Welcome_to_0CTF_2017}
integrity (Crypto)
AES暗号の問題で、ncで接続した際に"r"で指定した文字を暗号化、"l"で復号する。
その際、暗号化する文字列は32バイト以下で、"admin"を暗号化することはできない。
その状況で、復号して"admin"になる暗号文を指定することができれば、フラグが得られる。
暗号化の概要は以下の通り。
1) 指定した文字を16の倍数の文字長になるようパディング
2) 1)の文字列のMD5ダイジェストを1)の文字列の前につける。
3) 2)の文字列をランダムなIVでAES暗号化する。
4) 「IV + 3)の暗号化文字列」を16進数表記で返す。
このことから1ブロックずらして考え、目的の暗号データを取得できないか考える。
CBCモードなので、以下のようになる。
[平文1ブロック目] ^ IV --(暗号化)--> [暗号文1ブロック目]
[平文2ブロック目] ^ [暗号文1ブロック目] --(暗号化)--> [暗号文2ブロック目]
[平文3ブロック目] ^ [暗号文2ブロック目] --(暗号化)--> [暗号文3ブロック目]
平文のブロックを以下のようにする。
1ブロック目:2ブロック目+3ブロック目の文字列のMD5
2ブロック目:"admin\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"のMD5
3ブロック目:"admin\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
これで暗号化した結果から、復号する際に以下のように指定すれば、復号して"admin"になる。
IV = [暗号結果1ブロック目]
1ブロック目:[暗号結果2ブロック目]
2ブロック目:[暗号結果3ブロック目]
これをプログラムにする。
import socket from hashlib import md5 BS = 16 pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) uname = pad('admin') h_uname = md5(uname).digest() s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('202.120.7.217', 8221)) data = s.recv(1024).strip('\n') print data data = s.recv(1024).strip('\n') print data s.sendall('r\n') print 'r' name = h_uname + uname s.sendall(name + '\n') print h_uname + uname data = s.recv(1024).strip('\n') print data data = s.recv(1024).strip('\n') print data enc = data.split('\n')[0] enc = enc[BS * 2:] s.sendall('l\n') print 'l' s.sendall(enc + '\n') print enc data = s.recv(1024).strip('\n') print data data = s.recv(1024).strip('\n') print data
実行結果は以下の通り。
Welcome to 0CTF encryption service! Please [r]egister or [l]ogin r !・キ・・フ└rEィ渋dmin Here is your secret: 06f03d4dbaf5e6e99a049ce5c213fa6b1f82eaf57d71bfb74195417e9cf4c0a91389f95efc7f82af6f7c1b2decdaf2bfae0118b0d03887586632ebc95fcb2194 Please [r]egister or [l]ogin l 1f82eaf57d71bfb74195417e9cf4c0a91389f95efc7f82af6f7c1b2decdaf2bfae0118b0d03887586632ebc95fcb2194 Welcome admin! flag{Easy_br0ken_scheme_cann0t_keep_y0ur_integrity}
flag{Easy_br0ken_scheme_cann0t_keep_y0ur_integrity}