ACTF2023

# PREFACE:强度巨高在打,但是赛中算没出题 hhhh5555555,复盘以及复现一下

# tree

# 赛中最接近出的题

直接打开看行为可以发现它不解析 #include 头文件,会给你编译一个 cpp 代码

初步观察可以看到三个 check,全过可以从 server 返回 flag

image-20231031083726042

查找字符串,一开始以为是模仿的 gcc 编译器,但是问学长说不像(这里就缺乏经验了已经,问的时候学长就说先看是啥的编译器,自己调了很久也没有去想)字符串发现是 clang

image-20231031083617728

源码非常复杂,而且有很多 handler 的结构,导致函数调用往往是跳转表,这里一定得恢复符号(后面 check3 不恢复符号几乎没办法分析,相反能恢复符号就好做)

check1 有一个 == 25 ,这里其实很容易测试,这里会计算符号的优先级,直接从最高等级的 * / 视到低等级的比较条件运算符,等级越低数字越大,然后就测到 += 1 可以让 check1 + 1,其它会减一,所以最后的结构里面 += 1 比其它的多一即可

image-20231031083839468

其实是这个

image-20231031084453947

然后有这个函数(这里已经恢复了符号表):

image-20231031084427849

这里会捕捉一个名为 AAA 的 class

image-20231031084514268

我们要让我们的结构通过这里的 check:

image-20231031084749639

(这部分都是当时猜的,然后也没有去恢复符号表了)

image-20231031085122412

然后多虚函数继承就过了这里的 check2

下午四点就过了 check2,本以为形势一片大好,结果游戏才刚刚开始,check3 做到早上四点,没救

小折腾了一下,直接搞了个 clang15.0.4 给 bindiff 进去,但这里当时是看不到的,而且直观上会往这里面看

image-20231031085452353

总之这里一直边调边猜(现在看看这个凌晨两点的记录有些消愁了,但是确实调了几个小时没进展,各种玩意也在反复试)

image-20231031085542600

image-20231031085710771

image-20231031085647733

image-20231031085814563

晚上的时候放出 hint:需要看一下 ast_matcher 相关,然后我们一直在找已有的符号以及字符串

不料其实不是这样,应该自己搞一个 ast_matcher 的 api 调用,再 bindiff 进去,就好看了

这部分已经是赛后做的了(小问了一下出题人)

自己把 clang 安了,然后编译一个这个(这个会匹配 for(int i=1;i<2;i++){}

#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/Tooling.h"
#include "clang/Tooling/CommonOptionsParser.h"
using namespace clang;
using namespace clang::ast_matchers;
using namespace clang::tooling;
static llvm::cl::OptionCategory MyToolCategory("my-tool options");
class LoopPrinter : public MatchFinder::MatchCallback {
public:
    virtual void run(const MatchFinder::MatchResult &Result) {
        if (const ForStmt *FS = Result.Nodes.getNodeAs<clang::ForStmt>("forLoop")) {
            FS->dump();
        }
    }
};
int main(int argc, const char **argv) {
    auto ExpectedParser = CommonOptionsParser::create(argc, argv, MyToolCategory);
    if (!ExpectedParser) {
        llvm::errs() << ExpectedParser.takeError(); // Print any errors.
        return 1;
    }
    CommonOptionsParser& OptionsParser = ExpectedParser.get();
    if (argc < 2) {
        llvm::errs() << "Usage: " << argv[0] << " <C++ source file>\n";
        return 1;
    }
    auto Matcher =
        forStmt(
            hasLoopInit(declStmt(
                hasSingleDecl(varDecl(
                    hasInitializer(integerLiteral(equals(1)))
                ))
            )),
            hasCondition(binaryOperator(
                hasOperatorName("<"),
                hasLHS(ignoringParenImpCasts(declRefExpr(
                    to(varDecl(hasType(isInteger())))
                ))),
                hasRHS(integerLiteral(equals(2)))
            )),
            hasIncrement(unaryOperator(
                hasOperatorName("++"),
                hasUnaryOperand(ignoringParenImpCasts(declRefExpr(
                    to(varDecl(hasType(isInteger())))
                )))
            ))
        ).bind("forloop");
    LoopPrinter Printer;
    MatchFinder Finder;
    Finder.addMatcher(Matcher, &Printer);
    ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList());
    return Tool.run(newFrontendActionFactory(&Finder).get());
}
// clang++ -g test_tree.cpp -o loop_printer -I/usr/lib/llvm-14/include -L/usr/lib/llvm-14/lib -lclangTooling -lclangASTMatchers -lclangFrontend -lclangSerialization -lclangDriver -lclangParse -lclangSema -lclangAnalysis -lclangEdit -lclangAST -lclangLex -lclangBasic -lLLVM-14 -std=c++14 -pthread -ldl

然后 bindiff 进去,结果:

image-20231031090305967

对照自己编译出来的 loop_printer,就好搞多了

<++ 的匹配点比较明显

这里一开始一直找不到比较的数字,然后发现会写在栈上:

image-20231031090457603

前面的 ++< 比较容易识别,就是这里的数字有点难找,然后注意这里的 "1" 和 "2" 和 "f" 是一样的,只是一个符号,相当于

image-20231031090609600 没有实际意义

匹配上 check3 以后,把 check3=5 构造一下,把 check1 抵消一下,得到最后的 payload:

class d{
    public:
    int AAA(){
    }
};
class c : virtual public d{
};
class b : virtual public d{
};
namespace AAA{
    namespace AAA{
        struct AAA: virtual public b, virtual public c{
        public:
            AAA() {
            for (int a = 1 ;a < 10; a++){
                a += 1;
                a += 1;
            }
            for (int a = 2 ;a < 10; a++){
                a += 1;
            }
            for (int a = 0 ;a < 10; a++){
                a += 1;
            }
            for (int a = 0 ;a < 10; a++){
                a += 1;
            }
            for (int a = 0 ;a < 10; a++){
                a += 1;
            }
            }
        };
    }
}

image-20231031090731200

发给服务器远程也是可以通的(没截图),没问题了

这题有一个很麻烦的点,第三个 check 必须恢复符号表,否则根本看不出来它是匹配的符号还是自定义的符号名,前两个 check 中间过程比较清晰,好猜些,第三个的中间比对过程相当难看与抽象,甚至最后一个数字我还找了好一会

# Obfuse