DVWA靶场——SQL Injection(SQL注入) SQL InjectionSQL注入是指当应用程序没有对用户输入的数据进行充分的验证和过滤攻击者就可以通过构造恶意的输入数据将 SQL 语句插入到应用程序提交给数据库的查询中从而改变查询的逻辑获取敏感信息、篡改数据或者执行其他恶意操作。SQL注入漏洞的危害是巨大的常常会导致整个数据库被“脱裤”尽管如此SQL注入仍是现在最常见的Web漏洞之一。sql注入原理参数可控使用户可以绕过防护对后端内容进行操作。参数包含危险语句使得对敏感文件进行操作。11’报错验证原理正常:select * from users where id 1 正常查询错误: select * from users where id 1’ 出现报错 同理 ‘1’’ 也会报错如果出现报错则可能存在SQL注入(2)11 与 12 验证(11为TURE 而 12 为FALSE)select * from users where id 1 and 11 正常查询 与了一个TUREselect * from users where id 1 and 12 查询为空并不会报错 与了一个FALSE判断是否存在注入打点:id 1 and 11 --id 1 and 12 --id 1 or 11 --id 1 or 11--id 1 or 11--或 id 1 and sleep(10) ----是用于注释掉后面的代码闭合我们的命令明确一点--是URL中的注释符号有时会因为编码问题过滤掉#SQL注入流程拿到一个查询条件的web网页一般需要对输入框做以下的事情1.判断是否存在注入注入是字符型还是数字型2.猜解SQL查询语句中的字段数3.确定显示的字段顺序4.获取当前数据库5.获取数据库中的表6.获取表中的字段名7.下载数据low首先抓个包看到它是以GET模式提交数据包到后端的这种模式下URL中会带着id参数类似与这种?id1SubmitSubmit#然后我们分别输入1和1点击submit可以看到1时正常返回了数据而1报错了这就说明存在SQL注入再输入1 and 11 和 1 and 12前者正常显示后者无数据这说明他是字符型注入单引号闭合1我们输入1 or 11点击submit可以看到正常返回了所有数据2使用二分法判断字段order by 5,3,2最终判断存在2个字段1 order by 5 # 1 order by 3 # 1 order by 2 #3显示报错位1 union select 1,2 #4查找库名1 union select 1,database() #源码分析?php if( isset( $_REQUEST[ Submit ] ) ) { // Get input //获取ID字段 $id $_REQUEST[ id ]; // Check database //拼接SQL语句并查询 $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 preID: {$id}br /First name: {$first}br /Surname: {$last}/pre; } mysqli_close($GLOBALS[___mysqli_ston]); } ?通过分析源码我们发现其对用户输入全盘接受没有过滤。攻击者可以闭合单引号注入任意 SQL 代码。并且数据库会将报错信息直接输出到页面Medium这个级别用的POST提交我们看不到他URL中的信息我们f12打开开发者工具找到表单所在的位置将其中value标签改为1 or 11再submit可以看到给我们返回了所有数据可以用同样的方法拼接其他SQL语言拿到我们需要的信息这个关卡除了在开发中工具中注入也可以直接抓包改变id参数进行注入这里就不做演示了High?php if( isset( $_SESSION [ id ] ) ) { // Get input $id $_SESSION[ id ]; // Check database //【select * from tableName limit i,n 】 tableName : 为数据表 i : 为查询结果的索引值默认从0开始 n : 为查询结果返回的数量 查询第一条数据 select * from student limit 1 查询第二条数据 select * from student limit 1,1 $query SELECT first_name, last_name FROM users WHERE user_id $id LIMIT 1;; $result mysqli_query($GLOBALS[___mysqli_ston], $query ) or die( preSomething 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 echo preID: {$id}br /First name: {$first}br /Surname: {$last}/pre; } ((is_null($___mysqli_res mysqli_close($GLOBALS[___mysqli_ston]))) ? false : $___mysqli_res); } ?通过对源代码的分析我们发现它相较于low级别主要是通过会话Session控制将输入和结果分步处理增加了自动化盲注的复杂度其中有这么一行代码他的意思是即使or条件让所有用户记录都符合where条件LIMIT 1也强制数据库只返回第一条记录。所以你看到的永远只是user_id为1的用户信息而不是所有数据。SELECT first_name, last_name FROM users WHERE user_id $id LIMIT 1;所以我们需要用#注释掉后面的LIMIT 1Impossible?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 preID: {$id}br /First name: {$first}br /Surname: {$last}/pre; } } } // Generate Anti-CSRF token generateSessionToken(); ?这段代码主要通过参数化查询preparebindParam将输入强制绑定为整数从根本上防御SQL注入同时辅以is_numeric()过滤数字、rowCount()1限制单条返回、以及Anti-CSRF Token防御跨站请求伪造。常规注入手段完全无效整体防御非常可靠。