CTF Pwn

むぅ。攻撃手法について、ぜんぜん体系化できん。。もうちょっと理解が必要だ。
ちょっとずつ修正中。

Linuxにおけるソフトウェアの脆弱性とセキュリティ機構まとめ

がんばって整理してみる。

fig01.png

Pwnable の流れ

ユーザからの入力は、ローカル変数(stack)、グローバル変数(.data、もしくは、.bss)、ヒープ領域(heap)のいずれかに格納される。

C言語における変数の種類 格納される領域 格納される方向
ローカル変数 stack アドレスの値が大きいほうから小さい方へ
グローバル変数 .data、もしくは、.bss アドレスの値が小さいほうから大きい方へ
malloc関数で確保した領域 heap アドレスの値が小さいほうから大きい方へ

もし仮に、 Stack buffer over flow の脆弱性があった場合、ユーザからの入力はローカル変数で定義したサイズを超えて、stack内のメモリ領域を書き換える事ができる。
書き換える内容を工夫することで、以下ができる。

  • 他の領域(heapや.bss等々)を読みこんだり書き換えたりできる。
  • プログラムの流れを変え、任意の命令を実行できる。

Linux kernelのメモリ管理

全セクション

調査方法

# readelf -S /bin/ls
# gdb /bin/ls
(gdb) b exit
(gdb) r
(gdb) i files
(gdb) i proc
(gdb) shell cat /proc/[PID]/maps
セクション名 開始アドレス(サンプル) NX bit 説明
.interp 0x00400238 r-xp 実行形式のロードと動的リンクを行う共有ライブラリ(/lib64/ld-linux-x86-64.so.2とか)を指定 ゆずさん研究所
.note.ABI-tag 0x00400254 r-xp このセクションは、何らかの方法でファイルに印をつける情報を保持している ゆずさん研究所
.note.gnu.build-id 0x00400274 r-xp ビルドされたファイルに対するユニークなIDが入っている。core dumpに含まれる わらばんし仄聞記
.gnu.hash 0x00400298 r-xp シンボル名の検索を高速化するための .dynsym に関連付けられたハッシュテーブルゆずさん研究所
.dynsym 0x004002d0 r-xp 動的リンク用のシンボルテーブル。わらばんし仄聞記 ゆずさん研究所
.dynstr 0x00400f30 r-xp .dynsymセクションヘッダのsh_nameが参照する文字列(シンボル名)を格納している わらばんし仄聞記 ゆずさん研究所
.gnu.version 0x004014ec r-xp .dynsymで定義されるシンボルに対応するバージョンの一覧 わらばんし仄聞記
.gnu.version_r 0x004015f8 r-xp .gnu.versionが指すバージョン値についての情報が示されているセクション わらばんし仄聞記
.rela.dyn 0x00401688 r-xp リロケータブルなシンボルを dynamic に解決するためのセクション 新千葉 ガーベージ・コレクション
.rela.plt 0x00401760 r-xp 動的リンクのために書き替えが必要なアドレスのリスト。アドレスとシンボルをペアにして関連付けている 七誌の開発日記 わらばんし仄聞記
.init 0x00402228 r-xp このセクションにはプロセスが実行される前に実行される実行可能な命令が格納されます。プログラムの実行が始まるときに、OSはメインプログラムエントリー(C言語ではmain関数)をコールする前にこのセクションのコードを実行します。
.plt 0x00402250 r-xp 遅延リンクのために使われる。関数本体へのジャンプコードの集合 わらばんし仄聞記
.text 0x00402990 r-xp プログラムのうち、機械語の部分を格納するためのセクション
.fini 0x0041295c r-xp プロセスの実行終了時に実行される実行可能な命令が格納される。プログラムが正常終了するときにOSはこのセクションのコードを実行する。
.rodata 0x00412980 r-xp プログラムのうち、定数(const)を格納するためのセクション。C言語では、「プログラム中の文字列定数」「const宣言された定数」などが格納される。セクションとか.textとか
.eh_frame_hdr 0x00416650 r-xp C++のランタイムが eh_frame にアクセスするためのコードが入るセクション メモ書き
.eh_frame 0x00416d98 r-xp 例外をサポートしている言語の場合、情報を保持しておくセクション。バックトレース(スタックトレース)をとるための情報が入ったフレーム わらばんし仄聞記
.init_array 0x0061a320 r--p 関数のアドレスが格納された配列になっている。.initセクション実行の後に、順番に実行される。
.fini_array 0x0061a328 r--p 関数のアドレスが格納された配列になっている。.finiセクション実行よりも前に、順番に実行される。
.jcr 0x0061a330 r--p Java Class Reference らしい。
.data.rel.ro 0x0061a340 r--p RELRO関係??
.dynamic 0x0061ada8 r--p 動的リンクに必要な情報を集めたテーブル
.got 0x0061afa8 r--p 動的リンクされた関数などのアドレステーブル。ここをインタプリタで書き替えることにより、動的リンクを実現する。
.got.plt 0x0061b000 rw-p 動的リンクされた関数などのアドレステーブル。ここを書き替えることにより、動的リンクを実現する。Full RELROの場合は、存在しない。 七誌の開発日記
.data 0x0061b3c0 rw-p プログラムのうち、初期値を持つ変数を格納するためのセクション。C言語では、「0以外の初期値を持つ大域変数」「0以外の初期値を持つ静的局所変数」がここに置かれる。データとして初期値を持ち、プログラムローダは書き込み可能なメモリを確保した後、初期値を書き込む。
.bss 0x0061b600 rw-p プログラムのうち、初期値を持たない変数を格納するためのセクション。C言語では、「初期値が指定されない大域変数」「初期値が0の大域変数」「初期値が指定されない静的局所変数」「初期値が0の静的局所変数」が格納される。C言語の規約では、「この領域はすべて0で初期化されなければならない」と規定されているため、プログラムローダは書き込み可能なメモリを確保した後、すべて0で初期化する。
[heap]↓ 0x0061c000-0x0063e000 rw-p C言語におけるmalloc関数等で確保した領域が配置される
shared-object 7ffff0415000-7ffff7ffa000 以下参照 共有ライブラリ(.soのファイル)のメモリ配置場所
[vdso] 7ffff7ffa000-7ffff7ffc000 r-xp ここ(CTF Pwn)参照
shared-object 7ffff7ffc000-7ffff7fff000 以下参照 共有ライブラリ(.soのファイル)のメモリ配置場所
[stack]↑ 7ffffffea000-7ffffffff000 rw-p C言語における関数呼び出し元のアドレス退避先、及び、ローカル変数のメモリ配置先
[vsyscall] ffffffffff600000-ffffffffff601000 r-xp カーネル空間の実行コードをユーザ空間から参照できる int0x80 と sysenter を切り替える vsyscall
kernel-area カーネルが使うところ

NX bit

r = read
w = write
x = execute
s = shared
p = private (copy on write)

shared-object の NX bit の例 (soによって中身が4分割されてNX bitを設定されたり、まちまち)

7ffff0415000-7ffff693c000 r--p 00000000 fd:00 181108739                  /usr/lib/locale/locale-archive
7ffff693c000-7ffff6952000 r-xp 00000000 fd:00 72515859                   /usr/lib64/libpthread-2.17.so
7ffff6952000-7ffff6b52000 ---p 00016000 fd:00 72515859                   /usr/lib64/libpthread-2.17.so
7ffff6b52000-7ffff6b53000 r--p 00016000 fd:00 72515859                   /usr/lib64/libpthread-2.17.so
7ffff6b53000-7ffff6b54000 rw-p 00017000 fd:00 72515859                   /usr/lib64/libpthread-2.17.so
7ffff6b54000-7ffff6b58000 rw-p 00000000 00:00 0 
7ffff6b58000-7ffff6b5c000 r-xp 00000000 fd:00 72518875                   /usr/lib64/libattr.so.1.1.0
7ffff6b5c000-7ffff6d5b000 ---p 00004000 fd:00 72518875                   /usr/lib64/libattr.so.1.1.0
7ffff6d5b000-7ffff6d5c000 r--p 00003000 fd:00 72518875                   /usr/lib64/libattr.so.1.1.0
7ffff6d5c000-7ffff6d5d000 rw-p 00004000 fd:00 72518875                   /usr/lib64/libattr.so.1.1.0
7ffff6d5d000-7ffff6d60000 r-xp 00000000 fd:00 72515794                   /usr/lib64/libdl-2.17.so
7ffff6d60000-7ffff6f5f000 ---p 00003000 fd:00 72515794                   /usr/lib64/libdl-2.17.so
7ffff6f5f000-7ffff6f60000 r--p 00002000 fd:00 72515794                   /usr/lib64/libdl-2.17.so
7ffff6f60000-7ffff6f61000 rw-p 00003000 fd:00 72515794                   /usr/lib64/libdl-2.17.so
・
・
・

ELFの動的リンク
http://www.slideshare.net/7shi/startprintf2-elf

主要なセクション

CTFだけ考えるなら、以下を押さえておけば、だいたいなんとかなる。

セクション名 開始アドレス(サンプル) NX bit ざっくり説明
.plt 0x00402250 r-xp ここ(CTF Pwn)参照
.text 0x00402990 r-xp 実行される機械語のところ
.rodata 0x00412980 r-xp プログラム中の文字列定数、const宣言された定数
.got 0x0061afa8 r--p ここ(CTF Pwn)参照
.got.plt 0x0061b000 rw-p ここ(CTF Pwn)参照。Full RELROの場合は、存在しない。
.data 0x0061b3c0 rw-p 初期値を持つ変数
.bss 0x0061b600 rw-p 初期値を持たない変数
[heap]↓ 0x0061c000-0x0063e000 rw-p malloc関数を実行した際のメモリ確保先
shared-object 7ffff0415000-7ffff7fff000 いろいろ 共有ライブラリ
[stack]↑ 7ffffffea000-7ffffffff000 rw-p 関数呼び出し元のアドレス退避先、ローカル変数のメモリ配置先
kernel-area

PLT(Procedure Linkage Table)とGOT(Global Offset Table)

libc.soなどにある外部関数のアドレスを動的に求める機構。
.pltセクションが外部アドレスを解決し、.got.pltに保存(キャッシュ)する。スタティックリンクだと存在しない。

Partial RELROの場合、遅延バインドという動作になる。
共有ライブラリにある関数アドレスに対して、初回呼び出し時に、.got.pltにキャッシュする方式。
そのため、.got.pltセクションは書き込み可能な状態で存在し、GOT overwriteという攻撃を受けるリスクがある。

fig02.png

例えば、C言語でputs関数を呼び出すコーディングをした際の動きは、以下の通り。

  • ①.textセクションにputs関数を呼び出す機械語が書かれている。puts関数の呼び出し先アドレスは、.pltセクションのputs関数がエントリされている部分になる。
  • ②呼び出された.pltセクションのputs関数の箇所は、さらに .got.pltセクションのputs関数がエントリされているアドレスを呼び出す。
  • ③.got.pltセクションは、本物の共有ライブラリ(libc.so)のputs関数を呼び出す。

Full RELROの場合は、.got.pltセクションは存在しない。
遅延バインドを使わず、プロセス起動時に外部アドレスを解決して.gotセクションに書き込む。書き込み後にNXによりリードオンリーにする。
動きは、.got.pltが、.gotに代わる以外は、Partial RELROの場合と同様である。

fig03.png

PLTエントリはELF中の固定アドレスであり、ASLRが有効であってもアドレスは固定。
PIEが適用されている場合はアドレスがランダムとなる。

参考

ψ(プサイ)の興味関心空間 - ELFの再配置シンボルの解決
http://ledyba.org/2014/06/13093609.php

脱力系日記 GOT、PLTとIAT
http://tkmr.hatenablog.com/entry/2017/02/28/030528

vDSO - 仮想 ELF 動的共有オブジェクトの概要

一部のアーキテクチャの時間関数などは、高速化のためカーネル空間に切り替わらず、vsyscallのみで実現している。
vsyscallに必要な関数群をユーザー空間のアプリケーションに提供する仕組み。

Man page of VDSO

スタックとスタックフレームの仕組み

fig04.png

黄色の部分は SSP による canary値。後述する。

ELF Auxiliary Table
要はカーネルから渡される各種値のテーブル。アンチデバッグとして、稀にこの値が利用されることがある。

Linuxのセキュリティ機構

NX bit

プロセスの全てのメモリ領域において、読み・書き・実行が可能だと、セキュリティ上良くない。
セクション毎に、読み・書き・実行の権限を制御できるようにしたのが、NX。

Microsoft Windows的には DEP と呼ばれている。

ASLR

通常、同じプログラムであれば、関数のアドレスや、変数の格納先アドレス等、何度実行しても変化することはない。
この「変化しない」という性質を利用して、任意の関数を呼ばれたりして悪意のある攻撃につながってしまう。

ASLRは、heap領域以降のアドレスをランダム化(アドレス空間配置のランダム化)することで、これらの攻撃を防ぐのが目的。
ASLRだと起動するたびにheap領域以降のアドレスが変化する。

ただし、ランダム化されるのheap領域以降と限定されるため、アドレスが固定化されている部分を利用した攻撃に対するリスクは残っている。

通常のheapの開始アドレスは、0x0804XXXXとかになるが、ASLRだと0xfXXXXXXXで始まるアドレスになる。

SSP(GCC Stack-Smashing Protector)

スタックバッファオーバーフローを防ぐセキュリティ機構の一つ。
SSPを有効にすると、関数の呼び出し時にスタックにcanaryと呼ばれる値が置かれる。
関数から出る時(リターン前)に、canaryが変更されていないか検証(__stack_chk_fail関数の呼出)され、書き換えられていたら強制終了する。

master canaryはどこにあるか

master canary というスタックに置かれた値との比較元は、どこにあるか。

  • THREAD_SET_STACK_GUARD にて決められている。
    • 7アーキテクチャにて定義
    • canaryがTLS(thread local storage)に入る。TLSはヒープ領域に格納される。
    • 定義されていないならmaster canaryは.bssへ
    • ヒープも.bssセクションも、通常は書き込み権限があるので、書き換え可能。
    • canaryは、バイナリが再起動するまで変化しない。

参考

@potetisenseiCODE BLUEの時の発表が神解説。
https://www.youtube.com/watch?v=UTC2iWxQ4qc&feature=youtu.be&a
http://www.slideshare.net/codeblue_jp/master-canary-forging-by-code-blue-2015
https://github.com/potetisensei/MasterCanaryForging-PoC

RELRO

外部ライブラリ(共有オブジェクト *.so)を利用するとき、それらはアドレス空間の色々なところにマッピングされている。
これらで提供されている関数のアドレスを毎回計算で求めるのは大変なので、一度計算したら保存しておくテーブルがあると便利。
そのテーブルのことをGOT(Global Offset Table)と呼び、アドレス固定領域に存在している。

このGOTテーブルが、もし書き換えられると任意の関数を実行できてしまう。
それを防ぐ手段として、Partial RELRO と Full RELRO の二種類がある。

種類 遅延バインド 説明
Partial RELRO 有効 .got.pltセクションが存在し、一部書き換え可能
Full RELRO 無効(起動時間が遅くなる) .got.pltセクションは無い。リードオンリーな .gotセクションのみがある。

遅延バインドについて

普通、遅延バインドと言うと、Partial RELRO の時の動きを言うんだと思う。

種類 説明
RELRO 無し オブジェクトがロードされた時(プログラムの起動時)に、dynamic linkerが全てのGOTのエントリに本当の関数のアドレス(libc.soのputsなど)を埋める。
Partial RELRO オブジェクトがロードされた時(プログラムの起動時)には、.got.pltセクションに特別な値を入れておき、本当の関数のアドレス調査を、その関数の初回呼び出し時まで遅延する
Full RELRO 遅延BINDしない。プログラム実行開始時に.gotセクションを全部書き換える。全部書き換え終わったら、.gotセクションを書き込み禁止にする

PIE

ASLRが有効な場合、スタック領域・ヒープ領域や共有ライブラリが置かれるアドレスは一定の範囲の中でランダムに決められる。
一方、実行ファイルそのものが置かれるアドレスは基本的には固定であるが、PIE (Position-Independent Executables) となるようにコンパイル・リンクすることでランダムなアドレスに置けるようにできる。

ASCII-armor

共有ライブラリをメモリ上に配置するときにNULL(0x00) を含むアドレスへ配置するようにする。
strcpy 等を利用してのメモリ書き換えを防止するため。

Vulnerabilities(脆弱性)

Stack Buffer Overflow(スタックバッファオーバーフロー)

wikipedia_StackBufferOverflow.png

入力値チェックの無い変数にサイズオーバーで値を詰めると、スタックの底の方を書き換えられる。
スタックの底には、call時に関数復帰先のアドレスが設定される。

書き換える内容にシェルコードを含めておき、復帰先は、そこに飛ぶようにうまく上書きすれば、スタックの中だけでexploit処理が完結できる。ただし、NX bitで、この方法を無効にできる。

Heap Buffer Overflow(ヒープバッファオーバーフロー)

NEC 古賀さんによるありがたい解説
http://www.soi.wide.ad.jp/class/20040011/slides/19/45.html
https://www.nic.ad.jp/ja/materials/security-seminar/20041004/3-koga.pdf

ヒープの仕組み

  1. ヒープはフリーリストという構造になっている。
  2. この1つの要素を共有する状態を作る。
  3. 共有すると要素が抱える次の要素と前の要素を指すポインタを書き換えることができる。
  4. 書き換えられれば、指し先をスタックにして、任意の値を書き込むことで、任意コードの実行まで出来る。

図を入れる。入れたい。

malloc(3)のメモリ管理構造
http://www.valinux.co.jp/technologylibrary/document/linux/malloc0001/

mallocの旅(glibc編)
http://www.slideshare.net/kosaki55tea/glibc-malloc

神解説

Use After Free

mallocで一度確保され解放された後に、尚そのアドレスに書き込むことが可能な場合に起きる。
既にそのアドレスは別の用途に転用されている場合、そのアドレスのデータを書き換え可能。

Double Free

free()での二重解放。
解放されているアドレスを解放されていないものと思い込んで使い続けると、ヒープ内のデータが破損する可能性がある。

NEC 古賀さんによるありがたい解説
http://www.soi.wide.ad.jp/class/20040011/slides/19/61.html

off-by-one error

NEC 古賀さんによるありがたい解説
http://www.soi.wide.ad.jp/class/20040011/slides/19/59.html

Race Condition(リソース競合)

排他書が正しく実装されてなかったりした時、Use After FreeやDouble Free,もしくはスタックBOF/ヒープBOFに繋がる。

Format String Bug (FSB)

参考

sekai013's blog - Format String Attack でメモリの中身を書き換える Mac OS X 10.10
http://sekai013.hatenablog.com/entry/2015/08/20/195649

NEC 古賀さんによるありがたい解説
http://www.soi.wide.ad.jp/class/20040011/slides/19/67.html

Time-of-check-Time-of-use Race Condition (TOCTOUリソース競合)

ある処理AとBの間に、別の動作を無理やり割り込ませて、想定外の動作を引き起こす方法。

Exploit Techniques - メモリ領域を上書きする

.got overwrite

.gotセクションは外部関数アドレスのキャッシュであるため、ここを任意の関数のアドレスに書き換えることで、任意の関数が実行できる。
RELROにより、セクション内の書き込み権限がない場合は、成立しない。

ユーザの入力をそのまま受け付ける以下の関数があれば、.gotをsystem()に書き替えておくと,呼び出された時system(user_input)になる。

  • strlen()
  • strcmp()/memcmp()
  • atoi()/strtol()
  • free()

Heap Buffer Overflow(ヒープバッファオーバーフロー)からの Unlink Attack と fastbins Unlink Attack

Unlink Attack

ヒープバッファオーバーフローで,直下がfree済みチャンクの際,fd/bkメンバを上書き。
ただし、2004年以降のlibcにおけるfree()では、チェック機構が加わり、このUnlink Attackは起きない。

fastbins Unlink Attack

fastbinsは、高速化のため実装された機構
fastbinsに入る小さなチャンクが直下にある状態で、ヒープバッファオーバーフローし、fdメンバを上書きできる
これによりfastbins UnlinkAttackができてしまう可能性がある。

参考

katagaitai CTF勉強会 #1 pwnables編 - DEFCON CTF 2014 pwn1 heap
http://www.slideshare.net/bata_24/katagaitai-ctf-1-57598200

bataさん神

ネットワークソケットを利用したシェル起動

(整理中)
ここに置くのが適切かわからん。。。

_IO_jump_t overwrite

(整理中)
ここに置くのが適切かわからん。。。

Exploit Techniques - 任意のアドレス(またはアドレスにある値)を漏洩させる

DT_DEBUG,dl_runtime_resolve

dl_runtime_resolveやDT_DEBUGを利用することで、libc内のアドレスを動的に求めることができる。

dl_runtime_resolve

PLTで使われる,外部関数のアドレスを動的解決する関数

Exploit Techniques - セキュリティ機構を回避する

byte-by-byte bruteforceによるSSP回避(x86)

1バイトずつブルートフォースすれば、256*4回の試行でStack Canaryを特定できる
(x64なら256*8回)
Stack Canaryは,TLS(Thread local storage)に格納されている
x86ではgs+0x14,x64ではfs+0x28にポインタが存在する
この値を書き換えられるなら,Stack Canaryは無効化できる

Improper Null Terminationを利用したSSP回避

(作成中)

Partial overwrite

ASLRおよびPIEが有効な場合、.textセクションもランダム化される。
しかしリトルエンディアン環境においては、リターンアドレスなどの下位バイトのみを書き換えることで付近のコードにジャンプさせることが可能となる。

リトルエンディアンの場合0x12345678はスタック上で 0x78563412と格納されている。
よってBOFなどにより例えば0x78の下位2バイトのみを書き換える事で、近いアドレスにジャンプさせる事ができ る。
飛ばせる先が限られている(他の手法と組み合わせ て使う場合が多い)、リトルエンディアンでしか使えない。

Heap spray

(作成中)

Exploit Techniques - 命令を実行する

(作成中)

はて、どうやって整理したものか

ret2系

(作成中)

種類 説明
ret2libc NX bitによる実行制御を回避するため、libcにあるsystem関数にretするようスタックを書き換える。いい感じにスタックポインタも操作して、書き換えた"/bin/sh"を指すようにする。ASLRやPIEでランダム化されると、厳しい。
ret2esp スタック中にjmp espや、call espに復帰するようなアセンブラコードを仕込む。当然ながらjmp espやcall espがコード中に無ければ成立しない。
ret2plt PLTを引数/戻り先と一緒にスタックへ仕込めば,通常の関数呼出と区別できない。
ret2pop pop,pop,pop,pop,pop,pop,ret 等のガジェットを見つけて、スタックを減らして、次に実行したい関数と引数を積む技。
ret2strcpy
ret2resolve

ROP系

(作成中)

↓けっこう参考になる

ROP 輕鬆談
http://www.slideshare.net/hackstuff/rop-40525248

ASLRは、通常実行体まではランダム化されないため、実行体の中の小さな命令(ROP gadgetsと言う)を集めてシェルコードを作る。
スタックにある関数復帰先のアドレスを制御し続けることで成し得る。

例えば、main関数から関数funcを読んだ先に脆弱性があったとする。
スタックが書き換えられるが、リターンの先をmainからgadgetに書き換える。
gadgetもretするが、その復帰先は、次のgadgetを指すようにする。
これを繰り返す。

PIEまでやられて、実行体もランダム化されると、この方法によるシェル奪取が難しくなる。

ROP系小技

  • __libc_csu_init gadgets

スタックからレジスタへ値を入れられる汎用ガジェットがある

  • alarm(x)

x86/x64で、EAX/RAXレジスタにROPで任意の値を入れたいケース
ROPガジェットを探索しても、pop eax/raxが見つからない場合がよくある
alarm()を使うと、代替可能
ret2pltでalarm(x)-> alarm(0)と2回行えば、EAX/RAXレジスタにxが入る

  • ROP stager

攻撃に使える領域のサイズが制限されている場合、readなどの関数を用いて再度メモリに書き込む方法をstagerと呼ぶ。

  • DROP(Dynamic ROP)

漏洩させたlibcのアドレスを元に,***libc内のガジェット***を利用してROPを構築
.textのガジェットが少ない場合の対処法
相手環境のlibcがわかっていることが前提

  • One-gadget-RCE

DragonSectorの資料
http://j00ru.vexillium.org/?p=2485

x64でsystem("/bin/sh")を呼ぶ場合、条件付き(x64かつxinetd型でのみ有効)だが8バイトの書き込みで代替する方法がある

SROP(Sigreturn-oriented Programming)

vdsoには、シグナル割り込みから復帰する際に、ユーザーランドのスタック上に作成したsignal frameに保存している値を全てのレジスタへ戻すsigreturnという命令が存在する。つまり、popadが廃止されたx64においても、sigreturnによってスタック上の値を複数のレジスタにセットすることができる。これによって、任意のシステムコールを呼び出すことが可能となるほか、関数の呼び出しがレジスタ渡しの場合においてもROPが容易になる。なお、vsyscallはASLRが有効であっても固定アドレスである。
ulimit -s unlimitedを用いてvdsoのマッピングアドレスを固定できる場合はCTFでも活用できそうだ。

JOP(Jump-oriented programming) と COP(Call-oriented programming)

通常、retの次にはそのサブルーチンを呼び出したcallの次の命令が存在する。そこで、コールスタックを辿ることでROPによってretが使われていないか検出するROPguardが考案された。ROPguardはMicrosoftの脆弱性対策ツールであるEMET 3.5の根幹を成す理論だった。
そこで、retの代わりにjmpを用いるJump-oriented programmingが考案された。また、retやjmpの代わりにcallを用いるCall-oriented programmingも可能である。例えば以下のコードスニペットにおいて、callはjmpと実質的に等価である。

pop esi;
ret;
push eax;
call esi;

; call先
pop esi ;retアドレスを除去
;eaxを用いる処理

COPでは、pushのような表現力の高い命令を用いることができる。

Exploit Techniques - シェルコードを置くメモリ領域を確保する

Stack pivot

スタックのサイズ上、リターンアドレスの下にROP chainを構築できないような場合、xchg esp,eaxなどのgadgetを用いてスタックのアドレスを移動させる方法をstack pivotと呼ぶ。

スタックアドレスの設定先は、.bssセクションが使える(write権限がある)。.bssの先頭付近ではなく,中間ぐらいに設定するとよい(スタックの頭打ちを防ぐため)。

Stager

BOFにより、書き換えられる量が少ない場合

  • 短いアセンブリコードをBOFで送り込む
  • これを最初に実行させ,shellcodeを追加読込をさせる
  • 追加読み込みした部分へ制御を移す

と言う流れで対応することをstagerと言う。

Command gadgets

katagaitai ctf study session - setup
http://pastebin.com/dWUV06ug

各種PLT/GOTを調査

$ objdump -d -M intel /bin/cat|grep "@plt>:" -A1
00000000004015b0 <__uflow@plt>:
  4015b0:       ff 25 62 aa 20 00       jmp    QWORD PTR [rip+0x20aa62]        # 60c018 <__sprintf_chk@plt+0x20a608>
--
00000000004015c0 <getenv@plt>:
  4015c0:       ff 25 5a aa 20 00       jmp    QWORD PTR [rip+0x20aa5a]        # 60c020 <__sprintf_chk@plt+0x20a610>
--
00000000004015d0 <free@plt>:
  4015d0:       ff 25 52 aa 20 00       jmp    QWORD PTR [rip+0x20aa52]        # 60c028 <__sprintf_chk@plt+0x20a618>
--

.
.
.


0000000000401a00 <iconv_open@plt>:
  401a00:       ff 25 3a a8 20 00       jmp    QWORD PTR [rip+0x20a83a]        # 60c240 <__sprintf_chk@plt+0x20a830>
--
0000000000401a10 <__sprintf_chk@plt>:
  401a10:       ff 25 32 a8 20 00       jmp    QWORD PTR [rip+0x20a832]        # 60c248 <__sprintf_chk@plt+0x20a838>

関数アドレスの調査

まず利用しているlibcのパスを調べる

$ ldd /bin/cat
        linux-vdso.so.1 =>  (0x00007fff3c3e2000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fc824120000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fc8244ed000)

libc内のsystemのオフセット調査

$ objdump -d /lib64/libc.so.6|grep "system>:"
0000000000041f00 <do_system>:
00000000000423d0 <__libc_system>:

固定文字列のアドレス調査

$ strings -tx /lib64/libc.so.6 |grep '/bin/sh'
 17b249 /bin/sh

アドレス固定のRW領域(.data)調査

IDA Proでもできます。

$ readelf -S ropasaurusrex |fgrep .data

gdb-pedaインストール手順

CentOS7の場合(既存のgdbを潰さない方法)

# yum install python-devel texinfo ※他に足りないパッケージがあったら、同じく入れる(たぶん大丈夫)
# su - user
$ mkdir /home/user/gdb-peda
$ cd /home/user/gdb-peda
$ wget http://ftp.gnu.org/gnu/gdb/gdb-7.9.tar.gz
$ tar pxzf gdb-7.9.tar.gz
$ cd gdb-7.9
$ ./configure --with-python=python && make
$ yum install git
$ git clone https://github.com/longld/peda.git /home/user/gdb-peda/peda
$ echo "source /home/user/gdb-peda/peda/peda.py" >> gdbinit

pedaの動作チェック
起動したら’start’と打ち,カラフルな画面が出てきたらインストール成功

$ /home/user/gdb-peda/gdb-7.9/gdb/gdb -q /bin/ls --data-directory=/home/user/gdb-peda/gdb-7.9/gdb/data-directory -x /home/user/gdb-peda/gdbinit

なので、こういう起動シェル作っとけば、既存のgdbと共存できる

gdb-peda.sh

#!/bin/bash
/home/user/gdb-peda/gdb-7.9/gdb/gdb -q ${1}  --data-directory=/home/user/gdb-peda/gdb-7.9/gdb/data-directory -x /home/user/gdb-peda/gdbinit ${2} ${3} ${4} ${5} ${6} ${7} ${8} ${9}

ちな、Ubuntu(x64)の場合(参考)

$ apt-get install libncurses5-dev g++ python-dev texinfo
$ cd /tmp
$ wget http://ftp.gnu.org/gnu/gdb/gdb-7.9.tar.gz
$ tar pxzf gdb-7.9.tar.gz
$ cd gdb-7.9
$ ./configure --with-python=python2 && make && make install
$ apt-get install git
$ git clone https://github.com/longld/peda.git ~/peda
$ echo "source ~/peda/peda.py" >> ~/.gdbinit

pedaの動作チェック
起動したら’start’と打ち,カラフルな画面が出てきたらインストール成功

$ gdb -q /bin/ls

pop×Nガジェットを調査(rp++の方が精度がいい)

$ gdb ./binary –q
gdb-peda $ start
gdb-peda $ ropgadget

gdb-dashboardインストール手順

ここから、.gdbinitをダウンロード
gdbinit_gdb-dashbordと名前を変える。

起動用シェルを作る。

$ vi gdb-dashboard.sh
+ gdb ${1} ${2} ${3} ${4} ${5} ${6} ${7} ${8} ${9} -x /home/user/gdbinit_gdb-dashboard

$ chmod ugo+x gdb-dashboard.sh

rp++インストール手順

$ wget https://github.com/downloads/0vercl0k/rp/rp-lin-x86
$ wget https://github.com/downloads/0vercl0k/rp/rp-lin-x64

ROPガジェットの抽出

$ rp-lin-x86 --file=binary --unique --rop=5

Metasploit Framework インストール手順

$ sudo apt-get -y install \
  build-essential zlib1g zlib1g-dev \
  libxml2 libxml2-dev libxslt-dev locate \
  libreadline6-dev libcurl4-openssl-dev git-core \
  libssl-dev libyaml-dev openssl autoconf libtool \
  ncurses-dev bison curl wget postgresql \
  postgresql-contrib libpq-dev \
  libapr1 libaprutil1 libsvn1 \
  libpcap-dev \
  libsqlite3-dev

$ sudo apt-get install ruby1.9.3       # rvmを使う代わりに直接インストール
$ cd /opt
$ sudo git clone https://github.com/rapid7/metasploit-framework.git
$ cd metasploit-framework
$ sudo gem install bundler --no-ri --no-rdoc
$ bundle install

EIPまでのオフセットを計算 (pattern_create.rb pattern_offset.rb)

ユニークな文字列生成

$ /opt/metasploit-framework/tools/pattern_create.rb 200
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag

コマンド引数に渡したりして、落ちる所を確認

$ gdb -q a.out
Reading symbols from /home/user/tmp/a.out...(no debugging symbols found)...done.
(gdb) r
Starting program: /home/user/tmp/a.out
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag

Program received signal SIGSEGV, Segmentation fault.
0x64413764 in ?? ()
(gdb) quit

EIPが0x64413764で落ちている。0x64413764が、生成した文字列のどの部分か調べる。

$ /opt/metasploit-framework/tools/pattern_offset.rb 0x64413764
[*] Exact match at offset 112

112バイト目からの4バイトがEIPになっている。

socatサーバ化ワンライナー

$ socat TCP-LISTEN:4444,reuseaddr,fork exec:./binary&

objdumpのdiffをいい感じに取る

$ diff -u1 -F '>:$' -I '[0-9a-f]\{6,\}' <(objdump -d test1 | cut -f2-) <(objdump -d test2 | cut -f2-)

詳細は、ももいろテクノロジー objdumpのdiffをいい感じに取る方法のメモ参照

LD_PRELOAD環境変数によるライブラリ関数フック

詳細は、ももいろテクノロジー LD_PRELOAD injectionでOpenSSLによる暗号化処理を覗いてみる参照

ここも

しゃろの日記 - rev問のソルバを書くときとかに使えるかもしれない小テク
http://charo-it.hatenablog.jp/entry/2016/12/15/084701

動的デバック環境

fork-server型とxinetd型について

fork-server型

  • バイナリ内にbind→listen→acceptがある
  • gdbではset follow-fork-mode childを設定する
  • 親プロセスが残り続けてしまうので、都度親プロセスのkillする工夫が必要

xinetd型

  • バイナリ内にbind→listen→acceptがない
  • xinetdにのせるのは、面倒なので、socatで代用する

xinetd型のための、socatとgdb-serverの利用

socatとgdb-serverを利用して、3つのTerminalをうまく使う

待ち受け側 - TerminalA

$ vimain.sh
gdbserver localhost:1234 ./a.out
$ chmod +x main.sh
$ socat TCP-LISTEN:1025,reuseaddr,fork EXEC:"./main.sh"

攻撃側 - TerminalB

$ perl -e'print "A"x140 ."BBBB"'|nc localhost 1025

デバッグ側 - TerminalC

vi cmd
file ./a.out
target remote localhost:1234
c
$ gdb ./a.out -q -x cmd

Study

NEC 古賀さんによるありがたい解説
http://www.soi.wide.ad.jp/class/20040011/slides/19/
https://www.nic.ad.jp/ja/materials/security-seminar/20041004/3-koga.pdf

Shellphishによるheap exploitのテクニック解説
https://github.com/shellphish/how2heap

katagaitai CTF勉強会資料
http://www.slideshare.net/bata_24/presentations

杨坤:掘金CTF ——CTF中的内存漏洞利用技巧, Geekon 2015
http://netsec.ccert.edu.cn/blog/2015/10/29/1093 http://netsec.ccert.edu.cn/wp-content/uploads/2015/10/2015-1029-yangkun-Gold-Mining-CTF.pdf

スライドの攻撃手法がまとまっていてよさ

Memo

Exploit系テクニック

ももいろテクノロジー - Exploit系複合テクニックのメモ
http://inaz2.hatenablog.com/entry/2016/12/17/180655

Modern Binary Exploitation
http://security.cs.rpi.edu/courses/binexp-spring2015/

ASLRのアドレス特定テクニック

この世にあるlibcをdatabase化すればいいじゃない!!

libcdb.com
http://libcdb.com/

niklasb/libc-database
https://github.com/niklasb/libc-database

Exploit DataBase

EXPLOIT DATABASE
https://www.exploit-db.com/

アンパック

サイバーセキュリティ研究所 - アンパック手順 覚え書き
http://www.wivern.com/malware20161101.html

fig01.png - プログラムの脆弱性とLinux kernelのセキュリティ機構の対応 (54.411 KB) kanata, 2016/03/21 20:31

fig02.png - Partial RELROでのPLT,GOTの役割 (24.886 KB) kanata, 2016/03/26 22:36

fig03.png - Full RELROでのPLT,GOTの役割 (22.732 KB) kanata, 2016/03/26 22:39

wikipedia_StackBufferOverflow.png - wikipediaより引用 二次利用の条項を確認済み (119.674 KB) kanata, 2016/04/03 10:32

fig04.png - スタックとスタックフレームの仕組み (41.725 KB) kanata, 2016/04/03 12:07

クリップボードから画像を追加 (サイズの上限: 100 MB)