
ASLRバイパス(情報漏洩なし)
この記事では、Samuel Groß が自身の Remote iPhone Exploitation Part 2: Bringing Light into the Darkness -- a Remote ASLR Bypass で説明した手法を、Linux x86_64 上の ASLR をバイパスするために適用することについて説明します。
これを示すために、Buckeye CTF の pwnable チャレンジである guess_god を解いてみます。
このコンテンツはできるだけ初心者向けにしようと思います。十分に自信がある方は、任意のセクションをスキップして、エクスプロイトだけを見たい場合は遠慮なく飛ばしてください。

私はその CTF には参加しませんでしたが、CTF 終了の約2時間前に、fibonhack の Discord で暗号関連のゴタゴタについて助けを求めていた Guray00 のおかげで、このチャレンジに興味を持ちました。
彼を助けることはできませんでしたが、pwnable チャレンジを調べてみて、P0 のブログ記事を理解し、うまくいけば報奨金を得るのに良いだろうと考えました。
Address Space Layout Randomization(ASLR)は、プロセスのアドレス空間内にある実行ファイルのベースアドレスや、ライブラリ、ヒープ、スタックの位置をランダムに配置するコンピュータセキュリティ技術です。
Linux では、/proc/<pid>/maps ファイルを読むことで、procfs を介して、pid に基づくプロセスのマッピングを調べることができます。
自分自身のメモリマッピングを知りたい場合は、/proc/self/maps を読むことができます。
例えば、cat を使って /proc/self/maps を読んでみることができます:```
root@088ec31b2ce9:/home/ctf/challenge# cat /proc/self/maps
55faeb01c000-55faeb01e000 r--p 00000000 fe:01 2497233 /usr/bin/cat
55faeb01e000-55faeb023000 r-xp 00002000 fe:01 2497233 /usr/bin/cat
55faeb023000-55faeb026000 r--p 00007000 fe:01 2497233 /usr/bin/cat
55faeb026000-55faeb027000 r--p 00009000 fe:01 2497233 /usr/bin/cat
55faeb027000-55faeb028000 rw-p 0000a000 fe:01 2497233 /usr/bin/cat
55faeb115000-55faeb136000 rw-p 00000000 00:00 0 [heap]
7fe15dfb1000-7fe15dfd5000 rw-p 00000000 00:00 0
7fe15dfd5000-7fe15dffb000 r--p 00000000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7fe15dffb000-7fe15e166000 r-xp 00026000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7fe15e166000-7fe15e1b2000 r--p 00191000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7fe15e1b2000-7fe15e1b5000 r--p 001dc000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7fe15e1b5000-7fe15e1b8000 rw-p 001df000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7fe15e1b8000-7fe15e1c3000 rw-p 00000000 00:00 0
7fe15e1c7000-7fe15e1c8000 r--p 00000000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7fe15e1c8000-7fe15e1ef000 r-xp 00001000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7fe15e1ef000-7fe15e1f9000 r--p 00028000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7fe15e1f9000-7fe15e1fb000 r--p 00031000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7fe15e1fb000-7fe15e1fd000 rw-p 00033000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7fff4388f000-7fff438b0000 rw-p 00000000 00:00 0 [stack]
7fff43989000-7fff4398d000 r--p 00000000 00:00 0 [vvar]
7fff4398d000-7fff4398f000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
root@088ec31b2ce9:/home/ctf/challenge# cat /proc/self/maps 55ffc0b1b000-55ffc0b1d000 r--p 00000000 fe:01 2497233 /usr/bin/cat 55ffc0b1d000-55ffc0b22000 r-xp 00002000 fe:01 2497233 /usr/bin/cat 55ffc0b22000-55ffc0b25000 r--p 00007000 fe:01 2497233 /usr/bin/cat 55ffc0b25000-55ffc0b26000 r--p 00009000 fe:01 2497233 /usr/bin/cat 55ffc0b26000-55ffc0b27000 rw-p 0000a000 fe:01 2497233 /usr/bin/cat 55ffc2108000-55ffc2129000 rw-p 00000000 00:00 0 [heap] 7f1ec6e0f000-7f1ec6e33000 rw-p 00000000 00:00 0 7f1ec6e33000-7f1ec6e59000 r--p 00000000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7f1ec6e59000-7f1ec6fc4000 r-xp 00026000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7f1ec6fc4000-7f1ec7010000 r--p 00191000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7f1ec7010000-7f1ec7013000 r--p 001dc000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7f1ec7013000-7f1ec7016000 rw-p 001df000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so 7f1ec7016000-7f1ec7021000 rw-p 00000000 00:00 0 7f1ec7025000-7f1ec7026000 r--p 00000000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7f1ec7026000-7f1ec704d000 r-xp 00001000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7f1ec704d000-7f1ec7057000 r--p 00028000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7f1ec7057000-7f1ec7059000 r--p 00031000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7f1ec7059000-7f1ec705b000 rw-p 00033000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so 7ffc72fa4000-7ffc72fc5000 rw-p 00000000 00:00 0 [stack] 7ffc72fe7000-7ffc72feb000 r--p 00000000 00:00 0 [vvar] 7ffc72feb000-7ffc72fed000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
### メモリマッピングのパターン
これを数回行うと、次のことが推測できるでしょう:
* バイナリのPIEベースは0x00005500_00000000〜0x00005700_00000000の範囲にあるはずです。つまり、2TBの候補アドレスがあります。
* ヒープはバイナリの近くにあります。
* ライブラリは0x00007f00_00000000〜0x00007fff_ffffffffの範囲にあり、1TBの候補アドレスがあります。
* スタックは(ほとんどの場合)0x00007ffc_00000000〜0x00007fff_ffffffffの範囲にあり、16GBの候補アドレスがあります。
* 範囲0xffffffffff600000〜0xffffffffff601000は常にマップされています。それが何か気になる場合は、[この記事](http://terenceli.github.io/%E6%8A%80%E6%9C%AF/2019/02/13/vsyscall-and-vdso)を読んでください。
## 1.3 情報漏えい(infoleak)なしでASLRを回避する方法
情報漏えいが不可能な場合に、ASLRを回避するためにできることについて説明します。
これは、Saeloのブログ記事を読んで得た内容を要約しようという試みです。
ASLRを回避するには、次のものが必要です:
* メモリスプレー技術。指定したサイズの連続メモリを、指定したアドレス範囲にマップすることができます。
彼が言うように、これには2つの方法があります:
1. メモリリーク(情報漏えいではない!)を悪用する方法。メモリのチャンクが「忘れられ」て解放されないバグを、目的の量のメモリがリークするまで複数回トリガーします。
2. 「増幅ガジェット」を見つけて悪用する方法。既存のデータチャンクを取得してそれをコピーするコード(潜在的に複数回コピーする)で、攻撃者が比較的少数のバイトを送信するだけで大量のメモリをスプレーできるようにします。
* アドレスがマップされているかどうかを教えてくれる `isAddressMapped` オラクル。
### LinuxでのASLR回避のPoC
LinuxでASLRを完全に破壊するために、saeloのPoCを再現してみましょう。
<p align="center"> <img src="https://assets.kitploit.com/production/public/readmes/48660/93fcc6aff8ffd2e8395f34b35bc2f5d244e74a53b2fac39c3dae35c802227339.png" > <i> saeloのPoC</i> <p/> <br/>
Linuxではそれほど簡単ではありません。16TBのメモリを割り当てることができる場合にのみ、ASLRを完全に破壊することが可能です。```C
#include <stdio.h>
#include <stdlib.h>
int main()
{
// 64gb
size_t size = 0x1000000000;
// 16TB allocations
for (int i = 0; i < 256; i++) {
void *mem = malloc(size); // this ends up calling mmap
if (!mem) {
puts("Failed");
return 1;
}
printf("%p\n", mem);
}
unsigned int *mem = (void*)0x7f0000000000ULL;
*mem = 0x41414141;
printf("R/W to %p: %x\n", mem, *mem);
return 0;
}
man malloc の注意書きより:
したがって、void *mem = malloc(size) は最終的に mmap(size + malloc_metadata_size, ...) を呼び出すことになる。
ライブラリは ld によって mmap 経由でプロセスにマッピングされるため、それらの割り当てはライブラリの近くに配置されることになる。
malloc が返すアドレスを見れば、何が起きているのかをよりよく理解できる。プロのヒント: 最上位バイトに注目しよう。