di shen pacsec_jp-final

37
型破りな利用法をエクスプロイットする美学 - Androidカーネル内のUse-after-freeバグ Di Shen a.k.a. Retme (@returnsme) Keen Lab of Tencent

Upload: pacsecjp

Post on 22-Jan-2018

174 views

Category:

Internet


1 download

TRANSCRIPT

Page 1: Di shen pacsec_jp-final

型破りな利用法をエクスプロイットする美学 - Androidカーネル内のUse-after-freeバグ

Di Shen a.k.a. Retme (@returnsme) Keen Lab of Tencent

Page 2: Di shen pacsec_jp-final

自己紹介 •  Di Shen a.k.a. Retme (@returnsme)

•  Keen Labのメンバー

•  2014年よりAndroidカーネルの脆弱性ハンティングと攻撃を行っている

•  Androidの普遍的なroot取得攻撃を成功させることを目指している

•  トロフィー(実績): •  CVE-2016-6787 & CVE-2017-0403 (kernel/events/core.c) •  CVE-2015-1805 (fs/pipe.c) の最初の実際に動く攻撃コード •  CVE-2015-4421,4422 (Huawei TrustZone) •  サムスン Galaxy S7 のKNOXバイパス (Black Hat USA 2017) •  すべての一般的チップセットのワイヤレス拡張部分を攻撃 (Black Hat EU

2016) •  さらに多くをアナウンスする予定

•  https://github.com/retme7/My-Slides にて公開

Page 3: Di shen pacsec_jp-final

アジェンダ

• Androidのrootを奪う:現在の状況

• カーネルでのUse-After-Free攻撃の概要 • 一般的なアプローチ • その後:rootを奪う

• 型破りなUse-After-Free攻撃 •  perfシステムの実装 •  CVE-2017-0403を活用 •  CVE-2016-6787を活用

• 結論

Page 4: Di shen pacsec_jp-final

Androidのrootを奪う:現在の状況

• 普遍的に攻撃可能な脆弱性は非常に少ない

• 攻撃可能な側面:

•  Linuxの一般的なシステムコール

•  Binder, ION, AshmemなどのAndroidユニバーサル ドライバ

Page 5: Di shen pacsec_jp-final

Androidのrootを奪う:現在の状況

• SELinux ポリシーの実行

• ほとんどのデバイスドライバにアクセスできない

• 多くのシステムコールは信頼できないアプリケーションからアクセスできない

• ソケットのioctlコマンドは部分的にしか実行できないように制限されている

Page 6: Di shen pacsec_jp-final

Androidのrootを奪う:現在の状況

• dm-verityカーネル機能による検証済みのブート • 獲得されたルート権限は非永続的

Page 7: Di shen pacsec_jp-final

Androidのrootを奪う:これからの挑戦

• Privileged Access Never (PAN) • KASLR • ポインタ認証

Page 8: Di shen pacsec_jp-final

カーネルにおけるUse-After-Free攻撃

• 簡単に悪用可能なUse-After-Freeバグには、通常、次のような

特徴がある:

•  解放されたオブジェクトに関数ポインタがある

• 攻撃者には解放されたオブジェクトに何かを追加するのに十分な時間

が有る

Page 9: Di shen pacsec_jp-final

一般的なUse-After-Free攻撃のアプローチ structsocket

(freed)�

ops->ioctl(…)

The image cannot be displayed. Your computer may not have enough memory to open the image, or the image may have been corrupted. Restart your computer, and then open the file again. If the red x still appears, you may have to delete the image and then insert it again.

structsocket(refilled)�

ops->ioctl(…) JOPガジェット

•  攻撃目標のオブジェクトを開放する

•  解放されたオブジェクトにヒープ・スプレーもしくはret2dir による不正な形式のデータを書き込む

•  関数ポインタにカーネル内のROP/JOPガジェットを指し示させる

•  任意のカーネルコードを実行させるため、カーネルにこの関数ポインタを参照させる

ioctl(sockfd,…) kernel_sock_ioctl() JOPガジェット

Page 10: Di shen pacsec_jp-final

その後、コード実行からルートへ

任意のカーネルコード実行プロセスのaddr_limitを

上書き

任意のカーネルメモリの上書き

Uid,securityid,selinux_enforcing

を上書き

Page 11: Di shen pacsec_jp-final

しかしながら…

• カーネルのすべてのUse-After-Freeバグが理想的なものではない

• 扱いにくい状況が多い…

• 攻撃対象のオブジェクトには関数ポインタが無いかもしれない

•  Use-After-Freeを行った直後にカーネルがクラッシュするかもしれない

• 攻撃者は書き込みを行ったオブジェクトを完全にコントロールできないかもしれない

Page 12: Di shen pacsec_jp-final

私が見つけた型破りなUse-After-Free

• すべては sys_perf_event_open() にて見つかった

•  Perf システムに多くのバグがある

• 昨年時点でアプリケーションによってアクセス可能

• しかし、今は、“perf_event_paranoid”という名の機能で制限がか

かっている

Page 13: Di shen pacsec_jp-final

私が見つけた型破りなUse-After-Free

•  CVE-2017-0403 •  Ever affected all devices shipped with 3.10 or earlier Linux kernel 3.10またはそれ以前のLinuxカーネルとともに出荷されたすべてのデバイスに影響

•  KingRootの1400万人以上のユーザーがスマートフォンでroot権限を取得

•  CVE-2016-6787 •  Qualcommベースのすべてのデバイスに影響する (Qucalcomm enabled ハードウェア perf エベントを有効化したQualcommのみ…)

•  サムスンKNOX 2.6をバイパスする一連の攻撃の一部

Page 14: Di shen pacsec_jp-final

sys_perf_event_open()

•  perf_event を作成

• 入力:perf_event_attr

•  必要なパフォーマンスイベントの種類を記述

• 入力: group_fd (optional)

•  新しいperf_eventのグループリーダーを指定

•  perf_eventのfdをユーザ空間に返す

Page 15: Di shen pacsec_jp-final

perfシステムの重要なカーネルオブジェクト •  perf_event

•  ユーザーが登録したパフォーマンスイベント •  perf_event_context

•  1つのプロセスで作成されたすべてのperfイベントのコンテナ •  各プロセスには、ソフトウェアイベント用とハードウェアイベント用 •  の2つのコンテキストが有る

•  Perfグループとグループリーダー •  複数のイベントでグループを構成できる •  1つのイベントがリーダー

perf_sw_context

perf_hw_context

task_struct

event event eventevent_list

event(group_leader)

event(group_leader)

Page 16: Di shen pacsec_jp-final

move_group

• ユーザーがソフトウェアグループにハードウェアイベントを作成しようとしたときに発生

Page 17: Di shen pacsec_jp-final

CVE-2016-6787 元のソフトウェアコンテキストからgroup_leaderを削除し、ハードウェアコンテキストにインストール

ソフトウェアのコンテキストからすべてのイベントを削除し、新しいハードウェアコンテキストにインストール

‘move_group’はコンテキストのrefcontを1つ減らす

Page 18: Di shen pacsec_jp-final

CVE-2016-6787 •  move_groupは並行性の問題を無視 •  Use-After-Freeは競合状態のために発生 •  攻撃者はmove_groupを同じグループリーダーで同時にトリガし、 •  group_leader-> ctxのrefカウントは0に減らすことができる •  ttask_struct-> perf_event_ctxp [perf_sw_context]が誤って解放される

オブジェクトが解放される

Page 19: Di shen pacsec_jp-final

解放された perf_event_context (PoC)

ソフトウェアgroup_leader

を作成

ハードウェアperf_event

を作成

ハードウェアperf_event

を作成

メインスレッド サブスレッド-1 サブスレッド-2

move_group,put_ctx()

move_group,put_ctx()

kfree_rcu(perf_event_context)

ctx->refcount = 1

ctx->refcount = 2

ctx->refcount = 0

Page 20: Di shen pacsec_jp-final

カーネルが直ちにクラッシュした

• perf_event_contextを解放した直後にカーネルがクラッシュ

• スレッドスケジューラはこのオブジェクトを連続して参照する必要がある

• オブジェクトに書き込みを行う 時間が無い L

Page 21: Di shen pacsec_jp-final

解決策:解放後にスレッドをフリーズする

•  スレッドスケジューラを遠ざける

•  攻撃者のスレッドの状態を実行中から割り込み(不)可能に切り替える

•  The thread will be frozen and kernel won’t

crash as soon as perf_event_context freed

•  スレッドはフリーズされperf_event_contextが

解放された直後にもカーネルはクラッシュしない

Page 22: Di shen pacsec_jp-final

ユーザ空間からスレッドをフリーズするには?

• Sleep() ? 動いていない • Use futex_wait_queue_me()

割り込み可能に切り替える

freezable_schedule()

Page 23: Di shen pacsec_jp-final

ソフトウェアgroup_leaderを作成

ハードウェアperf_event を作成

ハードウェアperf_eventを作成

メインスレッド サブスレッド-1 サブスレッド-2

move_group,put_ctx()

move_group,put_ctx()

kfree_rcu(perf_event_context)

futext_wait_queue_me()

フェーズ 1

フェーズ2

‘ret2dir’トリックを使用してヒープをスプレーし、不正な形式のperf_event_context{}を1024バイトごとに書き込み

futex_wake()を使ってメインスレッドを起動

フェーズ 4

schedule()

finish_task_switch()

perf_event_context_sched_in()

ctx->pmu->pmu_disable()

フェーズ 3

Page 24: Di shen pacsec_jp-final

CVE-2016-6787の簡単な要約

• 容易に競合に「勝つ」ことができ、バグを引き起こせる • 解放されたオブジェクトに書き込むのが困難(時間が無い) • コードフローを簡単に制御できる (破損したオブジェクトには関数ポインタがある)

• スレッドをフリーズすることで、オブジェクトに書き込む時間を増やす方法を提案

Page 25: Di shen pacsec_jp-final

レビュー:perfイベント、グループとグループリーダーの関係

• グループリーダーは sibling_list を持っている • sibling_list はグループに属しているperfイベントのリスト

perf_sw_context

perf_hw_context

task_struct

event event eventevent_list

event(group_leader)

event(group_leader)

Page 26: Di shen pacsec_jp-final

CVE-2017-0403 (PoC)

• perfイベントを 'A'として作成 • 別のperfイベントを 'B'として作成し、グループリーダーとして 'A'を指定

• グループリーダー ‘A’ を解放

• グループのsibling ‘B’ を解放 ß---- Use-after-free が

Page 27: Di shen pacsec_jp-final

根本的な原因

•  グループリーダー‘A’ が解放された

•  カーネルはその sibling list を空にしない

•  結果としてsibling’s event->group_entry

にぶら下がっているポインタを残してしまう

Page 28: Di shen pacsec_jp-final

根本的な原因

•  その後 sibling ‘B’ が解放される

•  list_del_event()

•  list_del_init(&event->group_entry);

•  解放されたグループリーダーへのポインタを上書き

Page 29: Di shen pacsec_jp-final

•  SLUB ポイズン情報

•  0xfffffc00fc2b1a0 が

(group_leader+ 0x20) に上書きされる

Page 30: Di shen pacsec_jp-final

型破りなシナリオ

• 私ができる唯一のことは、解放されたオブジェクトを次のように上書きすること

*(size_t*)(freed_object + 0x20) = (freed_object + 0x20)

Page 31: Di shen pacsec_jp-final

Linuxのパイプサブシステム •  readv() と writev() :パイプを通して複数のバッファを読み書き

• ユーザバッファを記述するためにstruct iovec{iov_base,iov_len} の配列を使用

• 書き込み側で利用可能なコンテンツがない場合、readv() はカーネル内でブロックされることがある

• そして、struct iovec {}の配列はカーネルのヒープに滞留するかもしれない

Page 32: Di shen pacsec_jp-final

パイプシステムを攻撃 •  readv() を呼び出す。

•  rw_copy_check_uvector() はすべての iov_base がユーザ空間を指していることを確認。

•  struct iovec{} の配列がヒープに加わった。パイプの書き込み終了から何も来ないので、readv() はブロックする。

• もし iovec{} を上書きできるなら、 iov_baseカーネルアドレスに

変更。。。

iov_base iov_len iov_base iov_len iov_base iov_len

…..

kernel_addr iov_len kernel_addr iov_len kernel_addr iov_len

Page 33: Di shen pacsec_jp-final

パイプシステムを攻撃

• パイプのもう一方の端に何かを書込む

•  pipe_iov_copy_to_user() は

iov_base を再チェックしない

• パイプに書かれたバッファは

カーネルアドレス にコピーされる

Page 34: Di shen pacsec_jp-final

········

Use-After-Freeを引き起こす、 2つの“A+0x20”の8バイト値を A+0x20のアドレスに書き込む

↓ 最初に解放されたオブジェクト, アドレスはA

解決策: Use-After-Freeを任意の読み書きに変換

↓ 2番目に解放されたオブジェクト, アドレスは B = A + 0x400

iovec をヒープスプレーに使用

Freed Data Freed Data Freed Freed Data Freed Data ·········

base len base len base base len base len ···················

base len A+0x20 A+0x20 base ·········· base len base len

バッファをパイプに書き込む、バッファは(A + 0x20) にコピーされる

·········

base len KADDR 8 KADDR ·········· KADDR 8 KADDR 8 ·········

バッファをパイプにもう一度コピーする,そうすると KADDR にコピーされる KADDR はどんなアドレス値をも取り得る、つまり、任意のカーネルメモリ上書きを達成

1

2

3

4

5

Page 35: Di shen pacsec_jp-final

CVE-2017-0403の簡単な要約

• 攻撃者は解放されたオブジェクトのファイル記述子を失う

• オブジェクトの関数ポインタに書き込んでコードを実行することはできない

• 解放されたオブジェクトのアドレス値は2回だけ解放されたオブジェクトに書き込むことができる

• 新しい方法を提案した:パイプシステムへの攻撃

Page 36: Di shen pacsec_jp-final

結論

• ほとんどのUse-After-Freeバグは攻撃できないように見えるが、他の方法があるかもしれない

• 分からない? しばらく置いておこう、しかし、見捨てないで…

• カーネルコードに詳しくなろう, カーネルの独自機能が攻撃に役立つかもしれない (例: CVE-2017-0403でのパイプ)

Page 37: Di shen pacsec_jp-final