Pages

Thursday, March 23, 2023

How I do my computing

This post is inspired from usesthis, a website where people talk about their computing setup. I've been meaning to do this for a while, but I've been procrastinating. I'm going to try to keep this short.

hardware

  • I use two computers, a laptop and a desktop:
    • The laptop is an ASUS ROG zephyrus G15, ryzen 6900hs, 40gb of ddr5 and a 3060 mobile. Running linux.
    • The desktop is an MSI, i9-12900k and amd 6700xt with multiple hdds and nvmes, usually sshing to it remotely to do heavy lifting . Running linux. 
    • Macs look and feel amazing and have great battery life, but I couldn't get used to the weird keyboard layout and MacOS. I also want to be able to upgrade my stuff.
      • Dream laptop is something like a macbook but has upgradable storage, has unified memory and can run Windows or Linux.
  • Raspberry pi zero w for pihole
  • Input devices
    • Logitech Mx keys
    • Logitech Mx master 3
    • The keyboard is meh, mouse is expensive but great, the 3rd party linux software is even better than the official windows software. 
  • Poco F3 running MIUI xiaomi.eu, it's good but still CCP spywayre. Great hardware for the price. Meh software (I can use other ROMs, but I have trust issues).
  • Networking rig:
    • This uses the cheapest stuff I could get:
      • Router: Mi Router 4a (CCP spywayre)
      • Switch: D-link DGS-108
  • Marshall major III, it doesn't sound bad for metal / hard rock, but the hinges broke, and the leather is bad quality and got fucked up after only 2 years. I 3d printed replacement hinges, planning to replace the leather also. It's sloppy design it's like they want this break after only 2-3 years on purpose.
    • Also not very comfortable for people like me with big heads.
  • DualShock 4 v2 controller for causual gaming.

software

  • OS: Previously I was on windows. but now I use archlinux with windows VMs with gpu passthrough when I need them. 
  • $SHELL: zsh with o-my-zsh on Linux. Powershell on Windows.
  • Browser: Brave for personal use and chrome for work
  • Filesystem:
    • BTRFS on linux. ext4 when I don't need COW.
    • NTFS on windows. Didn't try ReFS yet.
  • $EDITOR: Jetbrains all tools pack is $30 a month, but it's the amazing.
  • Mail: default windows mail app. on linux mailspring.
  • Communication: Discord for friends, Telegram for bots, Slack for work.
  • Music: Spotify, Foobar2000 for music not on spotify.
  • git: I'm a noob, can't use git from cli, I use smartgit, costs around $6 a month.
  • AI: I integrate LLMs in my workflow.
    • copilot
    • langchain: I index programming docs and ask questions about it
    • yolo-ai-cmdbot
    • chatgpt-telegram-bot: I like paying only for my usage and also sending voice messages that get transcribed using whisper when I can't type.

Monday, November 23, 2020

Cybertalents reverse engineering problems solutions

 I don't present solutions for all problems, most of these are just some notes with high level ideas on how I solved them, I leave solving the problems as an exercise to the reader.

Em3dkMbXYAMz8YA-md.jpg

The Cybertalents reverse ctf had a couple of nice problems, I had two remaining problems one windows keylogger shit for which I was to lazy to write a program to decrypt the flag file since windows crypto api is not fun to be honest, well I googled how to use this windows bcrypt (not to be confused with the password hashing function, this is some Microsoft thing), and I find this 500 line code example, well I wouldn't want to spend time on that 😂. And another word macro shit, which I had no experience in and I didn't bother doing.

The only problem with this ctf, is that there a lot of recycled or straight reused problems, which maybe gave other players an advantage over me, since I don't do problems on that site.

Solution for Curiosity

Simple VM, it uses 4 general purpose registers with a couple of instructions, mul, not, add, load immediate, load from register, syscall (does some read and write), and conditional jump instruction, many ways to reverse this, you could for example build a dis-assembler for the code and integrate it in binary ninja for example to see some nice CFG's but that won't be very useful for this problem since, control flow is only used once in the program, for me I chose to build an intel pin tool to print a log. I just run the program with the tool, and enter some random input, and then like figure out what the algorithm does, finally it's trivial to solve using z3:

Here is my pintool:

#include "pin.H"
#include <fstream>
#include <stdio.h>
#include <iostream>
using namespace std;

KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool", "o", "pin.out",
                            "specify output file name");
ofstream outFile;
ADDRINT l, h;

void lolbro(CONTEXT* ctx) {
  PIN_REGISTER rax;
  PIN_GetContextRegval(ctx, REG_RAX, reinterpret_cast<UINT8 *>(&rax));

  int pc = rax.dword[0];

  unsigned int reg1, reg2, reg3, reg4;
  PIN_SafeCopy(& reg1, (void*)0x555555756488, sizeof(int));
  PIN_SafeCopy(& reg2, (void*)0x555555756484, sizeof(int));
  PIN_SafeCopy(& reg3, (void*)0x555555756482, sizeof(int));
  PIN_SafeCopy(& reg4, (void*)0x555555756486, sizeof(int));

  outFile << "PC: 0x" << hex << pc << " REG1: 0x" << hex << reg1 << " REG2: 0x" << hex << reg2 << " REG3: 0x" << hex << reg3 << " REG4: 0x" << hex << reg4 << endl;

}

void INS1(CONTEXT* ctx) {
  PIN_REGISTER rdx, rcx;
  PIN_GetContextRegval(ctx, REG_RCX, reinterpret_cast<UINT8 *>(&rcx)); // register offset
  PIN_GetContextRegval(ctx, REG_RDX, reinterpret_cast<UINT8 *>(&rdx)); // value

  outFile << "MEMSTORE: REG" << (int) ((rcx.dword[0] / 8) +1) << " = 0x" << hex << +rdx.byte[0] << endl;
}

int get_register_number_by_address(long long address) {
  switch (address)
    {
    case 0x555555756488:
      return 1;
    case 0x555555756486:
      return 4;
    case 0x555555756484:
      return 2;
    case 0x555555756482:
      return 3;
    default:
      std::cerr << "oy there's an error bro" << std::endl;
      exit(0xff);
    } 


}

void INS2(CONTEXT* ctx) {
  PIN_REGISTER rdx, rcx;
  PIN_GetContextRegval(ctx, REG_RCX, reinterpret_cast<UINT8 *>(&rcx)); // store register offset
  PIN_GetContextRegval(ctx, REG_RDX, reinterpret_cast<UINT8 *>(&rdx)); // load register offset 
  outFile << "REGSTORE: REG" << (int) ((rcx.dword[0] / 8) +1) << " = REG" << get_register_number_by_address(rdx.qword[0]) << endl;

}

void ADD(CONTEXT* ctx) {
  PIN_REGISTER rsi, rdx; 
  PIN_GetContextRegval(ctx, REG_RSI, reinterpret_cast<UINT8 *>(&rsi)); // store register offset
  PIN_GetContextRegval(ctx, REG_RDX, reinterpret_cast<UINT8 *>(&rdx)); // memory value
  outFile << "ADD: REG" << (int) ((rsi.dword[0] / 8) +1) << " += " << "0x" << rdx.dword[0] << endl;
}

void NOT(CONTEXT* ctx) {
  PIN_REGISTER rax; 
  PIN_GetContextRegval(ctx, REG_RAX, reinterpret_cast<UINT8 *>(&rax)); // register

  outFile << "NOT: REG" << +rax.byte[0] << " = ~REG" << +rax.byte[0] << endl;

}

void MUL(CONTEXT* ctx) {
  PIN_REGISTER rsi, rdx; 
  PIN_GetContextRegval(ctx, REG_RSI, reinterpret_cast<UINT8 *>(&rsi));
  PIN_GetContextRegval(ctx, REG_RDX, reinterpret_cast<UINT8 *>(&rdx));
  outFile << "MUL: REG" << (int) ((rsi.dword[0] / 8) +1) << " *= " << "0x" << rdx.dword[0] << endl;
}

void INPUT(CONTEXT* ctx) {
  outFile << "  INPUT" << endl;
}

void OUTPUT(CONTEXT* ctx) {
  outFile << "  OUTPUT" << endl;
}
void XIT(CONTEXT* ctx) {
  outFile << "XIT" << endl;
}

void REG_ADD_1(CONTEXT* ctx ) {
  PIN_REGISTER rdx;
  PIN_GetContextRegval(ctx, REG_RDX, reinterpret_cast<UINT8 *>(&rdx));
  outFile << "REGADD: REG" << (int) ((rdx.dword[0] / 8) +1) << " += ";
}
void REG_ADD_2(CONTEXT* ctx ) {
  PIN_REGISTER rdx;
  PIN_GetContextRegval(ctx, REG_RDX, reinterpret_cast<UINT8 *>(&rdx));
  outFile << "REG" << (int) ((rdx.dword[0] / 8) +1) << endl;
}

VOID callback_instruction(INS ins, VOID *v) {
  if (INS_Address(ins) == 0x555555554CBD) {
    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)lolbro, IARG_CONTEXT,
                   IARG_END);
  }

  if (INS_Address(ins) == 0x555555554914) {
    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)INS1, IARG_CONTEXT,
                   IARG_END);        
  }

  if (INS_Address(ins) == 0x55555555499C) {
    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)INS2, IARG_CONTEXT,
                   IARG_END);        
  }
  if (INS_Address(ins) == 0x555555554AC0) {
    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)ADD, IARG_CONTEXT,
                   IARG_END);
  }

  if (INS_Address(ins) == 0x555555554B9E) {
    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)NOT, IARG_CONTEXT,
                   IARG_END);
  }
  if (INS_Address(ins) == 0x555555554B58) {
    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)MUL, IARG_CONTEXT,
                   IARG_END);
  }
  if (INS_Address(ins) == 0x555555554866) {
    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)INPUT, IARG_CONTEXT,
                   IARG_END);
  }
  if (INS_Address(ins) == 0x555555554874) {
    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)OUTPUT, IARG_CONTEXT,
                   IARG_END);
  }
  if (INS_Address(ins) == 0x555555554893) {
    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)XIT, IARG_CONTEXT,
                   IARG_END);
  }

  if (INS_Address(ins) == 0x5555555549FA) {
    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)REG_ADD_1, IARG_CONTEXT,
                   IARG_END);

  }

  if (INS_Address(ins) == 0x555555554A1D) {
    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)REG_ADD_2, IARG_CONTEXT,
                   IARG_END);
  }


}

VOID fini(INT32 code, VOID *v) { outFile.close(); }

int main(int argc, char *argv[]) {
  if (PIN_Init(argc, argv)) {
    perror("init");
    return 0;
  }
  outFile.open(KnobOutputFile.Value().c_str());
  INS_AddInstrumentFunction(callback_instruction, 0);
  PIN_AddFiniFunction(fini, 0);
  PIN_StartProgram();
  return 0;
}

I just run the program with this tool, and then like solve the constraints with a z3 script:

import z3
s = z3.Solver()
x = [z3.BitVec("x_{}".format(i), 8) for i in range(22)]
for c in x:
    s.add(z3.And(
            c >= 0x20,
            c < 0x7f))


""" PC: 0x173 REG1: 0x46 REG2: 0xff20 REG3: 0xff20002d REG4: 0x460000
MEMSTORE: REG3 = 0x74
PC: 0x176 REG1: 0x46 REG2: 0xff20 REG3: 0xff200074 REG4: 0x460000
ADD: REG3 += 0x45
PC: 0x179 REG1: 0x46 REG2: 0xff20 REG3: 0xff2000b9 REG4: 0x460000
REGADD: REG1 += REG3
PC: 0x17a REG1: 0xff REG2: 0xff20 REG3: 0xff2000b9 REG4: 0xff0000
NOT: REG0 = ~REG0
PC: 0x17b REG1: 0xff00 REG2: 0xff20 REG3: 0xff2000b9 REG4: 0xff000000
ADD: REG1 += 0x110
PC: 0x17e REG1: 0x10 REG2: 0xff20 REG3: 0xff2000b9 REG4: 0x100000
REGADD: REG4 += REG1
PC: 0x17f REG1: 0x10 REG2: 0x10ff20 REG3: 0xff2000b9 REG4: 0x100010
MEMSTORE: REG1 = 0x0
PC: 0x182 REG1: 0x0 REG2: 0x10ff20 REG3: 0xff2000b9 REG4: 0x10
"""

s.add((~(x[0] + 0x74 + 0x45))+0x110 == 0)

#FLAG{VGHIJKLMNOPQRSTUVWXYZ[

""" PC: 0x183 REG1: 0x47 REG2: 0xff20 REG3: 0xff2000b9 REG4: 0x470000
MUL: REG1 *= 0x3c
PC: 0x186 REG1: 0x10a4 REG2: 0xff20 REG3: 0xff2000b9 REG4: 0x10a40000
MEMSTORE: REG3 = 0x59
PC: 0x189 REG1: 0x10a4 REG2: 0xff20 REG3: 0xff200059 REG4: 0x10a40000
REGADD: REG1 += REG3
PC: 0x18a REG1: 0x10fd REG2: 0xff20 REG3: 0xff200059 REG4: 0x10fd0000
ADD: REG1 += 0xed9b
PC: 0x18d REG1: 0xfe98 REG2: 0xff20 REG3: 0xff200059 REG4: 0xfe980000
REGADD: REG4 += REG1
PC: 0x18e REG1: 0xfe98 REG2: 0xfe98ff20 REG3: 0xff200059 REG4: 0xfe98fe98
MEMSTORE: REG1 = 0x0
PC: 0x191 REG1: 0x0 REG2: 0xfe98ff20 REG3: 0xff200059 REG4: 0xfe98
"""
s.add(x[1] * 0x3c + 0x59 + 0xed9b == 0)

""" PC: 0x192 REG1: 0x48 REG2: 0xfe98ff20 REG3: 0xff200059 REG4: 0x48fe98
MUL: REG1 *= 0x30
PC: 0x195 REG1: 0xd80 REG2: 0xfe98ff20 REG3: 0xff200059 REG4: 0xd80fe98
MEMSTORE: REG3 = 0x3b
PC: 0x198 REG1: 0xd80 REG2: 0xfe98ff20 REG3: 0xff20003b REG4: 0xd80fe98
REGADD: REG1 += REG3
PC: 0x199 REG1: 0xdbb REG2: 0xfe98ff20 REG3: 0xff20003b REG4: 0xdbbfe98
ADD: REG1 += 0xedf5
PC: 0x19c REG1: 0xfbb0 REG2: 0xfe98ff20 REG3: 0xff20003b REG4: 0xfbb0fe98
REGADD: REG4 += REG1
PC: 0x19d REG1: 0xfbb0 REG2: 0xfa48ff20 REG3: 0xff20003b REG4: 0xfbb0fa48
MEMSTORE: REG1 = 0x0
PC: 0x1a0 REG1: 0x0 REG2: 0xfa48ff20 REG3: 0xff20003b REG4: 0xfa48
"""
s.add(x[2] * 0x30 + 0x3b + 0xf5 == 0)

""" PC: 0x1a1 REG1: 0x49 REG2: 0xfa48ff20 REG3: 0xff20003b REG4: 0x49fa48
MUL: REG1 *= 0x9
PC: 0x1a4 REG1: 0x291 REG2: 0xfa48ff20 REG3: 0xff20003b REG4: 0x291fa48
MEMSTORE: REG3 = 0x21
PC: 0x1a7 REG1: 0x291 REG2: 0xfa48ff20 REG3: 0xff200021 REG4: 0x291fa48
REGADD: REG1 += REG3
PC: 0x1a8 REG1: 0x2b2 REG2: 0xfa48ff20 REG3: 0xff200021 REG4: 0x2b2fa48
ADD: REG1 += 0xfd96
PC: 0x1ab REG1: 0x48 REG2: 0xfa48ff20 REG3: 0xff200021 REG4: 0x48fa48
REGADD: REG4 += REG1
PC: 0x1ac REG1: 0x48 REG2: 0xfa90ff20 REG3: 0xff200021 REG4: 0x48fa90
MEMSTORE: REG1 = 0x0
PC: 0x1af REG1: 0x0 REG2: 0xfa90ff20 REG3: 0xff200021 REG4: 0xfa90
"""
s.add(x[3] * 0x9 + 0x21 + 0x96 == 0)

""" PC: 0x1b0 REG1: 0x4a REG2: 0xfa90ff20 REG3: 0xff200021 REG4: 0x4afa90
MEMSTORE: REG3 = 0x92
PC: 0x1b3 REG1: 0x4a REG2: 0xfa90ff20 REG3: 0xff20ff92 REG4: 0x4afa90
REGADD: REG1 += REG3
PC: 0x1b4 REG1: 0xffdc REG2: 0xfa90ff20 REG3: 0xff20ff92 REG4: 0xffdcfa90
REGADD: REG4 += REG1
PC: 0x1b5 REG1: 0xffdc REG2: 0xfa6cff20 REG3: 0xff20ff92 REG4: 0xffdcfa6c
MEMSTORE: REG1 = 0x0
PC: 0x1b8 REG1: 0x0 REG2: 0xfa6cff20 REG3: 0xff20ff92 REG4: 0xfa6c
"""
s.add(x[4] + 0x92 == 0)

""" PC: 0x1b9 REG1: 0x4b REG2: 0xfa6cff20 REG3: 0xff20ff92 REG4: 0x4bfa6c
MEMSTORE: REG3 = 0x9f
PC: 0x1bc REG1: 0x4b REG2: 0xfa6cff20 REG3: 0xff20ff9f REG4: 0x4bfa6c
REGADD: REG1 += REG3
PC: 0x1bd REG1: 0xffea REG2: 0xfa6cff20 REG3: 0xff20ff9f REG4: 0xffeafa6c
REGADD: REG4 += REG1
PC: 0x1be REG1: 0xffea REG2: 0xfa56ff20 REG3: 0xff20ff9f REG4: 0xffeafa56
MEMSTORE: REG1 = 0x0
PC: 0x1c1 REG1: 0x0 REG2: 0xfa56ff20 REG3: 0xff20ff9f REG4: 0xfa56"""

s.add(x[5] + 0x9f == 0)

""" PC: 0x1c2 REG1: 0x4c REG2: 0xfa56ff20 REG3: 0xff20ff9f REG4: 0x4cfa56
MEMSTORE: REG3 = 0x6a
PC: 0x1c5 REG1: 0x4c REG2: 0xfa56ff20 REG3: 0xff20006a REG4: 0x4cfa56
ADD: REG3 += 0x38
PC: 0x1c8 REG1: 0x4c REG2: 0xfa56ff20 REG3: 0xff2000a2 REG4: 0x4cfa56
REGADD: REG1 += REG3
PC: 0x1c9 REG1: 0xee REG2: 0xfa56ff20 REG3: 0xff2000a2 REG4: 0xeefa56
NOT: REG0 = ~REG0
PC: 0x1ca REG1: 0xff11 REG2: 0xfa56ff20 REG3: 0xff2000a2 REG4: 0xff11fa56
ADD: REG1 += 0x10f
PC: 0x1cd REG1: 0x20 REG2: 0xfa56ff20 REG3: 0xff2000a2 REG4: 0x20fa56
REGADD: REG4 += REG1
PC: 0x1ce REG1: 0x20 REG2: 0xfa76ff20 REG3: 0xff2000a2 REG4: 0x20fa76
MEMSTORE: REG1 = 0x0
PC: 0x1d1 REG1: 0x0 REG2: 0xfa76ff20 REG3: 0xff2000a2 REG4: 0xfa76"""

s.add(~(0x6a + 0x38 + x[6]) + 0x10f == 0)

""" PC: 0x1d2 REG1: 0x4d REG2: 0xfa76ff20 REG3: 0xff2000a2 REG4: 0x4dfa76
MEMSTORE: REG3 = 0x87
PC: 0x1d5 REG1: 0x4d REG2: 0xfa76ff20 REG3: 0xff20ff87 REG4: 0x4dfa76
REGADD: REG1 += REG3
PC: 0x1d6 REG1: 0xffd4 REG2: 0xfa76ff20 REG3: 0xff20ff87 REG4: 0xffd4fa76
REGADD: REG4 += REG1
PC: 0x1d7 REG1: 0xffd4 REG2: 0xfa4aff20 REG3: 0xff20ff87 REG4: 0xffd4fa4a
MEMSTORE: REG1 = 0x0
PC: 0x1da REG1: 0x0 REG2: 0xfa4aff20 REG3: 0xff20ff87 REG4: 0xfa4a"""

s.add(x[7] + 0x87 == 0)

""" PC: 0x1db REG1: 0x4e REG2: 0xfa4aff20 REG3: 0xff20ff87 REG4: 0x4efa4a
MEMSTORE: REG3 = 0x8d
PC: 0x1de REG1: 0x4e REG2: 0xfa4aff20 REG3: 0xff20ff8d REG4: 0x4efa4a
REGADD: REG1 += REG3
PC: 0x1df REG1: 0xffdb REG2: 0xfa4aff20 REG3: 0xff20ff8d REG4: 0xffdbfa4a
REGADD: REG4 += REG1
PC: 0x1e0 REG1: 0xffdb REG2: 0xfa25ff20 REG3: 0xff20ff8d REG4: 0xffdbfa25
MEMSTORE: REG1 = 0x0
PC: 0x1e3 REG1: 0x0 REG2: 0xfa25ff20 REG3: 0xff20ff8d REG4: 0xfa25"""
s.add(x[8] + 0x8d == 0)

""" PC: 0x1e4 REG1: 0x4f REG2: 0xfa25ff20 REG3: 0xff20ff8d REG4: 0x4ffa25
MEMSTORE: REG3 = 0x38
PC: 0x1e7 REG1: 0x4f REG2: 0xfa25ff20 REG3: 0xff200038 REG4: 0x4ffa25
ADD: REG3 += 0x4f
PC: 0x1ea REG1: 0x4f REG2: 0xfa25ff20 REG3: 0xff200087 REG4: 0x4ffa25
REGADD: REG1 += REG3
PC: 0x1eb REG1: 0xd6 REG2: 0xfa25ff20 REG3: 0xff200087 REG4: 0xd6fa25
NOT: REG0 = ~REG0
PC: 0x1ec REG1: 0xff29 REG2: 0xfa25ff20 REG3: 0xff200087 REG4: 0xff29fa25
ADD: REG1 += 0xf1
PC: 0x1ef REG1: 0x1a REG2: 0xfa25ff20 REG3: 0xff200087 REG4: 0x1afa25
REGADD: REG4 += REG1
PC: 0x1f0 REG1: 0x1a REG2: 0xfa3fff20 REG3: 0xff200087 REG4: 0x1afa3f
MEMSTORE: REG1 = 0x0
PC: 0x1f3 REG1: 0x0 REG2: 0xfa3fff20 REG3: 0xff200087 REG4: 0xfa3f"""
s.add(~(x[9] + 0x38 + 0x4f) + 0xf1 == 0)

""" PC: 0x1f4 REG1: 0x50 REG2: 0xfa3fff20 REG3: 0xff200087 REG4: 0x50fa3f
MEMSTORE: REG3 = 0x60
PC: 0x1f7 REG1: 0x50 REG2: 0xfa3fff20 REG3: 0xff200060 REG4: 0x50fa3f
ADD: REG3 += 0x61
PC: 0x1fa REG1: 0x50 REG2: 0xfa3fff20 REG3: 0xff2000c1 REG4: 0x50fa3f
REGADD: REG1 += REG3
PC: 0x1fb REG1: 0x111 REG2: 0xfa3fff20 REG3: 0xff2000c1 REG4: 0x111fa3f
NOT: REG0 = ~REG0
PC: 0x1fc REG1: 0xfeee REG2: 0xfa3fff20 REG3: 0xff2000c1 REG4: 0xfeeefa3f
ADD: REG1 += 0x135
PC: 0x1ff REG1: 0x23 REG2: 0xfa3fff20 REG3: 0xff2000c1 REG4: 0x23fa3f
REGADD: REG4 += REG1
PC: 0x200 REG1: 0x23 REG2: 0xfa62ff20 REG3: 0xff2000c1 REG4: 0x23fa62
MEMSTORE: REG1 = 0x0
PC: 0x203 REG1: 0x0 REG2: 0xfa62ff20 REG3: 0xff2000c1 REG4: 0xfa62"""
s.add( ~(x[10] + 0x60 + 0x61) + 0x135 == 0)

""" PC: 0x204 REG1: 0x51 REG2: 0xfa62ff20 REG3: 0xff2000c1 REG4: 0x51fa62
MEMSTORE: REG3 = 0x5b
PC: 0x207 REG1: 0x51 REG2: 0xfa62ff20 REG3: 0xff20005b REG4: 0x51fa62
ADD: REG3 += 0x5e
PC: 0x20a REG1: 0x51 REG2: 0xfa62ff20 REG3: 0xff2000b9 REG4: 0x51fa62
REGADD: REG1 += REG3
PC: 0x20b REG1: 0x10a REG2: 0xfa62ff20 REG3: 0xff2000b9 REG4: 0x10afa62
NOT: REG0 = ~REG0
PC: 0x20c REG1: 0xfef5 REG2: 0xfa62ff20 REG3: 0xff2000b9 REG4: 0xfef5fa62
ADD: REG1 += 0x119
PC: 0x20f REG1: 0xe REG2: 0xfa62ff20 REG3: 0xff2000b9 REG4: 0xefa62
REGADD: REG4 += REG1
PC: 0x210 REG1: 0xe REG2: 0xfa70ff20 REG3: 0xff2000b9 REG4: 0xefa70
MEMSTORE: REG1 = 0x0
PC: 0x213 REG1: 0x0 REG2: 0xfa70ff20 REG3: 0xff2000b9 REG4: 0xfa70"""
s.add( ~(x[11] + 0x5b + 0x5e) + 0x119 == 0)

""" PC: 0x214 REG1: 0x52 REG2: 0xfa70ff20 REG3: 0xff2000b9 REG4: 0x52fa70
MEMSTORE: REG3 = 0x48
PC: 0x217 REG1: 0x52 REG2: 0xfa70ff20 REG3: 0xff200048 REG4: 0x52fa70
ADD: REG3 += 0x32
PC: 0x21a REG1: 0x52 REG2: 0xfa70ff20 REG3: 0xff20007a REG4: 0x52fa70
REGADD: REG1 += REG3
PC: 0x21b REG1: 0xcc REG2: 0xfa70ff20 REG3: 0xff20007a REG4: 0xccfa70
NOT: REG0 = ~REG0
PC: 0x21c REG1: 0xff33 REG2: 0xfa70ff20 REG3: 0xff20007a REG4: 0xff33fa70
ADD: REG1 += 0xe4
PC: 0x21f REG1: 0x17 REG2: 0xfa70ff20 REG3: 0xff20007a REG4: 0x17fa70
REGADD: REG4 += REG1
PC: 0x220 REG1: 0x17 REG2: 0xfa87ff20 REG3: 0xff20007a REG4: 0x17fa87
MEMSTORE: REG1 = 0x0
PC: 0x223 REG1: 0x0 REG2: 0xfa87ff20 REG3: 0xff20007a REG4: 0xfa87"""
s.add( ~(x[12] + 0x48 + 0x32) + 0xe4 == 0)

""" PC: 0x224 REG1: 0x53 REG2: 0xfa87ff20 REG3: 0xff20007a REG4: 0x53fa87
MEMSTORE: REG3 = 0x8d
PC: 0x227 REG1: 0x53 REG2: 0xfa87ff20 REG3: 0xff20ff8d REG4: 0x53fa87
REGADD: REG1 += REG3
PC: 0x228 REG1: 0xffe0 REG2: 0xfa87ff20 REG3: 0xff20ff8d REG4: 0xffe0fa87
REGADD: REG4 += REG1
PC: 0x229 REG1: 0xffe0 REG2: 0xfa67ff20 REG3: 0xff20ff8d REG4: 0xffe0fa67
MEMSTORE: REG1 = 0x0
PC: 0x22c REG1: 0x0 REG2: 0xfa67ff20 REG3: 0xff20ff8d REG4: 0xfa67"""
s.add( x[13] + 0x8d  == 0)

"""PC: 0x22d REG1: 0x54 REG2: 0xfa67ff20 REG3: 0xff20ff8d REG4: 0x54fa67
MEMSTORE: REG3 = 0x28
PC: 0x230 REG1: 0x54 REG2: 0xfa67ff20 REG3: 0xff200028 REG4: 0x54fa67
ADD: REG3 += 0x58
PC: 0x233 REG1: 0x54 REG2: 0xfa67ff20 REG3: 0xff200080 REG4: 0x54fa67
REGADD: REG1 += REG3
PC: 0x234 REG1: 0xd4 REG2: 0xfa67ff20 REG3: 0xff200080 REG4: 0xd4fa67
NOT: REG0 = ~REG0
PC: 0x235 REG1: 0xff2b REG2: 0xfa67ff20 REG3: 0xff200080 REG4: 0xff2bfa67
ADD: REG1 += 0xe0
PC: 0x238 REG1: 0xb REG2: 0xfa67ff20 REG3: 0xff200080 REG4: 0xbfa67
REGADD: REG4 += REG1
PC: 0x239 REG1: 0xb REG2: 0xfa72ff20 REG3: 0xff200080 REG4: 0xbfa72
MEMSTORE: REG1 = 0x0
PC: 0x23c REG1: 0x0 REG2: 0xfa72ff20 REG3: 0xff200080 REG4: 0xfa72"""
s.add(~(x[14] + 0x28 + 0x58) + 0xe0 == 0)

"""PC: 0x23d REG1: 0x55 REG2: 0xfa72ff20 REG3: 0xff200080 REG4: 0x55fa72
MEMSTORE: REG3 = 0x50
PC: 0x240 REG1: 0x55 REG2: 0xfa72ff20 REG3: 0xff200050 REG4: 0x55fa72
ADD: REG3 += 0x74
PC: 0x243 REG1: 0x55 REG2: 0xfa72ff20 REG3: 0xff2000c4 REG4: 0x55fa72
REGADD: REG1 += REG3
PC: 0x244 REG1: 0x119 REG2: 0xfa72ff20 REG3: 0xff2000c4 REG4: 0x119fa72
NOT: REG0 = ~REG0
PC: 0x245 REG1: 0xfee6 REG2: 0xfa72ff20 REG3: 0xff2000c4 REG4: 0xfee6fa72
ADD: REG1 += 0x126
PC: 0x248 REG1: 0xc REG2: 0xfa72ff20 REG3: 0xff2000c4 REG4: 0xcfa72
REGADD: REG4 += REG1
PC: 0x249 REG1: 0xc REG2: 0xfa7eff20 REG3: 0xff2000c4 REG4: 0xcfa7e
MEMSTORE: REG1 = 0x0
PC: 0x24c REG1: 0x0 REG2: 0xfa7eff20 REG3: 0xff2000c4 REG4: 0xfa7e"""
s.add(~(x[15] + 0x50 + 0x74) + 0x126 == 0)

""" PC: 0x24d REG1: 0x56 REG2: 0xfa7eff20 REG3: 0xff2000c4 REG4: 0x56fa7e
MEMSTORE: REG3 = 0x26
PC: 0x250 REG1: 0x56 REG2: 0xfa7eff20 REG3: 0xff200026 REG4: 0x56fa7e
ADD: REG3 += 0x3e
PC: 0x253 REG1: 0x56 REG2: 0xfa7eff20 REG3: 0xff200064 REG4: 0x56fa7e
REGADD: REG1 += REG3
PC: 0x254 REG1: 0xba REG2: 0xfa7eff20 REG3: 0xff200064 REG4: 0xbafa7e
NOT: REG0 = ~REG0
PC: 0x255 REG1: 0xff45 REG2: 0xfa7eff20 REG3: 0xff200064 REG4: 0xff45fa7e
ADD: REG1 += 0xd2
PC: 0x258 REG1: 0x17 REG2: 0xfa7eff20 REG3: 0xff200064 REG4: 0x17fa7e
REGADD: REG4 += REG1
PC: 0x259 REG1: 0x17 REG2: 0xfa95ff20 REG3: 0xff200064 REG4: 0x17fa95
MEMSTORE: REG1 = 0x0
PC: 0x25c REG1: 0x0 REG2: 0xfa95ff20 REG3: 0xff200064 REG4: 0xfa95"""
s.add( ~(x[16] + 0x26 + 0x3e) + 0xd2 == 0)

""" PC: 0x25d REG1: 0x57 REG2: 0xfa95ff20 REG3: 0xff200064 REG4: 0x57fa95
MEMSTORE: REG3 = 0x5e
PC: 0x260 REG1: 0x57 REG2: 0xfa95ff20 REG3: 0xff20005e REG4: 0x57fa95
ADD: REG3 += 0x6a
PC: 0x263 REG1: 0x57 REG2: 0xfa95ff20 REG3: 0xff2000c8 REG4: 0x57fa95
REGADD: REG1 += REG3
PC: 0x264 REG1: 0x11f REG2: 0xfa95ff20 REG3: 0xff2000c8 REG4: 0x11ffa95
NOT: REG0 = ~REG0
PC: 0x265 REG1: 0xfee0 REG2: 0xfa95ff20 REG3: 0xff2000c8 REG4: 0xfee0fa95
ADD: REG1 += 0x12a
PC: 0x268 REG1: 0xa REG2: 0xfa95ff20 REG3: 0xff2000c8 REG4: 0xafa95
REGADD: REG4 += REG1
PC: 0x269 REG1: 0xa REG2: 0xfa9fff20 REG3: 0xff2000c8 REG4: 0xafa9f
MEMSTORE: REG1 = 0x0
PC: 0x26c REG1: 0x0 REG2: 0xfa9fff20 REG3: 0xff2000c8 REG4: 0xfa9f"""
s.add( ~(x[17] + 0x5e + 0x6a) + 0x12a == 0)

""" PC: 0x26d REG1: 0x58 REG2: 0xfa9fff20 REG3: 0xff2000c8 REG4: 0x58fa9f
MEMSTORE: REG3 = 0x6d
PC: 0x270 REG1: 0x58 REG2: 0xfa9fff20 REG3: 0xff20006d REG4: 0x58fa9f
ADD: REG3 += 0x2a
PC: 0x273 REG1: 0x58 REG2: 0xfa9fff20 REG3: 0xff200097 REG4: 0x58fa9f
REGADD: REG1 += REG3
PC: 0x274 REG1: 0xef REG2: 0xfa9fff20 REG3: 0xff200097 REG4: 0xeffa9f
NOT: REG0 = ~REG0
PC: 0x275 REG1: 0xff10 REG2: 0xfa9fff20 REG3: 0xff200097 REG4: 0xff10fa9f
ADD: REG1 += 0x112
PC: 0x278 REG1: 0x22 REG2: 0xfa9fff20 REG3: 0xff200097 REG4: 0x22fa9f
REGADD: REG4 += REG1
PC: 0x279 REG1: 0x22 REG2: 0xfac1ff20 REG3: 0xff200097 REG4: 0x22fac1
MEMSTORE: REG1 = 0x0
PC: 0x27c REG1: 0x0 REG2: 0xfac1ff20 REG3: 0xff200097 REG4: 0xfac1"""
s.add( ~(x[18] + 0x6d + 0x2a) + 0x112 == 0)

""" PC: 0x27d REG1: 0x59 REG2: 0xfac1ff20 REG3: 0xff200097 REG4: 0x59fac1
MEMSTORE: REG3 = 0x97
PC: 0x280 REG1: 0x59 REG2: 0xfac1ff20 REG3: 0xff20ff97 REG4: 0x59fac1
REGADD: REG1 += REG3
PC: 0x281 REG1: 0xfff0 REG2: 0xfac1ff20 REG3: 0xff20ff97 REG4: 0xfff0fac1
REGADD: REG4 += REG1
PC: 0x282 REG1: 0xfff0 REG2: 0xfab1ff20 REG3: 0xff20ff97 REG4: 0xfff0fab1
MEMSTORE: REG1 = 0x0
PC: 0x285 REG1: 0x0 REG2: 0xfab1ff20 REG3: 0xff20ff97 REG4: 0xfab1"""
s.add( x[19] + 0x97 == 0 )

""" PC: 0x286 REG1: 0x5a REG2: 0xfab1ff20 REG3: 0xff20ff97 REG4: 0x5afab1
MEMSTORE: REG3 = 0x92
PC: 0x289 REG1: 0x5a REG2: 0xfab1ff20 REG3: 0xff20ff92 REG4: 0x5afab1
REGADD: REG1 += REG3
PC: 0x28a REG1: 0xffec REG2: 0xfab1ff20 REG3: 0xff20ff92 REG4: 0xffecfab1
REGADD: REG4 += REG1
PC: 0x28b REG1: 0xffec REG2: 0xfa9dff20 REG3: 0xff20ff92 REG4: 0xffecfa9d
MEMSTORE: REG1 = 0x0
PC: 0x28e REG1: 0x0 REG2: 0xfa9dff20 REG3: 0xff20ff92 REG4: 0xfa9d"""
s.add( x[20] + 0x92 == 0 )


""" PC: 0x28f REG1: 0x5b REG2: 0xfa9dff20 REG3: 0xff20ff92 REG4: 0x5bfa9d
MEMSTORE: REG3 = 0x99
PC: 0x292 REG1: 0x5b REG2: 0xfa9dff20 REG3: 0xff20ff99 REG4: 0x5bfa9d
REGADD: REG1 += REG3
PC: 0x293 REG1: 0xfff4 REG2: 0xfa9dff20 REG3: 0xff20ff99 REG4: 0xfff4fa9d
REGADD: REG4 += REG1
PC: 0x294 REG1: 0xfff4 REG2: 0xfa91ff20 REG3: 0xff20ff99 REG4: 0xfff4fa91
MEMSTORE: REG1 = 0x0
PC: 0x297 REG1: 0x0 REG2: 0xfa91ff20 REG3: 0xff20ff99 REG4: 0xfa91"""
s.add( x[21] + 0x99 == 0)

""" PC: 0x298 REG1: 0xa REG2: 0xfa91ff20 REG3: 0xff20ff99 REG4: 0xafa91
MUL: REG1 *= 0x27
PC: 0x29b REG1: 0x186 REG2: 0xfa91ff20 REG3: 0xff20ff99 REG4: 0x186fa91
MEMSTORE: REG3 = 0x40
PC: 0x29e REG1: 0x186 REG2: 0xfa91ff20 REG3: 0xff200040 REG4: 0x186fa91
REGADD: REG1 += REG3
PC: 0x29f REG1: 0x1c6 REG2: 0xfa91ff20 REG3: 0xff200040 REG4: 0x1c6fa91
ADD: REG1 += 0xecb5
PC: 0x2a2 REG1: 0xee7b REG2: 0xfa91ff20 REG3: 0xff200040 REG4: 0xee7bfa91
REGADD: REG4 += REG1
PC: 0x2a3 REG1: 0xee7b REG2: 0xe90cff20 REG3: 0xff200040 REG4: 0xee7be90c
REGSTORE: REG1 = REG4
PC: 0x2a4 REG1: 0xe90c REG2: 0xe90cff20 REG3: 0xff200040 REG4: 0xe90ce90c
MEMSTORE: REG2 = 0x67
PC: 0x2a7 REG1: 0xe90c REG2: 0xe90c0067 REG3: 0x670040 REG4: 0xe90ce90c
PC: 0x67 REG1: 0xe90c REG2: 0xe90c0067 REG3: 0x670040 REG4: 0xe90ce90c
MEMSTORE: REG1 = 0x1
PC: 0x6a REG1: 0x1 REG2: 0xe90c0067 REG3: 0x670040 REG4: 0x1e90c
MEMSTORE: REG2 = 0x19
PC: 0x6d REG1: 0x1 REG2: 0xe90c0019 REG3: 0x190040 REG4: 0x1e90c
ADD: REG2 += 0x3e
PC: 0x70 REG1: 0x1 REG2: 0xe90c0057 REG3: 0x570040 REG4: 0x1e90c"""


print(s.check())
print(s.model())
m = s.model()
flag = ""
for c in x:
    flag += chr(m[c].as_long())
print(flag)

Solution for Handbook

Some movfuscator shit, takes 16 bytes of input and compares it with some random string, use findcrypt, identify aes sbox, identify the key which was AAAABBBBCCCCDDDD.

Solution for Lim Rootkit

Use resource hacker, dump the 2 files, xor some 2 strings and get the flag.

Solution for Dead lock

Hardest part is finding a working tool it's UndertaleModtool, there is a room called secret, just replace the order of room0 and secret, save and get the flag

Solution for login if you can

Chrome can disassemble wasm files, look at the data assume it's xor, xor data with some constant 1 byte key. Get the flag.


Thursday, April 9, 2020

Bsides 2020, chameleon reverse engineering writeup

 

Introduction

BsidesSF had really really good reverse engineering challenges, but I loved two challenges. One windows reverse challenge called chameleon and another esp32 firmware reverse challenge called smart-locky which I didn't manage to solve in time.

The challenge

Problem statement

We are given two files chameleon.exe and flag.png.enc, this looks trivial enough we need to reverse the encryption algorithm to give us the original flag.png.

The problem statment also says that file was encrypted in the last months.

Reading the statment it looked like a simple challenge where you brute force the seed of a default mt19937 implementation, but the problem had only two solves, so I thought there must be some twist.

First glance at the binary

When you first execute the binary it gives you two options, to encrypt or decrypt files.

Usage: chameleon.exe [--encrypt|<--decrypt --id=<id>>] <infile> <outfile>

Example: chameleon.exe --encrypt plaintext.txt ciphertext.txt
Example: chameleon.exe --decrypt --id=abcd1234 ciphertext.txt plaintext.txt

I knew where to look and I didn't focus on the decryption stuff to be honest.

Reversing the binary

After identifying the encrypt function and changing some function names here is what the assembly looks like (you might want to zoom):

encrypt_func.png

And here is what the hexrays decompiler gives us after changing some types and variable names:

void __usercall encrypt(const CHAR *output@<ebx>, const CHAR *input)
{
  void *file_content;
  BYTE *new_file_ptr;
  FILE *v4;
  DWORD pdwDataLen;
  HCRYPTKEY phKey;
  HCRYPTPROV phProv;
  byte pbData[20];
  char v9[8];

  file_content = read_file(input, &pdwDataLen);
  new_file_ptr = (BYTE *)realloc(file_content, pdwDataLen + 16);
  if ( !CryptAcquireContextA(&phProv, 0, "Microsoft Enhanced Cryptographic Provider v1.0", 1u, 0xF0000000) )
    goto failure;
  mersenne_twister((int)v9);                    // generate 8 byte key
  *(_WORD *)&pbData[2] = 0;
  pbData[0] = 8;
  *(_DWORD *)&pbData[8] = 8;
  *(_DWORD *)&pbData[12] = *(_DWORD *)v9;
  *(_DWORD *)&pbData[16] = *(_DWORD *)&v9[4];
  pbData[1] = 2;
  *(_DWORD *)&pbData[4] = 26113;
  if ( !CryptImportKey(phProv, pbData, 0x14u, 0, 1u, &phKey)
    || !CryptEncrypt(phKey, 0, 1, 0, new_file_ptr, &pdwDataLen, pdwDataLen + 8) )
  {
failure:
    v4 = _iob_func();
    fprintf(v4 + 2, "Encryption failed\n");
    exit(1);
  }
  sub_E51AA0((int)v9);
  sub_E51F50(output, new_file_ptr, pdwDataLen);
  free(new_file_ptr);
}

All right looks simple enough, here are the algorithm steps:

  • call to CryptAcquireContextA, this is like an encryption algorithm factory in windows
  • call to a function that I called mersenne_twister
  • sets some values in pbData
  • call to CryptImportKey
  • call to CryptEncrypt

If you read this tutorial you can understand how CryptImportKey works.

This is an example key blob from the page above:

BYTE DesKeyBlob[] = {
    0x08,0x02,0x00,0x00,0x01,0x66,0x00,0x00, // BLOB header 
    0x08,0x00,0x00,0x00,                     // key length, in bytes
    0xf1,0x0e,0x25,0x7c,0x6b,0xce,0x0d,0x34  // DES key with parity
    };

We can see that our example also uses DES using CBC mode of operation, we also see that the DES key is passed as the last 8 bytes (with byte of parity) of the array.

Ok so the bread and butter of this problem is knowing how the mersenne twister works.

Reversing mersenne_twister

This is what the code looks like:

mersenne_twister.png

We can see that there are two loops.

Here is the decompiled code, it looks really clean after some type changes

char __usercall mersenne_twister@<al>(int a1@<edi>)
{
  int v1; // eax
  signed int v2; // ecx
  unsigned int v3; // esi
  char result; // al

  v1 = time64(0);
  v2 = 0;
  do
  {
    v1 = 29945647 * v1 - 1;
    mt[v2++] = v1;
  }
  while ( v2 < 351 );
  dword_E54018 = v2;
  v3 = 0;
  do
  {
    result = getrandom() ^ 0x55;
    *(_BYTE *)(v3++ + a1) = result;
  }
  while ( v3 < 8 );
  return result;
}

If you implemented mersenne twister, the first loop is the set seed function, this is gonna put numbers in an array I call mt.

The interesting part to notice is the call to time64, the twister is seeded with current time.

Let's reverse getrandom now

Reversing getrandom

Here is what the code looks like:

getrandom.png

The decompiled code looks good enough.

int getrandom()
{
  int v0; // eax
  int v1; // eax
  int *v2; // ecx
  int v3; // esi
  int v4; // eax
  int v5; // edi
  unsigned int v6; // esi
  int v7; // eax
  int v8; // esi
  unsigned int v9; // edi
  int v10; // eax
  int v11; // edi
  unsigned int v12; // esi
  int v13; // eax
  unsigned int v14; // esi
  unsigned int v15; // ecx
  int v16; // ecx

  v0 = dword_E54018;
  if ( dword_E54018 >= 351 )
  {
    v1 = 175;
    v2 = &dword_E54384;
    do
    {
      v3 = *v2;
      v4 = v1 + 1;
      *(v2 - 1) = ((*(v2 - 1) ^ (*v2 ^ *(v2 - 1)) & 0x7FFFFu) >> 1) ^ dword_E5437C[v4] ^ -((*((_BYTE *)v2 - 4) ^ (unsigned __int8)(*(_BYTE *)v2 ^ *((_BYTE *)v2 - 4))) & 1) & 0xE4BD75F5;
      if ( v4 >= 351 )
        v4 = 0;
      v5 = v2[1];
      v6 = ((v3 ^ (v3 ^ v2[1]) & 0x7FFFFu) >> 1) ^ mt[v4] ^ -(((unsigned __int8)v3 ^ (unsigned __int8)(v3 ^ *((_BYTE *)v2 + 4))) & 1) & 0xE4BD75F5;
      v7 = v4 + 1;
      *v2 = v6;
      if ( v7 >= 351 )
        v7 = 0;
      v8 = v2[2];
      v9 = ((v5 ^ (v5 ^ v2[2]) & 0x7FFFFu) >> 1) ^ mt[v7] ^ -(((unsigned __int8)v5 ^ (unsigned __int8)(v5 ^ *((_BYTE *)v2 + 8))) & 1) & 0xE4BD75F5;
      v10 = v7 + 1;
      v2[1] = v9;
      if ( v10 >= 351 )
        v10 = 0;
      v11 = v2[3];
      v12 = ((v8 ^ (v8 ^ v2[3]) & 0x7FFFFu) >> 1) ^ mt[v10] ^ -(((unsigned __int8)v8 ^ (unsigned __int8)(v8 ^ *((_BYTE *)v2 + 12))) & 1) & 0xE4BD75F5;
      v13 = v10 + 1;
      v2[2] = v12;
      if ( v13 >= 351 )
        v13 = 0;
      v14 = ((v11 ^ (v11 ^ v2[4]) & 0x7FFFFu) >> 1) ^ mt[v13] ^ -(((unsigned __int8)v11 ^ (unsigned __int8)(v11 ^ *((_BYTE *)v2 + 16))) & 1) & 0xE4BD75F5;
      v1 = v13 + 1;
      v2[3] = v14;
      if ( v1 >= 351 )
        v1 = 0;
      v2 += 5;
    }
    while ( (signed int)v2 < (signed int)&dword_E548FC );
    dword_E548F8 = dword_E54638 ^ ((dword_E548F8 ^ (dword_E548F8 ^ mt[0]) & 0x7FFFFu) >> 1) ^ -(((unsigned __int8)dword_E548F8 ^ (unsigned __int8)(dword_E548F8 ^ LOBYTE(mt[0]))) & 1) & 0xE4BD75F5;
    v0 = 0;
  }
  v15 = mt[v0];
  dword_E54018 = v0 + 1;
  v16 = ((((v15 >> 11) ^ v15) & 0xCABCA5) << 7) ^ (v15 >> 11) ^ v15;
  return (unsigned __int8)(v16 ^ ((((v16 & 0xFFFFFFAB) << 15) ^ v16) >> 17));
}

Looks like a normal getrandom function from mt19937 but the thing is, it is not. Because the length of the array is 351, this is called mt11213.

Putting it all together

After looking on the internet for some constants I found this page, maybe we can work with that.

The only difference with the program was the factor, also I had to change from long to int because this was written for a 32 bit machine (I spent two hours debugging this lol).

I wrote a program to generate all possible keys from october 2019 to february 2020.

#include <math.h>
#include <stdio.h>

#define RAND_MASK 0x3FFFFFFF

#define N 351
#define M 175
#define R 19
#define TEMU 11
#define TEMS 7
#define TEMT 15
#define TEML 17

#define MATRIX_A 0xE4BD75F5
#define TEMB     0x655E5280
#define TEMC     0xFFD58000

static unsigned int mt[N];
static int mti=N;


extern void set_seed (int seed) {
  unsigned int s = (unsigned int)seed;
  for (mti=0; mti<N; mti++) {
    // s = s * 29943829 - 1;
    s = s *  29945647 - 1;
    mt[mti] = s;
  }
  return;
}

void dump() {
    for (int i = 0; i < N; i++) {
       printf("%x\n", mt[i]);
    }
}

int genrandom () {
  unsigned int y;
  if (mti >= N) {
    const unsigned int LOWER_MASK = (1u << R) - 1;
    const unsigned int UPPER_MASK = -1 << R;
    int kk, km;
    for (kk=0, km=M; kk < N-1; kk++) {
      y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK);
      mt[kk] = mt[km] ^ (y >> 1) ^ (-(y & 1) & MATRIX_A);

      if (++km >= N) km = 0;
    }
    y = (mt[N-1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
    mt[N-1] = mt[M-1] ^ (y >> 1) ^ (-(unsigned int)(y & 1) & MATRIX_A);
    mti = 0;
  }
  y = mt[mti++];
  y ^=  y >> TEMU;
  y ^= (y << TEMS) & TEMB;
  y ^= (y << TEMT) & TEMC;
  y ^=  y >> TEML;

  return y&0xff;
}

int main(void) {
    for (int t = 1569888000 /*1546300800*/; t < 1582583270; t++) {
        set_seed(t);
        for (int i = 0; i<8; i++) {
            printf("%x", genrandom() ^ 0x55);
        }
        printf("\n");
    }
}

This generates a 263 MiB file:

gcc  bruteforce.c && ./a.out > keys.txt

Then I wrote a little program in python to try to brute force the key, since we know that the file is png, and the first 8 bytes are fixed.

from Crypto.Cipher import DES
cipher = "cd0c8716e99ae841".decode("hex")

with open("keys.txt") as myfile:
    lines = myfile.read().splitlines()
p = 0.01
pas = p
l = float(len(lines))
for i, key in enumerate(lines):
    if i / l >= p:
        print("{}%".format(p * 100))
        p += pas
    keyh = key
    key = key.zfill(16).decode("hex")
    out = DES.new(key).decrypt(cipher)
    if out[0] == chr(0x89) and out[1] == chr(0x50) and out[2] == chr(0x4e) and out[3] == chr(0x47) and \
        out[4] == chr(0x0d) and out[5] == chr(0xa) and out[6] == chr(0x1a) and out[7] == chr(0x0a):
        print("FOUND .....")
        print(keyh)
        exit(0)

After 5 minutes I find the key, and I decrypt the image:

from Crypto.Cipher import DES
key = "a051b8a16f8542da".decode("hex")
with open("flag.png.enc") as myfile:
    image=  myfile.read()

content = DES.new(key, DES.MODE_CBC, IV="\x00" * 8).decrypt(image)

with open("flag.png", "w") as myfile:
    myfile.write(content)

And yaas! we have the flag

flag.png

Splatter calc tamuctf2020, reverse engineering writeup

 

Introduction

We played tamuctf 2020, it was a 10 day long beginner to intermediate level ctf, 20 teams solved all the problems, we didn't solve all the problems unfortunately and we ended in rank 25.

The reverse problems were simple/okay-ish, most of them are in rust.

The challenge

This writeup is about a problem called splatter calc, it's a rust binary that asks for an initial rng

kali@kali:/mnt/shared/ctfs/tamuctf20/rev$ ./splatter-calc
Please enter an initial rng: 1
Oops, you didn't guess the right number. You got 13896084676171490950 with final state 62298657904
71643905

Analysis of the binary

Here is what the CFG looks like:

cfg.png

This looks really simple for a rust binary.

What interests us in the node in grey.

The code looks like this:

.text:0000555555559945                 mov     eax, ebx
.text:0000555555559947                 and     eax, 7
.text:000055555555994A                 shl     rax, 4
.text:000055555555994E                 mov     rdi, [rsp+rax+138h+var_B0]
.text:0000555555559956                 mov     rax, [rsp+rax+138h+var_A8]
.text:000055555555995E                 mov     esi, 0CAFEBABEh
.text:0000555555559963                 mov     rdx, rbx
.text:0000555555559966                 call    qword ptr [rax+18h]
.text:0000555555559969                 mov     r13, 83F66D0E3h
.text:0000555555559973                 imul    rbx, r13
.text:0000555555559977                 mov     rbp, 24A452F8Eh
.text:0000555555559981                 add     rbx, rbp
.text:0000555555559984                 mov     ecx, ebx
.text:0000555555559986                 and     ecx, 7
.text:0000555555559989                 shl     rcx, 4
.text:000055555555998D                 mov     rdi, [rsp+rcx+138h+var_B0]
.text:0000555555559995                 mov     rcx, [rsp+rcx+138h+var_A8]
.text:000055555555999D                 mov     rsi, rax
.text:00005555555599A0                 mov     rdx, rbx
.text:00005555555599A3                 call    qword ptr [rcx+18h]
.text:00005555555599A6                 imul    rbx, r13
.text:00005555555599AA                 add     rbx, rbp
.text:00005555555599AD                 mov     ecx, ebx

...

A function gets called based on the value of, ecx&7:

.text:0000555555559966                 call    qword ptr [rax+18h]

I extracted all the functions:

func_0:
mov     rax, rsi
retn

func_1:
lea     rax, [rsi+rdx]
retn

func_2:
mov     rax, rsi
sub     rax, rdx
retn

func_3:
mov     rax, rsi
imul    rax, rdx
retn

func_4:
mov     rax, rsi
imul    rax, rsi
retn

func_5:
mov     rcx, rdx
mov     rax, rsi
shl     rax, cl
retn

func_6:
mov     rcx, rdx
mov     rax, rsi
shr     rax, cl
retn

func_7:
mov     rax, rsi
xor     rax, rdx
retn

Finally the generated value needs to satisfy these constraints:

conds.png

Solving the binary

The algorithm is simple, so I rewrote it in python, if there was some delicate stuff that I couldn't implement correctly, I could have a used a symbolic execution framework like angr or triton for this.

import z3

s = z3.Solver()

def do_operation(i, rsi, rdx):
    return z3.If((i & 7) == 0,
        rsi,
        z3.If((i & 7) == 1,
            rsi + rdx,
            z3.If((i & 7) == 2,
                rsi - rdx,
                z3.If((i & 7) == 3,
                    rsi * rdx,
                    z3.If((i & 7) == 4,
                        rsi * rsi,
                        z3.If((i & 7) == 5,
                            rsi << (rdx & 8),
                            z3.If((i & 7) == 6,
                                rsi >> (rdx & 8),
                                rsi ^ rdx)))))))

rbx = z3.BitVec("input", 64)
r13 = 0x83F66D0E3
rbp = 0x24A452F8E
rsi = 0xCAFEBABE

for _ in range(8):
    rdx = rbx
    rax = do_operation(rbx, rsi, rdx)
    rbx = rbx * r13
    rbx = rbx + rbp
    ecx = (rbx & 0xffffffff)
    rsi = rax

s.add(0x471DE8678AE30BA1  == rbx)
s.add(0x0ACDEE2ED87A5D886 == rax)

print(s.check())
print(s.model())

And we get the value to use on the remote server

(hacking3) kali@kali:/mnt/shared/ctfs/tamuctf20/rev$ python solver.py
sat
[input = 982730589345]