1. 程式人生 > 實用技巧 >漏洞重溫之sql注入(五)

漏洞重溫之sql注入(五)

漏洞重溫之sql注入(五)

sqli-labs通關之旅

填坑來了!


Less-17

首先,17關,我們先檢視一下頁面,發現網頁正中間是一個登入框。

顯然,該關卡的注入應該為post型。

直接檢視原始碼。

首先,我們看到網頁封裝了一個check_input函式,在函式中,使用了mysql_real_escape_string函式對我們輸入的內容進行轉義,一般來說,在這麼操作之後,sql注入基本無法完成。

然後我們看一下,網頁對我們的哪些輸入使用到了check_input函式。

檢視下方程式碼:

$uname = check_input($_POST['uname']);
$passwd = $_POST['passwd'];

從上面程式碼中,我們可以看到,網頁對我們輸入的uname值進行了轉義,對passwd沒有,也就是說,我們可以將注入程式碼通過passwd引數完成注入攻擊。

隨後,我們關注下方程式碼:

@$sql="SELECT username, password FROM users WHERE username= $uname LIMIT 0,1";

這條程式碼是網頁向伺服器發起的第一條請求。該請求是網頁利用我們輸入的uname引數從user表中查詢username和password兩條資料。

因為uname引數是無法用來進行sql注入的,所以這條語句我們無法利用。

接著往下看:

$result=mysql_query($sql);
$row = mysql_fetch_array($result);

這兩條是執行剛才的那條查詢語句,並且將結果返回到$row引數中。

當然,如果我們是要進行注入的話,這兩條也沒用。

接著看:

	if($row)
	{
  		//echo '<font color= "#0000ff">';	
		$row1 = $row['username'];  	
		//echo 'Your Login name:'. $row1;
		$update="UPDATE users SET password = '$passwd' WHERE username='$row1'";
		mysql_query($update);
  		echo "<br>";

這是當uname查詢成功之後,所執行的程式碼塊,在這個程式碼塊中,我們總算看到了自己能夠利用的passwd引數。

但是由於這條語句使用的是UPDATE語句,所以我們沒有辦法直接利用這條語句查詢出我們想要的內容。

重點在下面,我們看下面這個程式碼塊:

if (mysql_error())
		{
			echo '<font color= "#FFFF00" font size = 3 >';
			print_r(mysql_error());
			echo "</br></br>";
			echo "</font>";
		}

這個程式碼塊的意思就是,如果程式碼出現了錯誤,那麼就使用print_r引數將報錯資訊列印到網頁上。

也就是說,雖然我們沒法利用update語句進行查詢,但是我們卻可以通過構造語句,使得資料庫報錯,然後通過上方的程式碼塊,將我們想查詢的資料返回到頁面上。

這裡,我使用的是updatexml函式進行報錯注入。

pyload如下:

uname=admin&passwd=1' or updatexml(1,concat(0x7e,(select database()),0x7e),1) -- &sumbit=Sumbit

updatexml函式的報錯注入,其實是有一種固定格式的,如果不想了解太深的話,只需要記住下面的格式,就可以使用了。

updatexml(1,concat(0x7e,(執行語句),0x7e),1)

而如果要理解的話,其實也很簡單,updatexml函式是一個改變文件中符合條件的節點值的函式。而這個函式後面是有三個值的。

其實,第一個值簡單來理解就是我們要查詢內容的名稱,第二個值是查詢的內容,第三個是替換內容。

但是,第二個值是有固定格式的,如果格式不正確,就會導致報錯。這也是可以使用updatexml函式進行報錯注入的原理。

concat函式的目的是返回結果為連線引數產生的字串,就是將我們程式碼執行成功之後的結果返回回來。

0x7e就是符號“~”。

當然,這裡要注意的一點是,updatexml函式只能擷取32位的資料,如果我們想要查詢所有資料庫名稱的話,直接按照之前的方法是不行的。

例如:

那這樣在實際情況中是不夠的,因為資料不可能只有32位,所以,為了能檢視到所有資料,我們可以使用limit函式來切割結果,一個一個的放出來,這樣位置就夠用了。

payload如下:

uname=admin&passwd=1' and updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 0,1),0x7e),1) -- &sumbit=Sumbit

第十七關,通關。

當然,報錯注入還是有其他的一些常用函式的,而且我的解釋是從我的視角出發,針對對報錯注入沒有了解,想要了解更多的人而做的。如果想要更深入的瞭解,可以看下這位keefe大佬的這篇文章。

http://aiyuanzhen.com/index.php/archives/34/

Less-18

單從網頁上看的話,第十八關似乎跟第十七關的區別不大,只是頁面上多了一條ip address的顯示。

我們直接檢視原始碼,看這一關設定了什麼難題。

這一關,是對於我們輸入的uname引數和passwd引數同時使用check_input函式進行了轉義,導致我們無法利用這兩個引數進行sql注入攻擊。

乍一看,似乎無懈可擊,但不著急,接著往下看。

首先是最上面一條的sql語句,網頁是判斷了使用者名稱和密碼,都正確之後才會進入下面的If判斷。

而我們可以利用的程式碼,就在這個裡面。

首先我們先檢視這段程式碼,他獲取了我們的ua屬性,所以在我們訪問的時候,才會在網頁上給出那段提示。注意,這個引數的獲取,是沒有被check_input函式轉移的,也就是說,我們可以在這個引數裡面插入sql語句進行注入攻擊。

然後,我們看這個函式被利用到的語句。

$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";

是insert語句,所以我們同樣無法直接利用這條語句返回我們希望看到的結果。

不過,在這條語句下方,依然有print_r(mysql_error())這條語句。這條語句的功能是列印資料庫報錯資訊,也就是說,我們依然可以選擇使用報錯注入獲取我們希望得到的資料。

當然,因為我們需要在請求包的UA中插入程式碼,所以我們需要使用Burp進行抓包,來修改UA屬性。

payload如下:

' or updatexml(1,concat(0x7e,(select databese()),0x7e),1) or '

這裡要注意的一點是,因為網頁是驗證了使用者名稱和密碼之後才會執行我們插入程式碼的語句,所以想要利用這個注入點,就需要有系統的賬戶名和密碼。

想要讀取多條資料的方式,和十七關一樣,使用limit函式將結果切割之後挨個輸出就可以了。

第十八關,通關。

Less-19

直接看十九關的頁面,發現跟十八關幾乎一模一樣,所以猜測過濾方式應該相同,直接看原始碼。

過濾果然一樣,唯一不同的點是,之前的$uagent引數是從請求包中的ua屬性中獲取,而這19關是從referer獲取。

也就是說,上一次我們需要在ua屬性裡面插入程式碼,這一次,我們需要在referer屬性裡面插入。

pyload一致,我就不寫了,直接放圖。

第十九關,通關。

Less-24

24關,看頁面就能明顯感覺跟之前的幾個post型注入有區別,而且,可以看到在頁面上多了忘記密碼和新建使用者兩個選項。

很明顯,這是一個二階注入。

二階注入呢,簡單來理解是,我們沒辦法直接在頁面插入sql語句,並且使其執行返回我們需要的結果。所以我們可以先把帶有sql程式碼的語句插入到資料庫裡,當我們再次調取之前插入的資料的時候,就可以完成注入。

這關應該怎麼破,我們直接看原始碼。

這關的檔案很多,但是有用的就兩個地方。

這段程式碼,就是我們新建使用者的時候所呼叫的程式碼,首先我們可以看到,網頁在接受我們輸入的資料的時候,使用了mysql_escape_string函式將我們的輸入進行了轉義,然後將我們輸入的使用者名稱先在資料庫中進行了一次查詢。

這次查詢的目的是看我們的使用者名稱在資料庫中是否重複,如果重複,提示我們更換使用者名稱,如果不重複,就將我們的註冊的使用者名稱和密碼寫入到資料庫中。

而這正是我們想要利用的地方。

然後,我們去看修改密碼頁面,因為我們無法通過該注入直接查詢到我們希望查詢到的資料,那就可以將某個使用者的密碼修改成我們已知的密碼,這樣也可以完成注入攻擊。

這是修改密碼位置的sql語句,可以看到,語句是使用單引號進行閉合的。

所以,如果我們想利用這條語句修改掉使用者密碼的話,我們在註冊賬戶的時候,就需要使用單引號去閉合。

具體操作步驟如下,使用者名稱我選擇使用 admin1' or 1=1 -- 使用or連線符的話,就可以將系統中所有使用者的密碼在執行該命令的時候全部修改掉。

密碼我選擇123456.

出現這個頁面,說明我們的使用者建立成功,然後使用該賬戶進行登入。

登入成功後,發現網頁的修改密碼位置。

輸入我們的使用者名稱,使用密碼12345.

點選修改之後,網頁會出現如下頁面。

這個時候,我們去資料庫檢視一下我們修改所有使用者密碼為12345的操作是否完成。

可以看到,密碼已經都被修改為了12345。

第二十四關,通關。

Less-25

開啟頁面,網頁提示輸入id,確認該位置注入屬於get型注入。

隨後我們看到頁面提示or和and不能使用了。

檢視原始碼。

可以發現,該位置的確封裝了一個函式將and和or過濾為空,但是因為是單詞,而且這個過濾只進行了一次,所以我們只需要利用雙寫便可以成功的進行注入。

從上面的sql語句我們可以確定該位置閉合需要使用單引號。

pyload如下:

1' anandd 1=2 union select 1,version(),database() -- 

第二十五關,通關。

25a關,過濾跟25關一致。細微的差別在下圖。

這裡的id引數沒有被任何符號包裹,所以想要構造攻擊payload,只需要將上一關使用的payload的單引號和過濾符號刪除就可以。

第25a關,通關。