| insert | 数据库中的插入关键字 |
| update | 数据库中的修改关键字 |
| delete | 数据库中的删除关键字 |
| where | 数据库中的条件关键字 |
| union | 数据库中的联合查询关键字 |
| drop | 数据库中的删除数据库关键字 |
| create | 数据库中的创建数据库关键字 |
| count | 数据库中的返回匹配行数关键字 |
| java.sql.Connection | 与特定数据库的连接类 |
| Statement | 是 Java 执行数据库操作的重要接口 |
| jdbcTemplate | 最基本的Spring JDBC模板 |
| PreparedStatement | 预编译的 SQL 语句的对象 |
| queryForInt | 数据库查询方法关键字 |
| queryForObject | 数据库查询方法关键字 |
| queryForMap | 数据库查询方法关键字 |
| getConnection | 获取sql连接 |
| outfile | 数据库中把表数据导出关键字 |
| load_file | 数据库中导入数据的关键字 |
①网络安全学习路线
②20份渗透测试电子书
③安全攻防357页笔记
④50份安全攻防面试指南
⑤安全红队渗透工具包
⑥网络安全必备书籍
⑦100个漏洞实战案例
⑧安全大厂内部教程
1.3Sql注入漏洞危害
1 、 攻击者可以做到
- 业务运营的所有数据被攻击
- 对当前数据库用户拥有的所有表数据进行增、删、改、查等操作
- 若当前数据库用户拥有file_priv权限,攻击者可通过植入木马的方式进一步控制DB所在服务器
- 若当前数据库用户为高权限用户,攻击者甚至可以直接执行服务器命令从而通过该漏洞直接威胁整个内网系统
2、可能对业务造成的影响
① 用户信息被篡改
② 攻击者偷取代码和用户数据恶意获取
线上代码被非法篡改,并造成为恶意攻击者输送流量或其他利益的影响
1.4 Sql注入漏洞代码示例
Java 代码动态构建 SQL
Statement stmt = null;
ResultSet rs = null;
try{
String userName = ctx.getAuthenticatedUserName(); //this is a constant
String sqlString = "SELECT * FROM t_item WHERE owner='" + userName + "' AND itemName='" + request.getParameter("itemName") + "'";
stmt = connection.createStatement();
rs = stmt.executeQuery(sqlString);
// ... result set handling
}
catch (SQLException se){
// ... logging and error handling
}
复制代码
这里将查询字符串常量与用户输入进行拼接来动态构建SQL查询命令。仅当itemName不包含单引号时,这条查询语句的行为才会是正确的。如果一个攻击者以用户名wiley发起一个请求,并使用以下条目名称参数进行查询:
name' OR 'a' = 'a
复制代码
那么这个查询将变成:
SELECT * FROM t_item WHERE owner = 'wiley' AND itemname = 'name' OR 'a'='a';
复制代码
此处,额外的OR ‘a’='a’条件导致整个WHERE子句的值总为真。那么,这个查询便等价于如下非常简单的查询:
SELECT * FROM t_item
复制代码
这个简化的查询使得攻击者能够绕过原有的条件限制:这个查询会返回items表中所有储存的条目,而不管它们的所有者是谁,而原本应该只返回属于当前已认证用户的条目。
在存储过程中动态构建SQL
Java代码:
CallableStatement = null
ResultSet results = null;
try
{
String userName = ctx.getAuthenticatedUserName(); //this is a constant
String itemName = request.getParameter("itemName");
cs = connection.prepareCall("{call sp_queryItem(?,?)}");
cs.setString(1, userName);
cs.setString(2, itemName);
results = cs.executeQuery();
// ... result set handling
}
catch (SQLException se)
{
// ... logging and error handling
}
SQL Server存储过程:
CREATE PROCEDURE sp_queryItem
@userName varchar(50),
@itemName varchar(50)
AS
BEGIN
DECLARE @sql nvarchar(500);
SET @sql = 'SELECT * FROM t_item
WHERE owner = ''' + @userName + '''
AND itemName = ''' + @itemName + '''';
EXEC(@sql);
END
GO
复制代码
在存储过程中,通过拼接参数值来构建查询字符串,和在应用程序代码中拼接参数一样,同样是有SQL注入风险的。
Hibernate 动态构建 SQL/HQL
原生SQL查询:
String userName = ctx.getAuthenticatedUserName(); //this is a constant
String itemName = request.getParameter("itemName");
Query sqlQuery = session.createSQLQuery("select * from t_item where owner = '" + userName + "' and itemName = '" + itemName + "'");
List<Item> rs = (List<Item>) sqlQuery.list();
复制代码
HQL查询:
String userName = ctx.getAuthenticatedUserName(); //this is a constant
String itemName = request.getParameter("itemName");
Query hqlQuery = session.createQuery("from Item as item where item.owner = '" + userName + "' and item.itemName = '" + itemName + "'");
List<Item> hrs = (List<Item>) hqlQuery.list();
复制代码
即使是使用Hibernate,如果在动态构建SQL/HQL查询时包含了不可信输入,同样也会面临SQL/HQL注入的问题。
HQL代码中,session.createQuery使用HQL语句将查询到的数据存到到list集合中,需要时在拿出来使用。而参数中itemName是通过request.getParameter直接获取。攻击者若在此处写入恶意语句,程序将恶意语句查询出来的数据存放在list集合中,再通过某处调用成功将数据显示在前台。
Mybatis注入分析
Mybatis框架下易产生SQL注入漏洞的情况主要分为以下三种:
1)模糊查询like
例如对人员姓名检索进行模糊查询,如果考虑安全编码规范问题,其对应的SQL语句如下:
Select * from user where name like '%#{name}%'
复制代码
但由于这样写程序会报错,研发人员将SQL查询语句修改如下:
Select * from user where name like '%${name}%'
复制代码
在这种情况下我们发现程序不再报错,但是此时产生了SQL语句拼接问题,如果java代码层面没有对用户输入的内容做处理势必会产生SQL注入漏洞。
2)in之后的参数
例如对人员姓名进行同条件多值检索的时候,如当用户输入001,002,003…时,如果考虑安全编码规范问题,其对应的SQL语句如下:
Select * from name where id in (#{id})
复制代码
但由于这样写程序会报错,研发人员将SQL查询语句修改如下:
Select * from name where id in (${id})
复制代码
修改SQL语句之后,程序停止报错,但是却引入了SQL语句拼接的问题,如果没有对用户输入的内容做过滤,势必会产生SQL注入漏洞。
3)order by之后(重点和区分点)
当根据姓名、id序号等信息用户进行排序的时候,如果考虑安全编码规范问题,其对应的SQL语句如下:
Select * from user where name = 'qihoo' order by #{id} desc
复制代码
但由于发布时间id不是用户输入的参数,无法使用预编译。研发人员将SQL查询语句修改如下:
Select * from user where name = 'qihoo' order by ${id} desc
复制代码
修改之后,程序未通过预编译,但是产生了SQL语句拼接问题,极有可能引发SQL注入漏洞。
1.5 .实战案例-OFCMS SQL注入漏洞分析
本文中使用ofcms进行SQL注入漏洞讲解,此CMS算是对新手学习代码审计比较友好的CMS。
上述为安装成功页面,如何安装CMS本章不在赘述。
后台页面:http://localhost:8080/ofcms-admin/admin/index.html
漏洞点:
ofcms-admin/src/main/java/com/ofsoft/cms/admin/controller/system/SystemGeneratrController.java
create方法
|
/**
* 创建表
*/
public void create() {
try {
String sql = getPara("sql");
Db.update(sql);
rendSuccessJson();
} catch (Exception e) {
e.printStackTrace();
rendFailedJson(ErrorCode.get("9999"), e.getMessage());
}
}
复制代码
上述代码中使用getpara获取sql的参数值,并update,跟进一下getpara和update方法。
跳转至 jfinal-3.2.jar/com/jfinal/core/controller.class
public String getPara(String name) {
return this.request.getParameter(name);
}
复制代码
上述代码无特殊用意,就是获取参数值,继续跟进 Db.update 方法。
跳转至 jfinal-3.2.jar/com/jfinal/plugin/activerecord/Db.class
public static int update(String sql) {
return MAIN.update(sql);
}
复制代码
发现调用 MAIN.update , 继续跟进。
跳转至 jfinal-3.2.jar/com/jfinal/plugin/activerecord/DbPro.class
public int update(String sql) {
return this.update(sql, DbKit.NULL_PARA_ARRAY);
}
复制代码
继续跟进到最后,发现华点。
public int update(String sql, Object... paras) {
Connection conn = null;
int var4;
try {
conn = this.config.getConnection();//连接
var4 = this.update(this.config, conn, sql, paras);//调用update更新
} catch (Exception var8) {
throw new ActiveRecordException(var8);
} finally {
this.config.close(conn);
}
return var4;
}
复制代码
重点:Object…
Object是所有类的基类,而 Object… 是不确定方法参数情况下的一种多态表现形式(可以传递多个参数)。
再继续跟进 update ,同文件代码
int update(Config config, Connection conn, String sql, Object... paras) throws SQLException {
PreparedStatement pst = conn.prepareStatement(sql);
config.dialect.fillStatement(pst, paras);
int result = pst.executeUpdate();
DbKit.close(pst);
return result;
}
复制代码
上述代码执行SQL语句,并返回结果。
至此,整个功能流程结束,在我们跟进的过程中,代码中无任何过滤语句,获取参数值,调用update方法更新,更新成功后返回结果。
漏洞验证
漏洞点打上断点,网页中输入poc进行验证
update of_cms_topic set topic_url=updatexml(1,concat(0x7e,(user())),0) where topic_id = 1
复制代码
根据如上截图可看出我们传入的SQL语句是被完整的接收,并未做任何过滤直接带入数据库执行,所以此处直接写入漏洞代码爆出当前数据库账户为 root。
上述为sqlmap工具跑出来的注入点。
1.6 漏洞修复方法
添加全局过滤器,过滤特殊字符
SQLFilter.java中:
PreparedStatement 参数化
如果使用参数化查询,则在SQL语句中使用占位符表示需在运行时确定的参数值。参数化查询使得SQL查询的语义逻辑被预先定义,而实际的查询参数值则等到程序运行时再确定。参数化查询使得数据库能够区分SQL语句中语义逻辑和数据参数,以确保用户输入无法改变预期的SQL查询语义逻辑。
在Java中,可以使用java.sql.PreparedStatement来对数据库发起参数化查询。在这个正确示例中,如果一个攻击者将itemName输入为name’ OR ‘a’ = ‘a,这个参数化查询将免受攻击,而是会查找一个itemName匹配name’ OR ‘a’ = 'a这个字符串的条目。
给大家的福利
零基础入门
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
同时每个成长路线对应的板块都有配套的视频提供:
因篇幅有限,仅展示部分资料
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!