b01lers CTF & WolvCTF 2023 部分Re WriteUp

这周打了两个比赛,个人感觉 b01lers CTF 偏简单一些,WolvCTF 比前者难一点。

养生型选手,一共做了三个题

b01lers CTF

Safe

一个嵌入式逆向的题目,是逆向 Arduino UNO 一个门锁一样的东西。附件给了固件,效果图,和电路原理图。

image

image

直接把给的固件拖到 ghidra ,因为固件是Intel Hex 格式的并不包含架构信息所以需要我们手动选择一下,这里选择 AVR8 gccArduino UNO 所采用的芯片使用的架构是 AVR8),首先找到入口函数,发现有 Reset 函数如下图

image

所以,被我重命名成 reset_impl 的函数应该就是入口了。

void reset_impl(void)
{
  undefined1 *puVar1;
  
  R1 = 0;
  SREG = 0;
  Ylo = 0xff;
  Yhi = 8;
  R17 = '\x01';
  X = &mem_100;
  Z = &DAT_codebyte_0582;
  while (puVar1 = X, (byte)X != 0x1c || X._1_1_ != (char)(R17 + ((byte)X < 0x1c))) {
    R0 = *Z;
    Z = Z + 1;
    X = X + 1;
    *puVar1 = R0;
  }
  R18 = '\x01';
  X = &DAT_mem_011c;
  while (puVar1 = X, (byte)X != 0x3a || X._1_1_ != (char)(R18 + ((byte)X < 0x3a))) {
    X = X + 1;
    *puVar1 = R1;
  }
  mem_const_7c = 0x7c;
  real_main();
  while_true();
  return;
}

显然就是设置了一堆内存信息,然后调用了两个函数,重点关注被我重命名为 real_main 的函数,while_true 那个就是一个空的死循环,在嵌入式中很常见。

后面了解了一下 Arduino 开发,这个 real_main 函数应该就是 setup 函数

real_main 的实现就比较复杂了,我在参考 Arduino 的标准库源码之后基本还原了所有用到的函数的符号,具体实现如下

void real_main(void)
{
  char cVar1;
  char *pcVar2;
  byte *pbVar3;
  char *pcVar4;
  // 初始化一堆寄存器
  R1 = 0;
  R25R24._0_1_ = TCNT2;
  R25R24._0_1_ = (byte)R25R24 | 2;
  TCNT2 = (byte)R25R24;
  R25R24._0_1_ = TCNT2;
  R25R24._0_1_ = (byte)R25R24 | 1;
  TCNT2 = (byte)R25R24;
  R25R24._0_1_ = TCCR2;
  R25R24._0_1_ = (byte)R25R24 | 2;
  TCCR2 = (byte)R25R24;
  R25R24._0_1_ = TCCR2;
  R25R24._0_1_ = (byte)R25R24 | 1;
  TCCR2 = (byte)R25R24;
  R25R24._0_1_ = DAT_mem_006e;
  R25R24._0_1_ = (byte)R25R24 | 1;
  DAT_mem_006e = (byte)R25R24;
  ICR3H = 0;
  R25R24._0_1_ = ICR3H;
  R25R24._0_1_ = (byte)R25R24 | 2;
  ICR3H = (byte)R25R24;
  R25R24._0_1_ = ICR3H;
  R25R24._0_1_ = (byte)R25R24 | 1;
  ICR3H = (byte)R25R24;
  R25R24._0_1_ = ICR3L;
  R25R24._0_1_ = (byte)R25R24 | 1;
  ICR3L = (byte)R25R24;
  R25R24._0_1_ = DAT_mem_00b1;
  R25R24._0_1_ = (byte)R25R24 | 4;
  DAT_mem_00b1 = (byte)R25R24;
  R25R24._0_1_ = DAT_mem_00b0;
  R25R24._0_1_ = (byte)R25R24 | 1;
  DAT_mem_00b0 = (byte)R25R24;
  R25R24._0_1_ = TCCR1C;
  R25R24._0_1_ = (byte)R25R24 | 4;
  TCCR1C = (byte)R25R24;
  R25R24._0_1_ = TCCR1C;
  R25R24._0_1_ = (byte)R25R24 | 2;
  TCCR1C = (byte)R25R24;
  R25R24._0_1_ = TCCR1C;
  R25R24._0_1_ = (byte)R25R24 | 1;
  TCCR1C = (byte)R25R24;
  R25R24._0_1_ = TCCR1C;
  R25R24._0_1_ = (byte)R25R24 | 0x80;
  TCCR1C = (byte)R25R24;
  DAT_mem_00c1 = 0;
  // 设置引脚模式
  pinMode(10,1);
  R25R24 = CONCAT11(R25R24._1_1_,11);
  pinMode(11,1);
  R15R14 = &DAT_mem_0118;
  Y = &DAT_mem_0114;
  do {
    pbVar3 = Y;
    Y = Y + 1;
    R25R24._0_1_ = *pbVar3;
    R25R24 = R25R24 & 0xff00 | (uint)(byte)R25R24;
                    /* pinMode(R24, INPUT_PULLUP) */
    pinMode((byte)R25R24,2);
    Z = R15R14 + 1;
    R17R16._1_1_ = *R15R14;
    R17R16 = (byte *)((uint)R17R16 & 0xff | (uint)R17R16._1_1_ << 8);
    R15R14 = Z;
    R25R24 = R25R24 & 0xff00 | (uint)R17R16._1_1_;
    pinMode(R17R16._1_1_,1);
    R25R24 = R25R24 & 0xff00 | (uint)R17R16 >> 8;
    R25R24._0_1_ = (byte)((uint)R17R16 >> 8);
    digitalWrite((byte)R25R24,'\x01');
    Z = (byte *)CONCAT11(1,(byte)Z);
  } while ((byte)Y != 0x18 || Y._1_1_ != (char)(((byte)Y < 0x18) + '\x01'));
  R10 = '9';
  R11 = 1;
  R17R16 = &DAT_mem_0125;
  R7 = 20;
  R8 = 0;
  R9 = '\0';
LAB_code_0242:
  R25R24 = 0x100;
  // 扫描按键
                    /* 5 4 3 2 */
  R15R14 = &DAT_mem_0118;
  Y._0_1_ = 0;
  do {
    Z = R15R14 + 1;
    R6 = *R15R14;
    R15R14 = Z;
    R25R24 = R25R24 & 0xff00 | (uint)R6;
    digitalWrite(R6,0);
                    /* 9 8 7 6  */
    R13R12 = &DAT_mem_0114;
    Y._1_1_ = 0;
    do {
      Z = R13R12 + 1;
      R5 = *R13R12;
      R13R12 = Z;
      R25R24 = digitalRead(R5);
      R25R24._0_1_ = (byte)R25R24 | R25R24._1_1_;
      R25R24 = R25R24 & 0xff00;
      if ((byte)R25R24 == 0) {
        do {
          R25R24 = digitalRead(R5);
          R25R24._0_1_ = (byte)R25R24 | R25R24._1_1_;
        } while ((byte)R25R24 == 0);
        Z = (byte *)CONCAT11(-((DAT_mem_0139 < 0xdb) + -2),DAT_mem_0139 + 0x25);
        R25R24._0_1_ = DAT_mem_0139;
        *Z = Y._1_1_ + (byte)Y;
        DAT_mem_0139 = (byte)R25R24 + 1;
        R25R24 = FUN_code_0156(0,0x32);
      }
      Y._1_1_ = Y._1_1_ + 1;
    } while (Y._1_1_ != 4);
    R25R24 = R25R24 & 0xff00 | (uint)R6;
    digitalWrite(R6,0x1);
    Y._0_1_ = (byte)Y + 0x4;
  } while ((byte)Y != 16);
  // 判断长度,等于20就对比 
  if (DAT_mem_0139 == 20) {
    Z = &DAT_mem_0125;
    X = &mem_100; // [0x0d, 0x05, 0x0f, 0x04, 0x0c, 0x00, 0x09, 0x07, 0x0d, 0x07, 0x09, 0x04, 0x05, 0x08, 0x06, 0x0d, 0x0e, 0x05, 0x05, 0x0f] maybe
LAB_code_0284:
    pcVar4 = (char *)Z;
    pcVar2 = X;
    Z = (byte *)((char *)Z + 1);
    R25R24._1_1_ = *pcVar4;
    X = X + 1;
    R25R24._0_1_ = *pcVar2;
    if (R25R24._1_1_ == (byte)R25R24) goto LAB_code_02ad;
    cVar1 = 5;
    do {
      R25R24 = CONCAT11(R25R24._1_1_,11);
      digitalWrite(11,1);
      FUN_code_0156(0,0xf4);
      R25R24 = CONCAT11(R25R24._1_1_,11);
      digitalWrite(11,0);
      R25R24._1_1_ = 0;
      FUN_code_0156(0,0xf4);
      cVar1 = cVar1 + -1;
    } while (cVar1 != 0);
    goto LAB_code_029f;
  }
  goto LAB_code_02a6;
LAB_code_02ad:
  if (R10 == (byte)Z && R11 == (char)(Z._1_1_ + (R10 < (byte)Z))) goto code_c0x02b0;
  goto LAB_code_0284;
code_c0x02b0: // 应该是开🔓的逻辑,也就是输入正确
  R25R24 = CONCAT11(R25R24._1_1_,10);
  digitalWrite(10,1);
  FUN_code_0156(0,16);
  R25R24 = CONCAT11(R25R24._1_1_,10);
  digitalWrite(10,0);
LAB_code_029f:
  DAT_mem_0139 = R1;
  Z = R17R16;
  R25R24._0_1_ = R7;
  do {
    pbVar3 = Z;
    Z = Z + 1;
    *pbVar3 = R1;
    R25R24._0_1_ = (byte)R25R24 + -1;
  } while ((byte)R25R24 != '\0');
LAB_code_02a6:
  if (R8 != R1 || R9 != (byte)(R1 + (R8 < R1))) {
    Reset();
  }
  // 继续扫描
  goto LAB_code_0242;
}

参考电路图和上述代码,以及题目给出的flag格式不包含 *#,得到 flag,最终也是狠狠的拿下了三血捏。

mem = [0x0d, 0x05, 0x0f, 0x04, 0x0c, 0x00, 0x09, 0x07, 0x0d, 0x07, 0x09, 0x04, 0x05, 0x08, 0x06, 0x0d, 0x0e, 0x05, 0x05,
       0x0f, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02]
keypad = '147*2580369#ABCD'
flag = 'bctf{'
for i in range(20):
    flag += keypad[mem[i]]
flag += '}'
print(flag)

baby noah

这个题目是和队友一起做的,很奇怪的题目,看起来是迷宫题,其实是一种奇怪的编程语言,普通的 linux x86_64 逆向过程,逆向过程不多说,大体流程翻译成 Python 如下

flag_score = 0x31337
map = [['\0' for i in range(24)] for j in range(50)]
x = 0
y = 0
offset_x = 0
offset_y = 0
score = 0
stack = []

con = 0
while con == 0:
    ins = map[y][x]
    if '!' == ins:
        stack.append(stack.pop() == 0)
    elif '#' == ins:
        stack.append(stack[-1])
    elif '%' == ins:
        a = stack.pop()
        b = stack.pop()
        stack.append(b % a)
    elif '&' == ins:
        a = stack.pop()
        b = stack.pop()
        stack.append(b & a)
    elif '*' == ins:
        a = stack.pop()
        b = stack.pop()
        stack.append(b * a)
    elif '+' == ins:
        a = stack.pop()
        b = stack.pop()
        stack.append(b + a)
    elif ',' == ins:
        a = stack.pop()
        b = stack.pop()
        stack.append(a)
        stack.append(b)
    elif '-' == ins:
        a = stack.pop()
        b = stack.pop()
        stack.append(b - a)
    elif '.' == ins:
        print(chr(stack[-1]), end='')
    elif '/' == ins:
        a = stack.pop()
        b = stack.pop()
        stack.append(b / a)
    elif ':' == ins:
        print(stack[-1], end='')
    elif '<' == ins:
        offset_x = -1
        offset_y = 0
    elif '>' == ins:
        offset_x = 1
        offset_y = 0
    elif '@' == ins:
        con = 1
        continue
    elif '^' == ins:
        offset_x = 0
        offset_y = -1
    elif '_' == ins:
        if stack[-1] == 0:
            offset_x = 1
        else:
            offset_x = -1
        offset_y = 0
    elif 'g' == ins:
        a = stack.pop()
        b = stack.pop()
        stack.append(ord(map[a][b]))
    elif 'p' == ins:
        a = stack.pop()
        b = stack.pop()
        c = stack.pop()
        map[b][c] = a
    elif 'v' == ins:
        offset_x = 0
        offset_y = 1
    elif '|' == ins:
        offset_x = 0
        if stack[-1] == 0:
            offset_y = 1
        else:
            offset_y = -1
    elif '~' == ins:
        stack.pop()
    else:
        stack.append(0)

    x = (24 + offset_x) % 24
    y = (50 + offset_y) % 50
    score += ord(ins) + 1
    if score > 0x313370:
        con = 2
    else:
        con = 0
if con == 1 and score == flag_score:
    print('flag')

其中 map 就是你要输入的值,nc 限制 8 行以内,下面是战队的其他师傅构造出来的符合条件的输入(想好咋构造之后就去吃饭啦,吃完神仙队友已经出啦,tql)

>00g#/#+#+#+#+#+#+#+v
???????????va4(($/##<
???????????>~~~~~->>|
????@000000000000@A3<

战队其他师傅发现类似这个,但是不完全一样,可以了解一下:

https://en.wikipedia.org/wiki/Befunge#Befunge-93_instruction_list

WolvCTF

64r2

MIPS64 MSB Linux 逆向,根据分析运行起来就能出 Flag 但是试了 qemu 跑不起来,又试了半天 unicorn 最后发现好像是不支持 MIPS64 MSB,虽然它有 MIPS64 的接口(反正我试了半天,是一个指令都没跑出来),最后直接掏出来 ghidra 静态干死它得了。

主要就两个函数,

  • 一个是对三组(或者说 4 组,其实有两组是连着的)数据进行解密我把那个函数叫做 decrypt1
  • 另一个是根据上面的数据得出 flag, 我叫他 set_flag_value

大体流程如下:

/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */
int main(longlong param_1)
{
  undefined4 uVar1;
  undefined8 in_zero;
  undefined8 flag;
  int iVar2;
  uint uVar3;
  
  iVar2 = 0;
  puts("What are you doing here?");
  flag = __libc_malloc(0x20);
  if (0 < param_1) {
    uVar3 = (int)param_1 - 1U & 3;
    if (uVar3 != 0) {
      iVar2 = 1;
      set_flag_value(flag);
      decrypt1(null_ARRAY_100b6840,0x3856);
      decrypt1(null_ARRAY_100b6810,0x6378);
      decrypt1(null_ARRAY_100b6820,0x8953);
      decrypt1(0x100b6830,0x9018);
      if (uVar3 != 1) {
        if (uVar3 != 2) {
          set_flag_value(flag);
          iVar2 = 2;
          _DAT_00000090 = getCopReg(2,0);
        }
        set_flag_value(flag);
        iVar2 = iVar2 + 1;
        uVar1 = getCopReg(2,0);
        *(undefined4 *)((longlong)iVar2 + 0x1b) = uVar1;
      }
    }
    set_flag_value(flag);
    for (iVar2 = iVar2 + 1; iVar2 != param_1; iVar2 = iVar2 + 4) {
      uVar1 = getCopReg(2,0);
      *(undefined4 *)((longlong)iVar2 + 0x6c) = uVar1;
      set_flag_value(flag);
      uVar1 = getCopReg(2,0);
      *(undefined4 *)((longlong)(iVar2 + 1) + 0x4f) = uVar1;
      set_flag_value(flag);
      uVar1 = getCopReg(2,0);
      *(undefined4 *)((longlong)(iVar2 + 2) + 0x36) = uVar1;
      set_flag_value(flag);
      setCopReg(2,in_zero,*(undefined4 *)((longlong)(iVar2 + 3) + -0x17));
      decrypt1(null_ARRAY_100b6840,0x3856);
      decrypt1(null_ARRAY_100b6810,0x6378);
      decrypt1(null_ARRAY_100b6820,0x8953);
      decrypt1(0x100b6830,0x9018);
      set_flag_value(flag);
    }
  }
  printf("Well since you\'re here anyway have a flag:\n%s\n",flag);
  free(flag);
  return 0;
}

虽然流程很乱,但是不要慌,先看 decrypt1 就一个 xor 所以,它执行单数次是一种情况,执行偶数次或者不执行是另一种情况,所以流程再乱,我都试一遍不就完了。

所以关键是 set_flag_value 函数,其实现如下:

undefined8 set_flag_value(undefined *param_1)
{
  undefined uVar1;
  undefined uVar2;
  undefined uVar3;
  undefined uVar4;
  undefined *key;
  undefined *iv;
  undefined chiper [200];
  // copy 数据
  key = (undefined *)__libc_malloc(0x10);
  iv = (undefined *)__libc_malloc(0x10);
  param_1[0x10] = null_ARRAY_100b6820[16];
  param_1[0x11] = null_ARRAY_100b6820[17];
  param_1[0x12] = null_ARRAY_100b6820[18];
  param_1[0x13] = null_ARRAY_100b6820[19];
  param_1[0x14] = null_ARRAY_100b6820[20];
  param_1[0x15] = null_ARRAY_100b6820[21];
  param_1[0x16] = null_ARRAY_100b6820[22];
  param_1[0x17] = null_ARRAY_100b6820[23];
  param_1[0x18] = null_ARRAY_100b6820[24];
  param_1[0x19] = null_ARRAY_100b6820[25];
  param_1[0x1a] = null_ARRAY_100b6820[26];
  param_1[0x1b] = null_ARRAY_100b6820[27];
  param_1[0x1c] = null_ARRAY_100b6820[28];
  param_1[0x1d] = null_ARRAY_100b6820[29];
  param_1[0x1e] = null_ARRAY_100b6820[30];
  uVar1 = null_ARRAY_100b6840[0];
  param_1[0x1f] = null_ARRAY_100b6820[31];
  *key = uVar1;
  uVar1 = null_ARRAY_100b6810[0];
  *param_1 = null_ARRAY_100b6820[0];
  uVar4 = null_ARRAY_100b6840[1];
  uVar3 = null_ARRAY_100b6820[1];
  uVar2 = null_ARRAY_100b6810[1];
  *iv = uVar1;
  key[1] = uVar4;
  iv[1] = uVar2;
  param_1[1] = uVar3;
  uVar2 = null_ARRAY_100b6820[2];
  uVar1 = null_ARRAY_100b6810[2];
  key[2] = null_ARRAY_100b6840[2];
  iv[2] = uVar1;
  param_1[2] = uVar2;
  uVar2 = null_ARRAY_100b6820[3];
  uVar1 = null_ARRAY_100b6810[3];
  key[3] = null_ARRAY_100b6840[3];
  iv[3] = uVar1;
  param_1[3] = uVar2;
  uVar2 = null_ARRAY_100b6820[4];
  uVar1 = null_ARRAY_100b6810[4];
  key[4] = null_ARRAY_100b6840[4];
  iv[4] = uVar1;
  param_1[4] = uVar2;
  uVar2 = null_ARRAY_100b6820[5];
  uVar1 = null_ARRAY_100b6810[5];
  key[5] = null_ARRAY_100b6840[5];
  iv[5] = uVar1;
  param_1[5] = uVar2;
  uVar2 = null_ARRAY_100b6820[6];
  uVar1 = null_ARRAY_100b6810[6];
  key[6] = null_ARRAY_100b6840[6];
  param_1[6] = uVar2;
  uVar4 = null_ARRAY_100b6840[7];
  uVar3 = null_ARRAY_100b6820[7];
  uVar2 = null_ARRAY_100b6810[7];
  iv[6] = uVar1;
  key[7] = uVar4;
  iv[7] = uVar2;
  param_1[7] = uVar3;
  uVar2 = null_ARRAY_100b6820[8];
  uVar1 = null_ARRAY_100b6810[8];
  key[8] = null_ARRAY_100b6840[8];
  iv[8] = uVar1;
  param_1[8] = uVar2;
  uVar2 = null_ARRAY_100b6820[9];
  uVar1 = null_ARRAY_100b6810[9];
  key[9] = null_ARRAY_100b6840[9];
  iv[9] = uVar1;
  param_1[9] = uVar2;
  uVar2 = null_ARRAY_100b6820[10];
  uVar1 = null_ARRAY_100b6810[10];
  key[10] = null_ARRAY_100b6840[10];
  iv[10] = uVar1;
  param_1[10] = uVar2;
  uVar1 = null_ARRAY_100b6810[11];
  key[0xb] = null_ARRAY_100b6840[11];
  iv[0xb] = uVar1;
  param_1[0xb] = null_ARRAY_100b6820[11];
  uVar2 = null_ARRAY_100b6840[12];
  uVar1 = null_ARRAY_100b6820[12];
  iv[0xc] = null_ARRAY_100b6810[12];
  key[0xc] = uVar2;
  param_1[0xc] = uVar1;
  uVar2 = null_ARRAY_100b6820[13];
  uVar1 = null_ARRAY_100b6810[13];
  key[0xd] = null_ARRAY_100b6840[13];
  iv[0xd] = uVar1;
  param_1[0xd] = uVar2;
  uVar2 = null_ARRAY_100b6820[14];
  uVar1 = null_ARRAY_100b6810[14];
  key[0xe] = null_ARRAY_100b6840[14];
  iv[0xe] = uVar1;
  param_1[0xe] = uVar2;
  uVar2 = null_ARRAY_100b6820[15];
  uVar1 = null_ARRAY_100b6810[15];
  key[0xf] = null_ARRAY_100b6840[15];
  iv[0xf] = uVar1;
  param_1[0xf] = uVar2;
  // 解密
  aes128_init(chiper,key,iv);
  aes128_decrypt_cbc((longlong)chiper,(longlong)param_1,32);
  free(key);
  free(iv);
  return 0;
}

关于算法的识别,我是人工识别的,无它唯手熟(⊙o⊙)…,当然 ghidra 上应该也有插件可以识别

from Crypto.Cipher import AES

def decrypt1(p1, p2, offset=0):
    for i in range(16):
        p1[i + offset] ^= p2 & 0xff
        u_var1 = p2 & 1
        p2 >>= 1
        if u_var1 != 0:
            p2 ^= 0xb400

flag = [0x52, 0xbc, 0x27, 0xb6, 0xe6, 0x2f, 0xcb, 0x4d, 0x17, 0x68, 0x4f, 0x4f, 0xc8, 0xa0, 0x3b, 0x7d, 0x63, 0x18,
        0x09, 0x59,
        0xa7, 0x5f, 0xfe, 0x1b, 0x22, 0xa7, 0x4c, 0xbd, 0x9d, 0xcd, 0x05, 0xa7]
key = [0x9b, 0x30, 0xe8, 0xe4, 0x96, 0x71, 0xbf, 0xa3, 0x3c, 0xf1, 0x97, 0x4e, 0xc0, 0x40, 0x70, 0x61]
iv = [0x63, 0x2b, 0x5f, 0x35, 0xcd, 0x63, 0x01, 0xeb, 0x26, 0xa5, 0x8a, 0x62, 0x1c, 0x89, 0xb9, 0xe3]

decrypt1(flag, 0x8953)
decrypt1(flag, 0x9018, 16)
decrypt1(key, 0x3856)
decrypt1(iv, 0x6378)

aes = AES.new(bytes(key), iv=bytes(iv), mode=AES.MODE_CBC)
real_flag = aes.decrypt(bytes(flag))
print(real_flag)

最后也是狠狠地拿下了一血

posted @ 2023-03-20 17:14  gaoyucan  阅读(377)  评论(0编辑  收藏  举报