InterKosenCTF writeup
追記
チームメイトもwrite upを書いてくれました。
2人で解いた問題に関してはこちらのwrite upも見るとわかりやすいと思います。
はじめに
InterKosenCTFに参加しました。結果は、91チーム中22位でした。
時間の取れない中ではそこそこ頑張りましたが、相変わらずWeb問の解けないWebエンジニアの様相を呈していました。
解けた問題
uploader
よくありそうな、SQLinjectionの問題です。
') UNION SELECT passcode FROM files; #
と検索欄に入力すると、
SELECT name FROM files WHERE instr(name, '') UNION SELECT passcode FROM files; #') ORDER BY id DESC
となり、the_longer_the_stronger_than_more_complicated
というpasscodeが手に入ります。
これでflagがダウンロードできます。
Kurukuru Shuffle
全探索をします。
encrypted = "1m__s4sk_s3np41m1r_836lly_cut3_34799u14}1osenCTF{5sKm" def is_prime(N): if N % 2 == 0: return False i = 3 while i * i < N: if N % i == 0: return False i += 2 return True L = len(encrypted) assert is_prime(L) for k in range(1,L): for a in range(L): for b in range(L): flag = list(encrypted) if a == b: continue i = k for _ in range(L): s = (i + a) % L t = (i + b) % L flag[s], flag[t] = flag[t], flag[s] i = (i + k) % L flag = "".join(flag) if flag.startswith('KosenCTF'): print(flag)
候補が6つほど出てきます。重複を除くと、
KosenCTF{us4m1m1_m4sk_s3np41_1s_r34lly_cut3_38769915} KosenCTF{5s4m1m1_m4rk_s3np41_1s_s38l9y_cut3_34769l1u} KosenCTF{5s4m1m1_m4sk_s3np41_1s_r34l9y_cut3_38769l1u}
となります。r34lly
のあたりを見て、really
かな?という感じで提出すると正解でした。
Hugtto!
ソースコードを見ると、「画像をflag長数ごとに区切り、ピクセルのRGBいずれかの下位1bitを、flagの各bitに変えていく」という操作を繰り返し行っていることがわかります。
flag長を予想して、統計的にflagを求めることができます。
ソースコードは以下のようになりました。(python部分はチームメンバーと協力して書いたものです)
from PIL import Image bin_flag = [] for c in "KosenCTF{": for i in range(8): bin_flag.append((ord(c) >> i) & 1) img = Image.open("./steg_emiru.png") w, h = img.size array=[] for x in range(w): for y in range(h): r, g, b = img.getpixel((x, y)) array.append([r&1, g&1, b&1]) pi = 0 ma = 0 ans = [0]*544 for i in range(len(array)): cnt = 0 for j in range(len(bin_flag)): if i>=1437*544: # out of range したので適当に終わらせている break if(bin_flag[j]==array[i+j][0] or bin_flag[j]==array[i+j][1] or bin_flag[j]==array[i+j][2]): cnt+=1 if(cnt==len(bin_flag)-1): # KosenCTF{とバイナリ列が一致 #print(i-pi) ma = max(i-pi,ma) pi=i if(i%544==0): # 長さが544と目星がついた #print(i/544) for k in range(544): ans[k] += (array[i+k][0]+array[i+k][1]+array[i+k][2]) # rgb全部足す。1400列も足せば統計的に0か1かわかる ans_str ="" for i in ans: if i > 2100: ans_str+="1" else: ans_str+="0" print(ans_str)
array = gets.chars puts( array.each_slice(8).map do |a| a.reverse.join.to_i(2).chr end.join )
最後に、python3 decrypt.py ~| ruby decrypt.rb
などとして、flagを得ます。
(ビッグエンディアンであることに気づかずにハマってました)
なお、最後にRubyを使っているのは、慣れているため速く書けるから以上の意味はありません。
ただ、Rubyは文字列操作はかなりやりやすい言語なので、CTFにも向いていると思います。
E_S_P
与えられたコードを見ると、平文の上位ビットが分かっているので、Coppersmith's attackをします。 やや与えられたビット数が足りないらしいのですが、参考実装を見ながら書いたら何故か通りました。
File: exploit.sage import time import sys # You will not known this # msg = b'Your PIN code is 4394' def long_to_bytes(data): data = str(hex(long(data)))[2:-1] return "".join([chr(int(data[i:i + 2], 16)) for i in range(0, len(data), 2)]) def bytes_to_long(data): return int(data.encode('hex'), 16) def main(): e,N = (5L, 11854673881335985163635072085250462726008700043680492953159905880499045049107244300920837378010293967634187346804588819510452454716310449345364124188546434429828696164683059829613371961906369413632824692460386596396440796094037982036847106649198539914928384344336740248673132551761630930934635177708846275801812766262866211038764067901005598991645254669383536667044207899696798812651232711727007656913524974796752223388636251060509176811628992340395409667867485276506854748446486284884567941298744325375140225629065871881284670017042580911891049944582878712176067643299536863795670582466013430445062571854275812914317) c = 4463634440284027456262787412050107955746015405738173339169842084094411947848024686618605435207920428398544523395749856128886621999609050969517923590260498735658605434612437570340238503179473934990935761387562516430309061482070214173153260521746487974982738771243619694317033056927553253615957773428298050465636465111581387005937843088303377810901324355859871291148445415087062981636966504953157489531400811741347386262410364012023870718810153108997879632008454853198551879739602978644245278315624539189505388294856981934616914835545783613517326663771942178964492093094767168721842335827464550361019195804098479315147 m = bytes_to_long("Yukko the ESPer: My amazing ESP can help you to get the flag! -----> KosenCTF{\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") P.<x> = PolynomialRing(Zmod(N), implementation='NTL') pol = (m + x)^e - c roots = pol.small_roots(epsilon=1/30) print("Potential solutions:") for root in roots: print(root, long_to_bytes(m+root)) main()
ちなみに、解いた直後にチームメイトも以下のコード(参考実装はこちら)で解読に成功していました。
# partial_m.sage n = 11854673881335985163635072085250462726008700043680492953159905880499045049107244300920837378010293967634187346804588819510452454716310449345364124188546434429828696164683059829613371961906369413632824692460386596396440796094037982036847106649198539914928384344336740248673132551761630930934635177708846275801812766262866211038764067901005598991645254669383536667044207899696798812651232711727007656913524974796752223388636251060509176811628992340395409667867485276506854748446486284884567941298744325375140225629065871881284670017042580911891049944582878712176067643299536863795670582466013430445062571854275812914317 e = 5 c = 4463634440284027456262787412050107955746015405738173339169842084094411947848024686618605435207920428398544523395749856128886621999609050969517923590260498735658605434612437570340238503179473934990935761387562516430309061482070214173153260521746487974982738771243619694317033056927553253615957773428298050465636465111581387005937843088303377810901324355859871291148445415087062981636966504953157489531400811741347386262410364012023870718810153108997879632008454853198551879739602978644245278315624539189505388294856981934616914835545783613517326663771942178964492093094767168721842335827464550361019195804098479315147 mbar = 0b10110010111010101101011011010110110111100100000011101000110100001100101001000000100010101010011010100000110010101110010001110100010000001001101011110010010000001100001011011010110000101111010011010010110111001100111001000000100010101010011010100000010000001100011011000010110111000100000011010000110010101101100011100000010000001111001011011110111010100100000011101000110111100100000011001110110010101110100001000000111010001101000011001010010000001100110011011000110000101100111001000010010000000101101001011010010110100101101001011010011111000100000010010110110111101110011011001010110111001000011010101000100011001111011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 kbits = 863-624 PR.<x> = PolynomialRing(Zmod(n), implementation='NTL') f = (mbar + x)^e - c x0 = f.small_roots(epsilon=1/30)[0] # find root < 2^kbits with factor = n print mbar + x0
ε=1
だと答えが出ませんでしたが、パラメータを調節すれば解けてしまいました。
Temple of Time
与えられたwiresharkのログを見ると、Time-based SQL Injectionの形跡があります。
wiresharkの機能でログをcsvに吐き出して、以下のようなRubyのコードでゴリゴリっとパースしたら答えが出ました。
require 'CSV' require 'URI' CSV.open("packet.csv").map do |line| URI.decode(line.last.split[1]).match(/\d*,1\)\)=\d*/)[0].split(",1))=") if line.join.match(/SELECT/) end.compact.each_cons(2).map do |elem| elem[0][1] if elem[0][0]!=elem[1][0] end.compact.map(&:to_i).map(&:chr).join
やはりRubyは文字列操作強いですね。
終わりに
もう少し解きたいのですが、いまいちうまい練習方法を見つけられていません。
とりあえずは、難易度が適切なCTFに出続けようと思います。
また、もしチーム等組んでくださる方がいましたら、声をかけてくださると非常に嬉しいです。
最後に、開催者および関係者の皆様、非常に楽しい時間をありがとうございました!!!