この大会は2016/4/9 9:00(JST)~2016/4/16 9:00(JST)に開催されました。
今回も個人で参戦。結果は410点で91位でした。
Competitorsとしての参加チームは749チームいたので、まあまあの順位です。
解けた問題をWriteupとして書いておきます。
When in Rome (Cryptography 10)
シーザー暗号をオンラインサイト http://planetcalc.com/1434/ で解読すると、ROT9で復号できた。
Welcome to sCTF! We hope you enjoy the problems we have written for the first quarter of 2016. Here is your first of (hopefully) many flags! sctf{wh3n_1n_ctf_d0_a5_ctf3r5_d0}
sctf{wh3n_1n_ctf_d0_a5_ctf3r5_d0}
Banana Boy (Forensics 20)
JPEGファイルが与えられている。JpegAnalyzerでファイルを分割すると、分割したファイルにフラグが書かれている。
sctf{tfw_d4nk_m3m3s_w1ll_a1w4y5_pr3v4il}
Ducks (Web 30)
ソースを見ると、パスワードを比較する前に extract($_POST); とあり、POSTパラメータが展開されている。比較するパラメータを2つとも同じ値で指定すればよい。
Fiddlerを起動し、Before Requestsでブレークする。POSTパラメータを「pass=a&thepassword_123=a」とするとフラグが表示された。
sctf{maybe_i_shouldn't_have_extracted_everything_huh}
Lengthy Lingo (Cryptography 35)
非常に大きい数字がカンマ区切りで多数並んでいる。
いろいろ試した結果、各データの長さをASCIIコードとして読み込むと、フラグが表示された。
with open('encrypted.dat', 'r') as f: data = f.read() data = data.replace('\n', '') nums = data.split(', ') flag = '' for num in nums: flag += chr(len(num)) print flag
sctf{101_th3_numb3r5_d1dn'7_3v3n_m4tt3r}
Tracking (Algorithmic 85)
いろんな3次元空間の位置からの4地点までの距離情報が与えられている。この位置の平均値を求める問題。高校数学レベルでx, y, zが求めることができれば答えは出る。
import math p = float(2000) q = float(2000) r = float(2000) s = float(3000) t = float(1500) u = float(1700) with open('tracking.txt', 'r') as f: lines = f.read() lines = lines.split('\n') num = len(lines) xSum = float(0) ySum = float(0) zSum = float(0) for line in lines: pos = line.split(' ') a = float(pos[0]) b = float(pos[1]) c = float(pos[2]) d = float(pos[3]) x = float(p**2 - (b**2-a**2)) / (p*2) y = float(r**2 - (c**2-b**2)) / (r*2) z = float((x-s)**2 - (x-q)**2 + (y-t)**2 - (y-r)**2 + u**2 - (d**2-c**2)) / (u*2) xSum += x ySum += y zSum += z xAvg = int(math.ceil(xSum / num)) yAvg = int(math.ceil(ySum / num)) zAvg = int(math.ceil(zSum / num)) print 'sctf{' + str(xAvg) + ', ' + str(yAvg) + ', ' + str(zAvg) + '}'
sctf{537, 516, 487}
Verticode (Cryptography 90)
問題の暗号は、画像の左側が色で、右側がASCIIコードを表し、色によりASCIIコードをずらすというもの。暗号アルゴリズムに従い、復号する。
import Image UNIT = 12 img = Image.open('code1.png') w, h = img.size y = 0 dec = '' while y < h: x = 0 index = 0 code = '' while x < w: r, g, b = img.getpixel((x, y)) if index > 6: if r == 0 and g == 0 and b == 0: code += '1' else: code += '0' elif x == 0: if r == 255 and g == 0 and b == 0: color = 0 elif r == 128 and g == 0 and b == 128: color = 1 elif r == 0 and g == 0 and b == 255: color = 2 elif r == 0 and g == 128 and b == 0: color = 3 elif r == 255 and g == 255 and b == 0: color = 4 elif r == 255 and g == 165 and b == 0: color = 5 x += UNIT index += 1 y += UNIT dec += chr(int(code, 2) - color) print dec
このスクリプトを実行すると、このような長い文字列で文章になっているのだが、スペースがなく読みにくい状態。
JoeLopowasamanofmildtemperamentshortstatureandhadthegoaltobecometheworldsfastesttelephoneeaterThoughLoponeverknewevenbasicphysicshecreatedatelescopecapableofsightingthesmallesthaironanalienwholivedquiteafewlightyearsawayJoeLopoquicklydestroyedalargeboulderandusedtheshatteredremainstoformeightsmallstatuesthatstronglyresembledtinycreaturesbeingorrelatedtothewaterfleaHeplacedtheminacircularpatterntoformasortofshrineandplacedthetelescopeinthemiddleofitHethenchanneledthepowerofthestonewaterfleasintothetelescopetoviewthepoweroftheheavensHewasinatrancewiththebeautyofthemysteriousdimensionanddidntevennoticetheverylargetornadoheadingtowardhimTheshrinewasquicklydemolishedandtheimmediatewithdrawlofpowersentJoeLobointoalairofpitchblacknessfoundtobeaparalleldimensionthatcausABCiamtheflagalllowercasenojokeDEFanyonewhosefirstnamebeganwithJalongwithMLandQtobecomeratheruncomfortableJoewasalsosuddenlyintroducedtoundroclamaticolomphasisciousytheeccentrictapewormwithastrongmorrocanaccentImundroclamaticolomphasisciousytheeccentrictapewormIlikepizzasohowareyadoinIhavenoideasaidJoe
https://www.wattpad.com/72172589-now-i-see-you-phonesに原文があるので、差のある箇所を見てみる。「ABCiamtheflagalllowercasenojokeDEF」という部分が違い、ABCとDEFに挟まれた小文字の部分がフラグだった。
sctf{iamtheflagalllowercasenojoke}
Vertinet (Cryptography 140)
ncコマンドで接続してみると、Data URIスキームで画像が指定されているHTMLが返ってくる。
$ nc problems1.2016q1.sctf.io 50000 <html><img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKgAAAFoCAIAAABMtifjAAAEqUlEQVR4nO3dQY7TMBiA0QblKpyql0kPM1yKw5QlYjE0EsZO53tvHTWGT954rD/b7fa8dW3T3vR8vv5/3rYx6znzrm9D3sTbET5K+Cjho4SPEj5K+Cjho4SPEj5K+Kj9+THvvPpqtvvrZ2aesZ9xZj1n2PFRwkcJHyV8lPBRwkcJHyV8lPBRwkcJH7WvXsBXMPM8f9Tv2PFRwkcJHyV8lPBRwkcJHyV8lPBRwkcJH7VvP1cv4dpm3pk/Y9TfBez4KOGjhI8SPkr4KOGjhI8SPkr4KOGjhI/aBo1UeUujjuFHzaUxr57/Tvgo4aOEjxI+Svgo4aOEjxI+Svgo4aP224/VS7i2UffYz/zOqHe5V8+nhI8SPkr4KOGjhI8SPkr4KOGjhI8SPsq9+hdm3pmf+S47Pkr4KOGjhI8SPkr4KOGjhI8SPkr4KOGj9u2xegnXdrU786PO8+34KOGjhI8SPkr4KOGjhI8SPkr4KOGjhI/an99XL2GdUV+Nnfn92VHvsuOjhI8SPkr4KOGjhI8SPkr4KOGjhI8SPmrffq5ewvsbdWd+Jjs+Svgo4aOEjxI+Svgo4aOEjxI+Svgo4aPcqx/xOxebRX/mXXZ8lPBRwkcJHyV8lPBRwkcJHyV8lPBRwkf5tuwkV7t7b8dHCR8lfJTwUcJHCR8lfJTwUcJHCR8lfNS+egH8NvM8346PEj5K+Cjho4SPEj5K+Cjho4SPEj5K+Kj99mP1Eq5t1Pn5zDvzZuDwKeGjhI8SPkr4KOGjhI8SPkr4KOGjhI/aH/dj9RoWOl4+MWo+/MzfOcOOjxI+Svgo4aOEjxI+Svgo4aOEjxI+Svio7fmxegnrbPfXz4z6JuwZo87h3avnU8JHCR8lfJTwUcJHCR8lfJTwUcJHCR/lXv2bGXX33o6PEj5K+Cjho4SPEj5K+Cjho4SPEj5K+Kjtdpt3b/x6rjVD/oxRs3Ts+Cjho4SPEj5K+Cjho4SPEj5K+Cjho4SP2o/bY/UaljlOPDPz27Iz5+3Y8VHCRwkfJXyU8FHCRwkfJXyU8FHCRwkfta9ewFcw85uwZuDwT4SPEj5K+Cjho4SPEj5K+Cjho4SPEj7KDJwBRp2fz5yTY8dHCR8lfJTwUcJHCR8lfJTwUcJHCR8lfNT2/Fi9hHW2+7x3mVfPJQgfJXyU8FHCRwkfJXyU8FHCRwkfJXzU/rgfq9ew0PHyiZnzbWb+jh0fJXyU8FHCRwkfJXyU8FHCRwkfJXyU8FHbqQ+sflXH6gX8yQwc/jvho4SPEj5K+Cjho4SPEj5K+Cjho4SP2kyr/7urza4xA4d/InyU8FHCRwkfJXyU8FHCRwkfJXyU8FHu1b+ZUX87sOOjhI8SPkr4KOGjhI8SPkr4KOGjhI8SPmpfvYCKq829t+OjhI8SPkr4KOGjhI8SPkr4KOGjhI8SPmo73vFy+SBn/u2j7rGPMuo8346PEj5K+Cjho4SPEj5K+Cjho4SPEj5K+KjtYkfRU5059javni9F+Cjho4SPEj5K+Cjho4SPEj5K+Cjho/bHdqxew0LHyydGnY2bgcMlCB8lfJTwUcJHCR8lfJTwUcJHCR8lfNQv5m7AS/gZVBAAAAAASUVORK5CYII='></img>
BASE64デコードしてファイル保存すると、Verticodeの問題と同じような画像になるので、これを踏まえスクリプトを作成する。
import socket import re import base64 import Image pattern = 'image/png;base64,(.*)\'' UNIT = 12 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('problems1.2016q1.sctf.io', 50000)) for i in range(1, 1000): data = s.recv(2048) print data if 'sctf{' in data: break m = re.search(pattern, data) img_b64 = m.group(1) with open('data.png', 'wb') as f: f.write(img_b64.decode('base64')) img = Image.open('data.png') w, h = img.size y = 0 dec = '' while y < h: x = 0 index = 0 code = '' while x < w: r, g, b = img.getpixel((x, y)) if index > 6: if r == 0 and g == 0 and b == 0: code += '1' else: code += '0' elif x == 0: if r == 255 and g == 0 and b == 0: color = 0 elif r == 128 and g == 0 and b == 128: color = 1 elif r == 0 and g == 0 and b == 255: color = 2 elif r == 0 and g == 128 and b == 0: color = 3 elif r == 255 and g == 255 and b == 0: color = 4 elif r == 255 and g == 165 and b == 0: color = 5 x += UNIT index += 1 y += UNIT dec += chr(int(code, 2) - color) print '%d times: %s' %(i, dec) s.sendall(dec)
スクリプトを実行し、200回正解すると、フラグが表示された。
sctf{y0ub34tth3v3rt1c0d3}