Reverse Engineering

Challenge
Link

baby-asm (100 pts)

baby-vm (100 pts)

baby-crack (100 pts)

baby-asm (100 pts)

Description

-

Solution

Given ELF 64 bit file, open it using IDA

int __fastcall main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rbx
  __int64 v4; // rbx
  __int64 v5; // rbx
  __int64 v6; // rbx
  __int64 v7; // rbx
  __int64 v8; // rbx
  __int64 v9; // rbx
  __int64 v10; // rbx
  __int64 v11; // rbx
  __int64 v12; // rbx
  __int64 v13; // rbx
  __int64 v14; // rbx
  __int64 v15; // rbx
  __int64 v16; // rbx
  __int64 v17; // rbx
  __int64 v18; // rbx
  _QWORD *v20; // [rsp+8h] [rbp-128h]
  char s[8]; // [rsp+10h] [rbp-120h] BYREF
  __int64 v22; // [rsp+18h] [rbp-118h]
  __int64 v23; // [rsp+20h] [rbp-110h]
  __int64 v24; // [rsp+28h] [rbp-108h]
  __int64 v25; // [rsp+30h] [rbp-100h]
  __int64 v26; // [rsp+38h] [rbp-F8h]
  __int64 v27; // [rsp+40h] [rbp-F0h]
  __int64 v28; // [rsp+48h] [rbp-E8h]
  __int64 v29; // [rsp+50h] [rbp-E0h]
  __int64 v30; // [rsp+58h] [rbp-D8h]
  __int64 v31; // [rsp+60h] [rbp-D0h]
  __int64 v32; // [rsp+68h] [rbp-C8h]
  __int64 v33; // [rsp+70h] [rbp-C0h]
  __int64 v34; // [rsp+78h] [rbp-B8h]
  __int64 v35; // [rsp+80h] [rbp-B0h]
  __int64 v36; // [rsp+88h] [rbp-A8h]
  __int64 v37; // [rsp+90h] [rbp-A0h]
  __int64 v38; // [rsp+98h] [rbp-98h]
  __int64 v39; // [rsp+A0h] [rbp-90h]
  __int64 v40; // [rsp+A8h] [rbp-88h]
  __int64 v41; // [rsp+B0h] [rbp-80h]
  __int64 v42; // [rsp+B8h] [rbp-78h]
  __int64 v43; // [rsp+C0h] [rbp-70h]
  __int64 v44; // [rsp+C8h] [rbp-68h]
  __int64 v45; // [rsp+D0h] [rbp-60h]
  __int64 v46; // [rsp+D8h] [rbp-58h]
  __int64 v47; // [rsp+E0h] [rbp-50h]
  __int64 v48; // [rsp+E8h] [rbp-48h]
  __int64 v49; // [rsp+F0h] [rbp-40h]
  __int64 v50; // [rsp+F8h] [rbp-38h]
  __int64 v51; // [rsp+100h] [rbp-30h]
  __int64 v52; // [rsp+108h] [rbp-28h]
  unsigned __int64 v53; // [rsp+118h] [rbp-18h]

  v53 = __readfsqword(0x28u);
  v20 = mmap(0LL, 0x1000uLL, 7, 34, 0, 0LL);
  if ( v20 == (_QWORD *)-1LL )
  {
    perror("mmap");
    exit(1);
  }
  *v20 = code;
  v20[1] = 0xC7480000002D358DLL;
  v20[2] = 0xC0314800000029C2LL;
  v20[3] = 0x1375063A1834078ALL;
  v20[4] = 0xFF48C6FF48C7FF48LL;
  v20[5] = 0x2021C0C748ED75CALL;
  v20[6] = 0x2020C0C748C30000LL;
  v20[7] = 0x282A5B5A5BC30000LL;
  qmemcpy(v20 + 8, "*,c +y~~+.,.!)/ }}*yy{/~.{*}| !/}z*e", 36);
  printf("Enter your flag: ");
  fgets(s, 256, _bss_start);
  v3 = v22;
  *(_QWORD *)((char *)v20 + 100) = *(_QWORD *)s;
  *(_QWORD *)((char *)v20 + 108) = v3;
  v4 = v24;
  *(_QWORD *)((char *)v20 + 116) = v23;
  *(_QWORD *)((char *)v20 + 124) = v4;
  v5 = v26;
  *(_QWORD *)((char *)v20 + 132) = v25;
  *(_QWORD *)((char *)v20 + 140) = v5;
  v6 = v28;
  *(_QWORD *)((char *)v20 + 148) = v27;
  *(_QWORD *)((char *)v20 + 156) = v6;
  v7 = v30;
  *(_QWORD *)((char *)v20 + 164) = v29;
  *(_QWORD *)((char *)v20 + 172) = v7;
  v8 = v32;
  *(_QWORD *)((char *)v20 + 180) = v31;
  *(_QWORD *)((char *)v20 + 188) = v8;
  v9 = v34;
  *(_QWORD *)((char *)v20 + 196) = v33;
  *(_QWORD *)((char *)v20 + 204) = v9;
  v10 = v36;
  *(_QWORD *)((char *)v20 + 212) = v35;
  *(_QWORD *)((char *)v20 + 220) = v10;
  v11 = v38;
  *(_QWORD *)((char *)v20 + 228) = v37;
  *(_QWORD *)((char *)v20 + 236) = v11;
  v12 = v40;
  *(_QWORD *)((char *)v20 + 244) = v39;
  *(_QWORD *)((char *)v20 + 252) = v12;
  v13 = v42;
  *(_QWORD *)((char *)v20 + 260) = v41;
  *(_QWORD *)((char *)v20 + 268) = v13;
  v14 = v44;
  *(_QWORD *)((char *)v20 + 276) = v43;
  *(_QWORD *)((char *)v20 + 284) = v14;
  v15 = v46;
  *(_QWORD *)((char *)v20 + 292) = v45;
  *(_QWORD *)((char *)v20 + 300) = v15;
  v16 = v48;
  *(_QWORD *)((char *)v20 + 308) = v47;
  *(_QWORD *)((char *)v20 + 316) = v16;
  v17 = v50;
  *(_QWORD *)((char *)v20 + 324) = v49;
  *(_QWORD *)((char *)v20 + 332) = v17;
  v18 = v52;
  *(_QWORD *)((char *)v20 + 340) = v51;
  *(_QWORD *)((char *)v20 + 348) = v18;
  if ( ((unsigned int (*)(void))v20)() == 8225 )
    puts("Congrats, this app successfully cracked!");
  else
    puts("Sorry, try again!");
  return 0;
}

From code above we can see that there is mmap function called at initial of main function. The third argument of mmap function is 7, it means that the we allowed to read, write, and execute memory. We can see that the static values and our input (s) are stored also in v20 and at the end there is a call to v20. So basically v20 contains shellcode that will process our input and static values. To know the instructions executed, we can debug it by stepping instruction on v20 call.

pie b 0x14B6
pie run

Input some random value then send "si" to step instruction.

So rdi is our input and rsi is static values. our input (rdi) will be moved each byte to al and then will be xored with 0x18. After that it will be compared with 1 byte value of rsi (static values). From that information we can get the valid value of input by xoring the static values with 0x18. Below is the script to solve the challenge

a = b"[Z[*(*,c +y~~+.,.!)/ }}*yy{/~.{*}| !/}z*e"
flag = b""
for i in a:
	flag += bytes([i ^ 0x18])
print(flag)

Flag: CBC2024{83aff36469178ee2aac7f6c2ed897eb2}

baby-vm (100 pts)

Description

-

Solution

Given ELF 64 bit, open it using IDA

int __fastcall main(int argc, const char **argv, const char **envp)
{
  FILE *stream; // [rsp+8h] [rbp-18h]
  __int64 size; // [rsp+10h] [rbp-10h]
  void *ptr; // [rsp+18h] [rbp-8h]

  stream = fopen("code.bin", "rb");
  if ( !stream )
  {
    puts("Failed to open code.bin");
    exit(1);
  }
  fseek(stream, 0LL, 2);
  size = ftell(stream);
  fseek(stream, 0LL, 0);
  ptr = malloc(size);
  fread(ptr, size, 1uLL, stream);
  fclose(stream);
  if ( (unsigned int)vm_exec(ptr, (unsigned int)size) )
    puts("Congrats, you own the flag!");
  else
    puts("Oops, wrong flag!");
  return 0;
}

The opcode are stored in code.bin and the function that will execute the opcode in vm_exec, so let's check vm_exec.

__int64 __fastcall vm_exec(__int64 a1, int a2)
{
  int v3; // ecx
  int v4; // eax
  char v5; // [rsp+1Eh] [rbp-122h] BYREF
  unsigned __int8 v6; // [rsp+1Fh] [rbp-121h]
  int v7; // [rsp+20h] [rbp-120h]
  int v8; // [rsp+24h] [rbp-11Ch]
  _QWORD v9[35]; // [rsp+28h] [rbp-118h]

  v9[34] = __readfsqword(0x28u);
  v7 = 0;
  v9[0] = 0LL;
  v8 = 0;
  while ( 2 )
  {
    if ( v7 < a2 )
    {
      v6 = *(_BYTE *)(v7 + a1);
      switch ( v6 )
      {
        case 0u:
          return LOBYTE(v9[0]);
        case 1u:
          *((_BYTE *)v9 + *(unsigned __int8 *)(v7 + 1LL + a1)) = *(_BYTE *)(a1 + v7 + 2LL);
          v7 += 3;
          continue;
        case 2u:
          v3 = *(unsigned __int8 *)(v7 + 1LL + a1);
          v4 = v8++;
          *((_BYTE *)&v9[1] + v4) = *((_BYTE *)v9 + v3);
          v7 += 2;
          continue;
        case 3u:
          *((_BYTE *)v9 + *(unsigned __int8 *)(v7 + 1LL + a1)) += *((_BYTE *)v9 + *(unsigned __int8 *)(v7 + 2LL + a1));
          v7 += 3;
          continue;
        case 4u:
          *((_BYTE *)v9 + *(unsigned __int8 *)(v7 + 1LL + a1)) -= *((_BYTE *)v9 + *(unsigned __int8 *)(v7 + 2LL + a1));
          v7 += 3;
          continue;
        case 7u:
          if ( *((_BYTE *)v9 + *(unsigned __int8 *)(v7 + 1LL + a1)) == *((_BYTE *)v9
                                                                       + *(unsigned __int8 *)(v7 + 2LL + a1)) )
            v7 += 4;
          else
            v7 = *(unsigned __int8 *)(v7 + 3LL + a1);
          continue;
        case 8u:
          __isoc99_scanf(&unk_2004, &v5);
          *((_BYTE *)v9 + *(unsigned __int8 *)(v7 + 1LL + a1)) = v5;
          v7 += 2;
          continue;
        case 9u:
          v7 = *(unsigned __int8 *)(v7 + 1LL + a1);
          continue;
        default:
          printf("Invalid opcode: %d\n", v6);
          exit(1);
      }
    }
    return LOBYTE(v9[0]);
  }
}

We can see that the instruction is not much and the operation available only addition and substraction. So basically at the end our input only will be added or subtracted although there are many operation of substraction or addition. With assumption that our input will be processed individually (each byte and independent) so lets set breakpoint at compare instruction (opcode 7).

pie b 0x1516
pie run

Lets do scripting and use 2 different input to check the values compared.

#!/usr/bin/python3
import string

def write_payload(data):
    f = open("payload.txt", "wb")
    f.write(bytes(data))
    f.close()

class SolverEquation(gdb.Command):
    def __init__ (self):
        super (SolverEquation, self).__init__ ("solve-equation",gdb.COMMAND_OBSCURE)

    def invoke (self, arg, from_tty):
        dict = {}
        for i in string.printable[:2]:
            list_al = []
            list_dl = []
            tmp = [ord(i)] * 45
            write_payload(tmp)
            gdb.execute("pie b 0x1516")
            gdb.execute("pie run < payload.txt")
            for _ in range(45):
                al = addr2num(gdb.selected_frame().read_register("al"))
                dl = addr2num(gdb.selected_frame().read_register("dl"))
                gdb.execute("set $al=$dl")
                gdb.execute("c")
                list_al.append(al & 0xff)
                list_dl.append(dl & 0xff)
            dict[i] = list_dl
        print(dict)
        print(list_al)
        gdb.execute("pie del")


def addr2num(addr):
    try:
        return int(addr)
    except:
        return long(addr)

SolverEquation()
dict = {'0': [255, 16, 17, 12, 225, 34, 20, 122, 189, 6, 64, 215, 66, 169, 239, 181, 189, 255, 98, 171, 202, 8, 148, 254, 101, 68, 160, 161, 23, 9, 71, 138, 58, 115, 9, 208, 2, 157, 34, 159, 32, 220, 41, 254, 203], '1': [0, 17, 18, 13, 226, 35, 21, 123, 190, 7, 65, 216, 67, 170, 240, 182, 190, 0, 99, 172, 203, 9, 149, 255, 102, 69, 161, 162, 24, 10, 72, 139, 59, 116, 10, 209, 3, 158, 35, 160, 33, 221, 42, 255, 204]}
cmp_val = [18, 34, 36, 14, 225, 36, 24, 197, 189, 78, 64, 215, 113, 219, 32, 231, 6, 46, 168, 232, 249, 71, 214, 45, 151, 117, 210, 234, 70, 61, 74, 199, 58, 177, 56, 22, 63, 204, 35, 208, 41, 18, 50, 49, 24]


for i in dict:
	arr = []
	for j in dict[i]:
		arr.append((ord(i) - j) & 0xff)
	print(arr)

We can see from image above that the different is same, so lets decode it by adding the known diff with the ciphertext.

dict = {'0': [255, 16, 17, 12, 225, 34, 20, 122, 189, 6, 64, 215, 66, 169, 239, 181, 189, 255, 98, 171, 202, 8, 148, 254, 101, 68, 160, 161, 23, 9, 71, 138, 58, 115, 9, 208, 2, 157, 34, 159, 32, 220, 41, 254, 203], '1': [0, 17, 18, 13, 226, 35, 21, 123, 190, 7, 65, 216, 67, 170, 240, 182, 190, 0, 99, 172, 203, 9, 149, 255, 102, 69, 161, 162, 24, 10, 72, 139, 59, 116, 10, 209, 3, 158, 35, 160, 33, 221, 42, 255, 204]}
cmp_val = [18, 34, 36, 14, 225, 36, 24, 197, 189, 78, 64, 215, 113, 219, 32, 231, 6, 46, 168, 232, 249, 71, 214, 45, 151, 117, 210, 234, 70, 61, 74, 199, 58, 177, 56, 22, 63, 204, 35, 208, 41, 18, 50, 49, 24]


for i in dict:
	arr = []
	for j in dict[i]:
		arr.append((ord(i) - j) & 0xff)

flag = b""
for i in range(len(cmp_val)):
	flag += bytes([(cmp_val[i] + arr[i]) & 0xff])
print(flag)

Flag: CBC2024{0x00_baby_vm_or_baby_d3m0n_vm_1a9f9c}

baby-crack (100 pts)

Description

-

Solution

Given luac file, it should be compiled lua. At first i tried to clone the repo but it takes long time so i try to find online decompiler and found https://luadec.metaworm.site/. Upload the .luac file and we got decompiled version of it.

-- filename: @main.lua
-- version: lua54
-- line: [0, 0] id: 0
encrypt = function(r0_1)
  -- line: [1, 9] id: 1
  local r1_1 = ""
  for r5_1 = 1, #r0_1, 1 do
    r1_1 = r1_1 .. string.char((string.byte(r0_1:sub(r5_1, r5_1)) ~ r5_1))
  end
  return r1_1
end
io.write("Validate ur flag: ")
if encrypt(io.read()) == "B@@6543s1ij>=j8$u%\'p\'/ {+\"#)*(.B\u{17}\u{1a}@A\u{16}EDMT" then
  print("Yeah, that\'s the correct flag!")
else
  print("Whoops, that\'s not the correct flag!")
end

We know that it does xor operation to our input and compare it with static values. So the easy way to solve is by calling the same function again.

encrypt = function(r0_1)
  -- line: [1, 9] id: 1
  local r1_1 = ""
  for r5_1 = 1, #r0_1, 1 do
    r1_1 = r1_1 .. string.char((string.byte(r0_1:sub(r5_1, r5_1)) ~ r5_1))
  end
  return r1_1
end

tmp ="B@@6543s1ij>=j8$u%\'p\'/ {+\"#)*(.B\u{17}\u{1a}@A\u{16}EDMT" 

print(encrypt(tmp))

Flag: CBC2024{8ca20d74d74d297c2885761b68ce3cce}

Last updated