原文链接:
https://xz.aliyun.com/news/18075
在一些攻防项目中,除了一些常见的大型系统外,常常还会遇到其他一些相对小众的系统,而在打不动大型系统时,这些相对小众的系统往往是比较好的入口点,这些系统更容易通过代码审计找到一些漏洞,从而getshell,在人力和时间成本上都比较契合一些攻防项目,下面就我遇到的一些案例进行分析,给各位师傅分享一些实战思路。
1.地市hw某市图书馆-某图书馆管理系统
在某次地市hw中,对某市图书馆进行资产收集时发现了如下系统,因为是tomcat部署的,所以能直接通过上传getshell,感觉有角度:
搜了下历史漏洞,发现有个权限绕过,试了下可以进后台,但点了一圈功能没打动,没办法只能另辟蹊径:
搜了下互联网资产,有一百多个,还行不算少,后面就是从这些站里面找个其他能拿权限的漏洞,借鉴一下代码看看:
首先查看web.xml,直接搜索upload,还挺多的,挨个看看,首先是/common/Upload:
要先判断这个接口是前台还是后台的,有两个方法,第一个就是直接访问,发现返回302跳转,那就是后台的:
或者找到对应的包,看前面引入的类,如果有session之类的,那多半是后台的:
这里先找前台的漏洞,按照上面的方法可以发现/loan/PictureUpload这个上传接口是没有进行权限校验的:
尝试上传个png,发现上传失败了,并且文件名被识别成了_.JPG,这样看还和一般的上传包不太一样:
那下面就详细跟下代码看看,大体看下,发现这里没有对上传文件进行处理的代码,先获取了web目录下的tmp路径,然后将其和请求传入了b.a()中:
跟进b.a():
首先是调用k.a()将tmp的路径赋值给了f.b:
然后调用了k.a()对请求进行了处理,跟进k.a(),先判断b是否为空,这里b在上面的流程中已被赋值成了tmp的路径,所以不为空,进入下面的流程,调用com.oreilly.servlet.MultipartRequest对请求进行处理,同时传入的参数还有b=tmp路径、c=500*1024:
跟进com.oreilly.servlet.MultipartRequest,对传入的参数进行了一些判断,如请求体是否为空、文件目录是否为空等等,然后调用了MultipartParser()对post请求体内容进行判断后赋值给了multipartParser,然后调用其readNextPart()方法:
跟进readNextPart(),就是对post表单内容进行详细处理的流程了,首先判断是不是以content-disposition开头:
这里我们是上传文件的内容格式,所以进入if语句调用extractDispositionInfo()进行处理,跟进extractDispositionInfo(),该方法返回的是个数组arrayOfString[str2,str3,str4,str5],一个一个分析:
首先是str2,调用substring截取Content-Disposition:后;前的值,也就是form-data:
然后是str3,截取name="和"之中值,也就是file:
str4和str5,都为filename的值1.png:
然后返回readNextPart(),str2=file,str3=1.png,str4=1.png,最后会实例化FilePart:
跟进FilePart,可发现会设置其fileName=1.png,filePath=1.png:
然后返回MultipartRequest(),这里part是上面实例化的FilePart,所以进入下面的第二个if语句,先获取filename的值给str1,这里是上面分析的FilePart.fileName=1.png,然后调用setRenamePolicy()方法设置filePart的policy为传入的paramFileRenamePolicy,这里为null,然后调用writeTo(),传入的file为tmp路径:
跟进writeTo(),可发现最后写入的文件是tmp目录+filename:
按照上面分析的流程,我们之前传的文件路径应该是tmp/1.png,但是访问发现tmp目录禁止访问:
而这里的filename参数在整个过程中是没有做过滤的,可以进行目录穿越,于是尝试进行穿越目录,发现还是上传失败,没道理啊,整个上传过程都分析清楚了:
在这里卡了半天,然后返回最开始的PictureUpload,发现服务端返回文件名中包含的_是从str4获取到的,而str4通过b.c()获取的:
那么跟进b.c()看看,这里有两个for循环,分别处理f和e键值对,而对应的赋值是在最上面的b.a()中进行的:
返回b.a(),这里f其实是post表单传入的参数和对应的值,而e这是上传文件相关的参数,所以上面的流程中会进入第二个循环,获取e的键值对进行处理:
所以继续跟进k.b(),str1调用a.getFileNames()获取,str2调用a.getFilesystemName()获取,然后判断str2中是否包含jpg、png等图片后缀,最后将str1和str2存入hashtable中,也就是上面的e,而a.getFileNames()和a.getFilesystemName()其实在之前的上传流程中见到过,简单看一下对应的值就行:
a.getFileNames()其实就是name的值,在上传包中也就是file:
a.getFilesystemName()其实就是filename的值,在上面的分析中其实就是1.png:
所以最后e中键值对在上传包中对应(name,filename),返回b.c()中第二个for循环,调用了b(filename,name):
跟进b(filename,name),会先判断filename中是否有jpg、png等图片后缀,然后和this.g实例化为e1,this.g这里是tmp的路径,所以e1其实就是tmp/1.png,而str则是name+b.b(filename):
跟进b.b(filename),会判断filename是否以jsp结尾,如果是则将jsp替换为txt,很明显可以传jspx进行绕过,然后就返回filename中.之后的内容作为文件的后缀,注意这里是首个而不是最后一个,所以上面判断filename中是否有图片后缀的校验可以用1.png.jspx进行绕过,这里最后上传时会用到:
所以e2其实就是tmp/file.png,最后由于未传入h(maxwidth)和i(maxheight)会进入else调用a(e1,e2),跟进,实例化两个File,file1(tmp/1.png),file2(tmp/file.png),然后将file1文件的内容复制到file2,最后在上面的b(filename,name)中会删除file1:
所以最后上传的文件其实是tmp/file.png,文件名由name控制,后缀则由filename控制,这也是最开始分析的上传流程最后访问不到上传文件tmp/1.png的原因,文件名是被修改成了name的值,由于tmp目录没有权限,使用目录穿越和上面分析的绕过后缀方法,上传jspx文件:
这个上传漏洞需要足够细心,主要是代码中的一些函数名都被设置成了a、b、c等字母,很容易忽略一些关键函数而浪费很多时间,但其实只要跟着代码流程挨个分析还是没什么问题的。
2.地市hw某发电公司-某广播系统
在某次地市hw时,打某发电公司,通过信息收集发现了如下广播系统,该系统是有弱口令的,但进后台点了半天还是没有发现能getshell的点,不过有个注入,同时通过报错可以获取系统的web路径,但是mysql没有堆叠也写不了文件,同样是tomcat部署的,于是决定搞一搞:
先搜下互联网资产,还行一百多个,然后就是通过其他漏洞借鉴代码了:
拿到代码后还是找上传之类能拿权限的功能,但看完了都只有查询添加之类的功能,正要放弃,发现了一个有意思的文件夹,整个部署包有三个文件夹,server、web、mysql,web是tomcat部署的文件,里面的文件分析完了的没有发现getshell的点,mysql文件夹则是sql的日志没啥看的,而server之前则是忽略了的,再返回去仔细看了下里面的文件,会发现有个jar文件,通过说明文档和conf里的配置文件发现该系统在部署时,需要运行这个jar文件,开启另外一个端口运行一些接口:
那么就再分析一下这个jar文件,接口路由都是由这几个文件控制:
尝试访问下login接口,发现接口存在,那分析的文件就没问题,需要注意的是这里的端口和web端口是不同的,要找这个端口也很简单,直接get请求会返回两个Continue:

所以这里通过list接口获取系统存在的deviceCode:
然后就是只能上传在E盘的问题,首先要知道系统的web路径,最开始说过该系统后台的sql注入通过报错是可以获取到web路径的,如下,同时比较幸运的是目标系统是在E盘部署的,所以可以通过目录穿越上传文件到web目录:
通过上面的分析构造上传包,上传jsp文件:
这个上传流程就很简单,代码一看就知道存在漏洞,同时该上传除了需要传入文件名和文件内容,还有deviceCode和文件路径需要通过其他接口或漏洞获取到,总体还是很简单的,主要是思路要灵活,当web目录下的文件分析完没角度时可以尝试从其他一些接口文件寻找入口。
3.医疗行业红队某市医院-某协同办公系统
在某市医疗行业的红队项目中,发现了某家医院使用了如下协同办公系统:
搜了下历史漏洞,发现有个反序列化和文件上传,不过试了发现目标系统都打不动:
互联网资产有点少,不过没关系,通过上面的历史漏洞可以借鉴代码:
还是先从web.xml中查找上传接口,实际上有很多个,需要挨个看,这里就直接看有漏洞的了:
上传流程也很简单,会先调用org.apache.commons.fileupload.servlet.ServletFileUpload.parseRequest对上传请求进行处理,这个是apache开源的上传库,就没必要跟了,其实就是post发送正常上传包就行了,需要注意的是filename中必须要有@,不然系统会抛出异常,同时这里会对filename中的@进行截取,分成两部分,第一部分和上传目录进行拼接形成新的上传目录,第二部分则是和新的上传目录拼接形成最后要生成的文件,所以只需要传入[email protected],就能在默认目录下上传1.jsp文件了:
然后默认的上传目录是cms/html:
构造上传包,上传jsp文件:
这个上传漏洞的流程同样也很简单,代码一看就懂,其实使用第三方组件对上传请求进行的处理就没必要分析了,像第一个案例分析的上传中对请求体进行处理的流程,就是第三方开源的组件,也只是普通获取上传包里的文件名之类的,不会有什么自定义的流程需要关注,而只有系统自己的代码逻辑才会影响最后的上传结果。
原文始发于微信公众号(网安探索员):攻防项目中的代码审计
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论