[Write-up] Chal6 {Flareon4}
Write-up for Chal6 of Flareon4. This may not be a good solution, but that’s the method how I found the final flag.
1. Collect information
- Target is a dll file: payload.dll
- Check the basic information:
o File format: PE+(64)
o Resource section of the file doesn’t contain any special information.
o API functions are imported primarily from kernel32.dll and user32.dll.
o Information related to Export function:
Ordinal: 1, Function Name: EntryPoint, RVA: 0x00005C00
o About the string, there are some important strings as following:
“Insert clever error message here!”
“rundll32 payload.dll, EntryPoint EntryPoint” ← This string suggests how to call the function that this dll has exported above.
2. Try to run payload.dll
Try running payload.dll with the above suggestion:
I received the message: “Missing entry: Entrypoint”. So, I think the exported Entrypoint function was altered when running payload.dll.
To check whether the exported function is changed, use x64dbg to debug:
- Load C:\Windows\System32\rundll32.exe into x64dbg.
- Modify the input parameters at Debug > Change Command Line. Edit similarly as following: “”C:\Windows\System32\rundll32.exe” C:\Users\manowar\Desktop\FlareOn4\Chal6\payload.dll, EntryPoint EntryPoint”
- Reconfigure the Options > Preferences, Settings > Envents, select DLL Load & DLL Entry
Then press F9 to run and watch carefully when payload.dll is loaded, stop at DLLMain and execute completely:
Don’t close the above message, using the OllyDumpEx plugin for dumping the entire payload.dll, and then saving as the new name payload_dump_64.dll. Check the dumped file with CFF Explorer, Export function information is changed as following:
Note: Ordinal is still 1, thus can concluding the Entrypoint function has been renamed to basophileslapsscrapping.
Based on above information, try to execute again with the following command: rundll32.exe payload.dll, basophileslapsscrapping basophileslapsscrapping. As a result, I received a message box about 24th character of the flag: (0x6f -> ASCII: ‘o’):
In addition, during the test, try to run the payload.dll by ordinal: rundll32.exe payload.dll, #1. Get the error message box:
3. Analyze and debug payload.dll
Load payload.dll into IDA, first go to exported EntryPoint function at 0x0000000180005C00. Here, I see the code that only shows the usage instructions:
Following the “Insert clever error message here!” string, go to listing at sub_180005A50. Along with the process of analyzing code in IDA, I also use x64dbg to load the payload.dll with parameters passed is (“C:\Windows\System32\rundll32.exe” C:\Users\manowar\Desktop\FlareOn4\Chal6\payload.dll, basophileslapsscrapping basophileslapsscrapping), set a breakpoint at the instruction:
0000000180005A50 mov dword ptr ss:[rsp+0x20], r9d
After deep dive analysis and debug code, get the following information:
int __fastcall sub_180005A50(__int64 a1, __int64 a2, __int64 a3)
{
_DWORD *export_dir; // rax@1
__int64 img_export_dir; // ST48_8@1
unsigned int export_name_VA; // ST38_4@1
char *lpexport_name; // rax@1
__int64 k; // rcx@1
unsigned __int8 chr; // dl@2
int flag; // eax@4
int result; // eax@9
int index_val; // [sp+20h] [bp-168h]@1
signed __int64 len_key; // [sp+28h] [bp-160h]@7
LPVOID lpAddress; // [sp+30h] [bp-158h]@7
__int64 export_func_name; // [sp+40h] [bp-148h]@1
signed __int64 size; // [sp+50h] [bp-138h]@1
DWORD flOldProtect; // [sp+58h] [bp-130h]@7
_DWORD *export_dir_copy; // [sp+60h] [bp-128h]@1
__int64 v18; // [sp+68h] [bp-120h]@7
Rc4Context *context; // [sp+70h] [bp-118h]@9
__int64 v20; // [sp+190h] [bp+8h]@1
__int64 v21; // [sp+198h] [bp+10h]@1
__int64 lpKey; // [sp+1A0h] [bp+18h]@1
lpKey = a3;
v21 = a2;
v20 = a1;
export_dir = (_DWORD *)Get_ExportDirectory(); // Get Export Directory’s addr
export_dir_copy = export_dir;
img_export_dir = *export_dir + Dos_Header; // Get Export Directory Table data (Export_Dir’s RVA + DOS Header)
export_name_VA = *(_DWORD *)(Dos_Header + *(_DWORD *)(img_export_dir + 0x20));// Get RVA that points to export function name
export_func_name = export_name_VA + Dos_Header;// function name
index_val = *(_DWORD *)(img_export_dir + 4) & 0xFF;// index_val = TimeDateStamp & 0xFF
size = (_BYTE *)*(&off_1800198E0 + (unsigned int)(index_val + 1))
- (_BYTE *)*(&off_1800198E0 + (unsigned int)index_val);// off_1800198E0 in .data section
lpexport_name = (char *)(export_name_VA + Dos_Header);
k = lpKey — (_QWORD)lpexport_name;
// Cause command is rundll32 payload.dll, EntryPoint EntryPoint
// This loop checks if the two strings are equal, Export function equals Key (eg. EntryPoint = EntryPoint)
while ( 1 )
{
chr = *lpexport_name;
if ( *lpexport_name != lpexport_name[k] )
{
break;
}
++lpexport_name;
if ( !chr )
{
flag = 0;
goto LABEL_6;
}
}
flag = -(chr < (unsigned __int8)lpexport_name[k]) | 1;
LABEL_6:
if ( flag )
{
MessageBoxA(0i64, “Insert clever error message here!”, “Error”, 0);
result = 0;
}
else
{
lpAddress = *(&off_1800198E0 + (unsigned int)index_val);
VirtualProtect(*(&off_1800198E0 + (unsigned int)index_val), 0x1000ui64, PAGE_EXECUTE_READWRITE, &flOldProtect);// Changes the protection on a region in .text section to ERW; size: 0x1000
v18 = export_func_name;
len_key = 0xFFFFFFFFFFFFFFFFi64;
do
{
++len_key;
}
while ( *(_BYTE *)(v18 + len_key) );
// Initialize an RC4 context using the supplied key
// @param[in] context Pointer to the RC4 context to initialize
// @param[in] key Pointer to the key
// @param[in] length Length of the key
// @return Error code
rc4Init((Rc4Context *)&context, export_func_name, len_key);
// Encrypt/decrypt data with the RC4 algorithm
// @param[in] context Pointer to the RC4 context
// @param[in] input Pointer to the data to encrypt/decrypt
// @param[in] output Pointer to the resulting data
// @param[in] length Length of the input data
rc4Cipher((Rc4Context *)&context, (__int64)lpAddress, (__int64)lpAddress, size);
result = ((int (__fastcall *)(__int64, __int64, __int64, _QWORD))lpAddress)(v20, v21, lpKey, (unsigned int)size);// Call to decrypted payload
}
return result;
}
In summary, sub_180005A50 does the following tasks:
- B1: Get the name of the function exported by payload.dll.
- B2: Calculate an index value based on the TimeDateStamp (this TimeDateStamp value will vary and for each exported function name). Index = TimeDateStamp & 0xFF
- B3: Calculate a size value, this value will be used as the size for the data to be decoded by RC4 algorithm.
- B4: Perform a loop to verify that the name of the exported function is the same as the key to be passed. Because, according to the original instruction, payload.dll must be run with the following syntax (rundll32 payload.dll, EntryPoint EntryPoint), so if not the same, it will show the error message: “Insert clever error message here!”
- B5: Based on the index value obtained in B2, it will be added to off_1800198E0 to retrieve the address location in the .text section as the location to decrypt the data. Through the VirtualProtect API, the access protection of this memory area is changed to PAGE_EXECUTE_READWRITE with a size of 0x1000.
- B6: RC4 context is initialized by using the key provided (key coincides with the name of the exported function).
- B7: Use the initialized context to decode the data region that is obtained in B5 with the calculated size in B3.
- B8: Finally, jump to the decrypted code and execute payload to display the message about the character of final flag in hexadecimal code.
However, I recognized sub_180005A50 that analyzed above is a function handler after the original EntryPoint function was replaced by another function name. Thus, the code that performs the replacement of a new function name has already taken before. How to find this listing?
Deep dive into the code, due to involve taking information about the export function, sub_180005A50 analyzed earlier has other sub_180004760 function (which has been renamed to Get_ExportDirectory):
Placing yourself in the position of coder, they will often write a generic function to perform a certain task. Therefore, the Get_ExportDirectory() function will be called somewhere earlier. By using IDA’s xrefs feature, I found the call to this function at sub_180005D30.
Set a breakpoint at:
0000000180005D30 mov qword ptr ss:[rsp+0x8], rcx
After deep dive analysis and debug code at sub_180005D30, get the following information:
__int64 __fastcall sub_180005D30(__int64 a1)
{
__int64 addr_180005D4D; // rax@1
HANDLE hProcess; // rax@5
__int64 exp_directory; // ST40_8@5
unsigned int index; // ST38_4@5
_WORD *lpAddress; // [sp+30h] [bp-48h]@1
DWORD flOldProtect; // [sp+58h] [bp-20h]@5
__int64 v8; // [sp+80h] [bp+8h]@1
v8 = a1;
LODWORD(addr_180005D4D) = sub_180005E70(); // get addr 0x180005D4D
// Retrieve ImageBase & PE headear address of payload.dll when mapping to memory
for ( lpAddress = (_WORD *)(addr_180005D4D & 0xFFFFFFFFFFFFF000ui64);
!(unsigned int)GetDOSHeader(lpAddress) || !(unsigned int)GetPEHeader((__int64)lpAddress);
lpAddress += 0xFFFFF800 )
{
;
}
hProcess = GetCurrentProcess();
// lpAddress -> ImageBase (0x180000000) of payload.dll
// Change protection to Execute_Read_Write mode
VirtualProtectEx(hProcess, lpAddress, 0x1000ui64, PAGE_EXECUTE_READWRITE, &flOldProtect);
exp_directory = Get_ExportDirectory();
index = GetIndexFromTime();
DecryptExportFunction(index); // Decrypt data base on index value, set new TimeDateStamp and new export function
*(_DWORD *)exp_directory = (index << 9) + (unsigned __int64)byte_180001000 — (_DWORD)lpAddress;// set new RVA of IMAGE_EXPORT_DIRECTORY
*(_DWORD *)(exp_directory + 4) = 0x200; // set new size of IMAGE_EXPORT_DIRECTORY
return v8;
}
In summary sub_180005D30 does the following tasks:
- B1: Perform a loop to obtain information related to ImageBase and PE header of payload.dll when mapped into memory.
- B2: Call VirtualProtectEx to change the access protection on the memory area at the ImageBase to PAGE_EXECUTE_READWRITE.
- B3: Call Get_ExportDirectory() to get information of Export Directory.
- B4: Calculate and index value based on SystemTime, index = (SystemTime.wYear + SystemTime.wMonth) % 26. Thereby, I can conclude that the index value will be in the range of 0 -> 25.
- B5: Based on the calculated index earlier, call DecryptExportFunction(index) function to decode data, and then reset the TimeDateStamp value and the new name of export function.
- B6: Finally, adjust the RVA value and new size of IMAGE_EXPORT_DIRECTORY.
After obtaining the new information about TimeDateStamp and the new export function name, they will be used at sub_180005D30 to decode corresponding to the index position in the flag.
So, if I manually change each index value that returned by GetIndexFromTime() function, in the range from 0–25, I finally get the following export functions :
index[0] = filingmeteorsgeminately
index[1] = leggykickedflutters
index[2] = incalculabilitycombustionsolvency
index[3] = crappingrewardsanctity
index[4] = evolvablepollutantgavial
index[5] = ammoniatesignifiesshampoo
index[6] = majesticallyunmarredcoagulate
index[7] = roommatedecapitateavoider
index[8] = fiendishlylicentiouslycolouristic
index[9] = sororityfoxyboatbill
index[10] = dissimilitudeaggregativewracks
index[11] = allophoneobservesbashfulness
index[12] = incuriousfatherlinessmisanthropically
index[13] = screensassonantprofessionalisms
index[14] = religionistmightplaythings
index[15] = airglowexactlyviscount
index[16] = thonggeotropicermines
index[17] = gladdingcocottekilotons
index[18] = diagrammaticallyhotfootsid
index[19] = corkerlettermenheraldically
index[20] = ulnacontemptuouscaps
index[21] = impureinternationalisedlaureates
index[22] = anarchisticbuttonedexhibitionistic
index[23] = tantalitemimicryslatted
index[24] = basophileslapsscrapping
index[25] = orphanedirreproducibleconfidences
According to FLARE-ON’s flag structure, the final flag will has the format: [chars]@flare-on.com. Based on the above index information, I conclude that the flag has 26 characters (the 24th character is known as ‘o’). So try each export function until get the message that the character corresponding to index = 13 has a hex code is 0x40 (ASCII code is ‘@’).
Finally got the flag to submit is: wuuut-exp0rts@flare-on.com