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
_skylinereturns 0x46 (F)_suprareturns 0x49 (I)_civicreturns 0x41 (A)_silviareturns 0x7B_rx7returns 0x41_ae86returns 0x47_nsxreturns 0x61_lancerreturns 0x69_evolutionreturns 0x4E_imprezareturns 0x53_wrxreturns 0x74_stireturns 0x5F_integrareturns 0x4A_typerreturns 0x55_engagereturns 0x53_func_300zxreturns 0x54_fairladyzreturns 0x5F_s2000returns 0x4C_mr2returns 0x31_chaserreturns 0x4B_markiireturns 0x33_crestareturns 0x5F_crownreturns 0x30_soarerreturns 0x4C_celicareturns 0x64_gtfourreturns 0x5F_starletreturns 0x74_pulsarreturns 0x49_gtirreturns 0x6D_gtrreturns 0x65_skylinegtrreturns 0x35_laurelreturns 0x2E_cefiroreturns 0x5F_cedricreturns 0x59_gloriareturns 0x30_s13returns 0x75_rx8returns 0x2C_miatareturns 0x5F_altezzareturns 0x6D_aristoreturns 0x33_levinreturns 0x2C_truenoreturns 0x5F_ftoreturns 0x4A_gtoreturns 0x44_galantreturns 0x4D_eclipsereturns 0x5F_preludereturns 0x54_crxreturns 0x48_beatreturns 0x45_legacyreturns 0x5F_brzreturns 0x57_savannareturns 0x4F_roadsterreturns 0x52_cosmoreturns 0x4C_cappuccinoreturns 0x44_eunosreturns 0x7D
So the flag is: FIA{AGaiNSt_JUST_L1K3_0Ld_tIme5._Y0u,_m3,_JDM_THE_WORLD}