里面很多都是像laterain學習到的, 如果能考上cuit的話 自動獻菊花了。
首先拿到一份源碼 肯定是先install上。 而在安裝文件上又會經常出現問題。
一般的安裝文件在安裝完成后 基本上都不會自動刪除這個安裝的文件 我遇到過的會自動刪除的好像也就qibocms了。
其他的基本都是通過生成一個lock文件 來判斷程序是否安裝過了 如果存在這個lock文件了 就會退出了。 這里首先 先來說一下安裝文件經常出現的問題。
這種的雖然不多 但是有時還是會遇到個。 在安裝完成后 并不會自動刪除文件 又不會生成lock來判斷是否安裝過了。 導致了可以直接重裝過
因為install 一般都會有step 步驟啥的。。 Step 1 check 啥啥 step 2 是安裝啥的。 而一些cms 默認step是1 而step又是GET 來的 而他check lock的時候就是在step1里面。 這時候如果我們直接用GET提交step 2 那么就直接進入下一步了 就沒check lock了。
例如某cms中的安裝文件
#!php
if (empty ($step))
{
$step = 1;//當用戶沒有提交step的時候 賦值為1
}
require_once ("includes/inc_install.php");
$gototime = 2000;
/*------------------------
顯示協議文件
------------------------*/
if ($step == 1) //當1才檢測lock
{
if (file_exists('installed.txt'))
{
echo '<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>
<body>
你已經安裝過該系統,如果想重新安裝,請先刪除install目錄下的 installed.txt 文件,然后再安裝。
</body>
</html>';
exit;
}
include_once ("./templates/s1.html");
exit ();
}
/*------------------------
測試環境要求
------------------------*/
else
if ($step == 2) // 我們直接提交step為2 就不check lock了
{
$phpv = @ phpversion();
$sp_os = $_ENV["OS"];
$sp_gd = @ gdversion();
$sp_server = $_SERVER["SERVER_SOFTWARE"];
$sp_host = (empty ($_SERVER["SERVER_ADDR"]) ? $_SERVER["SERVER_HOST"] : $_SERVER["SERVER_ADDR"]);
$sp_name = $_SERVER["SERVER_NAME"];
$sp_max_execution_time = ini_get('max_execution_time');
$sp_allow_reference = (ini_get('allow_call_time_pass_reference') ? '<font color=green>[√]On</font>' : '<font color=red>[×]Off</font>');
$sp_allow_url_fopen = (in
#!php
header("Content-Type: text/html; charset={$lang}");?
foreach(Array('_GET','_POST','_COOKIE') as $_request){?
foreach($$_request as $_k => $_v) ${$_k} = _runmagicquotes($_v);?
}?
function _runmagicquotes(&$svar){?
if(!get_magic_quotes_gpc()){?
if( is_array($svar) ){?
foreach($svar as $_k => $_v) $svar[$_k] = _runmagicquotes($_v);?
}else{?
$svar = addslashes($svar);?
}?
}?
return $svar;?
}?
if(file_exists($insLockfile)){?
exit(" 程序已運行安裝,如果你確定要重新安裝,請先從FTP中刪除 install/install_lock.txt!");?
}
foreach($$_request as $_k => $_v) ${$_k} = _runmagicquotes($_v);
這里是一個經常遇到的一個變量覆蓋。
導致了我們可以覆蓋掉$insLockfile 從而讓file_exists 為false就不會退出了。導致再次重裝。 這個變量覆蓋不知道咋的 能在一些小cms的安裝文件里看到。
之前看的xdcms 和 frcms 都存在這個變量覆蓋。
這個從早期的phpdisk 的那個 header bypass 到現在的又遇到各種。
很久前的phpdisk的安裝文件中。
判斷是否存在lock文件 如果存在lock文件了 就會header到index.php
但是header 后 他并沒有exit 所以并不會退出 導致了又是一個重裝。
跟這種類似的還有javascript 彈個框 啥的 也沒exit的。
例子: WooYun: 開源輕論壇StartBBS前臺getshell
例子: WooYun: FengCMS 修復不當導致getshell
這個也比較少, 就隨便說句。 就是像dedecms很久以前的那樣 在安裝完成后會在install.php rename 為 Install.php.bak 但是由于apache的解析漏洞 如果無法識別最后的一個后綴的話 就會向上解析,那么就又成php了。 然后又結合dedecms安裝時的變量覆蓋 又成重裝了。
這種例子也不算太多, 自己好像也沒遇到過太多。
首先以之前發過的sitestar舉例下
#!php
if(file_exists($lockfile) && ($_a=='template' || $_a=='setting' || $_a=='check')) {?
exit('please delete install.lock!');?
}
這里我們來理解一下這個邏輯, 這里的file_exists($lockfile) 因為安裝成功后 lockfile 肯定存在的 所以這里肯定會是true 然后再看一下 這里是一個 && true true 才會進入語句塊。 那么如果$_a 不為 template 、 setting 、 check 的話 那么后面的就為false True and false => false就不會進入這個語句塊 就不會exit 再配合后面的
#!php
else if($_a=="create"){?
$link = mysql_connect($db_host,$db_user,$db_pwd);
剛好有個其他的 如果$_a 為 create 那么就不會退出這個腳本
剛好這個create 能達到Getshell的效果
例子: WooYun: 建站之星Sitestar前臺Getshell一枚
剩下的還有hdwiki之前也有一個基本差不多這樣的例子
#!php
if (file_exists(HDWIKI_ROOT.'/data/install.lock') && $step != '8') {
echo "<font color='red'>{$lang['tipAlreadyInstall']}</font>";
exit();
}
如果step為8的話 那么就不會執行exit了。
#!php
case 8:
require_once HDWIKI_ROOT.'/config.php';
require_once HDWIKI_ROOT.'/lib/hddb.class.php';
require_once HDWIKI_ROOT.'/lib/util.class.php';
require_once HDWIKI_ROOT.'/lib/string.class.php';
$db = new hddb(DB_HOST, DB_USER, DB_PW, DB_NAME, DB_CHARSET);
//install
$setting=$db->result_first('select `value` from '.DB_TABLEPRE.'setting WHERE `variable` = \'site_appkey\'');
if ($setting){
echo "<span style='font-size:20px;'>百科聯盟開通成功.</span><a href='../'>進入首頁</a>";
break;
}
//update info
$data = $_GET['info'];
$data = str_replace(' ', '+', $data);
$info = base64_decode($data);
if ($info){
$obj = unserialize($info);
if(is_array($obj)){
$url2 = 'http://localhost/count2/in.php?action=update&sitedomain='.$_SERVER['SERVER_NAME'].'&info='.$data;
$data = util::hfopen($url2);
//if gbk then toutf8
if ($lang['commonCharset'] == 'GBK'){
$obj['sitenick'] = string::hiconv($obj['sitenick'], 'gbk', 'utf-8');
剛好這里step 8 又能執行一些特殊的操作。。 現在就把case 8 注釋掉了。
這里代碼我就不復制過了 免得占篇幅。
這里差不多是我比較常遇到的一些安裝文件經常遇到的問題了,突然想也想不到其他啥的了。
這里再來談一下包含
其實包含也并沒有什么好說的。
包含一般也就分為LFI RFI local file inclusion 和 remote嘛
對于LFI的話 因為很多都限制了包含的后綴結尾必須為.php Include ($a.'.php') 例如這種的
所以我們想包含我們的圖片馬兒的話 那么就需要截斷后面的這.php
1: 00截斷 需要gpc off && php<5.3.4 2: 長文件名截斷 反正這個我很少成功。 3: 轉換字符集造成的截斷 這個對包含的話基本用不上。上傳的話 就是felixk3y牛發的那個轉換字符集造成的上傳截斷那個。
還有一些cms限制包含的后綴必須為.php的時候用的是截取字符判斷是不是.php 例如下面一段簡單的代碼
#!php
$include_file=$_GET[include_file];
if ( isset( $include_file ) && strtolower( substr( $include_file, -4 ) ) == ".php" )
{
require( $include_file );
}
對傳遞過來的截取了后面4個字符 判斷是不是.php 如果是.php才進行包含。
這里可以用zip(或者phar)協議嘛(當然這個也是找laterain學的 哈哈)。
首先新建一個1.php 里面隨便寫個phpinfo把
然后壓縮成.zip 然后把zip的名字改成 yu.jpg
然后把這個.jpg上傳上去 然后包含
對于一些LFI 找不到上傳圖片的地方的話 也有很多牛發過了一些不能上傳圖片LFI的技巧 各種包含日志 環境變量啥的 這里我就也不多說了。
下面再來說RFI
如果能RFI的話 那么就是最方便的了。
包含遠程文件 或者又是php://input data啥的 各種偽協議。
但是也都知道RFI最大的限制條件就是需要allow_url_include on
且 變量前未定義路徑 或者 常量。
Allow_url_include 默認都是off
那么無論是allow_url_include on 還是 變量前無路徑 或者 常量
那都是rfi的硬傷。
這里介紹一種在allow_url_include off的情況下也能rfi的
但是成功率也并不太高。
首先在php.ini里看一下allow_url_include
; Whether to allow include/require to open URLs (like http:// or ftp://) as files.
allow_url_include = Off
翻譯一下,允許包含url 例如 http:// ftp:// 之類的協議。
當off的時候肯定就是不允許去包含這樣的協議。
這里我們先來測試一下
#!php
<?php
include($_GET[yu]);
首先 allow_url_include && allow_url_fopen 都為on的時候
成功RFI。
然后 allow_url_include 為 on allow_url_fopen 為off
直接包含遠程文件失敗 這時候我們用一下偽協議試試。
再次成功rfi。
當allow_url_include && allow_url_fopen 為off的時候。
偽協議失敗。
包含文件
URL file-access is disabled in the server configuration 不允許包含。
肯定還有不少人記得很久以前的那個星外無可執行目錄的時候
利用遠程調用cmd繼續提權
那個利用的是共享文件 然后在星外主機上來執行。
那么這里我們也試試
包含共享文件成功。 這里只本地測試了 沒具體測試。
但是由于445的原因 可能基本都失敗。
下面來說一下注入。 這里談的是mysql。 注入大概也就是把用戶可控的一些變量, 帶入到了數據庫的各種操作當中且沒有做好很好的過濾。 比如注冊用戶的時候檢測用戶名是否存在的時候,把用戶提交的用戶名拿到數據庫中去查詢。 查詢是否存在這個用戶名, 如果這里對用戶名沒有做好過濾的話 那么用戶就可以提交一些特殊字符來注入了。
現在注入的主要原因是 很多程序員在寫sql語句的時候 還是搞的語句拼接。
一些用了預編譯或者是在查詢的函數中再來過濾 很多時候就給跪了。
select update insert delete
因為mysql query 并不能執行多行語句, 除非pdo啥的能多行 所以不能像mssql那樣 還能在select后執行個update管理的語句。
對于這四種類型的注入一般的語句的構造也不同。
如果有mysql error的話
那么這四種就都能用報錯注入 這種是比較方便的
如果沒mysql error的話
Select 的注入 一般是用union select 如果把對數據庫中的查詢結果展示出來的話那么就能直接出數據了。 如果無回顯的話 那么當然就是盲注了。
Update的注入 如果是在update set的位置的話 那么我們可以找找這個表的哪個column會被展示出來 例如如果一個update的注入點是在用戶表且是在set位置可控的話 那么我們可以update email這個column 然后去用戶資料看一下自己的email就出數據了 語句例如 update table set email=(select user()) 如果是在where后的話 那么一般也就是盲注了。
Insert 的注入 也是一般是通過找哪個column會不會顯示出來 盡量把要出的數據插入到這個column里面去。 如果沒顯示的話 也是盲注。
Delete的注入 一般都是盲注了。
數字型注入主要就是因為他的變量并沒有用單引號引住。
但是基本上都是被強制類型轉換了 intval啥的。
但是有時候會有遺漏的嘛。
而字符型和搜索型的 都是會有單引號引住的。
所以需要閉合單引號再來進行注入。
說到單引號不得不說個php.ini里的配置
Magic_quotes_gpc 在稍微高點的版本默認都是on
但是卻在應該是5.4就已經廢除了。
從字面意思上來看 就是對GPC QUOTE嘛
GPC 對應的就是GET POST COOKIE
會被轉義的字符為 ' “ \ NULL 會在前面添加上一個轉義符。
導致了失去本來的意義 無法閉合單引號進行注入。
(1) 全局沒有做addslashes的
像這種全局沒有對GET POST COOKIE 做addslashes的 這種廠商基本是會在查詢的時候 再對一些用戶可控的變量進行addslashes 甚至是不進行addslashes 直接帶入查詢的。
這樣的就算在查詢的時候進行addslashes 在很多時候也都能找到幾處遺漏了addslashes的。 這種的比較簡單 不多說。
(2) 全局做addslashes
現在稍微好一點的廠商都知道了在全局文件中對 GET POST COOKIE 做addslashes (甚至是在帶入查詢的函數中再做了轉義或者預編譯 這種給跪) 所以基本不用擔心哪里遺漏了哪里忘記了addslashes) 這種的基本是首先先get magic quotes gpc 判斷gpc是否開啟 如果沒開啟的話 再調用addslashes來轉義 。 如果開啟的話 就不用來addslashes了。 沒開啟就addslashes.
這里主要講的就是這種類型的注入的一些常見的
這個是一個老生常談的問題, 從一開始的數據庫字符集GBK的寬字節注入 到現在也有很久了。
但是并不是字符集為GBK的就能寬字節注入。
總有一些小伙伴說咋我看的cms 字符集是gbk的 但是咋不能寬字節呢?
這是因為數據庫的連接方式不同
Set names gbk 這樣的就能寬字節
但是現在這樣的基本都看不到了。 因為基本都是設置了二進制讀取了。
Binary。
這樣的寬字節基本沒了, 卻有了另外一種。
因為轉換字符集造成的寬字節注入
從utf8轉到gbk 或者從gbk轉到 utf8啥的。
錦 從UTF8 轉成 GBK之后成了 %e5%5c?74cms對GET POST COOKIE …… 都做了addslashes?所以' 轉義后為\'?->%5C %e5%5c%5c' 兩個\ 則單引號出來
例子2: WooYun: qibocms 下載系統SQL注入一枚(官網可重現)
因為在全局文件中addslashes
如果我們能找到一些解碼的 例如urldecode base64_decode的
那么我們先提交encode之后的 那么就能不被轉義了。
然后decode后 再帶入查詢 造成了注入 無視gpc。
這種的很常見。
例子很多 隨便找一個
例子: WooYun: qibocms B2b 注入一枚 //qibocms 注入
例子: WooYun: phpdisk V7 sql注入2 //phpdisk 注入
常見的變量覆蓋 有啥extract 和 parse_str 函數啥的
當然還有$$
變量覆蓋得結合一些具體的場景了。
例如extract($_POST)啥的 直接從POST數組中取出變量
這樣的還是遇到過幾個 然后覆蓋掉之前的一些變量。
覆蓋的話 一般是覆蓋掉表前綴之類的
Select * from $pre_admin where xxx 像這種的就覆蓋掉$pre
然后直接補全語句然后注入。
例子: WooYun: qibocms分類注入一枚可提升自己為管理
例子2: WooYun: phpmps 注入一枚
當然 $$ 也挺經常用到的 這個例子很不錯。
例子3: WooYun: MetInfo最新版(5.2.4)一處SQL盲注漏洞
一些cms中 總有一些逗比過濾函數
會把’ 啥的 replace 成空
但是他似乎忘記了自己全局有轉義?
用戶提交一個' 全局轉義成\' 然后這過濾函數又會把 ' replace 成空
那么就留下了\ 導致可以吃掉一個單引號 是double query的話
Select * from c_admin where username=’admin\’ and email=’inject#’
這樣就可以注入了。
話說之前還遇到過一個廠商。。 之前提交了漏洞 是因為他會把
' " 都會替換成空 然后提交之后 他就去掉了' 就是不把' 替換成空了。
但是他似乎忘記了 " 也會被轉義。。 那么提交一個 " 就又剩下了一個轉義符。
例子: WooYun: PHPCMS全版本通殺SQL注入漏洞
當然還有一些replace 是用戶可控的。就是說用戶可以想把啥提交成空就提交成空
例如很久前的cmseasy 和 ecshop的那個注入
例如這段代碼
#!php
$order_sn = str_replace($_GET['subject'],'',$_GET['out_trade_no']);
這里因為會被轉義 如果提交 ' 就成 \' 這里可以看到
這里清成空的 是我們get來的 那我們就想辦法把\ replace掉
但是如果我們GET提交把\ replace 那么會被轉義 就是replace掉\
但是我們只是 \' 所以不能把\去掉 如果我有\ 還要你清空個毛啊。
這里我們來理清一下思路。
Addslashes 會對' " \ NULL 轉義
' => \'
" => \"
\ => \\
NULL => \0
那這里我們就提交 %00’ 就會被轉義生成 \0\' 這時候我們再提交把0替換成空 那么就成了\' 單引號也就成功出來了。
例子: WooYun: cmseasy繞過補丁SQL注入一枚
因為在很多cms中 基本上都只是對GET POST COOKIE 進行addslashes
而沒有對SERVER進行轉義。
而一些SERVER的變量也是用戶可以控制的。
例如啥 QUERY_STRING X_FORWARDED_FOR CLIENT_IP HTTP_HOST ACCEPT_LANGUAGE 很多。
這里最常見的當然也就是X_FORWARDED_FOR
這個一般是在ip函數中用到 如果后面沒有進行驗證ip是否合法的話就直接return 這個大部分時候都會導致注入。
例子1: WooYun: Phpyun注入漏洞二
這里說到驗證ip 這里基本都是用的正則來驗證是否合法。
而一些廠商連正則都寫錯。
例如在cmseasy中的驗證ip的正則中(%.+)
導致了后面可以寫任意字符。
例子2: WooYun: CmsEasy最新版本無限制SQL注射
最近自己在看douphp 里面的驗證ip的正則自己也發現了一點小問題。
不過也就只是小問題而已。
Douphp中的獲取ip的函數。
#!php
function get_ip() {
static $ip;
if (isset($_SERVER)) {
if (isset($_SERVER["HTTP_X_FORWARDED_FOR"])) {
$ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
} else if (isset($_SERVER["HTTP_CLIENT_IP"])) {
$ip = $_SERVER["HTTP_CLIENT_IP"];
} else {
$ip = $_SERVER["REMOTE_ADDR"];
}
} else {
if (getenv("HTTP_X_FORWARDED_FOR")) {
$ip = getenv("HTTP_X_FORWARDED_FOR");
} else if (getenv("HTTP_CLIENT_IP")) {
$ip = getenv("HTTP_CLIENT_IP");
} else {
$ip = getenv("REMOTE_ADDR");
}
}
if (preg_match('/^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/', $ip)) {
return $ip;
} else {
return '127.0.0.1';
}
}
}
來看看驗證ip是否合法的正則
#!php
preg_match('/^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/', $ip)
這里我們仔細來看看 他這里是準備匹配小數點 但是他直接寫成了.
都知道在正則中.表示的是匹配任意字符 除開換行符意外 但是在開啟/s 修正符以后 換行符也會匹配。
不過他這個.后面沒啥+或者?的 導致也就只能寫一個字符。
他這里直接寫成了. 那在這里我們就能引入單引號了。不過也就一個字符。
這里的正確寫法應該是.
也差不多 也是因為全局只對COOKIE GET POST 轉義 遺漏了FILES 且不受gpc。
FILES 注入一般是因為上傳 會把上傳的名字帶到insert當中入庫。
然后這里文件的名字是我們可以控制的 所以導致了注入。
而這里的上傳的名字是我們可以控制的。
例子: WooYun: qibocms 黃頁系統SQL注入一枚
還有一些 在入庫的時候才對文件的名字進行了轉義 而在獲取后綴后 在入庫的時候對文件名轉義了卻沒有對后綴轉義也導致了注入
例子: WooYun: Supesite 前臺注入 #2 (Insert)
很久以前php<4.20的時候 為了方便 register_globals 默認都是on。
而到了后面 register_globals 的弊端也顯現了出來, 所以也在很久以前默認都是off了。
而到了現在, 很多cms 卻喜歡模仿register_globals 搞起了偽全局機制。
例如啥qibocms metinfo destoon 啥的啊。
這樣是方便了不少, 但是如果哪里遺漏了初始化 那么就會導致注入了。
感覺這種的挺好玩的 多找了幾個例子。
例子: WooYun: qibocms地方門戶系統注入一個問題(demo測試)
例子: WooYun: qibocms地方門戶系統注入(多處類似,demo測試)
例子: WooYun: 齊博地方門戶系統SQL注入漏洞(無需登錄可批量)
因為在對全局轉義的時候
很多cms 都只是判斷gpc 是否開啟
如果off 就對數組中的value就行addslashes
卻忘記了對數組中的key進行轉義。
那么這樣也導致了一個問題。 也就是在Gpc off的時候那么數組的key沒有被過濾 導致可以引入單引號。(聽說低版本的php對二維數組中的key就算gpc on 也不會轉義)
如果哪里把數組中的key 讀取出來 然后把key帶入到了查詢當中
那么也會造成安全問題。
而且這樣的例子很多。 簡直慘不忍睹。 例子: WooYun: qibocms V7 整站系統最新版SQL注入一枚 & 另外一處能引入轉義符的地方。 //數組key的注入例子: WooYun: qibocms多個系統繞過補丁繼續注入2
例子: WooYun: qibocms全部開源系統 Getshell
例子: WooYun: Discuz 5.x 6.x 7.x 前臺SQL注入漏洞一枚
這種算是比較常見的一種注入的。
代碼大概如
#!php
<?php
$key=0;
$a=$_GET[a][$key];
$b=$_GET[b];
Mysql_query("select * from table where xxx='$a' and xx='$b'")
如果這里$_GET[a] 提交的是一個數組 且含有一個key為0的那么$a就是對應的這個key的value
但是這里并沒有強制要求為數組。
那么我們提交一個字符串 那么后面的[0] 那么就是截取的第一個字符
在全局中 單引號被轉義為\' 截取第一個字符就為了\
吃掉一個單引號 然后就在$b處寫入inject可以注入了。
例子: WooYun: qibocms 地方門戶系統 注入#4(demo測試)
還有map發的那Disucz 7.2的那注入也一樣。
很常見的一種洞。
比較常見的uc 和 alipay tenpay chinabank 啥的
特別是uc 因為默認uc里面都會striplashes
Uc的話 一般會遇到的問題是uckey默認的。
或者是uckey這個常量根本就沒有初始化。
導致了uckey可控 再導致了Getshell 或者 注入啥的。
還有tenpay 和 alipay 啥的 一些是因為忘記把過濾的文件包含進來
且key默認是空的 導致可以通過驗證。
例子: WooYun: phpmps 注入 (可修改其他用戶密碼,官網成功) // phpmps uc致注入
例子: WooYun: PHPEMS (在線考試系統) 設計缺陷 Getshell一枚(官網已shell) /phpems uc致getshell
例子: WooYun: 最土團購注入一枚可直接提升自己為管理 & 無限刷錢。 //最土團購 chinabank致注入
例子: WooYun: Destoon Sql注入漏洞2(有條件) //destoon tenpay致注入
例子: WooYun: CSDJCMS程式舞曲最新版Sql 一枚 //csdj tenpay致注入
其實也不只是數字型 只是說一些忘記加單引號的地方都這樣。
只是一般數字型的都不會加單引號的。
一般的是
#!php
$id=$_GET[id];
Select * from table where id=$id;
$id 沒被單引號 且 沒有被強制類型轉換 那么就算addslashes了 由于不需要去閉合單引號 所以也無影響。
例子: WooYun: qibocms 地方門戶系統 注入#3 (demo測試)
并不是一些數字型 一些其他的點也有些忘記加單引號 導致了注入。 例子: WooYun: Supesite 前臺注入 #3 (Delete)
這里supesite的注入還涉及到了一個設計缺陷。?這里把
#!php
$query = $_SGLOBAL['db']->query('SELECT * FROM '.tname('spacetags').' WHERE itemid=\''.$itemid.'\' AND status=\''.$status.'\'') ?
$itemid 首先帶入到了查詢當中 是被單引號了的。。 如果查詢出來的有結果 才會帶入到delete中 如果無結果 就不執行delete的語句了。?而在數據庫中itemid中 存儲的是int類型 所以他這里本意是想要用戶只能提交數字型才能查詢出結果。 如果不是提交的數字的話 那么就查詢不出來結果 就不去執行下面的delete語句了。?但是由于mysql的類型轉換 因為他這里儲存的是int類型? ?所以我們提交4xxxxx 跟我們提交4 是一樣的
#!php
$_SGLOBAL['db']->query('DELETE FROM '.tname('spacetags').' WHERE itemid='.$itemid.' AND tagid IN ('.simplode($deletetagidarr).') AND status=\''.$status.'\'');
然后就執行這個delete語句 然后沒單引號 造成了注入。
例子: WooYun: phpyun v3.2 (20141226) 兩處注入。
這個phpyun的注入 主要是因為php是弱類型語言
一些廠商喜歡這樣寫
#!php
If ($a>1){
Mysql_query(select id from table where id=$a)
}
他這個本來是想用戶提交數字才能通過這個判斷 但是由于弱語言 1+asd啥的 都能通過 所以又導致了注入。
也是一種比較常見的注入。 涉及到的是入庫和出庫。 因為有全局轉義 然后入庫的時候
#!php
Insert into table (username) values ('a\'');
這樣入庫后 轉義符就會消失 那么就是a' 如果哪里再把這個查詢出來 那么也就是出庫的是a' 如果再把出庫的 再帶入到了查詢啥的 那么就再次成功的引入了單引號導致了注入
例子: WooYun: phpyun v3.2 (20141226) 兩處注入。 例子: WooYun: qibocms 地方門戶系統 二次注入#5(demo測試) 例子: WooYun: 74cms (20140709) 二枚二次注入 例子: WooYun: Hdwiki最新版二次注入一枚
比較是硬傷的是 很多時候數據庫中存儲的長度是有限制的。 所以一些也不是太好利用。
不知道也應不應該把這個歸為一類。
大概是因為一些查詢的時候 直接把$_POST啥的 直接帶入到了查詢函數當中
例如cmseasy的rec_insert的查詢函數中。
然后foreach key 出來 然后foreach 出來的key 做了查詢中的column
這種的防止方法一般是 把數據庫中的column查詢出來 然后in_array 判斷一下$_POST出來的key 是否在數據庫中的column中 下面兩個例子就是這樣修復的。
例子: WooYun: 云人才系統SQL注入,繞過WAF 例子: WooYun: Cmseasy SQL注射漏洞之三
有些cms 在全局addslashes后 然后在后面的文件中又stripslashes
去掉了轉義符 然后又可以閉合單引號了。
#!php
$_SESSION['flow_consignee'] = stripslashes_deep($consignee);
例子: http://www.2cto.com/Article/201301/182509.html //之前的ecshop注入 。
有些cms 有的時候會限制用戶輸入的長度
所以只截取一部分
例如uchome的cutstr($asd,32);
這樣只允許輸入32個字符 而且uchome里面的這個也沒有像dz那樣截取字符的后面加...
那么如果我們提交一個1111111111111111111111111111111’
被轉義后成1111111111111111111111111111111\’
然后截取32個字符 就是1111111111111111111111111111111\
如果又是double query的話 吃掉一個單引號 然后下一個連著的可控變量又可以注入了。
結果在uchome中找到了個能引入轉義符的 結果只有一個可控的。
例子: WooYun: Hdwiki (20141205) 存在7處SQL注入漏洞(含之前處理不當安全的漏洞) //里面的0x06
不知道放哪。這個也放到注入板塊來把。。
其實就是這次的DZ6.X 7.X 那個任意代碼執行的漏洞
#!php
if (isset($_REQUEST['GLOBALS']) OR isset($_FILES['GLOBALS'])) {
exit('Request tainting attempted.');
}
foreach(array('_COOKIE', '_POST', '_GET') as $_request) {
foreach($$_request as $_key => $_value) {
$_key{0} != '_' && $$_key = daddslashes($_value);
}
}
主要關鍵代碼就上面這兩段。 這里把GET POST COOKIE 循環出來 然后注冊一個變量 但是 這里不允許創建GLOBALS變量 然后DZ7.X 就是用這樣處理的 如果設置了REQUEST 的 GLOBALS
就直接退出
這段代碼在很久以前確實是沒什么問題
因為那時候的request order 還是gpc
但是在php 5.3 以后 request order 默認成了gp
也就是成了get 和 Post 不包含cookie了。
所以 $_REQUEST里面就不包含COOKIE提交來的了。
而且這后面也把COOKIE循環出來 注冊變量
所以這里我們在COOKIE里面提交GLOBALS 就不會被檢測出來了。
而且也成功注冊了GLOBALS變量。
所以在結合后面的一些些代碼就造成了代碼執行。
例子: WooYun: Discuz!某兩個版本前臺產品命令執行(無需登錄)
以上就差不多是我經常所遇到的注入問題 好像暫時也想不到其他什么的了
下面介紹一些我在cms遇到的找回密碼時候犯得錯誤。
找回密碼很多都是驗證的token 就是在找回密碼的時候生成一個token 然后存儲到數據庫中。 然后把找回密碼的地址發到郵箱中 url中就含有token 由用戶點開后就能修改密碼 基本就是驗證的這個token。 其實一般的可以找回任意用戶密碼的原因就是弱token 導致可以被攻擊者搞到。 包括很多廠商驗證的時候就是四位純數字啥的。 可以枚舉。 當然也可以延伸一下, 一些cms的密碼加密方式很難破掉。 有時候我們拿到了管理的密碼破不掉也是雞肋。 所以有時候也可以利用這種方法 一般找回密碼是用的郵箱 首先我們可以注入把管理的郵箱注入出來 然后再去找回密碼 再把數據庫的token注入出來 再構造一下地址 就能重置密碼。 這個給我印象比較深的是 在ssctf的比賽中嘛 當時機油問了問我 那wordpress那題 有個插件的注入 然后因為都知道wp的加密基本很難破。 所以也是用的這種方法。 因為一般都是弱token的問題 隨便找幾個例子了
#!php
$resetpwd = md5(rand());
可以看到這個生成的token 就是對rand()函數生成出來的數字進行md5一次
來看一下rand()
注釋:在某些平臺下(例如 Windows)RAND_MAX 只有 32768。如果需要的范圍大于 32768,那么指定 min 和 max 參數就可以生成大于 RAND_MAX 的數了,或者考慮用 mt_rand() 來替代它。 如果不指定一些參數的話 那么最大值才32768 一個并不算大的值 那么我們首先對這32768種可能 md5出來一個列表 然后我們直接枚舉這32768種可能 總會有一個對的。
例子: WooYun: Thinksaas找回密碼處設計錯誤利用賬戶可找回密碼。
#!php
$encryptstring=md5($this->time.$verification.$auth);
補丁后 多了一個$auth??$timetemp=date("Y-m-d H:i:s",$this->time);??$auth = util::strcode($timetemp, 'ENCODE');? 可以$auch 是對時間來了一個算法。 結果這個算法的KEY并沒有初始化 導致了如果我們知道了這個時間 就可以自己生成出來加密的字符串 這里帶入算法的是時間 這里是我們可以知道的。
例子: WooYun: Hdwiki設計缺陷知郵箱可改密碼(包括管理員) //繞過補丁繼續找回hdwiki任意用戶密碼
這個上傳就大概說說。
一般的上傳漏洞可能是未驗證上傳后綴 或者是驗證上傳后綴被bypass 或者是上傳的文件驗證了上傳后綴但是文件名不重命名。
對于那些驗證了后綴但是文件名不重命名的
一般可以試試截斷yu.php%00.jpg 當然%00 要urldecode
當然 畢竟截斷雞肋了。 上面提到過限制條件了。
還可以是結合各種webserver的解析漏洞
例如iis6的 xx.asp/yu.jpg yu.php;.jpg yu.asp;.jpg aspx 當然不能這樣解析了。
如果不重命名的就上傳這樣就行了。
Nginx的低版本解析漏洞: yu.jpg/1.php 對于這種直接上傳一個xxxx.jpg 再在這后面加上各種/.php 試試的
Apache解析漏洞 yu.php.xxx 在最后一個后綴識別不出來的時候 那么就向上解析
最終解析成.php
像phpweb后臺那個上傳漏洞。很多人遇到apache的時候
無法截斷的時候就上傳一個yu.php.jpg 有些人比較疑問的是為啥有時候成功有時候失敗。
這個主要是看os 像windows的話 .jpg 就直接是圖片了
所以在windows下 就直接識別成圖片了 而不是.php
而在linux下 .jpg不被識別 就向上識別成.php
這些解析漏洞在上傳中也挺經常遇到的。
上傳的驗證一般是 MIME、客戶端的JS驗證、白名單、黑名單。
前面兩種都比較簡單。
白名單就是允許用戶上傳哪些后綴的。 黑名單就是禁止用戶上傳哪些后綴的。
這兩種相比來說一般是黑名單容易bypass一點。 黑名單的繞過還是得具體看他黑名單的代碼。 有的直接大小寫就過。 有些沒對文件名trim的 直接在文件名后面加空格。 Windows下的 文件名后%81-%99 decode后的 或者是windows下的特性 .php::$data 這樣上傳上去依舊是.php
其實上傳還挺重要的。。 但是我又不知道說哪些。 還是具體看代碼把。
這個主要是涉及到的是 任意文件刪除 任意文件復制 任意文件重命名 任意文件移動 任意文件下載……。 因為像現在的cms很多都自帶得有加密 解密 函數 例如qibocms的mymd5 Dz的authcode 啥的。 對于這些任意文件操作的 首先可以試試拿到配置文件中的數據庫的連接帳號和密碼 嘗試外聯一下 但是很多時候都是只允許本地連的 很多時候不好利用的時候可以利用拿到配置文件 然后拿到這些函數的key 然后自己生成一個加密的字符串 然后再結合具體的代碼進行最大化的利用。
一般是挺不好利用的,還是結合具體的場景,有些因為全局的過濾而不能注入的,可以嘗試用任意文件刪除,刪掉這個文件,再進行注入 一般的利用還是通過刪除安裝文件生成的lock文件,然后達到重裝。
不過這樣弊很大。
例子: WooYun: phpyun (20141230) 任意文件刪除致注入可改任意用戶密碼(4處打包)
復制的話 肯定涉及到了 要復制的文件 要復制到的路徑。
如果是要復制的文件可控 要復制到的路徑不可控的話 例如qibocms之前的一個洞
#!php
copy(ROOT_PATH."$webdb[updir]/$value",ROOT_PATH."$webdb[updir]/{$value}.jpg");?
這里$value 是可控的 但是又不能截斷 復制到的路徑限制了.jpg結尾。
這時候我們就可以把$value控制為 保存了qibocms的加密函數的key的配置文件
然后復制后 成了一個.jpg 那我們就可以直接打開 看到key了
例子: WooYun: Qibocms圖片系統任意文件查看導致的多處注入(可提升自己為管理員)
如果兩個都完全可控的話 那肯定是直接把自己的圖片復制成一個.php馬兒了。
其實跟上面復制差不多, 很多時候也是通過下載配置文件 拿到key。 再進行各種操作。。
例子: WooYun: qibocmsV7整站系統任意文件下載導致無限制注入多處(可提升自己為管理 Demo演示)
這個例子還涉及到了一個win的特性bypass 黑名單
這種問題主要是想進各種辦法把這些加密函數的key拿到 或者想辦法加密一些特殊字符然后拿到加密的字符串
加密函數肯定就涉及到了各種算法。
算法問題一般主要是因為一些弱算法 導致了 知道明文 知道密文 可逆
拿到加密函數中的key 從而再自己生成一個自己想要的加密字符串。
再結合具體的點 然后進行具體的利用。
例子: WooYun: DedeCMS-V5.7-SP1(2014-07-25)sql注入+新繞過思路 例子: WooYun: phpcms最新版繞過全局防御暴力注入(官網演示)
還有的一類算是 一個點 要加密的是我們可控的 而且密文會輸出 而且這個可控的點能引入特殊字符 那么我們就把一些特殊字符帶入到這里面 然后拿到密文 再找到一處decode后會進行特殊操作的點 然后進行各種操作。
例子: WooYun: 程氏舞曲CMS某泄露,導致sql注入
例子: WooYun: PHPCMS最新版(V9)SQL注入一枚
例子: WooYun: 一個PHPWIND可拿shell的高危漏洞
例子: WooYun: PHPCMS V9 一個為所欲為的漏洞
寥寥草草的把這篇文章寫完了。
比自己預期想的少寫了很多, 因為在一開始寫的時候還是挺有感覺的。
因為讀書一個月也才放一次假, 都是抽時間在慢慢寫著。
后面差不多寫了1W字的時候,存稿竟然丟了, 弄了半天也沒找回
就感覺不想寫了, 后面又翻了翻 找到了一篇自己之前保存的寫了差不多兩三千字的
然后就再慢慢的開始寫了, 也就草草的結束了。
當然這里只是總結了一些常見的類型, 肯定在實戰中會遇到各種各樣的情況 各種過濾啥的。
各種邏輯錯誤需要自己慢慢去體會了。