从2025d3ctf——d3jtar学习tomcat文件上传绕过(详解)
1674701160110592 发表于 广东 CTF 725浏览 · 2025-05-31 16:57

环境&源码分析

给了一个war包,查看源码发现有

/view

/Upload

/Backup

这三个路由



view是只能传入文件名字,其实也就是文件上传成功之后生成的uuid

image.png


然后后面会利用一个ModelAndView,感觉就是模板文件的东西



Upload路由里面有个Upload.secureUpload的方法

然后黑名单是

"jsp", "jspx", "jspf", "jspa", "jsw", "jsv", "jtml", "jhtml", "sh", "xml", "war", "jar"

上传成功之后会返回文件名的uuid



Backup路由是利用了一个org.kamranzafar.jtar依赖创建tar的实体以及相关的压缩和解压读写文件功能

然后版本是2.3

网上搜了一圈也没找到这个依赖有漏洞点。



题目分析

image.png


题目提示了文件上传的类中,secureUpload的方法没什么问题

那么我们侧重看一下tar和untar这两个方法

tar就是把webapps/ROOT/WEB-INF/views目录下面的文件都进行打包到backup.tar里面去

主要的方法



然后再看一下untar解压



里面没有对entry.getName()进行检测,说明我们是可以进行目录的穿越的。



可以进行目录穿越也只能在解压我们恶意的tar包才起作用。

不要忘记了,我们上传文件的时候会重新命名成uuid的形式,并不是backup.tar



解题思路

根据上面的分析,开始推测,题目的本意会不会并不是让我们去目录穿越呢?

但是我们知道,肯定是跟org.kamranzafar.jtar下面的类脱不了关系的



而org.kamranzafar.jtar下面也只有这几个类,审计起来还是挺方便的。

所以我们开始调试,主要关注用到org.kamranzafar.jtar下面的类,同时打上断点



调试tar

发现Files.walk(inputDirectoryAbsolute).forEach((entry)的entry就是

relativeName就是Error.jsp

然后new一个实体

也就是我们的tar实体,新建完之后传入到这个方法里面去

然后把文件名字每个字符都放到一个数组里面去



我们跟进去entry.writeEntryHeader(header)这个函数里面去

源码是长这样字的

在offset = TarHeader.getNameBytes(this.header.name, outbuf, offset, 100);方法中

文件的名字会以这种方式被读取

注意看,这个字符是经过byte强转的,而这里也是解题的关键,当然我们这里是英文的字符,所以当时根本没发现编码的问题。



最后赋值给private TarEntry currentEntry返回给out

然后接下来就是把Error.jsp文件里面的内容写入到Tar的实体里面去,

其中175是Error.jsp文件内容每个字符经过ascii编码之后,统计一共有多少个字符数量。





调试untar

在untar这里

同时也有一个getNextEntry()方法,但是是TarInputStream类的

打断点调试分析





调试了一遍发现跟tar也是差不多的流程,都是会传入Error.jsp这个名字然后给数组去循环遍历每一个字符。



回想一下,由于环境是在tomcat下面的,如果要getshell,那么传入的得是jsp之类的文件。

而题目的org.kamranzafar.jtar依赖一直对文件的名字没有过滤或者waf之类的,

那么有没有可能是TarHeader.getNameBytes的方法生成文件名字的时候出现jsp呢?



柳暗花明&解题

我尝试在本地去新建了一个你好.html的文件名字

神奇的一幕就出现了



”你“字变成了96

打包之后的文件也是变成了反引号和花括号了



那不就说明通过中文字符是能够生成jsp的吗?

先看看jsp的ascii编码是什么



我们再来看看你好的ascii码值为什么最终会变成96和125

我们写一段测试的代码

最后打印的结果是96

为什么呢?

因为,在 Java 中,char 类型使用 UTF-16 编码,每个字符占用 两个字节(16 位)。

所以 '你' 并不是单字节字符,它的 Unicode 编码是:U+4F60

但是利用了(byte),强制转换成了 byte,这是 从 16 位转成 8 位,Java 会丢弃高 8 位,只保留低 8 位

'你' 字的十进制如下20320

拆分成高低 8 位:

高 8 位(0100 1111)= 0x4F = 79

低 8 位(0110 0000)= 0x60 = 96



那么我们现在就找低8位分别是106、115、112

强转之后就会变成jsp这样的字符了



那么我们只需要随便选三个出来就是了

这里我选了

shell.jsp木马



上传成功之后得到uuid



先1后2

然后用view去访问

view?page=48ec95bb-da76-4300-9234-26bd8662e28b







总结

这题非常有意思的点是他涉及编码细节的问题

如果没有去调试代码,或者去加上自己的一点猜测的话,可能真的很难发现这个RCE的点

实在是很难去发现来个强转会导致编码问题,从而改变文件后缀名字。

总的来说学到的东西还是很多的。

0 条评论
某人
表情
可输入 255