首页
社区
课程
招聘
[原创] 2026软件安全赛现场赛writeup-web方向
发表于: 4天前 1128

[原创] 2026软件安全赛现场赛writeup-web方向

4天前
1128

讲解前吐槽为什么考的全是cve,要是我没记录cve的话真的要现场挖吗(bushi)

所有题目附件在文章末尾,可以直接下载,jdbc的题目jar包因为过大所以使用了分块压缩

附件里提供了题目的完整环境,在webstorm里打开文件夹即可。

题目仅提供了两个附件:app.js与package.json。

先审计app.js,发现了一个递归合并属性的merge函数:

alt text

递归合并就要考虑是否有原型链污染了,虽然其在遇到__proto__会跳过,但是我们可以通过consturctor.prototype的链子进行绕过。

检查该函数在哪里使用:

alt text

可以看到其在修改密码时毫无过滤的使用了merge函数来进行合并。

alt text

后面的/sandbox很显然是一个沙箱逃逸,但是需要admin权限,因此思路很清晰:利用原型链污染进行提权,进行后续的沙箱逃逸

alt textalt text

可以看到新注册的用户已经变为了admin。

alt text

可以看到其使用vm2进行沙箱代码执行,检查vm2版本

alt text

该版本存在一个今年的cve高危漏洞:CVE-2026-22709

JavaScript 中的一个关键特性是:async 函数总是返回一个原生的 globalPromise 对象,而不是 vm2 制造的 localPromise。

因此,攻击者只需在沙箱代码中定义一个 async 函数,就能获取一个未被“清理”的 globalPromise 对象。

我们可以让这个globalPromise被reject,并调用未被“清理”的 .catch 方法。这个 .catch 回调将在沙箱外部的宿主环境中执行。

注意:函数体内变量(如 require)的解析规则:遵循 JavaScript 的静态作用域(lexical scoping),即在定义该函数的位置向上查找作用域链,因此如果你在catch里直接写require,虽然是外部宿主环境,但是仅匹配定义位置是否有require,没有就会报错并不执行,这样的外带就不行了

因此我们需要想办法让这个globalPromise获取到一个外部对象,通过这个外部对象来获取外部的Function,进而获取到process与require,完成后续RCE。

我们可以很快速的想到:在这个globalPromise里制造异常,会不会就能获取到一个外部对象呢?很可惜,vm2沙箱内得到的Error对象都是经过其包装过的代理Error,其 constructor 指向的是沙箱内的 Function,无法进行逃逸......吗?

通过以上构造,当访问 error.stack 时,V8 引擎内部会调用 error.name.toString()。但 Symbol 不能隐式转为字符串,会抛出 TypeError,被catch捕获到e上。关键在于:这个 TypeError 是在 vm2 的 host 上下文中创建的,而非沙箱内部。换句话讲,这个TypeError没有被沙箱包装代理。

通过e.constructor.constructor,我们就能获取到最顶上的函数构造器,进而利用其获取到process,利用process获取到require并引入child_process模块,进行后续的代码执行。

alt text

执行成功!

在idea里新建一个maven项目,将pom.xml、ctf-challenge.jar放进项目里并同步maven库即可,使用的jdk为jdk17。

该题目是一道AWD题,本篇wp仅针对attack阶段,不过我把利用的漏洞点说了你们也就知道哪里需要防了。

题目提供了三个附件:pom.xml、start.sh与Webconfig.java。

先分析一下pom.xml,发现了一个静态路径穿越漏洞:

alt text

这个漏洞需要在应用使用webflux来提供静态资源,且在提供静态资源时,明确使用了FileSystemResource来指定文件系统中的位置时才能被利用。有一段代码示例:

再看提供的Webconfig.java源码内容:

alt text

可以说是完全一致,由此我们确定了其存在路径穿越漏洞。

springboot在处理客户端发来的静态路径访问时会进行预处理,检查路径是否存在../这样路径穿越的问题,但是由于在检查../时代码逻辑存在缺陷,导致攻击者可以构造一个设计好的payload,使其包含../的同时不被认定为“包含了../”路径,进而实现路径穿越。

用于windows:/static/%5c/%5c/../../flag.txt

用于windows与linux:/static/%2f/%2f/../../flag.txt

(将flag.txt替换为你实际要访问的文件内容)

在对/static/.....发起一个Get请求后,springboot框架会调用spring-webflux-6.0.2.jar里的PathResourceLookupFunction类的apply方法进行路径解析,这里我们打好断点准备调试

alt text

提前在D盘根目录下放置一个111.txt文件

alt text

启动调试,访问/static/%5c/%5c/../../111.txt

alt text

可以看到确实走到我们的断点了

alt text

我们的目标就是走到这个判断语句的里面,也就是要保证isInvalidPath函数最终返回false。

跟进这个函数看看具体做了些什么操作

alt text

前面简单的检验了有没有敏感路径,去除了开头的绝对路径,检查有没有使用file://或者http://或者url://等协议

重点在下面,if (path.contains("..") && StringUtils.cleanPath(path).contains("../")),前面不用说肯定会返回true,重点是后面,我们要想办法让他返回false,这样就可以绕过非法性检查

继续跟进:

alt text

这里会将我们的url进行标准化处理(反斜杠转为斜杠,因为在前面的代码里使用/////写法会被合并为/)(使用第二个payload也完全可以),并且处理掉了开头的两个/,然后准备做元素分割

alt text

重点在这里!该处代码逻辑处理///这种写法会得到空元素,其会影响后续处理该段path的代码,并在最后得到一个不含..的版本(这里具体是怎么绕过的大家可以自行去调试源码逻辑)

alt text

最后返回的字符串就是"/"+"/1.txt"。很显然没有../,因此在StringUtils.cleanPath(path).contains("../")里会判定为false,进而绕过了非法判断,从而放过了我们的路径穿越payload,访问到了111.txt文件

alt text


传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2天前 被Fulucky0编辑 ,原因:
上传的附件:
收藏
免费 0
支持
分享
最新回复 (1)
雪    币: 1627
活跃值: (380)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
2
欢迎各位师傅在评论区里交流
2天前
0
游客
登录 | 注册 方可回帖
返回