SQL注入

1、通读技巧:

SQL注入经常出现在登录页面、获取HTTP头(user-agent/client-ip等)、订单处理,登录页面的注入现在来说大多是发生在HTTP头里面的client-ip和xforward-for,一般用来记录登录的IP地址,另外在订单系统里面,由于订单涉及购物车等多个交互,所以经常会发生二次注入。在通读代码挖掘漏洞的时候可以着重关注这几个地方。

2、搜索关键词:

select from、mysql_connect、mysql_query、mysql_fetch_row、update、insert、delete

3、场景:

(1)数据库查询功能

  1. <?php
  2. $hostName = "127.0.0.1";
  3. $usrName = "root";
  4. $passWord = "123456";
  5. $dbname = "test";
  6. $Id = $_GET['id'];
  7. $content = mysql_connect($hostName,$usrName,$passWord);
  8. mysql_select_db("test",$content);
  9. $sql = "SELECT * FROM user WHERE id = $Id";
  10. $result = mysql_query($sql) or die(mysql_error());
  11. while($row = mysql_fetch_array($result)){
  12. echo "ID:".$row['id']."<br>";
  13. echo "Username:".$row['usename']."<br>";
  14. echo "Passwd:".$row['passwd']."<br>";
  15. echo "email:".$row['Email']."<br>";
  16. }
  17. mysql_close($content);
  18. echo "<hr>";
  19. echo $sql;
  20. //查询数据;读写文件;执行命令

(2)用户登入功能

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
    <form action="./login.php" method="post">
        账号:<input type="text" name="username"><br >
        密码:<input type="password" name="passwd"> <br >
        <input type="submit" value="提交" name="login">
    </form>
</body>
</html>
<?php
if (!isset($_POST['login'])){
    echo "false";
}else{
    $username = $_POST['username'];
    $passwd = $_POST['passwd'];
    $connect = mysql_connect("127.0.0.1","root","123456");
    mysql_select_db("test",$connect);
    $sql = "SELECT * FROM user WHERE username = '$username'";
    $result = mysql_query($sql) or die ('执行MySQL语句失败!');
    while($row = mysql_fetch_array($result)){
        $db_username = $row['username'];
        $db_passwd = $row['passwd'];
    }
    if($db_passwd == $passwd && $db_username == $username){
        echo "success!";
    }else{
        echo "fale!";
    }
}

(3)宽字节问题

a、描述:

在使用PHP连接MySQL的时候,当设置“setcharacter_set_client=gbk”时会导致一个编码转换的注入问题,也就是我们所熟悉的宽字节注入,当存在宽字节注入漏洞时,注入参数里带入%df%27,即可把程序中过滤的\(%5c)吃掉,造成绕过。

b、搜索关键词:

character_set_client=gbk,SET NAMES’gbk’,mysql_set_charsetmysql_set_charset,urldecode,rawurldecode

c、漏洞的解决方法推荐如下几种方法:

1)在执行查询之前先执行SET NAMES’gbk’,character_set_client=binary设置character_set_client为binary。
2)使用mysql_set_charset(’gbk’)设置编码,然后使用mysql_real_escape_string()函数被参数过滤。
3)使用pdo方式,在PHP5.3.6及以下版本需要设置setAttribute(PDO::ATTR_EMULATE_PREPARES,false);来禁用prepared statements的仿真效果。
如上几种方法更推荐第一和第三种。
场景:

<?php
if (!isset($_POST['login'])){
    echo "false";
}else{
    $username = $_POST['username'];
    $passwd = $_POST['passwd'];
    $connect = mysql_connect("127.0.0.1","root","123456");
    mysql_select_db("test",$connect);
        mysql_query("SET NAMES 'GBK'");//此命令使数据库以GBK形式解码;
    $sql = "SELECT * FROM user WHERE username = '$username'";
    $result = mysql_query($sql) or die ('执行MySQL语句失败!');
    while($row = mysql_fetch_array($result)){
        $db_username = $row['username'];
        $db_passwd = $row['passwd'];
    }
    if($db_passwd == $passwd && $db_username == $username){
        echo "success!";
    }else{
        echo "fale!";
    }
}

d、拓展

只要字符被进行转换就有可能产生漏洞,如果某处使用了urldecode或者rawurldecode函数,则会导致二次解码生成单引号而引发注入。原理是我们提交参数到WebServer时,WebServer会自动解码一次,假设目标程序开启了GPC,我们提交/1.php?id=1%2527,因为我们提交的参数里面没有单引号,所以第一次解码后的结果是id=1%27,%25解码的结果是%,如果程序里面使用了urldecode或者rawurldecode函数来解码id参数,则解码后的结果是id=1’单引号成功出现引发注入。

(4)二次注入

a、函数介绍

addslashes ( string $str ) : string
• 返回字符串,该字符串为了数据库查询语句等的需要在某些字符前加上了反斜线。这些字符是单引号()、双引号()、反斜线(\)与 NUL(NULL 字符)。
注册功能

<?php
header("content-type:text/html;charset=utf-8");
if(!empty($_POST['submit'])){
    $id = addslashes($_POST['id']);
    $username = addslashes($_POST['username']);
    $passwd = addslashes($_POST['passwd']);
    $email = addslashes($_POST['Email']);
    $connect = mysql_connect("127.0.0.1","root","123456");
    mysql_select_db("test",$connect);
    $sql = "INSERT INTO user (id,username,passwd,email) VALUE ('$id','$username','$passwd','$email');";
    $result = mysql_query($sql) or die("执行命令失败".mysql_error());
    if($result){
        echo "regist success!";
    }else{
        echo "regist fail!";
    }
}else{
    echo "not POST!";
}
?>
<form action="reg.php" method="POST">
    ID: <input type="text" name="id"><br>
    username: <input type="text" name="username"><br>
    passwd: <input type="password" name="passwd"><br>
    Email: <input type="text" name="Email"><br>
    <input type="submit" name="submit" value="提交">
</form>

查询功能

<?php
header("content-type:text/html;charset=utf-8");
if(!empty($_POST['submit'])){
    $id = $_POST['id'];
    $connect = mysql_connect("127.0.0.1","root","123456");
    mysql_select_db("test",$connect);
    $sql1 = "SELECT * FROM user WHERE id = '$id'";
    $result1 = mysql_query($sql1);
    while($row1 = mysql_fetch_array($result1)){
        $username = $row1['username'];
        $sql2 = "SELECT * FROM user WHERE username = '$username';";//此处为二次注入利用语句
        $result2 = mysql_query($sql2) or die(mysql_error());
        while ($row2 = mysql_fetch_array($result2)){
            echo "ID:".$row2['id']."<br>";
            echo "Username:".$row2['username']."<br>";
            echo "Passwd:".$row2['passwd']."<br>";
            echo "email:".$row2['Email']."<br>";
        }
    }
}

?>
<form action="search.php" method="POST">
    search ID :<input type="text" name="id">
    <input type="submit" name="submit" value="提交">
</form>

4、漏洞修复