notcrypto (rev)

Below is the main function pseudocode:

void main(void)

{
  long *plVar1;
  char cVar2;
  ulong v4_cpy;
  ulong uVar3;
  byte v3;
  byte v7;
  ulong v1_cpy;
  ulong v2_cpy;
  byte v4;
  byte v2;
  byte v5;
  byte v1;
  byte idx;
  int iVar4;
  uint uVar5;
  ulong v0_cpy;
  byte v0;
  byte v6;
  undefined *local_58;
  ulong local_50;
  undefined local_48;
  undefined7 uStack_47;
  undefined *local_38;
  
  local_50 = 0;
  local_48 = 0;
  plVar1 = *(long **)(*(long *)(std::cin + -0x18) + 0x105148);
  local_58 = &local_48;
  if (plVar1 == (long *)0x0) {
                    /* try { // try from 001013d0 to 00101408 has its CatchHandler @ 001015dc */
    std::__throw_bad_cast();
  }
  if (*(char *)(plVar1 + 7) == '\0') {
    std::ctype<char>::_M_widen_init();
    cVar2 = (**(code **)(*plVar1 + 0x30))(plVar1,10);
  }
  else {
    cVar2 = *(char *)((long)plVar1 + 0x43);
  }
  std::getline<>((istream *)&std::cin,(string *)&local_58,cVar2);
  v0 = 8;
  if ((local_50 & 7) != 0) {
    v0 = 8 - ((byte)local_50 & 7);
  }
  uVar5 = (uint)v0;
  do {
    v4_cpy = local_50;
    uVar3 = 0xf;
    if (local_58 != &local_48) {
      uVar3 = CONCAT71(uStack_47,local_48);
    }
    v1_cpy = local_50 + 1;
    if (uVar3 < v1_cpy) {
                    /* try { // try from 0010146d to 00101481 has its CatchHandler @ 001015ce */
      std::string::_M_mutate((ulong)&local_58,local_50,(char *)0x0,0);
    }
    local_58[v4_cpy] = v0;
    local_58[v4_cpy + 1] = 0;
    uVar5 = uVar5 - 1;
    local_50 = v1_cpy;
  } while (uVar5 != 0);
                    /* flag is 48 chars */
  if (v1_cpy == 0x38) {
                    /* pointer to input */
    local_38 = local_58;
    uVar3 = 0;
                    /* encrypted flag */
    while( true ) {
      v0 = local_58[uVar3];
      v1 = local_58[uVar3 + 1];
      v2 = local_58[uVar3 + 2];
      v3 = local_58[uVar3 + 3];
      v4 = local_58[uVar3 + 4];
      v5 = local_58[uVar3 + 5];
      v6 = local_58[uVar3 + 6];
      v7 = local_58[uVar3 + 7];
      iVar4 = 0;
      do {
        v0_cpy = (ulong)v0;
        v1_cpy = (ulong)v1;
        v2_cpy = (ulong)v2;
        v4_cpy = (ulong)v4;
        idx = (byte)iVar4;
        v0 = (&DAT_00104050)[v6] ^ idx;
        v1 = (&DAT_00104050)[v0_cpy] ^ idx;
        v2 = (&DAT_00104050)[v7] ^ idx;
        v3 = (&DAT_00104050)[v3] ^ idx;
        v4 = (&DAT_00104050)[v1_cpy] ^ idx;
        v5 = (&DAT_00104050)[v5] ^ idx;
        v6 = (&DAT_00104050)[v2_cpy] ^ idx;
        v7 = (&DAT_00104050)[v4_cpy] ^ idx;
        iVar4 = iVar4 + 1;
      } while (iVar4 != 0x1000);
      if (((((v0 != (&DAT_00102010)[uVar3]) || (v1 != (&DAT_00102011)[uVar3])) ||
           (v2 != (&DAT_00102012)[uVar3])) ||
          ((v3 != (&DAT_00102013)[uVar3] || (v4 != (&DAT_00102014)[uVar3])))) ||
         ((v5 != (&DAT_00102015)[uVar3] ||
          ((v6 != (&DAT_00102016)[uVar3] || (v7 != (&DAT_00102017)[uVar3])))))) break;
      uVar3 = uVar3 + 8;
      if (55 < uVar3) {
        puts("Correct flag methinks.");
                    /* WARNING: Subroutine does not return */
        exit(0);
      }
    }
  }
  puts("Wronk flag methinks.");
                    /* WARNING: Subroutine does not return */
  exit(1);
}

The flag checking is done in blocks of 8 characters at a time.

In each iteration, the characters are first taken into v0,v1,v2,v3,v4,v5,v6,v7 and are used to obtain new characters from DAT_00104050. Then the value is XORd with the idx of the current iteration. This is done until idx reaches 0x1000.

The final v0,v1,v2,v3,v4,v5,v6,v7 variables are then checked against characters in DAT_00102010.

If the check passes for every block, then the application tells us we have the correct flag.

Notice that the first 256 characters in DAT_00104050 are distinct, which reminds me of an sbox. This also means that the substitution of characters is an operation that can be reversed.

As such, to retrieve the flag, for each block of 8 characters we can loop from idx=0xfff to idx=0 and reverse the operations performed. Performing this for all blocks will reveal the flag.

Below is my solve script. data1 is the sbox and data2 is the block's expected result of the substitution and xor operations.


data1 = b'\x63\x7c\x77\x7b\xf2\x6b\x6f\xc5\x30\x01\x67\x2b\xfe\xd7\xab\x76\xca\x82\xc9\x7d\xfa\x59\x47\xf0\xad\xd4\xa2\xaf\x9c\xa4\x72\xc0\xb7\xfd\x93\x26\x36\x3f\xf7\xcc\x34\xa5\xe5\xf1\x71\xd8\x31\x15\x04\xc7\x23\xc3\x18\x96\x05\x9a\x07\x12\x80\xe2\xeb\x27\xb2\x75\x09\x83\x2c\x1a\x1b\x6e\x5a\xa0\x52\x3b\xd6\xb3\x29\xe3\x2f\x84\x53\xd1\x00\xed\x20\xfc\xb1\x5b\x6a\xcb\xbe\x39\x4a\x4c\x58\xcf\xd0\xef\xaa\xfb\x43\x4d\x33\x85\x45\xf9\x02\x7f\x50\x3c\x9f\xa8\x51\xa3\x40\x8f\x92\x9d\x38\xf5\xbc\xb6\xda\x21\x10\xff\xf3\xd2\xcd\x0c\x13\xec\x5f\x97\x44\x17\xc4\xa7\x7e\x3d\x64\x5d\x19\x73\x60\x81\x4f\xdc\x22\x2a\x90\x88\x46\xee\xb8\x14\xde\x5e\x0b\xdb\xe0\x32\x3a\x0a\x49\x06\x24\x5c\xc2\xd3\xac\x62\x91\x95\xe4\x79\xe7\xc8\x37\x6d\x8d\xd5\x4e\xa9\x6c\x56\xf4\xea\x65\x7a\xae\x08\xba\x78\x25\x2e\x1c\xa6\xb4\xc6\xe8\xdd\x74\x1f\x4b\xbd\x8b\x8a\x70\x3e\xb5\x66\x48\x03\xf6\x0e\x61\x35\x57\xb9\x86\xc1\x1d\x9e\xe1\xf8\x98\x11\x69\xd9\x8e\x94\x9b\x1e\x87\xe9\xce\x55\x28\xdf\x8c\xa1\x89\x0d\xbf\xe6\x42\x68\x41\x99\x2d\x0f\xb0\x54\xbb\x16\x38\x50\x10\x00\x00\x00\x00\x00'
data1 = data1[:256]
# data2 = bytearray(b'\x16\x2d\x79\xca\x56\xc6\x65\xe9')
# data2 = bytearray(b'\xe9\x16\x66\x23\x09\x2d\x1b\x09')
# data2 = bytearray(b'\x1c\x09\xc6\x1c\x1f\xad\xe9\xda')
# data2 = bytearray(b'\xa0\xc6\x1a\x66\x09\xad\x81\x1c')
# data2 = bytearray(b'\x80\x39\xa0\x21\x09\x65\x2d\x30')
data2 = bytearray(b'\xf6\x57\xf6\xa2\x65\x65\x21\xa2')
d = dict()
# map char to its position
for i,x in enumerate(data1):
    # print(f"x: {x}, i: {i}")
    d[x] = i 
for x in range(0x1000-1, -1, -1):
    new_data = [0] * 8
    x2 = x & 0xff
    new_data[5] = d[data2[5] ^ x2]
    new_data[3] = d[data2[3] ^ x2]
    new_data[6] = d[data2[0] ^ x2]
    new_data[2] = d[data2[6] ^ x2]
    new_data[7] = d[data2[2] ^ x2]
    new_data[4] = d[data2[7] ^ x2]
    new_data[1] = d[data2[4] ^ x2]
    new_data[0] = d[data2[1] ^ x2]
    # print(f"new data: {''.join(chr(x) for x in data2)}")
    data2 = new_data

print(''.join(chr(x) for x in data2)) # x3c{pwndbg_and_pwntools_my_belowed_573498532832}

Last updated