はじめに

Return-oriented programming(ROP)が提唱されて久しい.CTFにおいても,ROPは当たり前のように要求される技術となってきている.一方で,ROPに代わる新たな攻撃手法も模索されている.ここでは,そういったROP以後の攻撃手法を概観する.

Return-oriented programming

コンセプト

まずは簡単にROPをおさらいする.
ROPは主にハードウェアDEPという脆弱性対策技術に対抗するために編み出された攻撃手法である.ハードウェアDEPはCPUのNX bitを有効化することで,スタック上でコードを実行する攻撃を無効化する.
そこで,ROPはコード領域のretで終わる命令列(gadget)を実行することによってこれを回避する.単体のgadgetではわずかな処理しか行えないが,スタックに次のgadgetのアドレスを積むことで,gadgetを組み合わせたchainを構築することができる.ROPの作成にはコード領域からgadgetを探索する段階とgadgetを組み合わせる段階を要する.レジスタの操作はpopによって行う.

ASLR/PIEの迂回

ASLRはスタックやヒープなどが読み込まれる位置を実行ごとにランダマイズすることで,アドレスを決め打ちした攻撃を困難にする.だが,ASLRはROPに対処しうる技術ではない.
まず,LinuxにおいてASLRはコード領域のランダマイズを行わないため,スタックオーバーフローなどをROPの起点とすることができる.また,bssセグメントに存在する変数のアドレスを取得することができるのは,ASLRの重大な欠陥である.さらに,32bitの場合は総当りによってアドレスを特定できるほか,ulimit -s unlimitedによってASLRを無効化することができる.
PIEはASLRに加えてgadgetのアドレスまでランダマイズの対象となるため,攻撃にあたってはleakが前提となる.

RELROの迂回

RELROはセクションをread onlyに設定する機能である.Partial RELROとFull RELROがあり,前者の場合はGOTを利用した攻撃が可能である.後者の場合はread onlyにしうる全てのセクションがread onlyとなるが,ROPによって攻撃を成立させることができる.

Stager

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

Stack Pivot

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

論文

BROP: Blind return-oriented programming

コンセプト

以下の要件を満たすサーバープログラムの場合は,バイナリが手元になくてもROPを試みることができる.

  • listen,forkを行う
  • スタックオーバーフローで子プロセスが落ちた場合も動き続ける
  • GOTにstrcmpなど第三引数を操作できる関数が存在する
  • GOTにwriteなどleakさせることができる関数が存在する
  • 総当りなどでGOTのアドレスが分かる

目的は,自身をwriteによってダンプさせることである.
まず,リターンアドレスを総当りして,無限ループに陥るようなgadget(STOP gadgetと呼称)を探す.次に,関数にinnvalidな引数を渡した場合であっても,GOTに正しく飛んだ場合はSEGVしないという点に着目し,0x08040000周辺に存在しているはずであるGOTセクションの位置を推定する.推定にあたっては,+0x6のアドレスや次のGOTエントリに飛んだ場合もSEGVしないという特性を用いる.そして,fdを総当りするか,多数のコネクションからwriteを探り当てる.
GOTエントリは要素数のサイズが同じであるため,スタックのleakからゴリ押しできる,といった内容のようだが,実際のところどうなのだろうか.CTFのネタとしては面白いのではないか.

論文

実例

SROP: Sigreturn-oriented Programming

コンセプト

vdsoには,シグナル割り込みから復帰する際に,ユーザーランドのスタック上に作成したsignal frameに保存している値を全てのregisterへ戻す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のような表現力の高い命令を用いることができる.

論文

ROPguard

JOP

COP

終わりに

ざっとROP以後の攻撃手法を列挙した.これ以外にも,AlphanumericなROPを作成する技術やバイナリから自動的にROPを作成する技術などが研究されている.また,roputilsBAPなど,ROPを支援するフレームワークも開発されている.これらについてもいずれ解説したい.