former-seccomp (SECCON Beginners CTF 2024) WriteUp

# はじめに
この記事は [1日1CTF Advent Calendar 2024](https://qiita.com/advent-calendar/2024/1-day-1-ctf) の 6 日目の記事です。

# 問題
former-seccomp (問題出典: SECCON Beginners CTF 2024)

> フラグチェック用のシステムコールを自作してみました


リポジトリ: https://github.com/SECCON/SECCON_Beginners_CTF_2024/tree/main/reversing/former-seccomp

## 考察

ghidra でデコンパイルして、本質を頑張って探すと、この関数っぽい。
```c

undefined8 FUN_00101737(char *param_1)

{
  size_t sVar1;
  undefined8 uVar2;
  long lVar3;
  ulong local_28;
  long local_20;
  
  sVar1 = strlen(param_1);
  if (sVar1 == 0x1a) {
    for (local_28 = 0; sVar1 = strlen(&DAT_00104030), local_28 < sVar1; local_28 = local_28 + 1) {
      (&DAT_00104030)[local_28] = (char)local_28 + 0x20U ^ (&DAT_00104030)[local_28];
    }
    lVar3 = FUN_00101497(&DAT_00104010,&DAT_00104030);
    for (local_20 = 0; (param_1[local_20] != '\0' && (*(char *)(local_20 + lVar3) != '\0'));
        local_20 = local_20 + 1) {
      if (param_1[local_20] != *(char *)(local_20 + lVar3)) {
        return 0;
      }
    }
    uVar2 = 1;
  }
  else {
    uVar2 = 0;
  }
  return uVar2;
}

```


頑張って人力で読みやすくすると、こうなる。

```c
int check(char *inp){
    int inp_len = strlen(inp);
    if(inp_len == 0x1a) {
        for(byte i = 0;i < strlen(key); i++) {
            key[i] = (i + 0x20U) ^ key[i];
        }
        byte *res = FUN_00101463();
        for(int i = 0; (inp[i] != '\0' && (res[i] != '\0')); i++) {
            if(inp[i] != res[i]) {
                return 0;
            }
        }
        return 1;
    }
    else {
        return 0;
    }
}
```

入力の長さが `0x1a` か確認して、 `FUN_00101497()` 関数でフラグを生成して、入力と比較している。

`FUN_00101497()` 関数も見てみる。

```c
void * FUN_00101497(char *param_1,char *param_2)

{
  size_t __size;
  void *pvVar1;
  size_t sVar2;
  long in_FS_OFFSET;
  ulong local_158;
  ulong local_150;
  ulong local_148;
  ulong local_140;
  ulong local_138;
  ulong local_130;
  byte local_118 [264];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  __size = strlen(param_1);
  pvVar1 = malloc(__size);
  sVar2 = strlen(param_2);
  for (local_158 = 0; local_158 < 0x100; local_158 = local_158 + 1) {
    local_118[local_158] = (byte)local_158;
  }
  local_148 = 0;
  for (local_150 = 0; local_150 < 0x100; local_150 = local_150 + 1) {
    local_148 = (ulong)((int)param_2[local_150 % (ulong)(long)(int)sVar2] +
                        (uint)local_118[local_150] + (int)local_148 & 0xff);
    FUN_00101463(local_118 + local_150,local_118 + local_148);
  }
  local_140 = 0;
  local_138 = 0;
  for (local_130 = 0; local_130 < __size; local_130 = local_130 + 1) {
    local_140 = (ulong)((int)local_140 + 1U & 0xff);
    local_138 = (ulong)((int)local_138 + (uint)local_118[local_140] & 0xff);
    FUN_00101463(local_118 + local_140,local_118 + local_138);
    *(byte *)(local_130 + (long)pvVar1) =
         local_118[(int)(uint)(byte)(local_118[local_138] + local_118[local_140])] ^
         param_1[local_130];
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return pvVar1;
}
```
これも頑張って人力で直す。

```c
byte * sub_check(){
    byte buf[264];
    long local_10;

    int dat_len = strlen(dat);
    byte *res = malloc(dat_len);

    int key_len = strlen(key);
    for(int i = 0; i < 0x100; i++) {
        buf[i] = i;
    }
    byte t = 0;
    for(int i = 0; i < 0x100; i++) {
        t = (key[i % key_len] + buf[i] + t) & 0xff;
        FUN_00101463(&buf[i],&buf[t]);
    }
    byte x = 0,y = 0;
    for(int i = 0; i < dat_len; i++) {
        x = (x + 1) & 0xff;
        y = (y + buf[x]) & 0xff;
        FUN_00101463(&buf[x],&buf[y]);
        res[i] = buf[(buf[y] + buf[x]) & 0xff] ^ dat[i];
    }

    return res;
}
```

途中で `FUN_00101463()` 関数が呼ばれているが、これは swap してるだけっぽい。

```c

void FUN_00101463(undefined *param_1,undefined *param_2)

{
  undefined uVar1;
  
  uVar1 = *param_1;
  *param_1 = *param_2;
  *param_2 = uVar1;
  return;
}
```

ということで、すべてを繋いで、`sub_check()` の結果を出力すればいい。

```solve.c
...

int check(char *inp){
...
        byte *res = sub_check();
        printf("%s\n",res); // 追加
...
}

int main(){
    check("AAAAAAAAAAAAAAAAAAAAAAAAAA"); // "A" * 0x1a
}
```

## solver

ここまでをまとめてコードを書く。

```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define byte unsigned char

byte dat[] = {0xa5, 0xd2, 0xbc, 0x02, 0xb2, 0x7c, 0x86, 0x38, 0x17, 0xb1, 0x38, 0xc6, 0xe4, 0x5c, 0x1f, 0xa0, 0x9d, 0x96, 0xd1, 0xf0, 0x4b, 0xa6, 0xa6, 0x5c, 0x64, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
byte key[] = {0x43, 0x55, 0x44, 0x17, 0x46, 0x1f, 0x14, 0x17, 0x1a, 0x1d, 0x00};

void swap(byte *a,byte *b){
    byte tmp = *a;
    *a = *b;
    *b = tmp;
}

byte * sub_check(){
    byte buf[264];
    long local_10;

    int dat_len = strlen(dat);
    byte *res = malloc(dat_len);

    int key_len = strlen(key);
    for(int i = 0; i < 0x100; i++) {
        buf[i] = i;
    }
    byte t = 0;
    for(int i = 0; i < 0x100; i++) {
        t = (key[i % key_len] + buf[i] + t) & 0xff;
        swap(&buf[i],&buf[t]);
    }
    byte x = 0,y = 0;
    for(int i = 0; i < dat_len; i++) {
        x = (x + 1) & 0xff;
        y = (y + buf[x]) & 0xff;
        swap(&buf[x],&buf[y]);
        res[i] = buf[(buf[y] + buf[x]) & 0xff] ^ dat[i];
    }

    return res;
}


int check(char *inp){
    int inp_len = strlen(inp);
    if(inp_len == 0x1a) {
        for(byte i = 0;i < strlen(key); i++) {
            key[i] = (i + 0x20U) ^ key[i];
        }
        byte *res = sub_check();
        printf("%s\n",res);
        for(int i = 0; (inp[i] != '\0' && (res[i] != '\0')); i++) {
            if(inp[i] != res[i]) {
                return 0;
            }
        }
        return 1;
    }
    else {
        return 0;
    }
}

int main(){
    check("AAAAAAAAAAAAAAAAAAAAAAAAAA"); // "A" * 0x1a
}
```

flag: `ctf4b{p7r4c3_c4n_3mul4t3_sysc4ll}`

# おわりに

Reversing 問題、「頑張る」以外に解説できることが本当にない…