强网杯-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}