Skip to content

Solve - Midnight CTF Challenge

Every functions are decompiled by using IDA Free

Step 1: Map out the necessary functions written by the author.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char Str1[256]; // [esp+1Ch] [ebp-108h] BYREF
  char *v5; // [esp+11Ch] [ebp-8h]

  __main();
  v5 = "To assemble the ultimate JDM collection: awaken the Skyline, unleash the Supra,\n"
       "rev the Civic, slide the Silvia, rotate the RX7, drift the AE86, polish the NSX,\n"
       "launch the Lancer Evolution, grip the Impreza WRX STI, refine the Integra Type-R,\n"
       "engage the 300ZX, carve corners with the Fairlady-Z, scream the S2000, balance the MR2,\n"
       "cruise the Chaser, flex the Mark-II, prowl the Cresta, glide the Crown,\n"
       "respect the Soarer, charge the Celica GT-Four, hunt apexes with the Starlet,\n"
       "push the Pulsar GTI-R, dominate with the GTR, perfect the Skyline-GTR bloodline,\n"
       "admire the Laurel, honor the Cefiro, praise the Cedric, salute the Gloria,\n"
       "tune the S13, spin the RX8, drop the Miata, test the Altezza,\n"
       "boost the Aristo, race the Levin, rally the Trueno, track the FTO,\n"
       "unleash the GTO, tune the Galant, modify the Eclipse, upgrade the Prelude,\n"
       "restore the CRX, treasure the Beat, respect the Legacy, embrace the BRZ,\n"
       "preserve the Savanna, collect the Roadster, study the Cosmo, enjoy the Cappuccino, cherish the Eunos.\n";
  if ( argc > 1 )
  {
    memset(Str1, 0, sizeof(Str1));
    puts(v5);
    midn1ght_wAngAn_load3r(v5, (int)Str1);
    if ( !strcmp(Str1, argv[1]) )
      printf("[+] welcome to MidnightClub: %s\n", Str1);
    else
      puts("[-] Nahhhhh!!!!");
    return 0;
  }
  else
  {
    printf("[-] Missing arguments, usage %s <FLAG_STR>\n", *argv);
    return 1;
  }
}
char *__cdecl midn1ght_wAngAn_load3r(char *Source, int a2)
{
  char *result; // eax
  _BYTE *v3; // ebx
  char Destination[2048]; // [esp+14h] [ebp-814h] BYREF
  char *String; // [esp+814h] [ebp-14h]
  int i; // [esp+818h] [ebp-10h]
  char *Str1; // [esp+81Ch] [ebp-Ch]

  strncpy(Destination, Source, 0x7FFu);
  Destination[2047] = 0;
  result = strtok(Destination, " \n\t");
  for ( Str1 = result; Str1; Str1 = result )
  {
    tuneIt(Str1);
    for ( i = 0; i <= 55; ++i )
    {
      if ( !strcmp(Str1, (&souls)[i]) )
      {
        String = (char *)funcs[i]();
        v3 = (_BYTE *)(i + a2);
        *v3 = strtol(String, 0, 16);
        break;
      }
    }
    result = strtok(0, " \n\t");
  }
  return result;
}
int __cdecl tuneIt(int a1)
{
  int v1; // eax
  int result; // eax
  int v3; // [esp+18h] [ebp-10h]
  int v4; // [esp+1Ch] [ebp-Ch]

  v4 = 0;
  v3 = 0;
  while ( *(_BYTE *)(v4 + a1) )
  {
    if ( isalnum(*(unsigned __int8 *)(v4 + a1)) )
    {
      v1 = v3++;
      *(_BYTE *)(v1 + a1) = tolower(*(unsigned __int8 *)(v4 + a1));
    }
    ++v4;
  }
  result = v3 + a1;
  *(_BYTE *)(v3 + a1) = 0;
  return result;
}

Step 2: Analyze each functions

Function: main

  if ( argc > 1 )
  {
    memset(Str1, 0, sizeof(Str1));
    puts(v5);
    midn1ght_wAngAn_load3r(v5, (int)Str1);
    if ( !strcmp(Str1, argv[1]) )
      printf("[+] welcome to MidnightClub: %s\n", Str1);
    else
      puts("[-] Nahhhhh!!!!");
    return 0;
  }
  else
  {
    printf("[-] Missing arguments, usage %s <FLAG_STR>\n", *argv);
    return 1;
  }
}

the variable argc seems to be checking whether is there any arguments input. If there is an input, the condition is True.

Case True :

    memset(Str1, 0, sizeof(Str1)); # Fill this block of memory with 0 (NULL)
    puts(v5); # Print out the message to the console
    midn1ght_wAngAn_load3r(v5, (int)Str1); 
    if ( !strcmp(Str1, argv[1]) )
      printf("[+] welcome to MidnightClub: %s\n", Str1);
    else
      puts("[-] Nahhhhh!!!!");
    return 0;

Base on the arguments put into midn1ght_wAngAn_load3r function, I can tell that v5 is the key for the obfuscation and Str1 is the flag output

Case False:

    printf("[-] Missing arguments, usage %s <FLAG_STR>\n", *argv);
    return 1;

Tested with no arguments when executing the binary file. The output is: [-] Missing arguments, usage ./midnight <FLAG_STR>

Function: midn1ght_wAngAn_load3r

  strncpy(Destination, Source, 0x7FFu); # 0x7FFu = 2047
  Destination[2047] = 0; # Set the end of the string
  result = strtok(Destination, " \n\t"); # break the string into smaller pieces, seperated by ' \n\t'
  for ( Str1 = result; Str1; Str1 = result )
  {
    tuneIt((int)Str1);
    for ( i = 0; i <= 55; ++i )
    {
      if ( !strcmp(Str1, (&souls)[i]) )
      {
        String = (char *)funcs[i]();
        v3 = (_BYTE *)(i + a2);
        *v3 = strtol(String, 0, 16);
        break;
      }
    }
    result = strtok(0, " \n\t");
  }
  return result;

strncpy copies all the data from Source to Destination

The for loop is gonna go through all the pieces of the string of variable Destination. Every pieces of the string is stored in Str1. Variable Str1 goes through function tuneIt in order to tune it into the correct format so it can be compared to elements of string in array souls

.data:00405020 _souls          dd offset aSkyline      ; DATA XREF: _midn1ght_wAngAn_load3r+60↑r
.data:00405020                                         ; "skyline"
.data:00405024                 dd offset aSupra        ; "supra"
.data:00405028                 dd offset aCivic        ; "civic"
.data:0040502C                 dd offset aSilvia       ; "silvia"
.data:00405030                 dd offset aRx7          ; "rx7"
.data:00405034                 dd offset aAe86         ; "ae86"
.data:00405038                 dd offset aNsx          ; "nsx"
.data:0040503C                 dd offset aLancer       ; "lancer"
.data:00405040                 dd offset aEvolution    ; "evolution"
.data:00405044                 dd offset aImpreza      ; "impreza"
.data:00405048                 dd offset aWrx          ; "wrx"
.data:0040504C                 dd offset aSti          ; "sti"
.data:00405050                 dd offset aIntegra      ; "integra"
.data:00405054                 dd offset aTyper        ; "typer"
.data:00405058                 dd offset aEngage       ; "engage"
.data:0040505C                 dd offset a300zx        ; "300zx"
.data:00405060                 dd offset aFairladyz    ; "fairladyz"
.data:00405064                 dd offset aS2000        ; "s2000"
.data:00405068                 dd offset aMr2          ; "mr2"
.data:0040506C                 dd offset aChaser       ; "chaser"
.data:00405070                 dd offset aMarkii       ; "markii"
.data:00405074                 dd offset aCresta       ; "cresta"
.data:00405078                 dd offset aCrown        ; "crown"
.data:0040507C                 dd offset aSoarer       ; "soarer"
.data:00405080                 dd offset aCelica       ; "celica"
.data:00405084                 dd offset aGtfour       ; "gtfour"
.data:00405088                 dd offset aStarlet      ; "starlet"
.data:0040508C                 dd offset aPulsar       ; "pulsar"
.data:00405090                 dd offset aGtir         ; "gtir"
.data:00405094                 dd offset aGtr          ; "gtr"
.data:00405098                 dd offset aSkylinegtr   ; "skylinegtr"
.data:0040509C                 dd offset aLaurel       ; "laurel"
.data:004050A0                 dd offset aCefiro       ; "cefiro"
.data:004050A4                 dd offset aCedric       ; "cedric"
.data:004050A8                 dd offset aGloria       ; "gloria"
.data:004050AC                 dd offset aS13          ; "s13"
.data:004050B0                 dd offset aRx8          ; "rx8"
.data:004050B4                 dd offset aMiata        ; "miata"
.data:004050B8                 dd offset aAltezza      ; "altezza"
.data:004050BC                 dd offset aAristo       ; "aristo"
.data:004050C0                 dd offset aLevin        ; "levin"
.data:004050C4                 dd offset aTrueno       ; "trueno"
.data:004050C8                 dd offset aFto          ; "fto"
.data:004050CC                 dd offset aGto          ; "gto"
.data:004050D0                 dd offset aGalant       ; "galant"
.data:004050D4                 dd offset aEclipse      ; "eclipse"
.data:004050D8                 dd offset aPrelude      ; "prelude"
.data:004050DC                 dd offset aCrx          ; "crx"
.data:004050E0                 dd offset aBeat         ; "beat"
.data:004050E4                 dd offset aLegacy       ; "legacy"
.data:004050E8                 dd offset aBrz          ; "brz"
.data:004050EC                 dd offset aSavanna      ; "savanna"
.data:004050F0                 dd offset aRoadster     ; "roadster"
.data:004050F4                 dd offset aCosmo        ; "cosmo"
.data:004050F8                 dd offset aCappuccino   ; "cappuccino"
.data:004050FC                 dd offset aEunos        ; "eunos"
    for ( i = 0; i <= 55; ++i )
    {
      if ( !strcmp(Str1, (&souls)[i]) )
      {
        String = (char *)funcs[i]();
        v3 = (_BYTE *)(i + a2);
        *v3 = strtol(String, 0, 16);
        break;
      }
    }

if it match, it calls a function from funcs to get a char then v3 gets the memory address of (i + a2) (because v3 is a pointer). After that strtol converts the string to a hex number and saves it into the memory address of v3

Now let's look into funcs array of 56 function pointers

Function: funcs

  1. _skyline returns 0x46 (F)
  2. _supra returns 0x49 (I)
  3. _civic returns 0x41 (A)
  4. _silvia returns 0x7B
  5. _rx7 returns 0x41
  6. _ae86 returns 0x47
  7. _nsx returns 0x61
  8. _lancer returns 0x69
  9. _evolution returns 0x4E
  10. _impreza returns 0x53
  11. _wrx returns 0x74
  12. _sti returns 0x5F
  13. _integra returns 0x4A
  14. _typer returns 0x55
  15. _engage returns 0x53
  16. _func_300zx returns 0x54
  17. _fairladyz returns 0x5F
  18. _s2000 returns 0x4C
  19. _mr2 returns 0x31
  20. _chaser returns 0x4B
  21. _markii returns 0x33
  22. _cresta returns 0x5F
  23. _crown returns 0x30
  24. _soarer returns 0x4C
  25. _celica returns 0x64
  26. _gtfour returns 0x5F
  27. _starlet returns 0x74
  28. _pulsar returns 0x49
  29. _gtir returns 0x6D
  30. _gtr returns 0x65
  31. _skylinegtr returns 0x35
  32. _laurel returns 0x2E
  33. _cefiro returns 0x5F
  34. _cedric returns 0x59
  35. _gloria returns 0x30
  36. _s13 returns 0x75
  37. _rx8 returns 0x2C
  38. _miata returns 0x5F
  39. _altezza returns 0x6D
  40. _aristo returns 0x33
  41. _levin returns 0x2C
  42. _trueno returns 0x5F
  43. _fto returns 0x4A
  44. _gto returns 0x44
  45. _galant returns 0x4D
  46. _eclipse returns 0x5F
  47. _prelude returns 0x54
  48. _crx returns 0x48
  49. _beat returns 0x45
  50. _legacy returns 0x5F
  51. _brz returns 0x57
  52. _savanna returns 0x4F
  53. _roadster returns 0x52
  54. _cosmo returns 0x4C
  55. _cappuccino returns 0x44
  56. _eunos returns 0x7D

So the flag is: FIA{AGaiNSt_JUST_L1K3_0Ld_tIme5._Y0u,_m3,_JDM_THE_WORLD}