この大会は2018/2/10 10:00(JST)~2018/2/20 13:00(JST)に開催されました。
今回もチームで参戦。結果は2256点で2146チーム中101位でした。
自分で解けた問題をWriteupとして書いておきます。
Hello, world! (Intro 10)
Hello, World!と表示させるプログラムを書けばよい。
print 'Hello, world!'
Linux (Intro 10)
$ ls -a . .. .bash_logout .bashrc .cache .cloud-locale-test.skip .flag .profile $ cat .flag easyctf{i_know_how_2_find_hidden_files!}
隠しファイルにフラグが書いてあった。
easyctf{i_know_how_2_find_hidden_files!}
The Oldest Trick in the Book (Intro 10)
シーザー暗号。https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。
easyctf{w3lc0m3_70_345yc7f_fd2e53}
Web (Intro 10)
HTMLのソースにフラグが書かれている。
easyctf{hidden_from_the_masses_072e33}
Soupreme Encoder(Cryptography 20)
16進コードをデコードするだけ
>>> '68657869745f6d6174655f3139353961643733346539386132386561363734'.decode('hex') 'hexit_mate_1959ad734e98a28ea674'
easyctf{hexit_mate_1959ad734e98a28ea674}
Netcat (Intro 20)
ncで接続するだけ。
$ nc c1.easyctf.com 12481 enter your player key: 1357494912 thanks! here's your key: easyctf{hello_there!_EdaB38f2DD98ae99}
easyctf{hello_there!_EdaB38f2DD98ae99}
Hashing (Miscellaneous 20)
ファイルのsha512を計算すればよい。
$ sha512sum ecd758d450f4158ef2276ab80aa038ca6761c7f2e1a78374c359ca1c629ded53_image.png c5b8c1b2173c66d0752e6f66a970a0cce42c966ef44cdb2e89b4440eb8a16b6efda199d5af9ce2e40854616005ceb41a3503fbc5c87a5bafa61a084ab80435b0 ecd758d450f4158ef2276ab80aa038ca6761c7f2e1a78374c359ca1c629ded53_image.png
easyctf{c5b8c1b2173c66d0752e6f66a970a0cce42c966ef44cdb2e89b4440eb8a16b6efda199d5af9ce2e40854616005ceb41a3503fbc5c87a5bafa61a084ab80435b0}
Exclusive (Programming 20)
2つの入力値のXORを出力するプログラムを書けばよい。
ab = raw_input() a = int(ab.split(' ')[0]) b = int(ab.split(' ')[1]) print a ^ b
Look At Flag (Forensics 30)
$ file 4680b45d33184b3e3ad99338907d1fe7dfec8ddd4b43ac71da69154ce9a6035c_flag.txt 4680b45d33184b3e3ad99338907d1fe7dfec8ddd4b43ac71da69154ce9a6035c_flag.txt: PNG image data, 332 x 44, 8-bit/color RGBA, non-interlaced
拡張子をpngにして開く。
easyctf{FLaaaGGGGGg}
EzSteg (Forensics 30)
jpgファイルのFF D9 より後ろにフラグが入っている。
easyctf{l00k_at_fil3_sigS}
Taking Input (Programming 30)
Hello, <入力文字列>! と出力するプログラムを書けばよい。
name = raw_input() print 'Hello, %s!' % name
Over and Over (Programming 30)
Over and Over and ... and Overと入力した値の数だけOverを出現させるようプログラムを書けばよい。
n = input() op = '' for i in range(n): op += 'over' if i != n - 1: op += ' and ' print op
Teaching Old Tricks New Dogs (Programming 40)
入力した値の数だけ、入力文字列の各文字をシフトするようプログラムを書けばよい。
rot = input() enc = raw_input() dec = '' for i in range(len(enc)): if enc[i] == ' ': dec += ' ' else: code = ord(enc[i]) - rot if code < ord('a'): code += 26 dec += chr(code) print dec
hexedit (Reverse Engineering 50)
stringsで調べるだけ。
$ strings hexedit | grep easyctf easyctf{d93a506e}
easyctf{d93a506e}
Substitute (Cryptography 50)
https://quipqiup.com/で復号する。
YO! NICE?OWLOFSOUP JUST MADE A NEW FLAG FOR THE CTF AND IS TOTALLY PROUD OF ITS INGENUITY. THIS IS ALSO THE SECOND PRO?LEM EVER MADE FOR EASYCTF. HERE: EASYCTF{THIS_IS_AN_EASY_FLAG_TO_GUESS} USE CAPITAL LETTERS.
きちんと復号しきれていないが、フラグの部分はわかる。
EASYCTF{THIS_IS_AN_EASY_FLAG_TO_GUESS}
Markov's Bees (Linux 50)
探すべきファイルは非常に多い。grepで探す。
$ grep easyctf -rl ./ ./c/e/i/bee913.txt $ cat ./c/e/i/bee913.txt | grep easyctf easyctf{grepping_stale_memes_is_fun}
easyctf{grepping_stale_memes_is_fun}
xor (Cryptography 50)
1つのキーで総当たりでXORし、印字可能な文字列だけになり、フラグの形式になるものを探す。
with open('8763dea5b0f7b29b0324f0a53bf9b5cccdd9762ffdb1376596b285588c461278_xor.txt', 'rb') as f: data = f.read() for key in range(256): flag = '' ng = False for i in range(len(data)): code = ord(data[i]) ^ key if code < 32 or code > 126: ng = True break else: flag += chr(code) if ng == False: if 'easyctf' in flag or 'EASYCTF' in flag: print str(key) + ':' + flag
実行結果は以下の通り。
15:easyctf{nfzkkcwopxfwrklydozaoqfji} 47:EASYCTF[NFZKKCWOPXFWRKLYDOZAOQFJI]
easyctf{nfzkkcwopxfwrklydozaoqfji}
Subset Counting (Programming 55)
入力の数だけ数字が提示され、部分和が入力の値になる組み合わせの数を出力するプログラムを書けばよい。
import itertools NS = raw_input() N = int(NS.split(' ')[0]) S = int(NS.split(' ')[1]) elems = map(int, raw_input().split(' ')) combs = [] for n in range(1, N + 1): for c in itertools.combinations(elems, n): if sum(c) == S: combs.append(c) print len(combs)
In Plain Sight (Web 70)
DNS SPYでこのドメインを検索。https://dnsspy.io/scan/blockingthesky.comを見ると、TXTレコードにフラグが書いてある。
easyctf{betcha_wish_you_could_have_used_ANY}
Nosource, Jr. (Web 80)
ソースを見る。
var flag = 'Fg4GCRoHCQ4TFh0IBxENAE4qEgwHMBsfDiwJRQImHV8GQAwBDEYvV11BCA==';
これと鍵とでXORして復号する必要がある。復号するとeasyctf{になることを想定し、XOR鍵を見つける。
keyがsoupysouとなっているので、soupyの繰り返しで使えば良さそう。
b64_enc = 'Fg4GCRoHCQ4TFh0IBxENAE4qEgwHMBsfDiwJRQImHV8GQAwBDEYvV11BCA==' enc = b64_enc.decode('base64') flag_head = 'easyctf{' key = '' for i in range(len(flag_head)): code = ord(enc[i]) ^ ord(flag_head[i]) key += chr(code) print key ## soupysou -> key = soupy key = key[:5] flag = '' for i in range(len(enc)): code = ord(enc[i]) ^ ord(key[i%len(key)]) flag += chr(code) print flag
easyctf{congrats!_but_now_f0r_n0s0urc3_...}
Zippity (Miscellaneous 80)
$ nc c1.easyctf.com 12483 +======================================================================+ | Welcome to Zippy! We love US zip codes, so we'll be asking you some | | simple facts about them, based on the 2010 Census. Only the | | brightest zip-code fanatics among you will be able to succeed! | | You'll have 30 seconds to answer 50 questions correctly. | +======================================================================+ 3... 2... 1... Go! Round 1 / 50 What is the longitude (degrees) of the zip code 12463?
https://www.census.gov/geo/maps-data/data/gazetteer2010.htmlからデータファイルを取ってきて、該当する情報を答えていく。
import socket import re def search(rows, zipcode): fnd_record = '' for row in rows: if row[:5] == zipcode: fnd_record = row.strip() break return fnd_record def getLatitude(row): return row.split('\t')[7].strip() def getLongitude(row): return row.split('\t')[8].strip() def getLandArea(row): return row.split('\t')[3].strip() def getWaterArea(row): return row.split('\t')[4].strip() with open('Gaz_zcta_national.txt', 'r') as f: lines = f.readlines()[1:] pattern1 = 'What is the (.+) \(degrees\) of the zip code (.+)\?' pattern2 = 'What is the (.+) area \(m\^2\) of the zip code (.+)\?' s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('c1.easyctf.com', 12483)) data = s.recv(1024) data += s.recv(1024) data += s.recv(1024) print data for i in range(50): data = s.recv(1024) print data m = re.search(pattern1, data) if m is None: m = re.search(pattern2, data) attr = m.group(1) code = m.group(2) area = search(lines, code) ans = '' if attr == 'latitude': ans = getLatitude(area) elif attr == 'longitude': ans = getLongitude(area) elif attr == 'land': ans = getLandArea(area) else: ans = getWaterArea(area) print ans s.sendall(str(ans) + '\n') data = s.recv(1024) print data
easyctf{hope_you_liked_parsing_tsvs!}
Keyed Xor (Cryptography 100)
先頭がeasyctf{になることからkeyの先頭を割り出す。
with open('keyed_xor.txt', 'rb') as f: data = f.read() flag_head = 'easyctf{' key = '' for i in range(len(flag_head)): code = ord(flag_head[i]) ^ ord(data[i]) key += chr(code) print key
この結果 tambouri とわかる。wordsから探すと、tambourineのようだ。もう一つはブルートフォースで探す。
import string def checkFlag(s): for i in range(len(s)): if ord(s[i]) < 32 or ord(s[i]) > 126: return False if '{' in s[8:-1]: return False elif '}' in s[8:-1]: return False if s[i] != '}': return False return True with open('keyed_xor.txt', 'rb') as f: data = f.read() with open('words.txt', 'r') as f: words = f.readlines() key1 = 'tambourine' for word in words: key2 = word.strip() key = key1 + key2 flag = '' for i in range(len(data)): code = ord(data[i]) ^ ord(key[i%len(key)]) flag += chr(code) if checkFlag(flag): print flag
実行結果は以下の通り。
easyctf{flagflagflagflagxeavjtwkmumukhbeqsjrfytkkpsezflnfilrwchgowsfmgqmok} easyctf{flagljac|qbpflagxeavjtwkgsmqquarqsjrfytkkpsep`lj|toewchgowsfmgqmem} easyctf{fllldjackqhmflagxeavjtz`osmqfukoqsjrfytkkp~nx`ljktexwchgowsfmg|fmm} easyctf{flgonjakitagflagxeavjtqcesmydpbeqsjrfytkkpumr`lbiqlrwchgowsfmgwegm} easyctf{flllnjakmvngflagxeavjtz`esmy`rmeqsjrfytkkp~nr`lbmscrwchgowsfmg|fgm} easyctf{fl`pxlapalhmflagxeavjtv|sumblhkoqsjrfytkkprrdflyaiexwchgowsfmgpzqk} easyctf{flflkhanglemflagxeavjtp``qm|jhfoqsjrfytkkptnwblggihxwchgowsfmgvfbo} easyctf{flag~lac|qbpflagxeavjtwkuumqquarqsjrfytkkpsebflj|toewchgowsfmgqmwk}
この中で英文字だけで構成されているものが一番フラグっぽい。
easyctf{flagflagflagflagxeavjtwkmumukhbeqsjrfytkkpsezflnfilrwchgowsfmgqmok}
Zipperoni (Miscellaneous 160)
bigin.zipを解凍すると、以下のファイルが展開される。
・filename.txt ・hash.txt ・pattern.txt
filename.txtの内容は次の解凍対象のファイル名、pattern.txtの内容は解凍する際のパスワードのフォーマット(0:数字、A:英大文字、a:英小文字)、hash.txtの内容は解凍する際のパスワードのsha1となっていることを前提にプログラムにする。
import zipfile import itertools import string import hashlib def applyText(text, cat, c, cnt): appText = text for i in range(cnt): appText = appText.replace(cat, c[i], 1) return appText DIR = 'zip_files/' for i in range(100): print 'Round %d' % (i+1) if i == 0: fname = DIR + 'begin.zip' pw = 'coolkarni' else: with open('filename.txt', 'r') as f: fname = f.read().strip() with open('hash.txt', 'r') as f: h = f.read().strip() with open('pattern.txt', 'r') as f: ptn = f.read().strip() ptn = ptn.replace('0', '#') ptn = ptn.replace('A', '$') ptn = ptn.replace('a', '%') cnt_0 = ptn.count('#') cnt_A = ptn.count('$') cnt_a = ptn.count('%') for c0 in itertools.product(string.digits, repeat=cnt_0): for cA in itertools.product(string.uppercase, repeat=cnt_A): for ca in itertools.product(string.lowercase, repeat=cnt_a): text = ptn text = applyText(text, '#', c0, cnt_0) text = applyText(text, '$', cA, cnt_A) text = applyText(text, '%', ca, cnt_a) if hashlib.sha1(text).hexdigest() == h: pw = text print pw break zf = zipfile.ZipFile(fname, 'r') zf.setpassword(pw) zf.extractall('.') zf.close()
100個目のファイルを解凍すると、flag.txtが展開され、フラグが記載されている。
easyctf{you_must_REALLY_luv_zip_files_by_now!}
RSA_v (Cryptography 200)
RSA暗号であることから、以下が言える。
pow(m, e1, n) = c1 pow(c1, e2, n) = c2 pow(c2, e3, n) = c3 pow(c3, e4, n) = c4 pow(c4, e5, n) = c →pow(m, e1*e2*e3*e4*e5, n) = c
Wiener's attackで復号する。
from fractions import Fraction def egcd(a, b): x,y, u,v = 0,1, 1,0 while a != 0: q, r = b//a, b%a m, n = x-u*q, y-v*q b,a, x,y, u,v = a,r, u,v, m,n gcd = b return gcd, x, y def decrypt(p, q, e, c): n = p * q phi = (p - 1) * (q - 1) gcd, a, b = egcd(e, phi) d = a pt = pow(c, d, n) return hex(pt)[2:-1].decode('hex') def continued_fractions(n,e): cf = [0] while e != 0: cf.append(int(n/e)) N = n n = e e = N%e return cf def calcKD(cf): kd = list() for i in range(1,len(cf)+1): tmp = Fraction(0) for j in cf[1:i][::-1]: tmp = 1/(tmp+j) kd.append((tmp.numerator,tmp.denominator)) return kd def int_sqrt(n): def f(prev): while True: m = (prev + n/prev)/2 if m >= prev: return prev prev = m return f(n) def calcPQ(a,b): if a*a < 4*b or a < 0: return None c = int_sqrt(a*a-4*b) p = (a + c) /2 q = (a - c) /2 if p + q == a and p * q == b: return (p,q) else: return None def wiener(n,e): kd = calcKD(continued_fractions(n,e)) for (k,d) in kd: if k == 0: continue if (e*d-1) % k != 0: continue phin = (e*d-1) / k if phin >= n: continue ans = calcPQ(n-phin+1,n) if ans is None: continue return (ans[0],ans[1]) n = 9247606623523847772698953161616455664821867183571218056970099751301682205123115716089486799837447397925308887976775994817175994945760278197527909621793469 e1 = 11 e2 = 41 e3 = 67623079903 e4 = 5161910578063 e5 = 175238643578591220695210061216092361657427152135258210375005373467710731238260448371371798471959129039441888531548193154205671 c = 7117565509436551004326380884878672285722722211683863300406979545670706419248965442464045826652880670654603049188012705474321735863639519103720255725251120 e = e1 * e2 * e3 * e4 * e5 p, q = wiener(n, e) flag = decrypt(p, q, e, c) print flag
easyctf{keblftftzibatdsqmqotemmty}
Hidden Key (Crypto 250)
2 * d + phi(n) = A とする。 A * e = 2 * d * e + e * phi(n) mod phi(n) → A * e = 2 mod phi(n) → phi(n) = A * e - 2 と考えられる。
これを元に復号のコードを書く。
from gmpy2 import * n = 26710688765026267138148264204920703883007904874128562799028393547554949765922542277895189518670065560334868811558510266979231934353002179757403268531715067062010281686471508752161203685149228268075021781933099554658431559853466187242611729894977296432641592421613434814603355905619394581849422912654259602509865122156934015070617275449499084493067258800113716929834658774822670480345848547269860867133466260711123975552273716095623892922027240248317967859377410150566076391929436484714414414857035650003266909701614148602709844136551594429588283632818636865431390365833016164255279255802323640923522353818320309748633 e = 65537 c = 6155261872379721610545844543378067292233506464929342294811835612600388915322721422713773519949082615325444696280961405812555399866766226739563377342138484240116517586749074812783395471895199325930180026213789355825092956319857260136986497376123985897638747818201019539905731342033491297584936188106793446672492960854016060922453033593011340995980680523319744413970189205384835729579064694334213775169787018937106526040708918216192341791478828953861227176828698102696958311016012460528271599429418317475682934164425649660020076980302878536164096467035358023176811709964428755690089778737252553786028779140848957236412 A = 53253460112850178018596037315460717011178095833855475112480704910365516369607568535550013776774464901421710311058761745335312922569418356218042111675709203846016562029353499192439190013411789736533843417778394708497184559463716663220686541213932946373526645238458498554186241197186952940378859218658633568565865387359341323473553517547778383854342692530241379613507904160499942231159638220574403240144802192330430882869766628877772376237739254238967605943185500475437018419456964233958276405329507956177859421308456900776164204921100052945746833393254101379336114249596223790020916083908846829231724794651160737011674 phi = A * e - 2 d = invert(e, phi) m = pow(c, d, n) flag = ('%x' % m).decode('hex') print flag
easyctf{lbrqfhlyy8fu2bxu3z}