您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
渗透攻防Web篇-深入浅出SQL注入
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
渗透攻防Web篇-深入浅出SQL注入
自猿其说Tech
2022-04-25
IP归属:未知
411浏览
### 1 背景 京东SRC(Security Response Center)收录大量外部白帽子提交的sql注入漏洞,漏洞发生的原因多为sql语句拼接和Mybatis使用不当导致。 ![](//img1.jcloudcs.com/developer.jdcloud.com/6b3f8007-23d1-4e6c-aad3-bcba9242b6ec20220425151309.png) ### 2 手工检测 #### 2.1 前置知识 mysql5.0以上版本中存在一个重要的系统数据库information_schema,通过此数据库可访问mysql中存在的数据库名、表名、字段名等元数据。information_schema中有三个表成为了sql注入构造的关键。 ###### 1)infromation_schema.columns: - table_schema 数据库名 - table_name 表名 - column_name 列名 ###### 2)information_schema.tables - table_schema 数据库名 - table_name 表名 ###### 3)information_schema.schemata - schema_name 数据库名 **SQL注入常用SQL函数** - length(str) :返回字符串str的长度 - substr(str, pos, len) :将str从pos位置开始截取len长度的字符进行返回。注意这里的pos位置是从1开始的,不是数组的0开始 - mid(str,pos,len) :跟上面的一样,截取字符串 - ascii(str) :返回字符串str的最左面字符的ASCII代码值 - ord(str) :将字符或布尔类型转成ascll码 - if(a,b,c) :a为条件,a为true,返回b,否则返回c,如if(1>2,1,0),返回0 #### 2.2 注入类型 ##### 2.2.1 参数类型分类 - 整型注入 例如?id=1,其中id为注入点,类型为int类型。 - 字符型注入 例如?id="1",其中id为注入点,类型为字符型,要考虑闭合后端sql语句中的引号。 ##### 2.2.2 注入方式分类 - 盲注 - - 布尔盲注:只能从应用返回中推断语句执行后的布尔值。 - - 时间盲注:应用没有明确的回显,只能使用特定的时间函数来判断,例如sleep,benchmark等。 - 报错注入:应用会显示全部或者部分的报错信息 - 堆叠注入:有的应用可以加入 ; 后一次执行多条语句 - 其他 #### 2.3 手动检测步骤(字符型注入为例) ```java // sqli vuln code Statement statement = con.createStatement(); String sql = "select * from users where username = '" + username + "'"; logger.info(sql); ResultSet rs = statement.executeQuery(sql); // fix code 如果要使用原始jdbc,请采用预编译执行 String sql = "select * from users where username = ?"; PreparedStatement st = con.prepareStatement(sql); ``` 使用未预编译原始jdbc作为demo,注意此demo中sql语句参数采用单引号闭合。 ##### 2.3.1 确定注入点 对于字符类型注入,通常先尝试单引号,判断单引号是否被拼接到SQL语句中。推荐使用浏览器扩展harkbar作为手工测试工具。https://chrome.google.com/webstore/detail/hackbar/ginpbkfigcoaokgflihfhhmglmbchinc 正常页面应该显示如下: ![](//img1.jcloudcs.com/developer.jdcloud.com/b5fcb103-4dac-4859-b30b-fafa99fea6c020220425153903.png) admin后加单引号导致无信息回显,原因是后端sql执行报错,说明引号被拼接至SQL语句中 ![](//img1.jcloudcs.com/developer.jdcloud.com/c9fca7df-a021-4329-a1cc-fc727aa71a6320220425153916.png) ![](//img1.jcloudcs.com/developer.jdcloud.com/ce8322e5-757f-4809-9f6d-49f6cb39209220220425153926.png) ```sql select * from users where username = 'admin' #正常sql select * from users where username = 'admin'' #admin'被带入sql执行导致报错无法显示信息 ``` ##### 2.3.2 判断字段数 mysql中使用order by ** 进行排序,**不仅可以是字段名也可以是字段序号。所以可以用来判断表中字段数,order by 超过字段个数的数字就会报错。 ![](//img1.jcloudcs.com/developer.jdcloud.com/e7e7b1a5-b1bc-400e-a85c-2a4ffe4ef4c820220425154020.png) **判断字段数** 当order by 超过4时会报错,所以此表共四个字段。 ![](//img1.jcloudcs.com/developer.jdcloud.com/4f10f4b6-685d-47fe-bed8-7d8adb50364020220425154843.png) **后端所执行的sql语句** ```sql select * from users where username = 'admin' order by 1-- ' ``` 此处我们将原本username的值admin替换为admin' order by 1 --+,其中admin后的单引号用于闭合原本sql语句中的前引号,--+用于注释sql语句中的后引号。--后的+号主要作用是提供一个空格,sql语句单行注释后需有空格,+会被解码为空格。 ##### 2.3.3 确定回显位置 主要用于定位后端sql字段在前端显示的位置,采用联合查询的方式确定。注意联合查询前后字段需一致,这也就是我们为什么做第二步的原因。 通过下图可知,后端查询并回显的字段位置为2,3位。 ![](//img1.jcloudcs.com/developer.jdcloud.com/df081f5e-4615-43c7-9b00-4a1f9df1095c20220425154935.png) 联合查询后的字段可以随意,本次采用的是数字1到4直观方便。 ![](//img1.jcloudcs.com/developer.jdcloud.com/3b4e573b-8a5a-4db2-b137-c1c21f772a5420220425162820.png) ##### 2.3.4 利用information_schema库实现注入 group_concat()函数用于将查询结果拼接为字符串。 - 查看存在数据库 ![](//img1.jcloudcs.com/developer.jdcloud.com/b33fd139-4194-4d82-a1bf-3422620515e320220425162855.png) - 查看当前数据库中的表 ![](//img1.jcloudcs.com/developer.jdcloud.com/744f518e-7241-4c24-b85a-0086d1fdca3320220425162920.png) - 查看指定表中字段 ![](//img1.jcloudcs.com/developer.jdcloud.com/790ce2a8-6f60-4805-9732-224049c4566920220425162937.png) - 利用以上获取信息读取users表中username和password ![](//img1.jcloudcs.com/developer.jdcloud.com/371529f3-7762-4a0e-ae82-1d36be99e02920220425162954.png) ### 3 自动化检测 #### 3.1 sqlmap 使用 sqlmap兼容python2和python3,可以自动化检测各类注入和几乎所有数据库类型。 ##### 3.1.1 常用命令 ``` -u 可能存在注入的url链接 -r读取http数据包 --data 指定post数据 --cookie 指定cookie --headers 指定http头 如采用token认证的情况下 --threads 指定线程数 --dbms 指定后端的数据库 --os 指定后端的操作系统类型 --current-user 当前用户 --users 所有用户 --is-dba 是否是dba --sql-shell 交互式的sqlshell -p指定可能存在注入点的参数 --dbs 穷举系统存在的数据库 -D指定数据库 --tables 穷举存在的表 -T指定表 --column 穷举字段 -C指定字段 --dump dump数据 ``` 直接检测 其中--cookie用于指定cookie,--batch 自动化执行,--dbms指定数据库类型 ![](//img1.jcloudcs.com/developer.jdcloud.com/64e329e7-5fdf-4bec-bf49-e7774b22ef9d20220425163046.png) 检测结果 ![](//img1.jcloudcs.com/developer.jdcloud.com/5cb9ab6f-f7b6-4f6f-8b90-7222c5f0a40120220425163100.png) 读取系统中存在数据库 --dbs读取当前用户下的数据库 ![](//img1.jcloudcs.com/developer.jdcloud.com/c117ff7b-ede6-4150-8027-ee3a502d0dbe20220425163116.png) 读取指定库下的表 -D java_sec_code --tables ![](//img1.jcloudcs.com/developer.jdcloud.com/50595fda-a0a6-4da3-906f-2f05d03b7cc720220425163231.png) dump users表数据 -D java_sec_code -T users --dump ![](//img1.jcloudcs.com/developer.jdcloud.com/e2f41703-b1a0-4b9c-9700-cdde89988edd20220425163243.png) ### 4 进阶 ##### 4.1 Mybatis注入 ###### 1)$错误使用导致注入 ```java //采用#不会导致sql注入,mybatis会使用预编译执行 @Select("select * from users where username = #{username}") User findByUserName(@Param("username") String username); //采用$作为入参可导致sql注入 @Select("select * from users where username = '${username}'") List<User> findByUserNameVuln01(@Param("username") String username); ``` ###### 2)模糊查询拼接 ```xml //错误写法 <select id="findByUserNameVuln02" parameterType="String" resultMap="User"> select * from users where username like '%${_parameter}%' </select> //正确写法 <select id="findByUserNameVuln02" parameterType="String" resultMap="User"> select * from users where username like concat(‘%’,#{_parameter}, ‘%’) </select> ``` ###### 3)order by 注入 order by 后若使用#{}会导致报错,因为#{}默认添加引号会导致找不到字段从而报错。 ```xml //错误写法 <select id="findByUserNameVuln03" parameterType="String" resultMap="User"> select * from users <if test="order != null"> order by ${order} asc </if> </select> //正确写法 id指字段id 此表字段共四个 所以id为1-4 <select id="OrderByUsername" resultMap="User"> select * from users order by id asc limit 1 </select> ``` ### <center>以上测试均在本地进行,请勿未授权进行渗透测试</center> ### 5 文章及资料推荐 slqmap手册:https://octobug.gitbooks.io/sqlmap-wiki-zhcn/content/Users-manual/Introduction.html sql注入详解:http://sqlwiki.radare.cn/#/ ------------ ###### 自猿其说Tech-JDL京东物流技术与数据智能部 ###### 作者:罗宇(物流安全小分队)
原创文章,需联系作者,授权转载
上一篇:联合索引下Order By和范围匹配SQL优化介绍
下一篇:前端进阶之路-那些当年踩过的
自猿其说Tech
文章数
426
阅读量
2149990
作者其他文章
01
深入JDK中的Optional
本文将从Optional所解决的问题开始,逐层解剖,由浅入深,文中会出现Optioanl方法之间的对比,实践,误用情况分析,优缺点等。与大家一起,对这项Java8中的新特性,进行理解和深入。
01
Taro小程序跨端开发入门实战
为了让小程序开发更简单,更高效,我们采用 Taro 作为首选框架,我们将使用 Taro 的实践经验整理了出来,主要内容围绕着什么是 Taro,为什么用 Taro,以及 Taro 如何使用(正确使用的姿势),还有 Taro 背后的一些设计思想来进行展开,让大家能够对 Taro 有个完整的认识。
01
Flutter For Web实践
Flutter For Web 已经发布一年多时间,它的发布意味着我们可以真正地使用一套代码、一套资源部署整个大前端系统(包括:iOS、Android、Web)。渠道研发组经过一段时间的探索,使用Flutter For Web技术开发了移动端可视化编程平台—Flutter乐高,在这里希望和大家分享下使用Flutter For Web实践过程和踩坑实践
01
配运基础数据缓存瘦身实践
在基础数据的常规能力当中,数据的存取是最基础也是最重要的能力,为了整体提高数据的读取能力,缓存技术在基础数据的场景中得到了广泛的使用,下面会重点展示一下配运组近期针对数据缓存做的瘦身实践。
自猿其说Tech
文章数
426
阅读量
2149990
作者其他文章
01
深入JDK中的Optional
01
Taro小程序跨端开发入门实战
01
Flutter For Web实践
01
配运基础数据缓存瘦身实践
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号