ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

通达OA任意文件上传叠加文件包含导致RCE的链路与加固

通达OA任意文件上传叠加文件包含导致RCE的链路与加固 1. 从一次内部应急演练说起这条RCE链路到底长什么样先说结论通达OA 任意文件上传叠加文件包含导致 RCE这条链路本质上不是某一个神级漏洞而是两个中危问题被串成了一条高危利用链。单个文件上传点可能只让你写进一个图片或文本单个文件包含点可能只让你读到配置文件但当上传点允许把一个内容可控的文件落到服务器上、而包含点又能把这个文件当作 PHP 代码去解析时事情的性质就彻底变了——从信息泄露变成远程代码执行。我在做企业办公系统安全评估的时候通达OA 这类协同办公平台一直是重点关注对象。原因很直接它承载的是组织内部的人员、审批、公文、流程数据接口数量多历史版本分支杂很多单位还部署在内网甚至直接映射到公网。通达OA 任意文件上传加文件包含导致的 RCE之所以被反复提及是因为它命中了一个经典公式上传提供投递能力包含提供执行能力两者一旦在同一个 Web 根目录、同一套运行环境下共存攻击者不需要交互也不需要管理员权限就能拿到服务器上以 Web 服务身份运行的命令执行能力。这篇文章我打算按真实评估的思路来写。给谁看给做代码审计的同学、做企业安全运维的同学、以及需要给办公系统做加固的同行。你不需要是那种天天刷漏洞库的高手也能看懂我会把为什么这么设计为什么这里会出问题实操时要注意什么都讲清楚。涉及具体操作的环节我会用授权测试场景和演示环境来描述重点是让你能理解原理、能复现认知、能落地防御而不是给一份拿来就能乱用的东西。这也是我写这类内容的底线技术细节讲透利用门槛讲清防御动作给足。另外提醒一句这类文章里出现的任何接口名、路径、参数都请只在你有明确授权的资产上做验证。没有授权就动手性质完全是另一回事这个从业者都懂。2. 任意文件上传从接口暴露到文件真正落地2.1 上传点为什么这么容易被忽略通达OA 的功能模块非常多公文流转、附件管理、邮件、即时通讯、文档中心几乎每个模块都有文件上传需求。开发早期为了赶功能很多上传接口是能用就行的状态前端做一层后缀限制后端只做简单校验或者干脆只判断 MIME 类型。问题恰恰在这里——前端校验是不可信的攻击者抓包改一下后缀、改一下 Content-Type前端的限制就形同虚设。我审计这类系统时习惯先梳理所有接收multipart/form-data的入口然后看后端到底怎么处理文件名。常见的几种看起来做了校验其实没用的写法只校验$_FILES[file][type]也就是客户端传上来的 MIME。这个值完全由客户端决定改包就能绕。用黑名单过滤后缀比如禁止php但忘了phtml、php5、php7、phar或者没考虑大小写PHP、PhP。校验了后缀但保存文件名时用的是原始文件名且没有对路径分隔符做处理给了目录穿越的空间。上面任意一条成立这个上传点就从受限上传退化成任意文件上传。而通达OA 的很多版本里上传目录是可以通过 Web 直接访问的这就为后续的包含埋下了伏笔。2.2 后缀绕过的几种典型姿势与原理要理解绕过先得理解服务器是怎么解析后缀的。我拿最常见的组合来说明。第一种是后缀黑名单不全。很多代码只str_replace掉php这个词那么pphphp这种写法替换一次之后会剩下php或者用php.在某些环境里会被解析、php%00低版本 PHP 的截断现代版本已经修复等。这类绕过依赖具体实现不能一概而论但审计时一定要看它到底是删一次还是循环删。第二种是大小写与特殊后缀。如果校验逻辑是if (strpos($ext, php) ! false)而服务器里.phtml、.php5也被解析为 PHP那么校验和解析就出现了错位。第三种是内容检测的盲区。有些实现会检测文件头是不是GIF89a但检测完并不删除你后面追加的内容于是构造一个以 GIF 头开头、后面跟代码的文件就能同时骗过图片校验和 PHP 解析。这就是典型的图片马思路。我在实际测试里踩过最深的坑是以为自己绕过了结果文件是传上去了但目录没有执行权限或者 Nginx 配置里对上传目录明确设置了location不解析 PHP。所以绕过只是第一步能不能执行取决于运行环境和目录配置。2.3 文件落地路径与权限的隐性约束上传成功后文件落在哪里非常关键。常见的情况有落在 Web 根目录下的upload/、attachment/、data/之类目录能通过 URL 直接访问。落在项目目录外或者文件名被重命名为随机串且路径不可预测。落进去了但目录权限是www-data只读或者上层有 WAF 拦截了对该路径的访问。这几种情况决定了攻击链能不能继续。做评估时我一般会先确认文件是否真的写进去了用最简单的方式验证上传一个内容里带随机标记的 txt然后尝试访问看看能不能读到那个标记。能读到说明写入可访问这条腿成立读不到就得换个上传点或者换个落地点。这里有个经验通达OA 这类系统往往有多个上传接口它们的保存路径、命名规则、权限控制各不相同。一个接口不行不代表全部不行。我把这一步叫做横向找同类,是评估里最耗时间但也最有效的环节。注意在做这一步验证时尽量上传无害的标记文件避免上传任何可能被判定为恶意内容的样本既保护环境也保护你自己。3. 文件包含把静态文件变成可执行代码的关键一步3.1 本地文件包含是怎么被触发的文件包含漏洞的根源是用户可控的输入被直接拼进了 include/require 语句。在 PHP 里这种写法非常典型include($_GET[page] . .php);只要page参数可控、后面的拼接不构成阻碍就能引入任意文件。当然现代 PHP 环境里allow_url_include默认关闭远程包含RFI大多行不通所以真正实用的往往是本地文件包含LFI——包含服务器上已经存在的文件。而已经存在的文件从哪来答案回到上一节我们自己上传进去的那个文件。这就是上传和包含的强耦合点。上传负责把内容可控的恶意文件投递到本地磁盘包含负责把这个文件当代码执行。两者缺一不可。历史上还有一种更裸的玩法日志文件包含。比如访问一个不存在的路径服务器会把这个路径原样写进 access.log攻击者把代码藏进 URL 里再通过 LFI 包含日志文件代码就被执行了。这也是日志文件包含这个词经常和 RCE 一起出现的原因。它不依赖上传点但依赖日志可读、路径可猜。相比之下上传包含更稳定、更可控所以成了这条链里的主力。3.2 上传文件为什么能被当作代码包含这里有个很多人一开始不理解的点上传的文件后缀是图片或者 txt凭什么能被当 PHP 执行原因是include 的行为是按文件内容来解释的不是按后缀。只要一个文件被 includePHP 引擎就会把它里面的?php ... ?标签识别出来并执行文件叫什么名字、后缀是什么对 include 来说不重要。后缀只在直接通过 URL 访问该文件时才决定服务器要不要交给 PHP 引擎处理。所以这条链成立的条件其实只有两个上传点允许我把?php ... ?的内容写进某个文件且落盘路径已知或可推测。存在一个包含点能让我控制包含的文件路径指向我上传的那个文件。一旦满足后缀校验做得多严格都拦不住——因为我压根不靠直接访问那个文件来执行我靠的是包含。3.3 系统命令执行与 pcntl_exec 之类的角色到了这一步代码执行已经拿到了接下来的选择就看目标环境允许什么。常见的是用system、exec、shell_exec、passthru这类函数直接跑命令。但如果目标对这些函数做了disable_functions禁用就得换思路。这时候像pcntl_exec这样的函数就会进入视野它能替换当前进程镜像、执行指定程序某些场景下可以绕过对常规命令执行函数的禁用。类似的还有putenvmail、imap_open、LD_PRELOAD劫持等思路。我不想在这里展开成一份绕过手册但你需要知道的是RCE 的最后一公里从来不是固定答案它取决于目标禁用了什么、允许了什么。评估报告里写命令执行容易实际能不能稳定执行、执行什么身份才是真实价值所在。用得多了会发现真正稳定的链往往是代码可执行但函数受限此时更实际的目标是读取配置、连接数据库、横向探测而不是死磕命令回显。这一点我在很多项目里都反复验证过。4. 完整链路复现与关键环节记录4.1 演示环境与前置条件说明为了把原理讲透我用一套授权范围内的演示环境来描述流程。这里不写具体产品版本号也不写可直接复用的攻击载荷重点在步骤逻辑和判断依据。前提条件是目标存在一个后缀校验不严的上传接口且上传目录可被 Web 访问。目标存在一个参数可控的 include 点。两个点在同一套运行环境里且包含点能读到上传目录。搭建这种环境其实不难很多靶场都提供了类似的上传包含组合场景。我建议你在本地或授权的靶场里跟着走一遍理解每一步的为什么,比记住 payload 重要得多。4.2 逐步操作记录与判断逻辑第一步确认上传点行为。先上传一个正常图片观察返回的路径结构判断文件是原样保存还是重命名、是否带时间戳、目录层级有没有规律。这一步的目的是把落盘路径的规律摸清楚。第二步测试后缀校验边界。用不同后缀、不同 Content-Type 提交观察哪些被拦、哪些放行。注意记录服务器返回的错误信息很多系统会直接告诉你不允许的后缀这反而帮你缩小了范围。这一步只看放不放行不放任何有害内容。第三步验证文件可访问性。把上一步放行的文件用一个明显可区分的标记内容投递进去然后尝试通过 URL 读取。能读到内容说明投递可访问成立。第四步定位包含点。在功能页面里找那些参数看起来像文件路径的地方比如?page、?mod、?template。用无害的方式测试是否能把参数替换成一个已知存在的文件路径观察行为变化比如报错信息、页面内容。能控制路径说明包含点成立。第五步串链验证。让包含点的参数指向第二步投递的那个文件。如果文件内容里的标记性代码被执行链路就打通了。整个过程的重点不是快而是每一步都有明确的判断依据,能证明自己确实控制了某一环。我实际做的时候会把这些步骤写成一张检查表每一步记录输入、预期、实际、结论。这样做的好处是到了写报告的时候每一条结论都有铁证支撑而不是我猜应该是这样。4.3 关键参数与结果解读在这条链里真正影响成败的参数其实不多我列一下自己关注的几个环节关键点判断依据常见失败原因上传后缀处理逻辑放行的后缀集合黑名单/白名单未摸清上传落盘路径返回路径或目录规律重命名不可预测上传目录可访问性能否通过 URL 读取目录无对外映射包含参数可控性路径是否随参数变化参数被过滤或拼接受限执行函数可用性目标函数是否被禁用disable_functions 限制这张表我在多个项目里都用过省了大量重复思考的时间。你会发现失败往往不是败在攻,而是败在没把某一环的约束搞清楚。评估的本质是不断缩小不确定性而不是赌一个 payload。5. 常见问题与排查技巧实录5.1 上传被拦截时怎么定位原因上传被拦截先别急着换 payload先分清是哪一层在拦。如果是前端在拦抓包直接能看到请求根本没发出去或者发出去后被前端 JS 校验了改一下请求即可。如果是 WAF 在拦通常是响应里出现统一拦截页面或者请求被 403。这时候要看拦的是后缀还是内容特征有时候换个直接一点的请求方式就能判断。如果是后端代码在拦往往会返回具体的错误文案比如文件类型不允许这种最友好等于告诉你规则在哪。我的习惯是逐层剥离先关掉浏览器 JS 逻辑用工具直接发请求排除前端再用最原始的请求体排除内容特征干扰最后才针对后端规则找边界。一层一层排除比盲试快得多。5.2 包含不生效的可能原因链路走到包含这一步失败通常有几种情况第一路径没拼对。你可能忽略了拼接前缀或后缀比如代码是include($path . .php)你传的值需要刚好凑出你想要的完整路径。第二文件确实存在但读不到。权限、open_basedir 限制都会导致包含失败。这时可以观察报错信息PHP 的open_basedir restriction in effect会直接告诉你被限制了。第三上传的文件内容没被正确解析。比如内容被转义了变成了lt;或者你写的内容被过滤器处理过。这种情况要回到上传环节看看内容是不是被处理了。第四包含点本身有白名单。有些实现会限制只能包含特定目录下的文件那就得想办法把文件投到那个目录里这就需要重新审视上传点的落盘路径。5.3 排查速查表我把上面的经验整理成一张速查表方便你在实际排查时快速定位现象可能原因排查动作上传返回 403WAF 或权限拦截换请求方式观察拦截特征上传成功但访问 404路径不对或目录未映射结合返回路径规律重新推断包含后无变化路径未命中或内容未解析用已知存在的文件先验证包含点报 open_basedir 错误目录范围受限找受限范围内的可写目录代码不执行函数被禁用或环境限制换执行思路优先读配置这些坑我基本都踩过。最有价值的一条经验是永远先验证最简单的假设。先用一个确定存在的文件测试包含点再用一个确定可访问的上传文件测试上传点两个点都单独确认没问题了再串起来。很多人一上来就串链失败了根本不知道是哪一环的问题。6. 检测、加固与应急响应思路6.1 代码层的加固要点从开发角度这类漏洞的修复其实很明确难的是落到每一处代码。上传侧正确的做法是白名单 重命名 存储隔离三件套只允许业务真正需要的后缀图片就只允许图片后缀保存时用系统生成的随机名加安全后缀最关键的——上传目录不要给执行权限让 Web 服务器对上传目录只读不解析。这一条能直接掐断上传文件被包含执行的可能性。包含侧杜绝用户输入直接进 include。如果业务真的需要动态包含就用一个映射表把用户传来的标识映射到固定的、写死的文件路径上用户永远碰不到真实路径。配置侧open_basedir限制目录范围disable_functions禁用危险的执行函数日志目录不要放在可被 Web 访问的位置这些都能显著抬高利用门槛。6.2 运维层的加固动作运维要做的事和开发同样重要。第一资产清点。办公系统往往有多个版本、多个实例还有不少是历史遗留、没人维护的。把这些都梳理出来比修某一个洞重要得多。第二边界收敛。办公系统到底需不需要对公网开放很多单位其实是内网办公却把系统映射到了公网。如果确实需要外部访问至少要在前面加访问控制和身份校验。第三WAF 与规则。针对上传和包含的特征做规则拦截能挡住大部分自动化扫描但记住 WAF 是缓解不是根治。第四补丁与升级。厂商发布的安全更新要及时评估、及时打别等出事才想起来。6.3 应急排查与处置建议如果怀疑已经被利用了排查应该从哪下手我的顺序是这样的第一看上传目录。找那些非业务命名、带可疑内容的文件尤其是后缀异常或者内容里含代码标签的。第二看访问日志。重点看那些对上传文件路径的异常访问以及请求参数里带路径特征的记录。第三看进程与网络。有没有 Web 服务身份跑起来的异常进程、异常外连。第四看时间线。把可疑文件的创建时间和日志里的异常请求时间对齐往往能还原出攻击过程。处置上先隔离受影响主机再清理可疑文件然后修漏洞、重置可能泄露的凭据最后做一次全面的复盘。这里没有捷径顺序错了就容易清不干净或者漏掉后门。我个人在多次处置里最深的体会是很多损失不是因为洞有多难修而是因为发现得太晚。日志没留、资产没盘、权限没收任何一个环节的疏忽都会让一次普通的安全事件拖成大事故。把该做的日常动作做到位比追多少新漏洞都实在。
返回列表