RIG#3-1

74dc690cab435bd58fe785cc72218ca4?s=47 RIG
October 09, 2019

 RIG#3-1

2019/10/09に実施されたRIGの第3回勉強会で使用した発表資料「Shellcode Injectionの実践」です.

74dc690cab435bd58fe785cc72218ca4?s=128

RIG

October 09, 2019
Tweet

Transcript

  1. Shellcode Injection RIG#3-1 (2019/10/09) Yuma Ueda (cyan)

  2. Shellcode Injection • BOFを利用してコンピュータに任意コードを実行させる攻撃 • そもそも/bin/shなどシェルの実行を目的とした攻撃が大半であったためこの 種の攻撃を行うためのペイロードを総称してShellcodeと呼んでいる 2

  3. How does it work? スタックオーバーフロー • スタックに乗っているリターンアドレスを書き換え,配置したshellcodeを実 行させる ヒープオーバーフロー •

    ハンドラーなどのアドレスを書き換える 3
  4. 実践 • 何事も手を動かして実践,実証! • 簡単なpwnableの問題でshellcode Injectionを実践しよう! 4

  5. start from pwnable.tw (https://pwnable.tw/challenge/#1) pwnable • 基本的にはサーバ上で動作するプログラ ムの脆弱性を突きflagを奪取する • バイナリはダウンロードさせてくれる

    (当然) とりあえずディスアセンブル • objdump -D $path_to_binary • コマンドはLinux前提で書いてます • 右のような出力が得られる 5
  6. start アセンブリを追う 8048060 push %esp 8048061 push $0x804809d <_exit> 8048066

    xor %eax, %eax 8048068 xor %ebx, %ebx 804806a xor %ecx, %ecx old_esp esp 何をしている? • スタックにespの値を積む. • ここで注意すべきはpush %espが 実行され,espが4Byte下がる前の 値がスタックに積まれること. 6
  7. start アセンブリを追う 8048060 push %esp 8048061 push $0x804809d <_exit> 8048066

    xor %eax, %eax 8048068 xor %ebx, %ebx 804806a xor %ecx, %ecx old_esp 0x804809d <_exit> esp 何をしている? • _exitセクションのアドレスをス タックに積む(ここにRETしたい) 7
  8. start アセンブリを追う 8048060 push %esp 8048061 push $0x804809d <_exit> 8048066

    xor %eax, %eax 8048068 xor %ebx, %ebx 804806a xor %ecx, %ecx 804806c xor %edx, %edx old_esp 0x804809d <_exit> esp 何をしている? • レジスタのゼロクリアを行ってい る なんでmov %eax, $0じゃないの? • xor命令はバイナリサイズが小さい (2Byte) 8
  9. start アセンブリを追う 804806c xor %edx, %edx 804806e push $0x3a465443 8048073

    push $0x20656874 8048078 push $0x20747261 804807d push $0x74732073 8048082 push $0x2774654c old_esp 0x804809d <_exit> 0x3a465443 (:FTC) 0x20656874([ws]eht) 0x20747261 ([ws]tra) 0x74732073 (ts[ws]s) 0x2774654c(‘teL) esp 何をしている? • “Let’s start the CTF:”という文字列をスタックに積む(リトルエンディアン) 9
  10. コラム: バイトオーダ リトルエンディアン • データの下位から1Byteずつメモリの 下位へ格納 10 ビッグエンディアン • データの上位から1Byteずつメモリの

    下位へ格納 図は2つともhttp://www.ertl.jp/~takayuki/readings/info/no05.htmlより引用
  11. start アセンブリを追う 8048082 push $0x2774654c 8048087 mov %esp, %ecx 8048089

    mov $014, %dl 804808b mov $0x1, %bl 804808d mov $0x4, %al 804808f int $0x80 old_esp 0x804809d <_exit> 0x3a465443 (:FTC) 0x20656874([ws]eht) 0x20747261 ([ws]tra) 0x74732073 (ts[ws]s) 0x2774654c(‘teL) esp 11 int $0x80 • eaxに指定されたシステムコール番号に対応するシステムコールを発行 • システムコール番号→cat /usr/include/asm/unistd_32.h (今回は32bitなので) • 引数はどう渡すの?→この場合はbl, cl, dlと汎用レジスタに順に格納すればよい
  12. start アセンブリを追う 8048082 push $0x2774654c 8048087 mov %esp, %ecx 8048089

    mov $014, %dl 804808b mov $0x1, %bl 804808d mov $0x4, %al 804808f int $0x80 old_esp 0x804809d <_exit> 0x3a465443 (:FTC) 0x20656874([ws]eht) 0x20747261 ([ws]tra) 0x74732073 (ts[ws]s) 0x2774654c(‘teL) esp 12 何をしている? • 命令はwrite(1, esp, 20)と解釈できる • espから20Byte読み出す→”Let`s start the CTF:”と標準出力される 20Byte
  13. start アセンブリを追う 804808f int $0x80 8048091 xor %ebx, %ebx 8048093

    mov $0x3c, %dl 8048095 mov $0x3, %al 8048097 int $0x80 old_esp 0x804809d <_exit> 0x3a465443 (:FTC) 0x20656874([ws]eht) 0x20747261 ([ws]tra) 0x74732073 (ts[ws]s) 0x2774654c(‘teL) esp 13 60Byte 何をしている? • 命令はread(0, esp, 60)と解釈できる • 標準入力から受け取った文字列をespか ら60Byteまで書き込む
  14. start アセンブリを追う 8048097 int $0x80 8048099 add $0x14, %esp 804809c

    ret old_esp 0x804809d <_exit> 0x3a465443 (:FTC) 0x20656874([ws]eht) 0x20747261 ([ws]tra) 0x74732073 (ts[ws]s) 0x2774654c(‘teL) esp 14 何をしている? • スタックポインタを20Byte上げて ,ret命令を発行 • _exitセクションに処理が移る だがしかし • 先程のread命令が発行された際に 20Byte以上のデータが書き込まれてい れば,_exitセクションのアドレスが書 き換えられる. esp 60Byte
  15. start アセンブリを追う 8048097 int $0x80 8048099 add $0x14, %esp 804809c

    ret old_esp 0x804809d <_exit> 0x3a465443 (:FTC) 0x20656874([ws]eht) 0x20747261 ([ws]tra) 0x74732073 (ts[ws]s) 0x2774654c(‘teL) esp 15 何をしている? • スタックポインタを20Byte上げて ,ret命令を発行 • _exitセクションに処理が移る だがしかし • 先程のread命令が発行された際に 20Byte以上のデータが書き込まれてい れば,_exitセクションのアドレスが書 き換えられる. esp 60Byte esp
  16. start ペイロードの作成 (1) readシステムコール発行 • |----適当な値20Byte----|----リターンしたいアドレス----| • 上記のようなデータを送信することで特定のアドレスに処理を移させられる ワンステップでShellcodeを実行させることはできない •

    ASLR有効 &&スタックの上部の空間を使う→old_espの値をリークさせる必 要がある • 1回目のret実行後のesp→old_espを指している && このバイナリはespから 20Byteのデータをwriteシステムコールを発行し標準出力している→もう一 度この処理を呼び出せば出力の先頭4Byteにold_espが含まれる! • これを実現するために,0x8048087にリターンさせる 16
  17. start ペイロードの作成 (2) 前ページの処理の実装 17 Pwntools • 有名なCTFチームがPwnableを解く際 に使ってるライブラリ •

    pwntoolsのほうが好きなんだけど ,socketについてちゃんと知ってほ しい感じがあるので今回は使用しない
  18. start ペイロードの作成 (3) Shellcodeの注入 • 処理が進んでいくと,再びreadシステ ムコールを発行する • | ----適当な値20Byte----

    | ---- old_esp + 20 ---- | ---- Shellcode ---- | のようなデータを送信すること でShellcodeが実行される 18 old_esp 0x804809d <_exit> esp esp old_esp + 20 esp | old_esp + 20 Shellcode old_esp
  19. start ペイロードの作成 (4) 前ページの処理の実装 • Shellが起動していることを確認できる • Shellcodeはragg2 -i exec

    -b 32 で作成 • 19
  20. start ペイロードの作成 (5) せっかくだしフラッグも取っていこう • 先程起動したShellでfind / -iname “*flag*”を実行 •

    ~/flagを確認できる 20
  21. コラム: shellcodeとバリデーション対策 NULL文字排除 • Shellcode→文字列(char *)として与えられる→0x00を含むことができない • mov eax, 1

    → NULLバイトを含む • xor eax, eax inc eax→NULLバイトを含まない Unicode / 英数字のみShellcode • 上で示したような自己書き換えコードを用いて実現可能 21
  22. 参考文献 • https://blog.ohgaki.net/modern-shellcode-and-secure-defensive-progr amming (2019/10/08 参照) • http://www.ertl.jp/~takayuki/readings/info/no05.html (2019/10/08 参

    照) • https://ja.wikipedia.org/wiki/%E3%82%B7%E3%82%A7%E3%83%AB% E3%82%B3%E3%83%BC%E3%83%89#%E7%AC%A6%E5%8F%B7%E5% 8C%96%E6%96%B9%E6%B3%95 (2019/10/08 参照) • http://web.archive.org/web/20150308214411/https://www.eecis.ude l.edu/~bmiller/cis459/2007s/readings/buff-overflow.html (2019/10/08 参照) 22