很多java环境下的程序,都是直接以一个jar包的形式运行的,当存在文件上传漏洞时,并不会直接解析jsp等模板文件。
这种情况下大多数的解决方案都是上传到计划任务、ssh key等,比较依赖系统的运行环境,对网络联通性也有要求。
以下是几种不依赖系统特性,在java层面,依靠任意文件写实现代码执行的方法。
覆盖charsets.jar
jvm采用懒加载,没有用到的class,不会加载到内存中。可以通过替换未加载的class所在的jar包,之后使其从上传的jar包中加载类。
上传时没办法上传到应用的jar中classpath中,但可以上传到JDK_HOME中。
注:windows下测试发现无法利用,无法覆盖已经打开的文件。1.需要inputStream的方式,才可以覆盖写入文件。2.覆盖写入后,需要重启
可以覆盖charsets.jar,构造恶意的字符集类,再通过指定http请求的字符集,触发上传的jar包
条件:
需要可以上传charsets.jar到JDK_HOME目录下,一般需要root权限,linux下监听1024以下端口默认都需要root。
jdk 自带文件
/jre/lib/***.jar
没被Opened
过- 以 charsets.jar 文件举例:程序代码中不使用
Charset.forName("GBK")
类似的调用,默认就不会Opened
charsets.jar 文件 - 值得注意的是,只能主动触发一次
Opened
.jar 文件,如果漏洞利用没有成功,则同名 jar 文件就不能再利用了
- 以 charsets.jar 文件举例:程序代码中不使用
分析
springboot下,会解析Accept头
1 | Accept: text/plain; charset=ibm33722 |
在org.springframework.http.MediaType#parseMediaType
->org.springframework.util.MimeTypeUtils#parseMimeTypeInternal
如果存在key中有charset,会用Charset.forName
加载
1 | org.springframework.util.MimeType#checkParameters |
Charset.forName
在执行java.nio.charset.Charset#lookup2
到,会通过三个方法依次来加载
sun.nio.cs.FastCharsetProvider#charsetForName
-> rt.jar ->sun.nio.cssun.nio.cs.AbstractCharsetProvider#charsetForName
->charsets.jar -> sun.nio.cs.extjava.nio.charset.Charset#lookupViaProviders
利用spi机制,加载providers,利用spi发现的providers,加载字符集。
前两种可以加载的字符集是不同的,并且都是固定的。
进入sun.nio.cs.FastCharsetProvider#charsetForName
->sun.nio.cs.FastCharsetProvider#lookup
使用class.forName加载class
`sun.nio.cs.AbstractCharsetProvider#charsetForName
会先检测缓存中有没有,如果没有才会继续加载,并且必须是classMap中存在的字符集。加载过一次后,会加入到缓存中。
缓存中不存在的,通过Class.forName加载。
这里就可以替换charsets.jar,通过accept头,来使spring加载其他未使用过的字符集。
主要利用第二种加载方式,因为charsets.jar不包含任何核心的代码,相比于替换tr.jar,更稳定,并且文件更小。
调用栈
1 | lookup:98, FastCharsetProvider (sun.nio.cs) |
charsets.jar包含的字符集
1 | Big5=IBM33722, Big5-HKSCS=IBM33722, EUC-JP=IBM33722, EUC-KR=IBM33722, GB18030=IBM33722, GB2312=IBM33722, GBK=IBM33722, IBM-Thai=IBM33722, IBM01140=IBM33722, IBM01141=IBM33722, IBM01142=IBM33722, IBM01143=IBM33722, IBM01144=IBM33722, IBM01145=IBM33722, IBM01146=IBM33722, IBM01147=IBM33722, IBM01148=IBM33722, IBM01149=IBM33722, IBM037=IBM33722, IBM1026=IBM33722, IBM1047=IBM33722, IBM273=IBM33722, IBM277=IBM33722, IBM278=IBM33722, IBM280=IBM33722, IBM284=IBM33722, IBM285=IBM33722, IBM290=IBM33722, IBM297=IBM33722, IBM420=IBM33722, IBM424=IBM33722, IBM500=IBM33722, IBM860=IBM33722, IBM861=IBM33722, IBM863=IBM33722, IBM864=IBM33722, IBM865=IBM33722, IBM868=IBM33722, IBM869=IBM33722, IBM870=IBM33722, IBM871=IBM33722, IBM918=IBM33722, ISO-2022-CN=IBM33722, ISO-2022-JP=IBM33722, ISO-2022-JP-2=IBM33722, ISO-2022-KR=IBM33722, ISO-8859-3=IBM33722, ISO-8859-6=IBM33722, ISO-8859-8=IBM33722, JIS_X0201=IBM33722, JIS_X0212-1990=IBM33722, Shift_JIS=IBM33722, TIS-620=IBM33722, windows-1255=IBM33722, windows-1256=IBM33722, windows-1258=IBM33722, windows-31j=IBM33722, x-Big5-HKSCS-2001=IBM33722, x-Big5-Solaris=IBM33722, x-COMPOUND_TEXT=IBM33722, x-euc-jp-linux=IBM33722, x-EUC-TW=IBM33722, x-eucjp-open=IBM33722, x-IBM1006=IBM33722, x-IBM1025=IBM33722, x-IBM1046=IBM33722, x-IBM1097=IBM33722, x-IBM1098=IBM33722, x-IBM1112=IBM33722, x-IBM1122=IBM33722, x-IBM1123=IBM33722, x-IBM1124=IBM33722, x-IBM1166=IBM33722, x-IBM1364=IBM33722, x-IBM1381=IBM33722, x-IBM1383=IBM33722, x-IBM300=IBM33722, x-IBM33722=IBM33722, x-IBM833=IBM33722, x-IBM834=IBM33722, x-IBM856=IBM33722, x-IBM875=IBM33722, x-IBM921=IBM33722, x-IBM922=IBM33722, x-IBM930=IBM33722, x-IBM933=IBM33722, x-IBM935=IBM33722, x-IBM937=IBM33722, x-IBM939=IBM33722, x-IBM942=IBM33722, x-IBM942C=IBM33722, x-IBM943=IBM33722, x-IBM943C=IBM33722, x-IBM948=IBM33722, x-IBM949=IBM33722, x-IBM949C=IBM33722, x-IBM950=IBM33722, x-IBM964=IBM33722, x-IBM970=IBM33722, x-ISCII91=IBM33722, x-ISO-2022-CN-CNS=IBM33722, x-ISO-2022-CN-GB=IBM33722, x-ISO-8859-11=IBM33722, x-JIS0208=IBM33722, x-JISAutoDetect=IBM33722, x-Johab=IBM33722, x-MacArabic=IBM33722, x-MacCentralEurope=IBM33722, x-MacCroatian=IBM33722, x-MacCyrillic=IBM33722, x-MacDingbat=IBM33722, x-MacGreek=IBM33722, x-MacHebrew=IBM33722, x-MacIceland=IBM33722, x-MacRoman=IBM33722, x-MacRomania=IBM33722, x-MacSymbol=IBM33722, x-MacThai=IBM33722, x-MacTurkish=IBM33722, x-MacUkraine=IBM33722, x-MS932_0213=IBM33722, x-MS950-HKSCS=IBM33722, x-MS950-HKSCS-XP=IBM33722, x-mswin-936=IBM33722, x-PCK=IBM33722, x-SJIS_0213=IBM33722, x-windows-50220=IBM33722, x-windows-50221=IBM33722, x-windows-874=IBM33722, x-windows-949=IBM33722, x-windows-950=IBM33722, x-windows-iso2022jp=IBM33722 |
构造jar包 && 利用
类名必须是 charsets.jar包含的字符集
利用时,Accept头中的charset和类名都是对应字符集中的key
jar包:
利用
- 上传
- 修改accept为类名对应的字符集
执行成功
结合fastjson下利用
没有进一步分析,记录一下payload
1 | POST /fastjson HTTP/1.1 |
jdbc url getConnection
1 | GET /jdbc?url=jdbc:mysql://127.0.0.1:3306/test?statementInterceptors=sun.nio.cs.ext.IBM33722 |
常见jdk目录
1 | /usr/lib/jvm/jre/lib/ |
上传SPI class
比较鸡肋,默认配置下,难以利用,一般需要重启java。
上面提到的第三种加载字符集的方式java.nio.charset.Charset#lookupViaProviders
使用SPI的方式,加载java.nio.charset.spi.CharsetProvider
的实现类
通过charsetForName处理传入的charsetName。
上传到哪:
看一下java的三个classLoader的加载路径
除了指定的几个jar包外,还有一个目录jre\classes
可以把class文件和META目录上传到jre\classes
这个路径下.
这种方式最大的问题是这个目录一般不是默认存在的,需要存在这个目录时,才可以利用。
程序启动后,再创建这个目录,也是不能直接利用的,因为classLoader不会去这个path下加载。
利用
构造class
1 | import java.nio.charset.Charset; |
1 | Oh3rCharsetProvider |
目录结构
分两次上传