这是萌萌我第一次出题,不是很难,还请师父们多多指教^^。
考点:PHP的字符串解析特性、正则表达式、脚本的编写
题目如下:
<?php error_reporting(0); highlight_file(__FILE__); include("config.php"); class qwq { function __wakeup() { die("Access Denied!"); } static function oao() { show_source("config.php"); } } $str = file_get_contents("php://input"); if(preg_match('/\`|\_|\.|%|\*|\~|\^|\'|\"|\;|\(|\)|\]|g|e|l|i|\//is',$str)) { die("I am sorry but you have to leave."); } else { extract($_POST); } if(isset($shaw_root)) { if(preg_match('/^\-[a-e][^a-zA-Z0-8]<b>(.*)>{4}\D*?(abc.*?)p(hp)*\@R(s|r).$/', $shaw_root)&& strlen($shaw_root)===29) { echo $hint; } else { echo "Almost there."."<br>"; } } else { echo "<br>"."Input correct parameters"."<br>"; die(); } if($ans===$SecretNumber) { echo "<br>"."Congratulations!"."<br>"; call_user_func($my_ans); }
首先看这里:
$str = file_get_contents("php://input"); if(preg_match('/\`|\_|\.|%|\*|\~|\^|\'|\"|\;|\(|\)|\]|g|e|l|i|\//is',$str)) { die("I am sorry but you have to leave."); } else { extract($_POST); }
该题会把以POST方式传入的全部数据进行检查(WAF),如果出现黑名单中的字符则退出,否则把POST的内容注册成变量。
if(isset($shaw_root)) { if(preg_match('/^\-[a-e][^a-zA-Z0-8]<b>(.*)>{4}\D*?(abc.*?)p(hp)*\@R(s|r).$/', $shaw_root)&& strlen($shaw_root)===29) { echo $hint; } else { echo "Almost there."."<br>"; }
前面并没有出现 $shaw_root 这个变量,如果要满足条件,就必须以POST的方式传入 $shaw_root ,但是下划线被拉入了WAF的黑名单中,即不允许输入下划线。这里涉及到PHP的字符串解析特性:
查询字符串在解析的过程中会将某些字符删除或用下划线代替。例如,
/?%20news[id%00=42
会转换为Array([news_id] => 42)
。
详情参考:https://blog.csdn.net/qq_45521281/article/details/105871192
POST方式传入shaw[root=123
即可满足isset($shaw_root)
的条件,输出“Almost there.”,如图所示:
接下来要满足条件判断里的正则表达式和字符串的长度,逐个分析该正则表达式:
- ^:声明字符串开头的位置。
- \-:匹配字符-,反斜杠是转义符。
- [a-e]:匹配单个字符,范围是小写字母a到小写字母e。
- [^a-zA-Z0-8]:匹配单个字符,范围是除了a-z、A-Z和0-8以外的任意字符。
- <b>:匹配一个<b>。
- (.*):匹配任意字符(行结束符除外)零到无数次。
- >{4}:精确匹配“>”四次。
- \D*?:匹配不是数字的任何字符(等同于[^ 0-9])零到无数次。
- (abc.*?):匹配字符串abc和任意一个字符(行结束符除外)。
- p(hp)*:匹配字符p和字符串hp零到多次。
- \@:匹配字符@,反斜杠是转义符。
- R:精确匹配字符R。
- (s|r):精确匹配字符s或r。
- .:匹配除了换行以外的所有字符。
除了满足上述条件外,传入的字符串的长度必须等于29,可以在某些“匹配零到无数次”的地方增减长度达到目的。例如,以POST方式传入:shaw[root=-a?<b>rrrrr>>>>RabcRphphp@Rrr
就可以满足条件,输出$hint
的值:
Here is a hint : md5("shaw".($SecretNumber)."root")==166b47a5cb1ca2431a0edfcef200684f && strlen($SecretNumber)===5
再往下看:
if($ans===$SecretNumber) { echo "<br>"."Congratulations!"."<br>"; call_user_func($my_ans); }
可以联想到通过变量覆盖来覆盖掉$SecretNumber
的值,但是字母e被过滤掉了,只能通过hint给出的提示猜测$SecretNumber
的值,看变量名可知它是数字,又已知长度为5,可以写脚本进行爆破:
import hashlib import re a = 'shaw' b = 'root' for i in range(10000, 99999): string = a + str(i) + b md5 = hashlib.md5(string.encode('utf-8')).hexdigest() if(re.findall("166b47a5cb1ca2431a0edfcef200684f", md5)): print(i)
秒出,得21475。
现在可以使用call_user_func
函数了,call_user_func
是PHP的内置函数,该函数允许用户调用直接写的函数并传入一定的参数,这里直接调用qwq类中的
oao:my[ans=qwq::oao