文章总结: 文章通过AI生成的文件上传代码示例,揭示了三种常见的文件上传漏洞:黑名单后缀匹配错误、indexOf判断逻辑缺陷和白名单绕过。作者提供了具体的代码示例和漏洞利用方法,并建议在白盒审计中检查黑白名单逻辑严密性,在黑盒测试中尝试特殊文件名绕过。文章指出AI生成的代码可能存在安全隐患,安全人员仍需保持警惕。
综合评分: 90
文章分类: 代码审计,WEB安全,漏洞分析,渗透测试,安全建设
AI给我写的上传代码,差点让黑客拿下整个服务器
原创
cwkiller
漫漫安全路
2025年7月14日 10:00
浙江
引子
这周本来没想好更新什么内容,写文件上传的示例代码时AI弹出一个提示,当时的代码提示如下。它这里想给我写一个文件上传的后缀白名单,此时我直接按下
tab因为我想写黑名单于是把jpg\jpeg改为了jsp\jspx然后我意识到这样写是存在问题的,感觉有点意思于是写下这篇文章,总结一下做审计的过程中文件上传容易忽略的几种错误写法,以及黑盒测试中应该如何发现这类问题。
案例一
使用AI提示的判断改为黑名单代码如下
<%@ page language="java" contentType="text/plain; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true"%>
<%@ page import="java.io.*" %>
<%@ page import="java.util.*" %>
<%@ page import="org.apache.commons.fileupload.*" %>
<%@ page import="org.apache.commons.fileupload.disk.*" %>
<%@ page import="org.apache.commons.fileupload.servlet.*" %>
<%
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
String uploadPath = application.getRealPath("/upload");
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) {
uploadDir.mkdirs();
}
List<FileItem> items = upload.parseRequest(request);
for (FileItem item : items) {
if (!item.isFormField() && item.getSize() > 0) {
String originalName = item.getName();
String extension = originalName.substring(originalName.lastIndexOf("."));
if (extension.equals("jsp") || extension.equals("jspx")) {
out.print("ERROR: 文件格式非法");
return;
}
File targetFile = new File(uploadDir, originalName);
item.write(targetFile);
out.print("SUCCESS: " + originalName);
}
}
} catch (Exception e) {
out.print("ERROR: " + e.getMessage());
}
%>
这么写如果开发者不经过测试会直接导致任意文件上传,因为当originalName="1.jsp"时originalName.substring(originalName.lastIndexOf("."))获取到的是带.的后缀.jsp而下面的判断是不带.会导致判断形同虚设。如果按照AI的代码写白名单那什么类型的文件都会上传失败。这类错误在实际审计过程中遇到过多次,最近一次遇到是在一个
.NET项目里代码如下写法差不多
AI可能真的是学多了垃圾代码导致自己也瞎写。
案例二
另外一种错误的黑名单写法
<%@ page language="java" contentType="text/plain; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*" %>
<%@ page import="java.util.*" %>
<%@ page import="org.apache.commons.fileupload.*" %>
<%@ page import="org.apache.commons.fileupload.disk.*" %>
<%@ page import="org.apache.commons.fileupload.servlet.*" %>
<%@ page import="java.util.UUID" %>
<%
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
String uploadPath = application.getRealPath("/upload");
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) {
uploadDir.mkdirs();
}
List<FileItem> items = upload.parseRequest(request);
for (FileItem item : items) {
if (!item.isFormField() && item.getSize() > 0) {
String originalName = item.getName();
if (originalName.indexOf(".jsp") > 0){
out.print("ERROR: 文件格式非法");
return;
}
String extension = originalName.substring(originalName.lastIndexOf("."));
String randomName = UUID.randomUUID().toString() + extension;
File targetFile = new File(uploadDir, randomName);
item.write(targetFile);
out.println("SUCCESS: " + originalName + " -> " + randomName);
}
}
} catch (Exception e) {
out.print("ERROR: " + e.getMessage());
}
%>
这里直接使用originalName.indexOf(".jsp") > 0判断文件名中是否包含.jsp我估计有一部分审计人员可能认为是安全的,实际上是不安全的当originalName=".jsp"时originalName.indexOf(".jsp")==0依然可以上传jsp文件。
有朋友这几天正好遇到一个黑盒案例估计代码写的差不多
所以以后不光审计需要注意,黑盒测试的时候遇到黑名单也可以使用
.jsp试试
案例三
一种错误的白名单写法
<%@ page language="java" contentType="text/plain; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true"%>
<%@ page import="java.io.*" %>
<%@ page import="java.util.*" %>
<%@ page import="org.apache.commons.fileupload.*" %>
<%@ page import="org.apache.commons.fileupload.disk.*" %>
<%@ page import="org.apache.commons.fileupload.servlet.*" %>
<%@ page import="java.util.UUID" %>
<%
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
String uploadPath = application.getRealPath("/upload");
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) {
uploadDir.mkdirs();
}
List<FileItem> items = upload.parseRequest(request);
for (FileItem item : items) {
if (!item.isFormField() && item.getSize() > 0) {
String originalName = item.getName();
if (originalName.indexOf(".zip") > 0){
String extension = originalName.substring(originalName.lastIndexOf("."));
String randomName = UUID.randomUUID().toString() + extension;
File targetFile = new File(uploadDir, randomName);
item.write(targetFile);
out.print("SUCCESS: " + originalName + " -> " + randomName);
}
else {
out.print("ERROR: 文件格式非法");
}
}
}
} catch (Exception e) {
out.print("ERROR: " + e.getMessage());
}
%>
这种写法在审计过程中也不少见,基本做审计的都可以看出来是任意文件上传。originalName="1.zip.jsp"即可
这种在黑盒里挺常见的遇到过很多次,因为我黑盒测试任意文件上传的流程是先上传白名单后缀文件,比如
1.zip如果可以上传我会直接测试1.zipa而不是1.jsp,测试1.zipa不行的话基本上就不行了,如果行的话大多数情况的可以shell。比如这个案例里1.zipa是行的然后我可能会直接测试1.zip.jsp即可成功shell
总结
白盒审计的时候需要注意黑名单/白名单逻辑是否严密,黑盒测试的话可以尝试1.jpg.jsp\.jsp这种文件名可能可以绕过某些错误的黑白名单判断。AI写的代码也可能会出现漏洞,如果一种错误写法在网上很多的话AI可能被污染导致某类漏洞大规模出现。有的时候会想AI会不会让代码审计人员失业,从这个例子来说好像不会😄?
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:漫漫安全路 cwkiller《AI给我写的上传代码,差点让黑客拿下整个服务器》