webshell上传攻击
2023年某次攻防演练中,上传红队只用了17秒就把一个jsp文件传到了目标服务器的攻击/webapps/ROOT/upload/目录下。文件内容就一行:`<% if(request.getParameter("cmd")!=null){ Process p=Runtime.getRuntime().exec(request.getParameter("cmd")); } %>`。上传三天后蓝队复盘,攻击发现这个上传接口连文件后缀都没校验——前端用JS拦了一下,上传后端直接信任了Content-Type。攻击
这就是上传webshell上传攻击最典型的场景。攻击者不靠漏洞利用框架,攻击不靠内存马,上传就靠一个正常的攻击文件上传功能,把一段可执行的上传代码塞进服务器能解析的目录里。传上去之后,攻击这个文件就成了攻击者在目标系统里的上传“遥控器”。
为什么上传功能总出事
文件上传本身是攻击业务刚需。头像、上传附件、导入Excel、上传视频——几乎每个Web应用都有。开发人员写上传逻辑时,脑子里想的是“用户会传什么正常文件”,而不是“攻击者会传什么畸形文件”。这种思维错位就是漏洞的温床。
我见过一个真实案例。某CMS的头像上传功能,后端代码大概是这样:
String fileName = file.getOriginalFilename(); String path = "/uploads/" + fileName; file.transferTo(new File(path)); 没有后缀白名单,没有重命名,没有内容检测。攻击者把文件名改成shell.jsp,Content-Type设为image/jpeg,直接上传。服务器返回200,文件落在/uploads/目录下。浏览器访问/uploads/shell.jsp?cmd=whoami,命令执行成功。
这个案例里最讽刺的是,前端确实做了校验——只允许jpg/png/gif。但攻击者用Burp Suite抓包改个后缀,前端校验形同虚设。后端信任前端,等于没校验。
绕过手段比想象中多
黑名单校验.jsp?那就传.jspx、.jspf,某些容器照样解析。或者传.jsp.(Windows下会自动去掉末尾的点)、.jsp%00.jpg(空字节截断,老版本Java有这问题)。
Content-Type校验?改请求头就行,image/png随便填。
文件头校验?在webshell前面加个GIF89a,图片马就做好了。服务器端如果只检查文件开头几个字节,这招必过。
还有更隐蔽的:传一个.htaccess文件,内容写AddType application/x-httpd-php .jpg,然后传一个shell.jpg。Apache会把jpg当php解析。这招在CTF里都快被玩烂了,但生产环境里依然能打穿。
防御不是加个白名单就完事
后缀白名单是最基本的。但光有白名单不够。文件要重命名——用UUID或者时间戳,别用用户提供的文件名。存储目录要设置不可执行权限,nginx里加location ~* /uploads/.*\.(php|jsp|asp)$ { deny all; }。上传目录和Web根目录最好分开,用单独域名或者CDN回源。
还有一点容易被忽略:文件内容检测。图片马能绕过后缀和Content-Type,但绕不过二次渲染。把上传的图片用GD库重新生成一遍,嵌入的PHP代码就没了。当然这招对PDF、Office文档不适用,得另想办法。
去年某金融客户被上传webshell,排查发现上传接口用的是MultipartFile.transferTo(),路径拼接时用了../没过滤。攻击者传了个../../../../webapps/ROOT/shell.jsp,直接写到Web目录。这种路径穿越+文件上传的组合拳,比单纯传webshell危害更大。
说到底,webshell上传攻击的本质是“让服务器执行了不该执行的文件”。防御的核心就两条:不让传上去,传上去也执行不了。大部分出事的系统,连第一条都没做到。