[SECCON Beginners CTF 2020] sneaky

Rumor has it that there's a hidden easter egg which can be activated by getting high score in this game......

解説

static link で stripped な最高にタチの悪いバイナリです
Ghidraをお使いのかたはpwn問で配っているlibcとCorrelatorで突き合わせるとだいぶマシになるのでお試しあれ

渡されたバイナリを実行するとスネークゲームが始まります
このゲームで高得点を取ればflagがもらえるようです

とりあえずデバッガでスコアを書き変えようとしてみますが何故かうまく実行されません
どうやらアンチデバッガが動いているようです

コード規模的にアンチデバッガを探して止めるより、初期スコアを書き変えたほうが速そうなので探します
ゲームのメイン処理を行っているあたりは以下のようになっています

undefined8 fcn.00401300(int64_t arg1)
{
    int64_t **ppiVar1;
    int16_t iVar2;
    int32_t iVar3;
    int64_t **arg1_00;
    int64_t **ppiVar4;
    int64_t iVar5;
    int64_t **ppiVar6;
    int64_t arg3;
    uint64_t arg3_00;
    int64_t **ppiVar7;
    int64_t *piVar8;
    int64_t arg5;
    int64_t in_R9;
    bool bVar9;
    int64_t arg7;
    undefined8 in_XMM1_Qa;
    undefined8 in_XMM2_Qa;
    undefined8 in_XMM3_Qa;
    undefined8 in_XMM4_Qa;
    undefined8 in_XMM5_Qa;
    undefined8 in_XMM6_Qa;
    undefined8 in_XMM7_Qa;

    arg5 = 0;
    iVar5 = 1;
    arg1_00 = (int64_t **)fcn.00407ee0((int64_t)*(int64_t ***)0x707088, 0x16, 0x16, 1, 0);
    fcn.00402b60((int64_t)arg1_00, 0x400);
    do {
        while( true ) {
            fcn.00402ee0((int64_t)arg1_00);
            fcn.0040afc0((int64_t)*(int64_t ***)0x707088, 0x400);
            arg7 = fcn.00405970((int64_t)*(int64_t ***)0x707088, 0, 0);
            fcn.00407f80(arg7, in_XMM1_Qa, in_XMM2_Qa, in_XMM3_Qa, in_XMM4_Qa, in_XMM5_Qa, in_XMM6_Qa, in_XMM7_Qa, 
                         (int64_t)"SCORE: %d", *(int64_t *)(arg1 + 0x20), arg3, iVar5, arg5, in_R9);
            in_R9 = 0;
            arg5 = 0;
            iVar5 = 0;
            fcn.00402ce0((int64_t)arg1_00, 0, 0, 0, 0, 0);
            fcn.00400f90(arg1);
            fcn.00401140(arg1);
            iVar3 = fcn.004011f0(arg1);
            if (iVar3 != 0) goto code_r0x00401430;
            fcn.004084c0((uint32_t)arg1_00);
            fcn.00472b70((uint64_t)*(uint32_t *)(arg1 + 0x18));
            iVar2 = fcn.004053c0((int64_t)*(int64_t ***)0x707088);
            if (iVar2 != 0x103) break;
            *(undefined4 *)(arg1 + 0x1c) = 3;
code_r0x004013f9:
            fcn.00413300();
        }
        if (0x103 < iVar2) {
            if (iVar2 == 0x104) {
                *(undefined4 *)(arg1 + 0x1c) = 2;
            } else {
                if (iVar2 != 0x105) goto code_r0x00401420;
                *(undefined4 *)(arg1 + 0x1c) = 1;
            }
            goto code_r0x004013f9;
        }
        if (iVar2 == 0x102) {
            *(undefined4 *)(arg1 + 0x1c) = 4;
            goto code_r0x004013f9;
        }
code_r0x00401420:
        fcn.00413300();
    } while (iVar2 != 0x1b);
code_r0x00401430:
    if ((arg1_00 == (int64_t **)0x0) || (*(int64_t ***)0x707038 == (int64_t **)0x0)) {
        return 0xffffffff;
    }
    bVar9 = true;
    ppiVar4 = *(int64_t ***)0x707038;
    do {
        while (arg1_00 == ppiVar4 + 2) {
            ppiVar4 = (int64_t **)*ppiVar4;
            bVar9 = false;
            if (ppiVar4 == (int64_t **)0x0) goto code_r0x00404347;
        }
        if (((*(uint8_t *)((int64_t)ppiVar4 + 0x1c) & 1) != 0) && (arg1_00 == (int64_t **)ppiVar4[0xb])) {
            return 0xffffffff;
        }
        ppiVar4 = (int64_t **)*ppiVar4;
    } while (ppiVar4 != (int64_t **)0x0);
code_r0x00404347:
    if (bVar9) {
        return 0xffffffff;
    }
    if ((*(uint8_t *)((int64_t)arg1_00 + 0xc) & 1) == 0) {
        piVar8 = *(int64_t **)(*(int64_t *)0x707068 + 0x80);
        if (piVar8 != (int64_t *)0x0) {
code_r0x00404368:
            arg3_00 = (uint64_t)((int32_t)*(int16_t *)((int64_t)piVar8 + 4) + 1);
            goto code_r0x0040436f;
        }
    } else {
        piVar8 = arg1_00[9];
        arg3_00 = 0xffffffff;
        if (piVar8 != (int64_t *)0x0) goto code_r0x00404368;
code_r0x0040436f:
        fcn.00409d30((int64_t)piVar8, 0, arg3_00);
    }
    if (arg1_00 == (int64_t **)0x0) {
        return 0xffffffff;
    }
    if (*(int64_t ***)0x707038 == (int64_t **)0x0) {
        return 0xffffffff;
    }
    ppiVar4 = (int64_t **)**(int64_t ***)0x707038;
    ppiVar1 = *(int64_t ***)0x707038;
    iVar5 = *(int64_t *)0x707070;
    if (arg1_00 == *(int64_t ***)0x707038 + 2) {
        ppiVar6 = (int64_t **)0x0;
        ppiVar7 = *(int64_t ***)0x707038;
        if (*(int64_t *)0x707070 == 0) goto code_r0x0040798c;
code_r0x00407956:
        do {
            if (arg1_00 == *(int64_t ***)(iVar5 + 0x80)) {
                bVar9 = arg1_00 == *(int64_t ***)0x707080;
                *(undefined8 *)(iVar5 + 0x80) = 0;
                if (bVar9) {
                    *(int64_t ***)0x707080 = (int64_t **)0x0;
                }
                break;
            }
            if (arg1_00 == *(int64_t ***)(iVar5 + 0x90)) {
                bVar9 = arg1_00 == *(int64_t ***)0x707088;
                *(undefined8 *)(iVar5 + 0x90) = 0;
                if (bVar9) {
                    *(int64_t ***)0x707088 = (int64_t **)0x0;
                }
                break;
            }
            if (arg1_00 == *(int64_t ***)(iVar5 + 0x88)) {
                bVar9 = arg1_00 == *(int64_t ***)0x707078;
                *(undefined8 *)(iVar5 + 0x88) = 0;
                if (bVar9) {
                    *(int64_t ***)0x707078 = (int64_t **)0x0;
                }
                break;
            }
            piVar8 = (int64_t *)(iVar5 + 0x590);
            iVar5 = *piVar8;
        } while (*piVar8 != 0);
        if (ppiVar6 == (int64_t **)0x0) goto code_r0x0040798c;
    } else {
        do {
            ppiVar7 = ppiVar4;
            ppiVar6 = ppiVar1;
            if (ppiVar7 == (int64_t **)0x0) {
                return 0xffffffff;
            }
            ppiVar4 = (int64_t **)*ppiVar7;
            ppiVar1 = ppiVar7;
        } while (ppiVar7 + 2 != arg1_00);
        if (*(int64_t *)0x707070 != 0) goto code_r0x00407956;
    }
    *ppiVar6 = (int64_t *)ppiVar4;
    ppiVar4 = *(int64_t ***)0x707038;
code_r0x0040798c:
    *(int64_t ***)0x707038 = ppiVar4;
    piVar8 = arg1_00[6];
    if (((*(uint8_t *)((int64_t)arg1_00 + 0xc) & 1) == 0) && (-1 < *(int16_t *)((int64_t)arg1_00 + 4))) {
        iVar5 = 0;
        iVar3 = 0;
        do {
            if (*(int64_t *)((int64_t)piVar8 + iVar5) != 0) {
                fcn.0043ef50(*(int64_t *)((int64_t)piVar8 + iVar5));
                piVar8 = arg1_00[6];
            }
            iVar3 = iVar3 + 1;
            iVar5 = iVar5 + 0x10;
        } while (iVar3 <= *(int16_t *)((int64_t)arg1_00 + 4));
    }
    fcn.0043ef50();
    fcn.0043ef50(ppiVar7);
    return 0;
}

SCORE: %d%dには*(int64_t *)(arg1 + 0x20)が渡されています
以前の記述でarg1を操作してる気配もないので呼び出し元の処理のmain()を追います

// WARNING: [r2ghidra] Var arg_0h is stack pointer based, which is not supported for decompilation.

undefined8 main(void)
{
    undefined8 *arg1;
    undefined8 *puVar1;

    arg1 = (undefined8 *)fcn.004414a0(0x28, 1);
    puVar1 = (undefined8 *)fcn.0043e690(0x10);
    puVar1[1] = 0;
    *puVar1 = 0xb0000000a;
    *arg1 = puVar1;
    arg1[1] = 10;
    arg1[2] = 0;
    arg1[3] = 200000;
    arg1[4] = 0;
    fcn.00401300((int64_t)arg1);
    return 0;
}

ここのarg1[4] = 0;がScoreの初期値を設定している部分です
無事に初期スコアに0を代入しているコードを発見したので、ここを適当に100000000くらいにしてみます

パッチした実行ファイルを実行するとflagが書いてあります

ctf4b{still_ez_2_cheat?}

最初処理を深追いしすぎて沼りました
寝て起きたら解けたので気分転換は大事だなと思いました