This is something I was planning for a long time.
THIS ARTICLE IS A GIANT SPOILER!
If you haven't completed the achievements already but plan to do it some day, you should stop reading it.
There - you've been warned.
I solved these challenges long ago. In matter of fact I solved them so long ago, I totally forgot some of them and basically did them as it was my first time.
One thing I should mention is that really tried to stay on point without falling into too much details. Still, some of the challenges got a bit chunky but oh well, you know me.
For all of the challenges I've used the following approach:
- PE forensic to determine the compiler/linker and pick the right tool for further analysis
- Look for the "Invalid password" message
- Look around the imported functions for StringCompare, strcmp, memcmp, etc.
- Real time debugging
To accomplish this task, I've used the following tools:
- PEiD v0.95 - Discontinued development, but still do the job done
- Detect it Easy v1.01 - Or simply DiE is the natural successor of PEiD
- TrID v2.24 - File Identifier
- x86dbg v25 - the better ollydbg
- HxD v1.7.7.0 - my choice of a HEX editor
- IDA v6.6 - Interactive disassembler
- VBReFormer v5.0 - it's what I got
- whatever I forgot to mention
Application Challenge #1 (Windows)
Target: app1win.exe
Requirement: Find the password
Difficulty: easy
Here's what I have here:
According to
DiE, the application is linked with "
Microsoft Linker(3.0)[EXE32]", but no compiler is detected:
PEiD wasn't helpful at all:
However,
TrID nailed it:
I intentionally wrote an article about
reverse engineering REALbasic applications some time ago.
Now I can use the IDAPython script to rebuild it and analyse it further in IDA. You can refer to the article if something is unclear.
Moving on.
After loading
app1win.exe in IDA, running the rebuilder script, and using IDA's decompiler, I'm here:
OVERLAY entry point, IDA decompileint main_overlay() {
RuntimeInit();
sub_570C9C();
RuntimeRegisterFileType(&prm_0002, &prm_0001, &prm_0000, 0);
sub_57083F();
sub_570B19();
RuntimeRun();
var_0001 = 0;
RuntimeUnlockObject(0);
RuntimeExit();
return 0;
}
Now, the named functions like
RuntimeInit(),
RuntimeRegisterFileType(), etc. are part of
REALbasic's Framework API, so I'll use them as milestones, without tracing inside them.
The parameters you see around, starting with "
prm_" is user defined data, and I'll watch them closer. Also, the procedures starting with "
sub_" are part of the user code, so I'll trace these too.
So, let's see what we have here.
Starting with
RuntimeInit(), then
RuntimeRegisterFileType(), some unknown procedures,
RuntimeRun() and
RuntimeExit() in the end.
Logically I should be interested in everything before
RuntimeRun() and in the current case this is
sub_570B19():
OVERLAY_EP->>sub_570B19(), IDA decompileint sub_570B19() {
// trimmed
RuntimeStackCheck();
RuntimeUnlockObject(0);
v2 = sub_56C5E7();
if ( !v2 )
RuntimeNilObject();
WindowForceUpdateFrontBuffer(v2);
RuntimeUnlockObject(v2);
return 0;
}
Here, we have
RuntimeUnlockObject(), some unknown procedure
sub_56C5E7() and
WindowForceUpdateFrontBuffer(), so we go inside
sub_56C5E7():
OVERLAY_EP->sub_570B19()->sub_56C5E7(), IDA decompileint sub_56C5E7() {
// trimmed
RuntimeStackCheck();
RuntimeUnlockObject(0);
RuntimeLockUnlockObjects(0, 0);
RuntimeUnlockObject(0);
v0 = sub_562716(&prm_0139);
v6 = RuntimeNewObject(v0);
WindowDefaultConstructor(v6);
RuntimeLockUnlockObjects(v6, 0);
RuntimeUnlockObject(0);
v1 = sub_562716(v4);
v5 = RuntimeCheckCast(v6, v1);
RuntimeLockObject(v5);
RuntimeUnlockObject(v6);
RuntimeUnlockObject(0);
RuntimeUnlockObject(v6);
RuntimeUnlockObject(v5);
return v5;
}
Again, lost of framework calls, and a unknown call
sub_562716(), taking
prm_0139 as parameter.
Checking what
prm_0139 holds shows I'm on the right track:
prm_0139, IDA disassemblyOVERLAY:00571541 prm_0139 struc_PRM <1, offset str_0139, 9, 7, 8000100h>
OVERLAY:00571555 str_0139 db 7,'Window1',0,0,0,0
This is obviously the window constructor procedure, so I'm going in:
OVERLAY_EP->sub_570B19()->sub_56C5E7()->sub_562716(), IDA decompileint sub_562716() {
// trimmed
RuntimeStackCheck();
if (!var_0032) {
v0 = RuntimeLookupClass(&prm_0079);
var_0032 = RuntimeNewClass(v0, 14, 0, 21);
*(_DWORD *)(var_0032 + 4) = &prm_0139;
RuntimeLockString(&prm_0139);
v1 = var_0032;
*(_DWORD *)(var_0032 + 16) = 0;
*(_DWORD *)(v1 + 12) = 140;
*(_DWORD *)(v1 + 20) = &unk_5626D8;
*(_DWORD *)(v1 + 24) = &unk_5626F7;
v2 = *(_DWORD *)(v1 + 32);
*(_DWORD *)v2 = &unk_562CE7;
*(_DWORD *)(v2 + 4) = &prm_0081;
RuntimeLockString(&prm_0081);
*(_DWORD *)(v2 + 8) = aUlBL;
*(_DWORD *)(v2 + 12) = &prm_0082;
RuntimeLockString(&prm_0082);
*(_DWORD *)(v2 + 16) = &unk_563AFF;
*(_DWORD *)(v2 + 20) = &prm_0083;
RuntimeLockString(&prm_0083);
*(_DWORD *)(v2 + 24) = &unk_563DD6;
*(_DWORD *)(v2 + 28) = &prm_0084;
RuntimeLockString(&prm_0084);
*(_DWORD *)(v2 + 32) = &unk_5640AD;
*(_DWORD *)(v2 + 36) = &prm_0085;
RuntimeLockString(&prm_0085);
*(_DWORD *)(v2 + 40) = &unk_564384;
*(_DWORD *)(v2 + 44) = &prm_0086;
RuntimeLockString(&prm_0086);
*(_DWORD *)(v2 + 48) = &unk_56465B;
*(_DWORD *)(v2 + 52) = &prm_0087;
RuntimeLockString(&prm_0087);
*(_DWORD *)(v2 + 56) = &unk_56AD50;
*(_DWORD *)(v2 + 60) = &prm_0088;
RuntimeLockString(&prm_0088);
*(_DWORD *)(v2 + 64) = &unk_56B027;
*(_DWORD *)(v2 + 68) = &prm_0089;
RuntimeLockString(&prm_0089);
*(_DWORD *)(v2 + 72) = &unk_56B3C7;
*(_DWORD *)(v2 + 76) = &prm_0090;
RuntimeLockString(&prm_0090);
*(_DWORD *)(v2 + 80) = &unk_56B767;
*(_DWORD *)(v2 + 84) = &prm_0091;
RuntimeLockString(&prm_0091);
*(_DWORD *)(v2 + 88) = &unk_56BB07;
*(_DWORD *)(v2 + 92) = &prm_0092;
RuntimeLockString(&prm_0092);
*(_DWORD *)(v2 + 96) = &unk_56BEA7;
*(_DWORD *)(v2 + 100) = &prm_0093;
RuntimeLockString(&prm_0093);
*(_DWORD *)(v2 + 104) = &unk_56C247;
*(_DWORD *)(v2 + 108) = &prm_0094;
RuntimeLockString(&prm_0094);
v3 = *(_DWORD *)(var_0032 + 40);
*(_DWORD *)v3 = &unk_562CE7;
*(_DWORD *)(v3 + 4) = &prm_0095;
RuntimeLockString(&prm_0095);
*(_DWORD *)(v3 + 8) = aUlBL;
*(_DWORD *)(v3 + 12) = &prm_0096;
RuntimeLockString(&prm_0096);
}
return var_0032;
}
This looks promising.
Because it's the Window constructor procedure, I expect to find things like creating window controls, static texts, menus, buttons, button actions and so on.
Since the application consists of a text field and a button, i'm interested in anything related to grabbing data from a text field and button actions.
Also, note that these variables starting with "
unk_" are procedures that weren't resolved automatically by IDA, so I'll basically have to go to the said address and press
'C' to convert it into code.
The logic in the above code is pretty simple.
Basically first we crate a Class using
RuntimeNewClass(), then assign procedures and parameters to it like this:
Procedures assignment, IDA decompile*(_DWORD *)v2 = &unk_562CE7; // Procedure 1
*(_DWORD *)(v2 + 4) = &prm_0081; // Procedure 1 parameter
RuntimeLockString(&prm_0081);
*(_DWORD *)(v2 + 8) = aUlBL; // Procedure 2
*(_DWORD *)(v2 + 12) = &prm_0082; // Procedure 1 parameter
RuntimeLockString(&prm_0082);
*(_DWORD *)(v2 + 16) = &unk_563AFF; // Procedure 3
*(_DWORD *)(v2 + 20) = &prm_0083; // Procedure 3 parameter
RuntimeLockString(&prm_0083);
Now i can take a look at every "
prm_" and take a look at the interesting ones.
The complete list of parameters here is this:
Rarameters list, IDA disassemblyOVERLAY:00571525 prm_0079 struc_PRM <1, offset str_0079, 8, 6, 600h>
OVERLAY:00571539 str_0079 db 6,'Window',0
OVERLAY:00571541 prm_0139 struc_PRM <1, offset str_0139, 9, 7, 8000100h>
OVERLAY:00571555 str_0139 db 7,'Window1',0,0,0,0
OVERLAY:00571561 prm_0081 struc_PRM <1, offset str_0081, 15h, 13h, 8000100h>
OVERLAY:00571575 str_0081 db 19,'#Layout%%o<Window1>',0,0,0,0
OVERLAY:0057158D prm_0082 struc_PRM <1, offset str_0082, 1Dh, 1Bh, 8000100h>
OVERLAY:005715A1 str_0082 db 27,'#CreateControls%%o<Window1>',0,0,0,0
OVERLAY:005715C1 prm_0083 struc_PRM <1, offset str_0083, 26h, 24h, 8000100h>
OVERLAY:005715D5 str_0083 db 36,'StaticText1%o<StaticText>%o<Window1>',0,0,0
OVERLAY:005715FD prm_0084 struc_PRM <1, offset str_0084, 1Dh, 1Bh, 8000100h> // / create the EDIT field
OVERLAY:00571611 str_0084 db 27,'key%o<EditField>%o<Window1>',0,0,0,0 // \
OVERLAY:00571631 prm_0085 struc_PRM <1, offset str_0085, 26h, 24h, 8000100h>
OVERLAY:00571645 str_0085 db 36,'StaticText2%o<StaticText>%o<Window1>',0,0,0
OVERLAY:0057166D prm_0086 struc_PRM <1, offset str_0086, 26h, 24h, 8000100h> // / create the BUTTON
OVERLAY:00571681 str_0086 db 36,'PushButton1%o<PushButton>%o<Window1>',0,0,0 // \
OVERLAY:005716A9 prm_0087 struc_PRM <1, offset str_0087, 2Dh, 2Bh, 8000100h> // / assign action to the BUTTON
OVERLAY:005716BD str_0087 db 43,'PushButton1_Action%%o<Window1>o<PushButton>',0,0,0,0 // \
OVERLAY:005716ED prm_0088 struc_PRM <1, offset str_0088, 26h, 24h, 8000100h>
OVERLAY:00571701 str_0088 db 36,'StaticText3%o<StaticText>%o<Window1>',0,0,0
OVERLAY:00571729 prm_0089 struc_PRM <1, offset str_0089, 21h, 1Fh, 8000100h>
OVERLAY:0057173D str_0089 db 31,'EditUndo%o<MenuItem>%o<Window1>',0,0,0,0
OVERLAY:00571761 prm_0090 struc_PRM <1, offset str_0090, 20h, 1Eh, 8000100h>
OVERLAY:00571775 str_0090 db 30,'EditCut%o<MenuItem>%o<Window1>',0
OVERLAY:00571795 prm_0091 struc_PRM <1, offset str_0091, 21h, 1Fh, 8000100h>
OVERLAY:005717A9 str_0091 db 31,'EditCopy%o<MenuItem>%o<Window1>',0,0,0,0
OVERLAY:005717CD prm_0092 struc_PRM <1, offset str_0092, 22h, 20h, 8000100h>
OVERLAY:005717E1 str_0092 db 32,'EditPaste%o<MenuItem>%o<Window1>',0,0,0
OVERLAY:00571805 prm_0093 struc_PRM <1, offset str_0093, 22h, 20h, 8000100h>
OVERLAY:00571819 str_0093 db 32,'EditClear%o<MenuItem>%o<Window1>',0,0,0
OVERLAY:0057183D prm_0094 struc_PRM <1, offset str_0094, 25h, 23h, 8000100h>
OVERLAY:00571851 str_0094 db 35,'FileQuit%o<QuitMenuItem>%o<Window1>',0,0,0,0
OVERLAY:00571879 prm_0095 struc_PRM <1, offset str_0095, 8, 6, 8000100h>
OVERLAY:0057188D str_0095 db 6,'Layout',0
OVERLAY:00571895 prm_0096 struc_PRM <1, offset str_0096, 10h, 0Eh, 8000100h>
OVERLAY:005718A9 str_0096 db 14,'CreateControls',0
So, I'm interested in
prm_0087 associated procedure:
sub_56465B(prm_0087), IDA decompileint __cdecl sub_56465B(int a1, int a2) {
// trimmed
RuntimeLockObject(a1);
RuntimeLockObject(a2);
RuntimeStackCheck();
RuntimeUnlockString(0);
RuntimeUnlockObject(0);
v167 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 24))(a1);
if ( !v167 )
RuntimeNilObject();
v166 = editTextGetter(v167); // Get the user entered code
RuntimeLockUnlockStrings(v166, 0);
if ( RuntimeStringCompare(v166, &prm_0106) ) { // Compare the user code with whatever prm_0106 holds
if ( RuntimeStringCompare(v166, &prm_0109) ) { // Compare the user code with whatever prm_0109 holds
if ( RuntimeStringCompare(v166, &prm_0112) ) { // Compare the user code with whatever prm_0112 holds
if ( RuntimeStringCompare(v166, &prm_0115) ) { // Compare the user code with whatever prm_0115 holds
if ( RuntimeStringCompare(v166, &prm_0118) ) { // Compare the user code with whatever prm_0118 holds
if ( RuntimeStringCompare(v166, &prm_0121) ) { // Compare the user code with whatever prm_0121 holds
RuntimeMsgBox(&prm_0124); // Throw message from prm_0124
} else {
// trimmed
Those
RuntimeStringCompare() calls look interesting, so let's see their params:
List of RuntimeStringCompare parameters, IDA disassemblyOVERLAY:005718E9 prm_0106 struc_PRM <1, offset str_0106, 15h, 13h, 8000100h>
OVERLAY:005718FD str_0106 db 19,'0130-1414-5624-1341',0,0,0,0
;...
OVERLAY:00571975 prm_0109 struc_PRM <1, offset str_0109, 15h, 13h, 8000100h>
OVERLAY:00571989 str_0109 db 19,'3810-1941-5861-5351',0,0,0,0
;...
OVERLAY:005719A1 prm_0112 struc_PRM <1, offset str_0112, 15h, 13h, 8000100h>
OVERLAY:005719B5 str_0112 db 19,'8361-9811-5511-8134',0,0,0,0
;...
OVERLAY:005719CD prm_0115 struc_PRM <1, offset str_0115, 15h, 13h, 8000100h>
OVERLAY:005719E1 str_0115 db 19,'9484-2341-5696-5321',0,0,0,0
;...
OVERLAY:005719F9 prm_0118 struc_PRM <1, offset str_0118, 15h, 13h, 8000100h>
OVERLAY:00571A0D str_0118 db 19,'0138-4411-5902-2411',0,0,0,0
;...
OVERLAY:00571A25 prm_0121 struc_PRM <1, offset str_0121, 15h, 13h, 8000100h>
OVERLAY:00571A39 str_0121 db 19,'7692-3349-1914-4567',0,0,0,0
Trying any of these gives me this result:
Nice. I've got what I need, but let's see where that "
smashthestate" came from.
The
prm_0124 holds the "
Sorry, you entered an incorrect serial number. Please re-enter", so that's the bad password message.
In the else statement the code goes like this:
Site code generation, IDA decompile// trimmed multiple calls to RuntimeUnlockString(0);
v30 = StringDBCSChr(115); // 's'
v29 = RuntimeAddString(&prm_0122, v30); // "Contratulations! The password to this level is \'" (the typo is not mine)
RuntimeUnlockString(0);
v28 = StringDBCSChr(109); // 'm'
v27 = RuntimeAddString(v29, v28);
RuntimeUnlockString(0);
v26 = StringDBCSChr(97); // 'a'
v25 = RuntimeAddString(v27, v26);
RuntimeUnlockString(0);
v24 = StringDBCSChr(115); // 's'
v23 = RuntimeAddString(v25, v24);
RuntimeUnlockString(0);
v22 = StringDBCSChr(104); // 'h'
v21 = RuntimeAddString(v23, v22);
RuntimeUnlockString(0);
v20 = StringDBCSChr(116); // 't'
v19 = RuntimeAddString(v21, v20);
RuntimeUnlockString(0);
v18 = StringDBCSChr(104); // 'h'
v17 = RuntimeAddString(v19, v18);
RuntimeUnlockString(0);
v16 = StringDBCSChr(101); // 'e'
v15 = RuntimeAddString(v17, v16);
RuntimeUnlockString(0);
v14 = StringDBCSChr(115); // 's'
v13 = RuntimeAddString(v15, v14);
RuntimeUnlockString(0);
v12 = StringDBCSChr(116); // 't'
v11 = RuntimeAddString(v13, v12);
RuntimeUnlockString(0);
v10 = StringDBCSChr(97); // 'a'
v9 = RuntimeAddString(v11, v10);
RuntimeUnlockString(0);
v8 = StringDBCSChr(116); // 't'
v7 = RuntimeAddString(v9, v8);
RuntimeUnlockString(0);
v6 = StringDBCSChr(101); // 'e'
v5 = RuntimeAddString(v7, v6);
v4 = RuntimeAddString(v5, &prm_0123); // "\'"
RuntimeMsgBox(v4); // Throws the complete message
So the valid site password is constructed character by character and appended to the "
Congratulations" message.
Application Challenge #1 (UNIX)
Target: app1.unix.bin
Requirement: Find the password
Difficulty: easy
The UNIX version is compiled for
FreeBSD which is... odd.
I just throw it in IDA and use the decompiler and here's what I got:
UNIX version, entry point, IDA disassemblyint __cdecl main(int argc, const char **argv, const char **envp) {
// trimmed
v9 = &argc;
std::string::string((std::string *)&v8);
std::string::string((std::string *)&v7);
std::string::operator=(&v7, "moo");
std::operator>><char,std::char_traits<char>,std::allocator<char>>(&std::cin, &v8);
s2 = (char *)std::string::c_str((std::string *)&v7);
v4 = (const char *)std::string::c_str((std::string *)&v8);
if ( strcmp(v4, s2) == 0 ) {
std::operator<<<std::char_traits<char>>(&std::cout, "IVannaHackHTS");
v6 = 0;
} else {
v6 = 77;
}
std::string::~string((std::string *)&v7);
std::string::~string((std::string *)&v8);
return v6;
}
Couldn't be simpler than that: if the user entered code is "
moo", it prints the site password "
IVannaHackHTS"
Application Challenge #1 (Mac)
Target: app1mac
Requirement: Find the password
Difficulty: easy
I tried literally everything, including trying it on a genuine Mac, and yet I wasn't even able to extract the .sit file.
That being said - fuck Mac and fuck Apple.
Application Challenge #2 (Windows)
Target: app2win.exe
Requirement: Find the Password
Difficulty: easy
The second challenge looks pretty much like the first one:
Again it's a executable written in
REALbasic, so you can refer to the first solution for more details.
Using the Python script in IDA, I'll fast forward to here:
OVERLAY entry point, IDA decompileint main_overlay() {
RuntimeInit();
sub_56F4E0();
RuntimeRegisterFileType(&prm_0002, &prm_0001, &prm_0000, 0);
sub_56F083();
sub_56F35D();
RuntimeRun();
var_0001 = 0;
RuntimeUnlockObject(0);
RuntimeExit();
return 0;
}
Same construction as before, and again I'm interested in the procedure right before
RuntimeRun() -
sub_56F35D():
OVERLAY_EP->sub_56F35D(), IDA decompileint sub_56F35D() {
// trimmed
RuntimeStackCheck();
RuntimeUnlockObject(0);
v2 = sub_56AE2B();
if ( !v2 )
RuntimeNilObject();
WindowForceUpdateFrontBuffer(v2);
RuntimeUnlockObject(v2);
return 0;
}
Continuing to
sub_56AE2B():
OVERLAY_EP->sub_56F35D()->sub_56AE2B(), IDA decompileint sub_56AE2B() {
// trimmed
RuntimeStackCheck();
RuntimeUnlockObject(0);
RuntimeLockUnlockObjects(0, 0);
RuntimeUnlockObject(0);
v0 = sub_5647EB(&prm_0169);
v6 = RuntimeNewObject(v0);
WindowDefaultConstructor(v6);
RuntimeLockUnlockObjects(v6, 0);
RuntimeUnlockObject(0);
v1 = sub_5647EB(v4);
v5 = RuntimeCheckCast(v6, v1);
RuntimeLockObject(v5);
RuntimeUnlockObject(v6);
RuntimeUnlockObject(0);
RuntimeUnlockObject(v6);
RuntimeUnlockObject(v5);
return v5;
}
and finally
sub_5647EB():
OVERLAY_EP->sub_56F35D()->sub_56AE2B()->sub_5647EB(), IDA decompileint sub_5647EB() {
// trimmed
RuntimeStackCheck();
if (!var_0043) {
v0 = RuntimeLookupClass(&prm_0110); // "Window"
var_0043 = RuntimeNewClass(v0, 18, 0, 21);
*(_DWORD *)(var_0043 + 4) = &prm_0169; // "Window1"
RuntimeLockString(&prm_0169);
v1 = var_0043;
*(_DWORD *)(var_0043 + 16) = 0;
*(_DWORD *)(v1 + 12) = 140;
*(_DWORD *)(v1 + 20) = &unk_5647AD;
*(_DWORD *)(v1 + 24) = &unk_5647CC;
v2 = *(_DWORD *)(v1 + 32);
*(_DWORD *)v2 = &unk_564ED8;
*(_DWORD *)(v2 + 4) = &prm_0112; // "#Layout%%o<Window1>"
RuntimeLockString(&prm_0112);
*(_DWORD *)(v2 + 8) = aUlBF;
*(_DWORD *)(v2 + 12) = &prm_0113; // "#CreateControls%%o<Window1>"
RuntimeLockString(&prm_0113);
*(_DWORD *)(v2 + 16) = &unk_56606E;
*(_DWORD *)(v2 + 20) = &prm_0114; // "StaticText1%o<StaticText>%o<Window1>"
RuntimeLockString(&prm_0114);
*(_DWORD *)(v2 + 24) = &unk_566345; // / The serial number EDIT control
*(_DWORD *)(v2 + 28) = &prm_0115; // \ "key%o<EditField>%o<Window1>"
RuntimeLockString(&prm_0115);
*(_DWORD *)(v2 + 32) = &unk_56661C;
*(_DWORD *)(v2 + 36) = &prm_0116; // "StaticText2%o<StaticText>%o<Window1>"
RuntimeLockString(&prm_0116);
*(_DWORD *)(v2 + 40) = &unk_5668F3;
*(_DWORD *)(v2 + 44) = &prm_0117; // "PushButton1%o<PushButton>%o<Window1>"
RuntimeLockString(&prm_0117);
*(_DWORD *)(v2 + 48) = sub_566BCA; // / Pressed BUTTON action
*(_DWORD *)(v2 + 52) = &prm_0118; // \ "PushButton1_Action%%o<Window1>o<PushButton>"
RuntimeLockString(&prm_0118);
*(_DWORD *)(v2 + 56) = &unk_566F60;
*(_DWORD *)(v2 + 60) = &prm_0119; // "status%o<StaticText>%o<Window1>"
RuntimeLockString(&prm_0119);
*(_DWORD *)(v2 + 64) = &unk_567237; // / Init a SOCKET method
*(_DWORD *)(v2 + 68) = &prm_0120; // \ "Socket1%o<Socket>%o<Window1>"
RuntimeLockString(&prm_0120);
*(_DWORD *)(v2 + 72) = sub_56750E; // / Read SOCKET data
*(_DWORD *)(v2 + 76) = &prm_0121; // \ "Socket1_DataAvailable%%o<Window1>o<Socket>"
RuntimeLockString(&prm_0121);
*(_DWORD *)(v2 + 80) = aUlB_; // / Connect SOCKET
*(_DWORD *)(v2 + 84) = &prm_0122; // \ "Socket1_Connected%%o<Window1>o<Socket>"
RuntimeLockString(&prm_0122);
*(_DWORD *)(v2 + 88) = &unk_569594;
*(_DWORD *)(v2 + 92) = &prm_0123; // "StaticText3%o<StaticText>%o<Window1>"
RuntimeLockString(&prm_0123);
*(_DWORD *)(v2 + 96) = &unk_56986B;
*(_DWORD *)(v2 + 100) = &prm_0124; // "EditUndo%o<MenuItem>%o<Window1>"
RuntimeLockString(&prm_0124);
*(_DWORD *)(v2 + 104) = &unk_569C0B;
*(_DWORD *)(v2 + 108) = &prm_0125; // "EditCut%o<MenuItem>%o<Window1>"
RuntimeLockString(&prm_0125);
*(_DWORD *)(v2 + 112) = &unk_569FAB;
*(_DWORD *)(v2 + 116) = &prm_0126; // "EditCopy%o<MenuItem>%o<Window1>"
RuntimeLockString(&prm_0126);
*(_DWORD *)(v2 + 120) = &unk_56A34B;
*(_DWORD *)(v2 + 124) = &prm_0127; // "EditPaste%o<MenuItem>%o<Window1>"
RuntimeLockString(&prm_0127);
*(_DWORD *)(v2 + 128) = &unk_56A6EB;
*(_DWORD *)(v2 + 132) = &prm_0128; // "EditClear%o<MenuItem>%o<Window1>"
RuntimeLockString(&prm_0128);
*(_DWORD *)(v2 + 136) = &unk_56AA8B;
*(_DWORD *)(v2 + 140) = &prm_0129; // "FileQuit%o<QuitMenuItem>%o<Window1>"
RuntimeLockString(&prm_0129);
v3 = *(_DWORD *)(var_0043 + 40);
*(_DWORD *)v3 = &unk_564ED8;
*(_DWORD *)(v3 + 4) = &prm_0130; // "Layout"
RuntimeLockString(&prm_0130);
*(_DWORD *)(v3 + 8) = aUlBF;
*(_DWORD *)(v3 + 12) = &prm_0131; // "CreateControls"
RuntimeLockString(&prm_0131);
}
return var_0043;
}
Alright, seems there's more than one places I should look.
First, as most obvious will be the button action
prm_0118, associated with
sub_566BCA():
sub_566BCA(prm_0118), IDA decompileint __cdecl sub_566BCA(int a1, int a2) {
RuntimeLockObject(a1);
RuntimeLockObject(a2);
RuntimeStackCheck();
RuntimeUnlockObject(0);
v5 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 56))(a1);
if ( !v5 )
RuntimeNilObject();
staticCaptionSetter(v5, 112, &prm_0141); // "Status: Connecting..."
RuntimeUnlockObject(0);
v4 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 64))(a1);
if ( !v4 )
RuntimeNilObject();
SocketConnect(v4);
// trimmed
return 0;
}
Nothing interesting here, so moving on.
prm_0120 is where the SOCKET is initialized and that's not interesting, so let's see the next one
prm_0121 where the socket data is received - procedure
sub_56750E():
sub_56750E(prm_0121), IDA decompileint __cdecl sub_56750E(int a1, int a2) {
// trimmed
RuntimeLockObject(a1);
RuntimeLockObject(a2);
RuntimeStackCheck();
RuntimeUnlockString(0);
if ( !a2 )
RuntimeNilObject();
v35 = SocketReadAll(a2); // Read the socket reply data
RuntimeLockUnlockStrings(v35, 0);
RuntimeUnlockObject(0);
v34 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 56))(a1);
if ( !v34 )
RuntimeNilObject();
staticCaptionSetter(v34, 112, &prm_0146); // 'Status: Reading data...'
RuntimeUnlockString(0);
RuntimeUnlockObject(0);
v33 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 24))(a1);
if ( !v33 )
RuntimeNilObject();
v32 = editTextGetter(v33);
if (!StringInStr(0, v35, v32)) // check if the user code is contained in the data read from the socket
goto LABEL_28;
RuntimeUnlockString(0);
RuntimeUnlockObject(0);
v31 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 24))(a1);
if ( !v31 )
RuntimeNilObject();
v30 = editTextGetter(v31);
if ( StringDBCSLen(v30) == 23 ) // valid code should be 23 characters long
v29 = 1;
else
LABEL_28:
v29 = 0;
if (v29) { // at this point the code is verified
RuntimeUnlockObject(0);
v28 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 56))(a1);
if ( !v28 )
RuntimeNilObject();
staticCaptionSetter(v28, 112, &prm_0147); // 'Status: Validated'
// trimmed RuntimeUnlockString(0);
v27 = StringDBCSChr(108); // 'l'
v26 = RuntimeAddString(&prm_0148, v27); // "Congratulations! The password to this level is \'"
RuntimeUnlockString(0);
v25 = StringDBCSChr(105); // 'i'
v24 = RuntimeAddString(v26, v25);
RuntimeUnlockString(0);
v23 = StringDBCSChr(98); // 'b'
v22 = RuntimeAddString(v24, v23);
RuntimeUnlockString(0);
v21 = StringDBCSChr(101); // 'e'
v20 = RuntimeAddString(v22, v21);
RuntimeUnlockString(0);
v19 = StringDBCSChr(114); // 'r'
v18 = RuntimeAddString(v20, v19);
RuntimeUnlockString(0);
v17 = StringDBCSChr(97); // 'a'
v16 = RuntimeAddString(v18, v17);
RuntimeUnlockString(0);
v15 = StringDBCSChr(116); // 't'
v14 = RuntimeAddString(v16, v15);
RuntimeUnlockString(0);
v13 = StringDBCSChr(105); // 'i'
v12 = RuntimeAddString(v14, v13);
RuntimeUnlockString(0);
v11 = StringDBCSChr(111); // 'o'
v10 = RuntimeAddString(v12, v11);
RuntimeUnlockString(0);
v9 = StringDBCSChr(110); // 'n'
v8 = RuntimeAddString(v10, v9);
v7 = RuntimeAddString(v8, &prm_0149);
RuntimeMsgBox(v7); // "\'"
RuntimeUnlockObject(0);
v6 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 64))(a1);
if ( !v6 )
RuntimeNilObject();
socketClose(v6);
} else {
RuntimeUnlockObject(0);
v5 = (*(int (__cdecl **)(_DWORD))(*(_DWORD *)(a1 + 4) + 56))(a1);
if ( !v5 )
RuntimeNilObject();
staticCaptionSetter(v5, 112, &prm_0150); // "Status: Serial invalid"
RuntimeMsgBox(&prm_0151); // "Sorry, you entered an incorrect serial number. Please re-enter"
RuntimeUnlockObject(0);
v4 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 64))(a1);
if ( !v4 )
RuntimeNilObject();
socketClose(v4);
}
// trimmed release functions
return 0;
}
Alright I got the validation code and I got the correct password for the site - "
liberation".
But where are the valid codes pulled from? I know it's a socket request, so let's see the socket connection procedure -
prm_0122 and
sub_568DD6():
sub_568DD6(prm_0122), IDA decompileint __cdecl sub_568DD6(int a1, int a2) {
RuntimeLockObject(a1);
RuntimeLockObject(a2);
RuntimeStackCheck();
RuntimeUnlockObject(0);
v12 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 56))(a1);
if ( !v12 )
RuntimeNilObject();
staticCaptionSetter(v12, 112, &prm_0152); // 'Status: Sending request...'
RuntimeUnlockObject(0);
v11 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 64))(a1);
if ( !v11 )
RuntimeNilObject();
RuntimeUnlockString(0);
RuntimeUnlockString(0);
RuntimeUnlockString(0);
RuntimeUnlockString(0);
RuntimeUnlockString(0);
v2 = StringDBCSChr(10);
v3 = RuntimeAddString(&prm_0153, v2); // "GET /application/app2/keys123.txt HTTP/1.1"
v4 = RuntimeAddString(v3, &prm_0154); // "Host: hackthissite.org"
RuntimeUnlockString(0);
v5 = StringDBCSChr(10); // '\n'
v6 = RuntimeAddString(v4, v5);
RuntimeUnlockString(0);
v7 = StringDBCSChr(10); // '\n'
v8 = RuntimeAddString(v6, v7);
SocketWrite(v11, v8);
// trimmed
return 0;
}
A
HTTP GET request builder.
The list of codes is located at
http://hackthissite.org/application/app2/keys123.txt and it contains these valid codes:
valid user codes63482-74819-88456-98378
45910-18394-85113-51290
10110-19101-59111-41563
11424-74719-19578-99238
25182-28381-85611-85258
62351-12939-12481-58020
63482-74819-88456-98378
45910-18394-85113-51290
18381-21931-98680-86523
32910-21944-12391-51939
12389-16781-72893-71892
83478-91933-89823-98511
Trying any of these will work:
Application Challenge #2 (Mac)
Target: app2mac
Requirement: Find the Password
Difficulty: easy
Again, non-working Stuffit archive.
Application Challenge #3 (Windows)
Target: app3win.exe
Requirement: Find the Password
Difficulty: easy
The third challenge is again written in REALbasic and it looks like this:
This one actually is currently broken, but first things first.
Using the know-how from the previous challenges, that were also written in REALbasic, I can fast forward to the window create and process code:
sub_5647B4(), IDA decompileint sub_5647B4() {
RuntimeStackCheck();
if ( !var_0043 ) {
v0 = RuntimeLookupClass(&prm_0110); // "Window"
var_0043 = RuntimeNewClass(v0, 18, 0, 21);
*(_DWORD *)(var_0043 + 4) = &prm_0173; // "Window1"
RuntimeLockString(&prm_0173); // "Window1"
v1 = var_0043;
*(_DWORD *)(var_0043 + 16) = 0;
*(_DWORD *)(v1 + 12) = 140;
*(_DWORD *)(v1 + 20) = &unk_564776;
*(_DWORD *)(v1 + 24) = &unk_564795;
v2 = *(_DWORD *)(v1 + 32);
*(_DWORD *)v2 = &unk_564EA1;
*(_DWORD *)(v2 + 4) = &prm_0112; // "#Layout%%o<Window1>"
RuntimeLockString(&prm_0112);
*(_DWORD *)(v2 + 8) = aUlBF;
*(_DWORD *)(v2 + 12) = &prm_0113; // "#CreateControls%%o<Window1>"
RuntimeLockString(&prm_0113);
*(_DWORD *)(v2 + 16) = &unk_566037;
*(_DWORD *)(v2 + 20) = &prm_0114; // "StaticText1%o<StaticText>%o<Window1>"
RuntimeLockString(&prm_0114);
*(_DWORD *)(v2 + 24) = &unk_56630E;
*(_DWORD *)(v2 + 28) = &prm_0115; // "key%o<EditField>%o<Window1>"
RuntimeLockString(&prm_0115);
*(_DWORD *)(v2 + 32) = &unk_5665E5;
*(_DWORD *)(v2 + 36) = &prm_0116; // "StaticText2%o<StaticText>%o<Window1>"
RuntimeLockString(&prm_0116);
*(_DWORD *)(v2 + 40) = &unk_5668BC;
*(_DWORD *)(v2 + 44) = &prm_0117; // "PushButton1%o<PushButton>%o<Window1>"
RuntimeLockString(&prm_0117);
*(_DWORD *)(v2 + 48) = aUlB;
*(_DWORD *)(v2 + 52) = &prm_0118; // "PushButton1_Action%%o<Window1>o<PushButton>"
RuntimeLockString(&prm_0118);
*(_DWORD *)(v2 + 56) = &unk_566F29;
*(_DWORD *)(v2 + 60) = &prm_0119; // "status%o<StaticText>%o<Window1>"
RuntimeLockString(&prm_0119);
*(_DWORD *)(v2 + 64) = &unk_567200;
*(_DWORD *)(v2 + 68) = &prm_0120; // "Socket1%o<Socket>%o<Window1>"
RuntimeLockString(&prm_0120);
*(_DWORD *)(v2 + 72) = sub_5674D7;
*(_DWORD *)(v2 + 76) = &prm_0121; // "Socket1_DataAvailable%%o<Window1>o<Socket>"
RuntimeLockString(&prm_0121);
*(_DWORD *)(v2 + 80) = sub_568DFE;
*(_DWORD *)(v2 + 84) = &prm_0122; // "Socket1_Connected%%o<Window1>o<Socket>"
RuntimeLockString(&prm_0122);
*(_DWORD *)(v2 + 88) = &unk_56986F;
*(_DWORD *)(v2 + 92) = &prm_0123; // "StaticText3%o<StaticText>%o<Window1>"
RuntimeLockString(&prm_0123);
*(_DWORD *)(v2 + 96) = &unk_569B46;
*(_DWORD *)(v2 + 100) = &prm_0124; // "EditUndo%o<MenuItem>%o<Window1>"
RuntimeLockString(&prm_0124);
*(_DWORD *)(v2 + 104) = &unk_569EE6;
*(_DWORD *)(v2 + 108) = &prm_0125; // "EditCut%o<MenuItem>%o<Window1>"
RuntimeLockString(&prm_0125);
*(_DWORD *)(v2 + 112) = &unk_56A286;
*(_DWORD *)(v2 + 116) = &prm_0126; // "EditCopy%o<MenuItem>%o<Window1>"
RuntimeLockString(&prm_0126);
*(_DWORD *)(v2 + 120) = &unk_56A626;
*(_DWORD *)(v2 + 124) = &prm_0127; // "EditPaste%o<MenuItem>%o<Window1>"
RuntimeLockString(&prm_0127);
*(_DWORD *)(v2 + 128) = &unk_56A9C6;
*(_DWORD *)(v2 + 132) = &prm_0128; // "EditClear%o<MenuItem>%o<Window1>"
RuntimeLockString(&prm_0128);
*(_DWORD *)(v2 + 136) = &unk_56AD66;
*(_DWORD *)(v2 + 140) = &prm_0129; // "FileQuit%o<QuitMenuItem>%o<Window1>"
RuntimeLockString(&prm_0129);
v3 = *(_DWORD *)(var_0043 + 40);
*(_DWORD *)v3 = &unk_564EA1;
*(_DWORD *)(v3 + 4) = &prm_0130;
RuntimeLockString(&prm_0130); // "Layout"
*(_DWORD *)(v3 + 8) = aUlBF;
*(_DWORD *)(v3 + 12) = &prm_0131; // "CreateControls"
RuntimeLockString(&prm_0131);
}
return var_0043;
}
Again sockets are involved, and I already have experience with those, so let's directly see what
Socket1_DataAvailable at
sub_5674D7() does:
Socket1_DataAvailable procedure, IDA decompileint __cdecl sub_5674D7(int a1, int a2) {
// trimmed
RuntimeLockObject(a1);
RuntimeLockObject(a2);
RuntimeStackCheck();
RuntimeUnlockString(0);
if ( !a2 )
RuntimeNilObject();
v35 = SocketReadAll(a2);
RuntimeLockUnlockStrings(v35, 0);
RuntimeUnlockObject(0);
v34 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 56))(a1);
if ( !v34 )
RuntimeNilObject();
staticCaptionSetter(v34, 112, &prm_0154); // "Status: Reading data..."
if ( StringInStr(0, v35, &prm_0147) ) { // is the server response "true" ?
RuntimeUnlockObject(0);
v33 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 56))(a1);
if ( !v33 )
RuntimeNilObject();
staticCaptionSetter(v33, 112, &prm_0148); // "Status: Validated"
// trimmed RuntimeUnlockString(0);
v32 = StringDBCSChr(102); // 'f'
v31 = RuntimeAddString(&prm_0149, v32); // "Contratulations! The password to this level is \'"
RuntimeUnlockString(0);
v30 = StringDBCSChr(105); // 'i'
v29 = RuntimeAddString(v31, v30);
RuntimeUnlockString(0);
v28 = StringDBCSChr(114); // 'r'
v27 = RuntimeAddString(v29, v28);
RuntimeUnlockString(0);
v26 = StringDBCSChr(101); // 'e'
v25 = RuntimeAddString(v27, v26);
RuntimeUnlockString(0);
v24 = StringDBCSChr(121); // 'y'
v23 = RuntimeAddString(v25, v24);
RuntimeUnlockString(0);
v22 = StringDBCSChr(111); // 'o'
v21 = RuntimeAddString(v23, v22);
RuntimeUnlockString(0);
v20 = StringDBCSChr(117); // 'u'
v19 = RuntimeAddString(v21, v20);
RuntimeUnlockString(0);
v18 = StringDBCSChr(114); // 'r'
v17 = RuntimeAddString(v19, v18);
RuntimeUnlockString(0);
v16 = StringDBCSChr(98); // 'b'
v15 = RuntimeAddString(v17, v16);
RuntimeUnlockString(0);
v14 = StringDBCSChr(111); // 'o'
v13 = RuntimeAddString(v15, v14);
RuntimeUnlockString(0);
v12 = StringDBCSChr(115); // 's'
v11 = RuntimeAddString(v13, v12);
RuntimeUnlockString(0);
v10 = StringDBCSChr(115); // 's'
v9 = RuntimeAddString(v11, v10);
v8 = RuntimeAddString(v9, &prm_0150); // "\'"
RuntimeMsgBox(v8);
RuntimeUnlockObject(0);
v7 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 64))(a1);
if ( !v7 )
RuntimeNilObject();
socketClose(v7);
} else if ( StringInStr(0, v35, &prm_0151) ) { // is the server response "false" ?
RuntimeUnlockObject(0);
v6 = (*(int (__cdecl **)(_DWORD))(*(_DWORD *)(a1 + 4) + 56))(a1);
if ( !v6 )
RuntimeNilObject();
staticCaptionSetter(v6, 112, &prm_0152); // "Status: Serial invalid"
RuntimeMsgBox(&prm_0153); // "Sorry, you entered an incorrect serial number. Please re-enter."
RuntimeUnlockObject(0);
v5 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 64))(a1);
if ( !v5 )
RuntimeNilObject();
socketClose(v5);
} else {
RuntimeUnlockObject(0);
v4 = (*(int (__cdecl **)(_DWORD))(*(_DWORD *)(a1 + 4) + 56))(a1);
if ( !v4 )
RuntimeNilObject();
staticCaptionSetter(v4, 112, &prm_0154); // "Status: Reading data..."
}
// trimmed
return 0;
}
I got the site's password again, but let's check the validation process.
The server response now is not a set of valid codes to be checked against the user entered code, but a boolean "
true" or "
false" value.
Let's see where's the validator URL, by checking
Socket1_Connected procedure -
sub_568DFE():
Socket1_Connected procedure, IDA decompileint __cdecl sub_568DFE(int a1, int a2) {
// trimmed
RuntimeLockObject(a1);
RuntimeLockObject(a2);
RuntimeStackCheck();
RuntimeUnlockObject(0);
v16 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 56))(a1);
if ( !v16 )
RuntimeNilObject();
staticCaptionSetter(v16, 112, &prm_0155); // "Status: Sending request..."
RuntimeUnlockObject(0);
v15 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 64))(a1);
if ( !v15 )
RuntimeNilObject();
// trimmed
v14 = (*(int (__cdecl **)(int))(*(_DWORD *)(a1 + 4) + 24))(a1);
if ( !v14 )
RuntimeNilObject();
v2 = editTextGetter(v14);
v3 = RuntimeAddString(&prm_0156, v2); // "GET /missions/application/app3/auth.php?key="
v4 = RuntimeAddString(v3, &prm_0157); // " HTTP/1.1"
RuntimeUnlockString(0);
v5 = StringDBCSChr(10); // '\n'
v6 = RuntimeAddString(v4, v5);
v7 = RuntimeAddString(v6, &prm_0158); // "Host: hackthissite.org"
RuntimeUnlockString(0);
v8 = StringDBCSChr(10); // '\n'
v9 = RuntimeAddString(v7, v8);
RuntimeUnlockString(0);
v10 = StringDBCSChr(10); // '\n'
v11 = RuntimeAddString(v9, v10);
SocketWrite(v15, v11);
// trimmed
return 0;
}
The HTTP request is now at
http://hackthissite.org/missions/application/app3/auth.php?key=<user-key> and the response can be either "
true" or "
false".
However, when I click "
Authenticate", the status gets stuck to "
Status: Reading data...".
I fired up
Wireshark to see what's going on, and here's what I got:
Request:0000 00 50 56 ea e8 4a 00 0c 29 58 d1 a4 08 00 45 00 .PV..J..)X....E.
0010 00 80 18 79 40 00 80 06 00 00 c0 a8 f1 9c c6 94 ...y@...........
0020 51 8b cb e7 00 50 e4 c2 5a ad 25 73 1b 68 50 18 Q....P..Z.%s.hP.
0030 fa f0 ca d7 00 00 47 45 54 20 2f 6d 69 73 73 69 ......GET /missi
0040 6f 6e 73 2f 61 70 70 6c 69 63 61 74 69 6f 6e 2f ons/application/
0050 61 70 70 33 2f 61 75 74 68 2e 70 68 70 3f 6b 65 app3/auth.php?ke
0060 79 3d 00 31 32 33 31 32 33 31 32 33 20 48 54 54 y=.123123123 HTT
0070 50 2f 31 2e 31 0a 48 6f 73 74 3a 20 68 61 63 6b P/1.1.Host: hack
0080 74 68 69 73 73 69 74 65 2e 6f 72 67 0a 0a thissite.org..
What is this
\x00 doing there?
Response:0000 00 0c 29 58 d1 a4 00 50 56 ea e8 4a 08 00 45 00 ..)X...PV..J..E.
0010 00 ce 3c 66 00 00 80 06 33 5f c6 94 51 8b c0 a8 ..<f....3_..Q...
0020 f1 9c 00 50 cb e7 25 73 1b 68 e4 c2 5b 05 50 18 ...P..%s.h..[.P.
0030 fa f0 0a af 00 00 3c 68 74 6d 6c 3e 0d 0a 3c 68 ......<html>..<h
0040 65 61 64 3e 3c 74 69 74 6c 65 3e 34 30 30 20 42 ead><title>400 B
0050 61 64 20 52 65 71 75 65 73 74 3c 2f 74 69 74 6c ad Request</titl
0060 65 3e 3c 2f 68 65 61 64 3e 0d 0a 3c 62 6f 64 79 e></head>..<body
0070 20 62 67 63 6f 6c 6f 72 3d 22 77 68 69 74 65 22 bgcolor="white"
0080 3e 0d 0a 3c 63 65 6e 74 65 72 3e 3c 68 31 3e 34 >..<center><h1>4
0090 30 30 20 42 61 64 20 52 65 71 75 65 73 74 3c 2f 00 Bad Request</
00a0 68 31 3e 3c 2f 63 65 6e 74 65 72 3e 0d 0a 3c 68 h1></center>..<h
00b0 72 3e 3c 63 65 6e 74 65 72 3e 6e 67 69 6e 78 3c r><center>nginx<
00c0 2f 63 65 6e 74 65 72 3e 0d 0a 3c 2f 62 6f 64 79 /center>..</body
00d0 3e 0d 0a 3c 2f 68 74 6d 6c 3e 0d 0a >..</html>..
Boom. Ok, let's see the string inside the code:
prm_0156, IDA disassemblyOVERLAY:005709F8 prm_0156 struc_PRM <1, offset str_0156, 2Fh, 2Dh, 8000100h>
OVERLAY:00570A0C str_0156 db 45,'GET /missions/application/app3/auth.php?key=',0,0,0
The string itself is
44 bytes long, but the length is set to
45 (i actually need to change the
0x2D in the
prm_0156 structure only). Seriously? It's buggy already?
Alright, I can easily patch this in HxD and try again, using my bad pass:
Request:0000 00 50 56 ea e8 4a 00 0c 29 58 d1 a4 08 00 45 00 .PV..J..)X....E.
0010 00 7f 0b 71 40 00 80 06 00 00 c0 a8 f1 9c c6 94 ...q@...........
0020 51 89 c0 81 00 50 39 23 fb ad 5e 5c 5c 89 50 18 Q....P9#..^\\.P.
0030 fa f0 ca d4 00 00 47 45 54 20 2f 6d 69 73 73 69 ......GET /missi
0040 6f 6e 73 2f 61 70 70 6c 69 63 61 74 69 6f 6e 2f ons/application/
0050 61 70 70 33 2f 61 75 74 68 2e 70 68 70 3f 6b 65 app3/auth.php?ke
0060 79 3d 31 32 33 31 32 33 31 32 33 20 48 54 54 50 y=123123123 HTTP
0070 2f 31 2e 31 0a 48 6f 73 74 3a 20 68 61 63 6b 74 /1.1.Host: hackt
0080 68 69 73 73 69 74 65 2e 6f 72 67 0a 0a hissite.org..
Ok, that looks better.
Response:0000 00 0c 29 58 d1 a4 00 50 56 ea e8 4a 08 00 45 00 ..)X...PV..J..E.
0010 02 cb 3d d6 00 00 80 06 2f f4 c6 94 51 89 c0 a8 ..=...../...Q...
0020 f1 9c 00 50 c0 81 5e 5c 5c 89 39 23 fc 04 50 18 ...P..^\\.9#..P.
0030 fa f0 d3 8e 00 00 48 54 54 50 2f 31 2e 31 20 33 ......HTTP/1.1 3
0040 30 31 20 4d 6f 76 65 64 20 50 65 72 6d 61 6e 65 01 Moved Permane
0050 6e 74 6c 79 0d 0a 44 61 74 65 3a 20 57 65 64 2c ntly..Date: Wed,
0060 20 31 39 20 4a 75 6c 20 32 30 31 37 20 31 38 3a 19 Jul 2017 18:
0070 33 33 3a 32 31 20 47 4d 54 0d 0a 43 6f 6e 74 65 33:21 GMT..Conte
0080 6e 74 2d 54 79 70 65 3a 20 74 65 78 74 2f 68 74 nt-Type: text/ht
0090 6d 6c 3b 20 63 68 61 72 73 65 74 3d 69 73 6f 2d ml; charset=iso-
00a0 38 38 35 39 2d 31 0d 0a 43 6f 6e 74 65 6e 74 2d 8859-1..Content-
00b0 4c 65 6e 67 74 68 3a 20 32 38 34 0d 0a 43 6f 6e Length: 284..Con
00c0 6e 65 63 74 69 6f 6e 3a 20 6b 65 65 70 2d 61 6c nection: keep-al
00d0 69 76 65 0d 0a 4c 6f 63 61 74 69 6f 6e 3a 20 68 ive..Location: h
00e0 74 74 70 3a 2f 2f 77 77 77 2e 68 61 63 6b 74 68 ttp://www.hackth
00f0 69 73 73 69 74 65 2e 6f 72 67 2f 6d 69 73 73 69 issite.org/missi
0100 6f 6e 73 2f 61 70 70 6c 69 63 61 74 69 6f 6e 2f ons/application/
0110 61 70 70 33 2f 61 75 74 68 2e 70 68 70 3f 6b 65 app3/auth.php?ke
0120 79 3d 31 32 33 31 32 33 31 32 33 0d 0a 53 65 72 y=123123123..Ser
0130 76 65 72 3a 20 48 61 63 6b 54 68 69 73 53 69 74 ver: HackThisSit
0140 65 20 4c 6f 61 64 20 42 61 6c 61 6e 63 65 72 0d e Load Balancer.
0150 0a 53 74 72 69 63 74 2d 54 72 61 6e 73 70 6f 72 .Strict-Transpor
0160 74 2d 53 65 63 75 72 69 74 79 3a 20 6d 61 78 2d t-Security: max-
0170 61 67 65 3d 33 31 35 33 36 30 30 30 3b 20 69 6e age=31536000; in
0180 63 6c 75 64 65 53 75 62 64 6f 6d 61 69 6e 73 3b cludeSubdomains;
0190 20 70 72 65 6c 6f 61 64 0d 0a 58 2d 43 6f 6e 74 preload..X-Cont
01a0 65 6e 74 2d 54 79 70 65 2d 4f 70 74 69 6f 6e 73 ent-Type-Options
01b0 3a 20 6e 6f 73 6e 69 66 66 0d 0a 0d 0a 3c 21 44 : nosniff....<!D
01c0 4f 43 54 59 50 45 20 48 54 4d 4c 20 50 55 42 4c OCTYPE HTML PUBL
01d0 49 43 20 22 2d 2f 2f 49 45 54 46 2f 2f 44 54 44 IC "-//IETF//DTD
01e0 20 48 54 4d 4c 20 32 2e 30 2f 2f 45 4e 22 3e 0a HTML 2.0//EN">.
01f0 3c 68 74 6d 6c 3e 3c 68 65 61 64 3e 0a 3c 74 69 <html><head>.<ti
0200 74 6c 65 3e 33 30 31 20 4d 6f 76 65 64 20 50 65 tle>301 Moved Pe
0210 72 6d 61 6e 65 6e 74 6c 79 3c 2f 74 69 74 6c 65 rmanently</title
0220 3e 0a 3c 2f 68 65 61 64 3e 3c 62 6f 64 79 3e 0a >.</head><body>.
0230 3c 68 31 3e 4d 6f 76 65 64 20 50 65 72 6d 61 6e <h1>Moved Perman
0240 65 6e 74 6c 79 3c 2f 68 31 3e 0a 3c 70 3e 54 68 ently</h1>.<p>Th
0250 65 20 64 6f 63 75 6d 65 6e 74 20 68 61 73 20 6d e document has m
0260 6f 76 65 64 20 3c 61 20 68 72 65 66 3d 22 68 74 oved <a href="ht
0270 74 70 3a 2f 2f 77 77 77 2e 68 61 63 6b 74 68 69 tp://www.hackthi
0280 73 73 69 74 65 2e 6f 72 67 2f 6d 69 73 73 69 6f ssite.org/missio
0290 6e 73 2f 61 70 70 6c 69 63 61 74 69 6f 6e 2f 61 ns/application/a
02a0 70 70 33 2f 61 75 74 68 2e 70 68 70 3f 6b 65 79 pp3/auth.php?key
02b0 3d 31 32 33 31 32 33 31 32 33 22 3e 68 65 72 65 =123123123">here
02c0 3c 2f 61 3e 2e 3c 2f 70 3e 0a 3c 2f 62 6f 64 79 </a>.</p>.</body
02d0 3e 3c 2f 68 74 6d 6c 3e 0a ></html>.
Oh for fucks sake, really? Now i need to change the domain and add "
www." in front of it.
But how:
prm_0158, IDA disassemblyOVERLAY:00570A5C prm_0158 struc_PRM <1, offset str_0158, 18h, 16h, 8000100h>
OVERLAY:00570A70 str_0158 db 22,'Host: hackthissite.org',0
I need to add 4 bytes more, but there's not enough space in here... decisions, decisions.
Can't lie, I fiddle for a hour thinking of a elegant solution for this. In the end, I just updated the string, overwriting the next parameter, crossed fingers and gave it a try:
Damn, that actually worked without crash!
It's a shit solution, but it's also a working solution.
Now, I know I already got the password for the site, but why not try to fool the app, that any password is correct?
prm_0147 and prm_0151, IDA disassemblyOVERLAY:00570884 prm_0147 struc_PRM <1, offset str_0147, 6, 4, 8000100h>
OVERLAY:00570898 str_0147 db 4,'true',0,0,0
; trimmed
OVERLAY:00570928 prm_0151 struc_PRM <1, offset str_0151, 7, 5, 8000100h>
OVERLAY:0057093C str_0151 db 5,'false',0,0
Due to the dword alignment, there's enough space to flip that "true" to "false" and make the bad passwords valid, so let's give it a try:
Nice, challenge #3 is officially completed.
Application Challenge #3 (Mac)
Target: app3win.sit
Requirement: Find the Password
Difficulty: easy
And yet again, broken Stuffit archive.
Application Challenge #4 (Windows)
Target: app4win.exe
Requirement: Press the Button
Difficulty: easy
Here's what I got here:
Trying to focus on a button disables the pointed one and enables the other.
According to DiE, this challenge is written in VisualBasic:
That's fine. A standard way to enable and disable controls is using the
EnableWindow() API function, so I'll set a breakpoint on it in
x86dbg and try to point a button:
EnableWindow(), x86dbg75A58D02 | 8B FF | mov edi,edi ; EnableWindow entry point
75A58D04 | 55 | push ebp
75A58D05 | 8B EC | mov ebp,esp
75A58D07 | 6A 62 | push 62
75A58D09 | FF 75 0C | push dword ptr ss:[ebp+C]
75A58D0C | FF 75 08 | push dword ptr ss:[ebp+8]
75A58D0F | E8 BD 18 00 00 | call user32.75A5A5D1
75A58D14 | 5D | pop ebp
75A58D15 | C2 08 00 | ret 8
This is a breakpoint inside
kernel32.dll, so now I need to execute until
RET instruction, so I end up in the
app4win.exe, which is here:
EnableWindow(), x86dbg0040286D | 8B F8 | mov edi,eax
0040286F | 6A 00 | push 0
00402871 | 57 | push edi
00402872 | 8B 0F | mov ecx,dword ptr ds:[edi]
00402874 | FF 91 8C 00 00 00 | call dword ptr ds:[ecx+8C] ; Call to a msvbvm60.dll wrapper over EnableWindow
0040287A | 85 C0 | test eax,eax
0040287C | DB E2 | fnclex
0040287E | 7D 12 | jge app4win.402892
That
PUSH 0 looks promising, so I'll flip it to
1 and see what's going to happen:
Alright, that made the upper button to be permanently enabled but now I can't click it.
So there must be something I'm missing here. Back tracing the code upwards in IDA led me here:
IDA disassembly.text:00402590 loc_402590: ; back trace to here
.text:00402590 push ebp
.text:00402591 mov ebp, esp
.text:00402593 sub esp, 0Ch
.text:00402596 push offset __vbaExceptHandler
.text:0040259B mov eax, large fs:0
; more code
.text:0040247C loc_40247C:
.text:0040247C sub dword ptr [esp+4], 33h ; Create main window
.text:00402484 jmp loc_402590 ; Back traced to here
.text:00402489 loc_402489:
.text:00402489 sub dword ptr [esp+4], 37h ; unknown
.text:00402491 jmp loc_402750
.text:00402496 loc_402496:
.text:00402496 sub dword ptr [esp+4], 37h ; Disable upper button
.text:0040249E jmp loc_402810
.text:004024A3 loc_4024A3:
.text:004024A3 sub dword ptr [esp+4], 3Bh ; unknown
.text:004024AB jmp loc_402910
.text:004024B0 loc_4024B0:
.text:004024B0 sub dword ptr [esp+4], 3Bh ; Disable lower button
.text:004024B8 jmp loc_4029D0
.text:004024BD loc_4024BD:
.text:004024BD sub dword ptr [esp+4], 37h ; unknown
.text:004024C5 jmp loc_402AD0
.text:004024CA loc_4024CA:
.text:004024CA sub dword ptr [esp+4], 3Bh ; unknown
.text:004024D2 jmp loc_403170
That looks like a event switch or something.
I've set breakpoints on these and it turns out that
0x33 is triggered when creating the main window, while
0x37 (addr
00402496) and
0x3B (addr
004024B0) are triggered when pointing the upper and respectively the bottom buttons.
The rest are unknown, so what can I do here? I can change the
JMP of a button pointing to one of the unknown jumps, so I change that
JMP loc_402810 to
JMP loc_402750.
Now the upper button is permanently enabled. The event at
004024A3 does the same, but for the bottom button.
So, only the events at
004024BD and
004024CA are unknown. Doing the same trick, I changed the
JMP loc_402810 to
JMP loc_402AD0 and here's the result:
Nice, I've relocated the event that originally hide the upper button to act as if the upper button is pressed and that way I got the site's password.
So the events at
004024BD and
004024CA are handling the button press events.
Knowing that, I can now check where the site's password is constructed, by following that
JMP loc_402AD0:
Site code construction ,IDA disassembly.text:00402B12 mov edi, ds:rtcVarBstrFromAnsi
.text:00402B18 lea eax, [ebp+var_28]
.text:00402B1B xor esi, esi
.text:00402B1D push 'P'
.text:00402B1F push eax
; trimmed
.text:00402C19 call edi ; rtcVarBstrFromAnsi
.text:00402C1B lea ecx, [ebp+var_38]
.text:00402C1E push 'a'
.text:00402C20 push ecx
.text:00402C21 call edi ; rtcVarBstrFromAnsi
.text:00402C23 lea edx, [ebp+var_58]
.text:00402C26 push 's'
.text:00402C28 push edx
.text:00402C29 call edi ; rtcVarBstrFromAnsi
.text:00402C2B lea eax, [ebp+var_78]
.text:00402C2E push 's'
.text:00402C30 push eax
.text:00402C31 call edi ; rtcVarBstrFromAnsi
.text:00402C33 lea ecx, [ebp+var_98]
.text:00402C39 push 'w'
.text:00402C3B push ecx
.text:00402C3C call edi ; rtcVarBstrFromAnsi
.text:00402C3E lea edx, [ebp+var_B8]
.text:00402C44 push 'o'
.text:00402C46 push edx
.text:00402C47 call edi ; rtcVarBstrFromAnsi
.text:00402C49 lea eax, [ebp+var_D8]
.text:00402C4F push 'r'
.text:00402C51 push eax
.text:00402C52 call edi ; rtcVarBstrFromAnsi
.text:00402C54 lea ecx, [ebp+var_F8]
.text:00402C5A push 'd'
.text:00402C5C push ecx
.text:00402C5D call edi ; rtcVarBstrFromAnsi
.text:00402C5F lea edx, [ebp+var_118]
.text:00402C65 push ' '
.text:00402C67 push edx
.text:00402C68 call edi ; rtcVarBstrFromAnsi
.text:00402C6A lea eax, [ebp+var_138]
.text:00402C70 push 'i'
.text:00402C72 push eax
.text:00402C73 call edi ; rtcVarBstrFromAnsi
.text:00402C75 lea ecx, [ebp+var_158]
.text:00402C7B push 's'
.text:00402C7D push ecx
.text:00402C7E call edi ; rtcVarBstrFromAnsi
.text:00402C80 lea edx, [ebp+var_178]
.text:00402C86 push ' '
.text:00402C88 push edx
.text:00402C89 call edi ; rtcVarBstrFromAnsi
.text:00402C8B lea eax, [ebp+var_198]
.text:00402C91 push '\''
.text:00402C93 push eax
.text:00402C94 call edi ; rtcVarBstrFromAnsi
.text:00402C96 lea ecx, [ebp+var_1B8]
.text:00402C9C push 'd'
.text:00402C9E push ecx
.text:00402C9F call edi ; rtcVarBstrFromAnsi
.text:00402CA1 lea edx, [ebp+var_1D8]
.text:00402CA7 push 'a'
.text:00402CA9 push edx
.text:00402CAA call edi ; rtcVarBstrFromAnsi
.text:00402CAC lea eax, [ebp+var_1F8]
.text:00402CB2 push 'y'
.text:00402CB4 push eax
.text:00402CB5 call edi ; rtcVarBstrFromAnsi
.text:00402CB7 lea ecx, [ebp+var_218]
.text:00402CBD push 't'
.text:00402CBF push ecx
.text:00402CC0 call edi ; rtcVarBstrFromAnsi
.text:00402CC2 lea edx, [ebp+var_238]
.text:00402CC8 push 'o'
.text:00402CCA push edx
.text:00402CCB call edi ; rtcVarBstrFromAnsi
.text:00402CCD lea eax, [ebp+var_258]
.text:00402CD3 push 'n'
.text:00402CD5 push eax
.text:00402CD6 call edi ; rtcVarBstrFromAnsi
.text:00402CD8 lea ecx, [ebp+var_278]
.text:00402CDE push 'a'
.text:00402CE0 push ecx
.text:00402CE1 call edi ; rtcVarBstrFromAnsi
.text:00402CE3 lea edx, [ebp+var_298]
.text:00402CE9 push '\''
.text:00402CEB push edx
.text:00402CEC call edi ; rtcVarBstrFromAnsi
.text:00402CEE cmp dword_404010, esi
.text:00402CF4 jnz short loc_402D06
.text:00402CF6 push offset dword_404010
.text:00402CFB push offset dword_402198
.text:00402D00 call ds:__vbaNew2
So that's how the password message is generated. Challenge completed!
Application Challenge #5 (Windows)
Target: app5win.exe
Requirement: Find the password
Difficulty: easy
We have a C/C++ console application here:
I find myself using the IDA's decompiler quite often lately, but it's just so useful:
Entry point, IDA decompileint __cdecl main(int argc, const char **argv, const char **envp) {
// trimmed
v8 = 0xA67; // / Those values look interesting
v9 = 0x6E697070; // |
v10 = 0x69727472; // |
v11 = 0x65776F70; // \
puts(aPleaseEnterThe);
memset(v4, 0, 0x10u);
v7 = 0;
v6 = 0;
v5 = 0;
do {
v13 = j___fgetchar(); // Read the user input
v4[v7++] = v13;
} while(v13 != 0xA && v13 && v7 < 0x10); // Read until ENTER is pressed (0x0A char)
// The user input should be less than 0x10 characters
v12 = (int)v4;
v6 = 0;
v5 = 3;
while (v6 < 0xD) { // The loop is limited to 0x0D characters (3 DWORDS)
if (*(_DWORD *)(v12 + 4 * (v6 >> 2)) != *(&v8 + v5)) { // "v12 + 4" takes a chunk of the user code as DWORD
// and compares it to v8
printf(aInvalidPasswor); // Bad password message
return 0;
}
v6 += 4; // The iteration is set to 4 bytes
--v5;
}
printf(aThePasswordIsS, v4); // The valid password for the site is the same as the user pass
return 0;
}
IDA's decompiler is good, but not perfect, so referring to the assembler code can throw light on the whole picture:
Entry point, IDA disassembly00401086 | 8D 45 CC | lea eax,dword ptr ss:[ebp-34]
00401089 | 89 45 F8 | mov dword ptr ss:[ebp-8],eax
0040108C | C7 45 E0 00 00 00 00 | mov dword ptr ss:[ebp-20],0
00401093 | C7 45 DC 03 00 00 00 | mov dword ptr ss:[ebp-24],3
0040109A | EB 12 | jmp app5win.4010AE
0040109C | 8B 4D E0 | mov ecx,dword ptr ss:[ebp-20]
0040109F | 83 C1 04 | add ecx,4
004010A2 | 89 4D E0 | mov dword ptr ss:[ebp-20],ecx
004010A5 | 8B 55 DC | mov edx,dword ptr ss:[ebp-24] ;/ EDX is the correct pass iterator
004010A8 | 83 EA 01 | sub edx,1 ;|
004010AB | 89 55 DC | mov dword ptr ss:[ebp-24],edx ;\
004010AE | 83 7D E0 0D | cmp dword ptr ss:[ebp-20],D ; The loop limit is set to 0x0D
004010B2 | 73 28 | jae app5win.4010DC ; if the loop limit is reached, break the loop
004010B4 | 8B 45 E0 | mov eax,dword ptr ss:[ebp-20] ;/ EAX is the user's pass iterator
004010B7 | C1 E8 02 | shr eax,2 ;\
004010BA | 8B 4D F8 | mov ecx,dword ptr ss:[ebp-8] ; ECX is the user's password - "123123123"
004010BD | 8B 55 DC | mov edx,dword ptr ss:[ebp-24]
004010C0 | 8B 04 81 | mov eax,dword ptr ds:[ecx+eax*4] ; Take a dword from the user code
004010C3 | 3B 44 95 E8 | cmp eax,dword ptr ss:[ebp+edx*4-18]; and compares it to this value and
004010C7 | 74 11 | je app5win.4010DA ; if they are equal, jumps and continues to compare the rest of it
004010C9 | 68 4C 70 40 00 | push app5win.40704C ; otherwise prints "Invalid Password"
004010CE | E8 20 00 00 00 | call app5win.4010F3 ; that's a printf()
004010D3 | 83 C4 04 | add esp,4
004010D6 | 33 C0 | xor eax,eax
004010D8 | EB 15 | jmp app5win.4010EF ; jump to program exit
004010DA | EB C0 | jmp app5win.40109C ; loop jump
004010DC | 8D 4D CC | lea ecx,dword ptr ss:[ebp-34]
004010DF | 51 | push ecx ; ECX holds the user entered password - "123123123"
004010E0 | 68 60 70 40 00 | push app5win.407060 ; format for the next printf() call is "The password is %s\n"
004010E5 | E8 09 00 00 00 | call app5win.4010F3 ; printf()
004010EA | 83 C4 08 | add esp,8
004010ED | 33 C0 | xor eax,eax
004010EF | 8B E5 | mov esp,ebp
004010F1 | 5D | pop ebp
004010F2 | C3 | ret ; program exit
So, the loop is limited to
0x0D, and with every iteration it compares a
DWORD of the user's pass with a
DWORD of something stored at
[ebp+edx*4-18].
If the DWORD values doesn't match, the "
Invalid Password" message is thrown.
This means, the loop will run four times (because
0x0D/sizeof(DWORD)=3+1), therefore
the valid password is 13 bytes long, stored in whatever
[ebp+edx*4-18] holds.
I can easily check this in x86dbg:
Code validation, IDA disassembly004010C0 | 8B 04 81 | mov eax,dword ptr ds:[ecx+eax*4]
004010C3 | 3B 44 95 E8 | cmp eax,dword ptr ss:[ebp+edx*4-18] ; tracing up to here
004010C7 | 74 11 | je app5win.4010DA
; EIP
; eax=31333231 ; my bad password's first DWORD 1231
; dword ptr[ebp+edx*4-18]=[0012FF3C]=65776F70 ; the correct password's first DWORD is located at 0x0012FF3C
; Memory
; 0012FF2C 0D 00 00 00 67 0A 00 00 70 70 69 6E 72 74 72 69 ....g...ppinrtri
; 0012FF3C 70 6F 77 65 14 FF 12 00 0A 20 40 00 88 FF 12 00 powe.ÿ... @..ÿ.. ; it's plaintext!
So the three DWORDs of the correct password are "
powe", "
rtri", "
ppin", "
g\n\x00\x00", and trying "
powertripping" as correct password gives me this:
Nice, challenge completed!
By the way, there's a light obfuscation thing, where the user password iterator is incremented by 4, but then it's shifted right by 2, thus ending as a simple 0, 1, 2, 3:
Obfuscation?, IDA disassembly0040109C | 8B 4D E0 | mov ecx,dword ptr ss:[ebp-20] ;/ Increment the iterator by 4
0040109F | 83 C1 04 | add ecx,4 ;|
004010A2 | 89 4D E0 | mov dword ptr ss:[ebp-20],ecx ;\
; trimmed
004010B4 | 8B 45 E0 | mov eax,dword ptr ss:[ebp-20] ;/ Shift it right by 2
004010B7 | C1 E8 02 | shr eax,2 ;\ this is basically equal to divide by 4
; trimmed
004010C0 | 8B 04 81 | mov eax,dword ptr ds:[ecx+eax*4] ; use it as times 4
Application Challenge #6 (Windows)
Target: app6win.exe
Requirement: Find the password
Difficulty: easy
Another console application written in VC++:
And the IDA disassembly:
Entry oint, IDA decompileint __cdecl main(int argc, const char **argv, const char **envp) {
// trimmed
VirtualQuery(&loc_4010D3, &Buffer, 0x1Cu);
v3 = Buffer.Protect & 0xFFFFFFDD;
LOBYTE(v3) = Buffer.Protect & 0xDD | 4;
VirtualProtect(&loc_4010D3, 0xD0u, v3, &flOldProtect);
v8 = (int)&loc_4010D3;
for ( i = 0; i < 0x33; ++i )
*(_DWORD *)(v8 + 4 * i) ^= 0xBEEFCABE;
return ((int (__stdcall *)(int, DWORD, PVOID, PVOID, DWORD, SIZE_T, DWORD, DWORD, DWORD, int, unsigned int))loc_4010D3)(
v5,
flOldProtect,
Buffer.BaseAddress,
Buffer.AllocationBase,
Buffer.AllocationProtect,
Buffer.RegionSize,
Buffer.State,
Buffer.Protect,
Buffer.Type,
v8,
i);
}
That XOR decryptor routine is so obvious...
If we look at the assembly code, there's some minor code obfuscation too:
Code obfuscation, IDA disassembly.text:00401056 loc_401056:
.text:00401056 mov eax, [ebp+var_4] ;/ EAX is the iterator
.text:00401059 add eax, 1 ;|
.text:0040105C mov [ebp+var_4], eax ;\
.text:0040105F loc_40105F:
.text:0040105F cmp [ebp+var_4], 33h ; The encrypted block is only 0x33 bytes long
.text:00401063 jnb short loc_40107E
.text:00401065 mov ecx, [ebp+var_4]
.text:00401068 mov edx, [ebp+var_8]
.text:0040106B mov eax, [edx+ecx*4] ; take a DWORD of the encrypted code
.text:0040106E xor eax, 0BEEFCABEh ; XOR it with that key
.text:00401073 mov ecx, [ebp+var_4]
.text:00401076 mov edx, [ebp+var_8]
.text:00401079 mov [edx+ecx*4], eax ; update the decrypted DWORD
.text:0040107C jmp short loc_401056
.text:0040107E loc_40107E:
.text:0040107E nop ;/ NOP "obfuscation"
;| trimmed
.text:004010C6 nop ;\
.text:004010C7 add esp, 4
.text:004010CA call loc_4010D3 ; Call the decrypted code
.text:004010CF mov esp, ebp
.text:004010D1 pop ebp
.text:004010D2 retn
I wrote a simple Python script that will decrypt the code for me in IDA:
Code decryptor, IDAPythonimport idc
codeStart = 0x004010D3
codeSize = 0x33
codeKey = 0xBEEFCABE
i = 0;
while(i < codeSize):
PatchDword(codeStart+(i*4), (Dword(codeStart+(i*4)) ^ codeKey))
i += 1
And the code is decrypted:
Decrypted code, IDA decompileint sub_4010D3() {
v5 = 0xA6C6163; // Correct code second DWORD
v6 = 0x6967616D; // Correct code first DWORD
puts(aPleaseEnterThe);
memset(v2, 0, 0x10u);
v4 = 0;
i = 0;
do { // Read the user's input
--stru_407088._cnt;
if (stru_407088._cnt < 0) {
v1 = _filbuf(&stru_407088);
} else {
v1 = *stru_407088._ptr;
++stru_407088._ptr;
}
v8 = v1;
v2[v4++] = v1;
} while (v8 != 10 && v8 && v4 < 0x10);
v7 = (int)v2;
for (i = 0; i < 8; i += 4) { // The correct code is 8 bytes long
if (*(_DWORD *)(v7 + 4 * (i >> 2)) != *(&v4 + ((v4 - i) >> 2))) { // Compare a DWORD from the user
// with a DWORD from the correct code
((void (__cdecl *)(_DWORD, int))sub_40140E)(aInvalidPasswor, v1); // Incorrect code message
return 0;
}
}
sub_40140E(aThePasswordIsS, v2); // Correct code message
return 0;
}
That looks pretty much the same way as in the previous challenge.
Converting the two parts of the correct code from DWORDs to ASCII gives us "
magi" and "
cal\x0A" or just "
magical":
Yep, this is it.
Application Challenge #7 (Windows)
Target: app7win.exe, encrypted.enc
Requirement: Find the password
Difficulty: medium
This one is marked as medium difficulty, and it's again a console VC++ application:
The encrypted.enc file contains this:
encrypted.enc, HxDOffset 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000 31 4D 39 35 33 45 61 44 65 16 54 56 16 54 56 15 1M953EaDe.TV.TV.
00000010 67 84 56 23 45 67 64 56 78 96 43 57 89 54 98 75 g„V#EgdVx–CW‰T.u
00000020 98 74 32 59 82 39 F9 8A D9 8F 42 98 F4 39 79 34 .t2Y‚9щЉЩЏB.ф9y4
00000030 8E F9 A8 DF 98 AD 7F 09 8D 98 43 F2 98 A7 F0 A8 ЋщЁЯ...Ќ.Cт.§рЁ
00000040 DD A9 F7 6D BA F9 7B 48 7A DF 7D A9 F8 69 8E A8 Э©чmєщ{HzЯ}©шiЋЁ
00000050 7A 6B 84 E6 F8 B7 68 74 A6 6A 48 7F A6 46 FA 9F zk„жш·ht¦jH.¦Fъџ
00000060 86 48 7A 79 A8 E6 92 38 F7 F9 76 92 38 69 82 35 †HzyЁж’8чщv’8i‚5
00000070 87 E6 F7 AD 6F 98 7A 57 8F 68 27 35 92 38 75 82 ‡жчo.zWЏh'5’8u‚
00000080 36 52 35 A8 7F 53 29 67 89 36 24 73 25 98 36 27 6R5Ё.S)g‰6$s%.6'
00000090 36 29 87 35 87 32 63 65 93 82 56 5A 87 3A 37 67 6)‡5‡2ce“‚VZ‡:7g
000000A0 3A 67 8A 58 72 63 87 65 32 78 56 A9 87 35 A6 98 :gЉXrc‡e2xV©‡5¦.
000000B0 A6 38 76 38 75 63 29 85 68 79 32 65 87 26 53 92 ¦8v8uc)…hy2e‡&S’
000000C0 85 63 28 95 6F 98 76 3A 97 86 9A 87 56 96 39 87 …c(•o.v:—†љ‡V–9‡
000000D0 5A 65 98 A3 76 59 A3 87 6A 59 87 69 87 5A 63 98 Ze.ЈvYЈ‡jY‡i‡Zc.
000000E0 76 FA 39 87 68 7A 56 87 FA 96 87 46 A8 96 98 47 vъ9‡hzV‡ъ–‡FЁ–.G
000000F0 6A 48 96 4A 9F 84 A7 F6 87 4A 55 E9 68 7D 6F A8 jH–Jџ„§ц‡JUйh}oЁ
00000100 97 A6 F9 69 84 7A 64 98 76 93 53 98 98 26 3F 89 —¦щi„zd.v“S..&?‰
00000110 73 26 F8 72 94 F8 79 87 24 82 8F 5A 08 7D F5 A8 s&шr”шy‡$‚ЏZ.}хЁ
00000120 43 53 29 F9 52 87 3E 59 F6 32 B9 6D 8A 9F AD 78 CS)щR‡>Yц2№mЉџx
00000130 FA 6F D0 9A F5 DA 9F AF 29 32 98 5F 49 59 F8 7D ъoРљхЪџЇ)2._IYш}
00000140 A5 FA D8 F6 9A 9D F9 64 29 F6 39 D6 5F 87 A6 7E ҐъШцљќщd)ц9Ц_‡¦~
00000150 78 D9 F6 98 E7 68 7D AF 67 9A 8D F8 A6 E6 FA 90 xЩц.зh}ЇgљЌш¦жъђ
00000160 86 F7 AD F7 F0 9A 06 E8 F4 A9 7F 49 A8 76 D7 FA †ччрљ.иф©.IЁvЧъ
00000170 D9 F6 7D FA D8 76 FA 98 76 F9 A8 7D FA F6 98 4F Щц}ъШvъ.vщЁ}ъц.O
00000180 66 34 FA DF A8 76 98 47 6F 84 79 A6 F8 7A DF AD f4ъЯЁv.Go„y¦шzЯ
00000190 FA 94 63 78 6F AD 57 6F A5 76 DF 5F DA FA FA 86 ъ”cxoWoҐvЯ_Ъъъ†
000001A0 F8 7D 6A F8 72 FA 87 6D 87 F6 A8 76 F8 74 6F 78 ш}jшrъ‡m‡цЁvшtox
000001B0 8A 68 76 F4 68 7F A6 87 46 82 76 58 73 26 58 76 Љhvфh.¦‡F‚vXs&Xv
000001C0 87 FA 68 7D A6 F8 7A 68 74 68 76 36 48 76 A8 A7 ‡ъh}¦шzhthv6HvЁ§
000001D0 6A 87 46 87 F6 A8 76 F4 87 A6 FA 67 A9 46 28 66 j‡F‡цЁvф‡¦ъg©F(f
And the code:
Entry point, IDA decompileint __cdecl main(int argc, const char **argv, const char **envp) {
puts(aPleaseEnterThe); // "Please enter the password:"
memset(v10, 0, 0x10u); // v10 will hold the correct pass
// and it's limited to 0x10 bytes
v8 = 0;
v9 = 0;
v6 = 0;
do { // / read the user's password
v11 = j___fgetchar(); // | sum the characters from the user's password
v8 += v11; // | this will also add the ENTER key code - 0x0A
} while (v11 != '\n' && v11); // \
v4 = fopen(aEncrypted_enc, aRb); // "encrypted.enc"
if (v4) {
while (!(v6 & 4) || !(v6 & 1)) { // Written like that, the loop limit is set to 5
if (sub_401279(&v7, 1u, 1u, v4) != 1) { // this is just a fread() wrapper
puts(aAnErrorOccured); // "An error occurred"
return 0;
}
v9 += v8 ^ (unsigned __int8)v7; // take the user's password sum and XOR it
// with a byte from the encrypted.enc file
// that value should later match the 0xDCA from down below
v10[v6] = v8 ^ v7; // / this will decrypt the correct site code
for (i = 0; i < v8; ++i) { // | by using the user code's sum
if (v10[v6] & 1) { // |
v10[v6] = (signed int)(unsigned __int8)v10[v6] >> 1; // |
v10[v6] |= 0x80u; // |
} else { // |
v10[v6] = (signed int)(unsigned __int8)v10[v6] >> 1; // |
} // |
} // |
v10[v6++] += 3; // \
}
fclose(v4);
if (v9 == 0xDCA) { // this check will actually help me beat the challenge!
printf(aCongratulation, v10); // "Congratulations, The password is \'"
} else {
puts(aInvalidPasswor); // "Invalid Password"
}
result = 0;
} else {
puts(aFailedToOpenEn); // "Failed to open encrypted.enc"
result = 0;
}
return result;
}
I can use few methods to defeat this challenge. First, let's see what information I have so far:
1. The user code is summed up, and later that sum is used for calculations. Therefore,
the user code itself doesn't matter much, as long the its sum is the correct one;
2. The second loop, that reads the
encrypred.enc file runs only 5 times, so
only the first 5 bytes of encrypted.enc are used;
3. The second loop calculates the verification sum (v9), using
a byte from the encrypted.enc XOR-ed with the sum of the user's code;
4. The second loop decrypts the site password (v10) using a byte from encrypted.enc XOR-ed with the sum of the user's code;
5. The final verification checks if the verification sum (v9) is
equal to the hardcoded value of 0xDCA;
Having in mind the above, it's obvious that there's a certain user code sum that XOR-ed and added with the first 5 bytes of the encrypted.enc must result in the value of
0xDCA.
I don't care about the exact user's code right now, so I can write some code to determine the exact sum that will end as
0xDCA:
User code sum bruteforcer, Pythonencrypted_enc_data = "1M953" # encrypted.enc first 5 bytes
sum = i = 0
while(sum != 0xDCA):
i += 1
sum = 0
for c in encrypted_enc_data:
sum += ord(c) ^ i
print "Correct user sum is: %X" % i
Running this code gives me the value of
0x2F1, and that's the correct sum that the user's code should make when every character is added.
From here I can subtract
0x0A, because that's the value of the ENTER key character (new line) and I now got
0x2E7.
I can now try to build a working user code, by subtracting character values from that value of
0x2E7:
Manual building of a working code0x2E7 - 0x7A ('z') = 0x26D
0x26D - 0x7A ('z') = 0x1F3
0x1F3 - 0x7A ('z') = 0x179
0x179 - 0x7A ('z') = 0xFF
0xFF - 0x7A ('z') = 0x85
; if I subtract 0x7A again, I'll end up with 0x0B, and there's no printable character with the value of 0x0B
; so instead I'll subtract 0x42 ('B') and leave with 0x43 ('C')
0x85 - 0x42 ('B') = 0x43 ('C')
According to my logic, a possible correct code could be "
zzzzzBC" so let's try it out:
Nice!
Since I knew the correct user code sum, I could completely skip the above by just writing a implementation of the site code decryption routine:
Site code decrypt, Pythonencrypted_enc_data = "1M953" # encrypted.enc first 5 bytes
sum = 0x2F1
i = 0
password = ""
for c in encrypted_enc_data:
a = (ord(c) ^ sum) & 0xFF
for i in range(sum):
if (a & 1):
a = a >> 1 | 0x80
else:
a = a >> 1
password += chr(a+3)
print "Site's password is:", password
Finally, to make this solution complete, I'll write a random key generator:
User code keygen, Pythonimport random
charset = range(0x21, 0x7E) # I'll use all of the printable characters
sum = 0x2F1 - 0x0A # subtract the value of \n
code = ""
while sum > 0:
c = charset[random.randint(0, len(charset))-1]
if (sum/2 in charset):
c = sum/2
code += chr(c)
code += chr(sum-c)
break
code += chr(c)
sum -= c
print "Correct app password: \"%s\"" % code
Alright, application challenge #7 is completed!
Application Challenge #8 (Windows)
Target: app8win.exe
Requirement: Find the 6 digit code
Difficulty: medium
Written in VB6, this one looks interesting (to say the least):
The user is required to enter 6 digits by pressing the keys on the keypad. On the 7th key pressed, the app will either show the site password or just exit without warning.
Even watching the IDA's navigator we can clearly distinct the the code blocks (in brown) out of the rest data:
Going to the beginning of the code, I already see something familiar from the previous challenge written in VB6 - #4:
VB6 event table, IDA disassembly.text:00405ECC loc_405ECC:
.text:00405ECC sub dword ptr [esp+4], 37h
.text:00405ED4 jmp loc_406040
.text:00405ED9 loc_405ED9:
.text:00405ED9 sub dword ptr [esp+4], 3Fh
.text:00405EE1 jmp loc_406100
.text:00405EE6 loc_405EE6:
.text:00405EE6 sub dword ptr [esp+4], 43h
.text:00405EEE jmp loc_406250
.text:00405EF3 loc_405EF3:
.text:00405EF3 sub dword ptr [esp+4], 47h
.text:00405EFB jmp loc_4063A0
.text:00405F00 loc_405F00:
.text:00405F00 sub dword ptr [esp+4], 4Bh
.text:00405F08 jmp loc_4064F0
.text:00405F0D loc_405F0D:
.text:00405F0D sub dword ptr [esp+4], 4Fh
.text:00405F15 jmp loc_406640
.text:00405F1A loc_405F1A:
.text:00405F1A sub dword ptr [esp+4], 53h
.text:00405F22 jmp loc_406790
.text:00405F27 loc_405F27:
.text:00405F27 sub dword ptr [esp+4], 57h
.text:00405F2F jmp loc_4068E0
.text:00405F34 loc_405F34:
.text:00405F34 sub dword ptr [esp+4], 5Bh
.text:00405F3C jmp loc_406A30
.text:00405F41 loc_405F41:
.text:00405F41 sub dword ptr [esp+4], 5Fh
.text:00405F49 jmp loc_406B80
.text:00405F4E loc_405F4E:
.text:00405F4E sub dword ptr [esp+4], 3Bh
.text:00405F56 jmp loc_406CD0
.text:00405F5B loc_405F5B:
.text:00405F5B sub dword ptr [esp+4], 63h
.text:00405F63 jmp loc_406D40
This obviously is the same event table that redirects the code flow according to user's interaction.
But let's assume this is cheating, and move to the debugger instead. I'll first get a list of the intermodular calls:
Intermodular calls, x86dbgAddress Disassembly Destination
00406D0F call dword ptr ds:[<&__vbaEnd>] app8win.&__vbaEnd
0040772B call dword ptr ds:[<&__vbaEnd>] app8win.&__vbaEnd
00407702 call dword ptr ds:[<&__vbaLenBstr>] app8win.&__vbaLenBstr
00406AD2 call dword ptr ds:[<&__vbaStrCat>] app8win.&__vbaStrCat
00406442 call dword ptr ds:[<&__vbaStrCat>] app8win.&__vbaStrCat
00406982 call dword ptr ds:[<&__vbaStrCat>] app8win.&__vbaStrCat
004066E2 call dword ptr ds:[<&__vbaStrCat>] app8win.&__vbaStrCat
00406C22 call dword ptr ds:[<&__vbaStrCat>] app8win.&__vbaStrCat
00406832 call dword ptr ds:[<&__vbaStrCat>] app8win.&__vbaStrCat
004061A2 call dword ptr ds:[<&__vbaStrCat>] app8win.&__vbaStrCat
004062F2 call dword ptr ds:[<&__vbaStrCat>] app8win.&__vbaStrCat
00406592 call dword ptr ds:[<&__vbaStrCat>] app8win.&__vbaStrCat
00406ADD call dword ptr ds:[<&__vbaStrMove>] app8win.&__vbaStrMove
004062FD call dword ptr ds:[<&__vbaStrMove>] app8win.&__vbaStrMove
004060A8 call dword ptr ds:[<&__vbaStrMove>] app8win.&__vbaStrMove
0040659D call dword ptr ds:[<&__vbaStrMove>] app8win.&__vbaStrMove
0040683D call dword ptr ds:[<&__vbaStrMove>] app8win.&__vbaStrMove
00406C2D call dword ptr ds:[<&__vbaStrMove>] app8win.&__vbaStrMove
0040698D call dword ptr ds:[<&__vbaStrMove>] app8win.&__vbaStrMove
004061AD call dword ptr ds:[<&__vbaStrMove>] app8win.&__vbaStrMove
004066ED call dword ptr ds:[<&__vbaStrMove>] app8win.&__vbaStrMove
0040644D call dword ptr ds:[<&__vbaStrMove>] app8win.&__vbaStrMove
00407470 call dword ptr ds:[<&__vbaVarDup>] app8win.&__vbaVarDup
004070CF call dword ptr ds:[<&__vbaVarTstEq>] app8win.&__vbaVarTstEq
004075A3 call dword ptr ds:[<&rtcMsgBox>] app8win.&rtcMsgBox
0040609D call dword ptr ds:[<&rtcStrReverse>] app8win.&rtcStrReverse
00401299 call <app8win.ThunRTMain> app8win.ThunRTMain
; for clarity I've removed calls to __vbaFreeObj, __vbaFreeObjList, __vbaFreeStr, __vbaFreeStrList, __vbaFreeVarList,
; __vbaHresultCheckObj and __vbaObjSet
From all these,
__vbaVarTstEq looks pretty interesting so, I'll put a breakpoint on it, and press some of the app's keys:
__vbaVarTstEq call, x86dbg004070CB | 52 | push edx
004070CC | FF D7 | call edi ; edi:__vbaVarCat
004070CE | 50 | push eax
004070CF | FF 15 4C 10 40 00 | call dword ptr ds:[<&__vbaVarTstEq>] ; break here
004070D5 | 8D 4D E4 | lea ecx,dword ptr ss:[ebp-1C]
004070D8 | 89 85 68 FB FF FF | mov dword ptr ss:[ebp-498],eax
004070DE | FF 15 B4 10 40 00 | call dword ptr ds:[<&__vbaFreeObj>]
If that's comparing the correct key with the user's one, I should look upwards to see how the valid code is constructed.
I can also check what EAX is holding:
__vbaVarTstEq parameter, x86dbg0012F114 08 00 00 00 90 2A 1D 00 D4 55 1E 00 50 04 00 00 .....*..ÔU..P...
0012F124 08 00 AB 01 E8 04 1E 00 0C 55 1E 00 27 CD 97 72 ..«.è....U..'Í.r
In VB6, the call parameters are actually objects with their own structure, so I can't just dump the address EAX points and expect to get the actual thing - in this case the valid code.
Here, the correct code is contained within the third DWORD -
D4 55 1E 00, or address
001E55D4.
But gain, let's assume this is cheating, and go upwards from
__vbaVarTstEq's call and look around:
Code before __vbaVarTstEq, x86dbg00406F28 | 50 | push eax |
00406F29 | 6A 01 | push 1 |
00406F2B | 8D 55 C4 | lea edx,dword ptr ss:[ebp-3C] |
00406F2E | 89 5D E8 | mov dword ptr ss:[ebp-18],ebx | ebx:rtcMidCharVar
00406F31 | 8B 1D 40 10 40 00 | mov ebx,dword ptr ds:[<&rtcMidCharVar>] |
00406F37 | 83 C6 34 | add esi,34 | esi:&L"987654321"
00406F3A | BF 08 40 00 00 | mov edi,4008 |
00406F3F | 51 | push ecx |
00406F40 | 52 | push edx |
00406F41 | C7 85 C4 FE FF FF 08 80 00 00 | mov dword ptr ss:[ebp-13C],8008 |
00406F4B | C7 45 DC 01 00 00 00 | mov dword ptr ss:[ebp-24],1 |
00406F52 | C7 45 D4 02 00 00 00 | mov dword ptr ss:[ebp-2C],2 |
00406F59 | 89 B5 4C FD FF FF | mov dword ptr ss:[ebp-2B4],esi | [ebp-2B4]:&L"987654321"
00406F5F | 89 BD 44 FD FF FF | mov dword ptr ss:[ebp-2BC],edi | edi:__vbaVarCat
00406F65 | FF D3 | call ebx | ebx:rtcMidCharVar
00406F67 | 8D 45 B4 | lea eax,dword ptr ss:[ebp-4C] |
00406F6A | 8D 8D 24 FD FF FF | lea ecx,dword ptr ss:[ebp-2DC] |
00406F70 | 50 | push eax |
00406F71 | 6A 08 | push 8 |
00406F73 | 8D 55 A4 | lea edx,dword ptr ss:[ebp-5C] |
00406F76 | 51 | push ecx |
00406F77 | 52 | push edx |
00406F78 | C7 45 BC 01 00 00 00 | mov dword ptr ss:[ebp-44],1 |
00406F7F | C7 45 B4 02 00 00 00 | mov dword ptr ss:[ebp-4C],2 |
00406F86 | 89 B5 2C FD FF FF | mov dword ptr ss:[ebp-2D4],esi | [ebp-2D4]:&L"987654321"
00406F8C | 89 BD 24 FD FF FF | mov dword ptr ss:[ebp-2DC],edi | edi:__vbaVarCat
00406F92 | FF D3 | call ebx | ebx:rtcMidCharVar
00406F94 | 8D 45 84 | lea eax,dword ptr ss:[ebp-7C] |
00406F97 | 8D 8D 04 FD FF FF | lea ecx,dword ptr ss:[ebp-2FC] |
00406F9D | 50 | push eax |
00406F9E | 6A 05 | push 5 |
00406FA0 | 8D 95 74 FF FF FF | lea edx,dword ptr ss:[ebp-8C] |
00406FA6 | 51 | push ecx |
00406FA7 | 52 | push edx |
00406FA8 | C7 45 8C 01 00 00 00 | mov dword ptr ss:[ebp-74],1 |
00406FAF | C7 45 84 02 00 00 00 | mov dword ptr ss:[ebp-7C],2 |
00406FB6 | 89 B5 0C FD FF FF | mov dword ptr ss:[ebp-2F4],esi | [ebp-2F4]:&L"987654321"
00406FBC | 89 BD 04 FD FF FF | mov dword ptr ss:[ebp-2FC],edi | edi:__vbaVarCat
00406FC2 | FF D3 | call ebx | ebx:rtcMidCharVar
00406FC4 | 8D 85 54 FF FF FF | lea eax,dword ptr ss:[ebp-AC] |
00406FCA | 8D 8D E4 FC FF FF | lea ecx,dword ptr ss:[ebp-31C] |
00406FD0 | 50 | push eax |
00406FD1 | 6A 08 | push 8 |
00406FD3 | 8D 95 44 FF FF FF | lea edx,dword ptr ss:[ebp-BC] |
00406FD9 | 51 | push ecx |
00406FDA | 52 | push edx |
00406FDB | C7 85 5C FF FF FF 01 00 00 00 | mov dword ptr ss:[ebp-A4],1 |
00406FE5 | C7 85 54 FF FF FF 02 00 00 00 | mov dword ptr ss:[ebp-AC],2 |
00406FEF | 89 B5 EC FC FF FF | mov dword ptr ss:[ebp-314],esi | [ebp-314]:&L"987654321"
00406FF5 | 89 BD E4 FC FF FF | mov dword ptr ss:[ebp-31C],edi | edi:__vbaVarCat
00406FFB | FF D3 | call ebx | ebx:rtcMidCharVar
00406FFD | 8D 85 24 FF FF FF | lea eax,dword ptr ss:[ebp-DC] |
00407003 | 8D 8D C4 FC FF FF | lea ecx,dword ptr ss:[ebp-33C] |
00407009 | 50 | push eax |
0040700A | 6A 06 | push 6 |
0040700C | 8D 95 14 FF FF FF | lea edx,dword ptr ss:[ebp-EC] |
00407012 | 51 | push ecx |
00407013 | 52 | push edx |
00407014 | C7 85 2C FF FF FF 01 00 00 00 | mov dword ptr ss:[ebp-D4],1 |
0040701E | C7 85 24 FF FF FF 02 00 00 00 | mov dword ptr ss:[ebp-DC],2 |
00407028 | 89 B5 CC FC FF FF | mov dword ptr ss:[ebp-334],esi | [ebp-334]:&L"987654321"
0040702E | 89 BD C4 FC FF FF | mov dword ptr ss:[ebp-33C],edi | edi:__vbaVarCat
00407034 | FF D3 | call ebx | ebx:rtcMidCharVar
00407036 | 8D 85 F4 FE FF FF | lea eax,dword ptr ss:[ebp-10C] |
0040703C | 8D 8D A4 FC FF FF | lea ecx,dword ptr ss:[ebp-35C] |
00407042 | 50 | push eax |
00407043 | 6A 02 | push 2 |
00407045 | 8D 95 E4 FE FF FF | lea edx,dword ptr ss:[ebp-11C] |
0040704B | 51 | push ecx |
0040704C | 52 | push edx |
0040704D | C7 85 FC FE FF FF 01 00 00 00 | mov dword ptr ss:[ebp-104],1 |
00407057 | C7 85 F4 FE FF FF 02 00 00 00 | mov dword ptr ss:[ebp-10C],2 |
00407061 | 89 B5 AC FC FF FF | mov dword ptr ss:[ebp-354],esi | [ebp-354]:&L"987654321"
00407067 | 89 BD A4 FC FF FF | mov dword ptr ss:[ebp-35C],edi | edi:__vbaVarCat
0040706D | FF D3 | call ebx | ebx:rtcMidCharVar
Ok, that looks promising and the fact that
x86dbg resolved the string "
987654321" makes it pretty obvious too.
rtcMidCharVar is actually the API name of Visual Basic's in-language function
Mid().
Using MSDN, we can see the
Mid() function is used to retrieve a part of a string, pretty much, like for example, JavaScript's
substr() works.
Watching the
PUSH instructions I can clearly see the third parameter is always an integer between
0 and
9 as follows:
indexes1, 8, 5, 8, 6, 2
I already know the correct code is 6 digits long, because that's in the challenge description, so it seems I'm on the right track.
Now, because
Mid() is used, those values should be indexes of a pre-existing string, and since x86dbg was so kind to resolve it, I know it's the string "
987654321".
There's something I should mention here. Usually indexes in arrays, strings, etc. start from 0, but this one actually starts from 1.
So, the index of 1 actually points to '9' instead of pointing to '8'.
Having that in mind, after applying the indexes from above to that string gives me the code "
925248":
Alright, my code is correct, and I have the site's password.
Speaking of which, where is the site's password created?
The answer lies beneath the
__vbaVarTstEq CALL:
Site password contruction, x86asm00407173 | 6A 08 | push 8 |
00407175 | 8D 55 C4 | lea edx,dword ptr ss:[ebp-3C] |
00407178 | 51 | push ecx |
00407179 | 52 | push edx |
0040717A | C7 45 DC 01 00 00 00 | mov dword ptr ss:[ebp-24],1 |
00407181 | C7 45 D4 02 00 00 00 | mov dword ptr ss:[ebp-2C],2 |
00407188 | 89 B5 4C FD FF FF | mov dword ptr ss:[ebp-2B4],esi | [ebp-2B4]:&L"987654321"
0040718E | C7 85 44 FD FF FF 08 40 00 00 | mov dword ptr ss:[ebp-2BC],4008 |
00407198 | FF D3 | call ebx | ebx:rtcMidCharVar
0040719A | 8D 45 A4 | lea eax,dword ptr ss:[ebp-5C] |
0040719D | 8D 8D 14 FD FF FF | lea ecx,dword ptr ss:[ebp-2EC] |
004071A3 | 50 | push eax |
004071A4 | 6A 04 | push 4 |
004071A6 | 8D 55 94 | lea edx,dword ptr ss:[ebp-6C] |
004071A9 | 51 | push ecx |
004071AA | 52 | push edx |
004071AB | C7 45 AC 01 00 00 00 | mov dword ptr ss:[ebp-54],1 |
004071B2 | C7 45 A4 02 00 00 00 | mov dword ptr ss:[ebp-5C],2 |
004071B9 | 89 B5 1C FD FF FF | mov dword ptr ss:[ebp-2E4],esi |
004071BF | C7 85 14 FD FF FF 08 40 00 00 | mov dword ptr ss:[ebp-2EC],4008 |
004071C9 | FF D3 | call ebx | ebx:rtcMidCharVar
004071CB | 8D 85 74 FF FF FF | lea eax,dword ptr ss:[ebp-8C] |
004071D1 | 8D 8D F4 FC FF FF | lea ecx,dword ptr ss:[ebp-30C] |
004071D7 | 50 | push eax |
004071D8 | 6A 06 | push 6 |
004071DA | 8D 95 64 FF FF FF | lea edx,dword ptr ss:[ebp-9C] |
004071E0 | 51 | push ecx |
004071E1 | 52 | push edx |
004071E2 | C7 85 7C FF FF FF 01 00 00 00 | mov dword ptr ss:[ebp-84],1 | [ebp-84]:"$hjw"
004071EC | C7 85 74 FF FF FF 02 00 00 00 | mov dword ptr ss:[ebp-8C],2 |
004071F6 | 89 B5 FC FC FF FF | mov dword ptr ss:[ebp-304],esi |
004071FC | C7 85 F4 FC FF FF 08 40 00 00 | mov dword ptr ss:[ebp-30C],4008 |
00407206 | FF D3 | call ebx | ebx:rtcMidCharVar
00407208 | 8D 85 44 FF FF FF | lea eax,dword ptr ss:[ebp-BC] |
0040720E | 8D 8D D4 FC FF FF | lea ecx,dword ptr ss:[ebp-32C] |
; continues
So, indexes 8, 4 and 6, pointing to '2', '6', '4' and so on.
And that's all about challenge #8
Application Challenge #9 (Windows)
Target: app9win.exe
Requirement: Match the beeps to the 'Play' button
Difficulty: medium
Yet another odd looking challenge written in VB6:
With the experience from the previous VB6 challenges, I can actually waltz through this one pretty easy.
Thanks to IDA's navigator, I'll locate the event table, set breakpoints and determine what's what:
Event table, IDA disassembly.text:00404F40 loc_404F40:
.text:00404F40 sub dword ptr [esp+4], 53h ; Main window create
.text:00404F48 jmp loc_4054C0
.text:00404F4D loc_404F4D:
.text:00404F4D sub dword ptr [esp+4], 4Bh ; "Play" button pressed
.text:00404F55 jmp loc_405570
.text:00404F5A loc_404F5A:
.text:00404F5A sub dword ptr [esp+4], 3Fh ; "Match1" button pressed
.text:00404F62 jmp loc_405610
.text:00404F67 loc_404F67:
.text:00404F67 sub dword ptr [esp+4], 43h ; "Match2" button pressed
.text:00404F6F jmp loc_405690
.text:00404F74 loc_404F74:
.text:00404F74 sub dword ptr [esp+4], 47h ; "Match3" button pressed
.text:00404F7C jmp loc_405710
.text:00404F81 loc_404F81:
.text:00404F81 sub dword ptr [esp+4], 4Fh ; mystery action, that is constantly running in loop
.text:00404F89 jmp loc_405790
If I want I can redirect some of the button's actions to that mystery action down there, but that wont show me the correct password.
First, let's see what the "Play" button does, and how these sounds are generated:
Play button action, IDA disassembly.text:00405570 loc_405570:
.text:00405570 push ebp
.text:00405571 mov ebp, esp
.text:00405573 sub esp, 0Ch
.text:00405576 push offset loc_4010F6
.text:0040557B mov eax, large fs:0
.text:00405581 push eax
.text:00405582 mov large fs:0, esp
.text:00405589 sub esp, 8
.text:0040558C push ebx
.text:0040558D push esi
.text:0040558E push edi
.text:0040558F mov [ebp-0Ch], esp
.text:00405592 mov dword ptr [ebp-8], offset dword_4010C0
.text:00405599 mov eax, [ebp+8]
.text:0040559C mov ecx, eax
.text:0040559E and ecx, 1
.text:004055A1 mov [ebp-4], ecx
.text:004055A4 and al, 0FEh
.text:004055A6 push eax
.text:004055A7 mov [ebp+8], eax
.text:004055AA mov edx, [eax]
.text:004055AC call dword ptr [edx+4]
.text:004055AF push 12Ch ;/ First sound
.text:004055B4 push 64h ;|
.text:004055B6 call sub_40519C ;\ DllFunctionCall(kernel32.Beep)
.text:004055BB mov esi, ds:__vbaSetSystemError
.text:004055C1 call esi ; __vbaSetSystemError
.text:004055C3 push 12Ch ;/ Second sound
.text:004055C8 push 1F4h ;|
.text:004055CD call sub_40519C ;\ DllFunctionCall(kernel32.Beep)
.text:004055D2 call esi ; __vbaSetSystemError
.text:004055D4 push 12Ch ;/ Third sound
.text:004055D9 push 3E8h ;|
.text:004055DE call sub_40519C ;\ DllFunctionCall(kernel32.Beep)
;trimmed
So, the sounds are generated using the Beep function. From MSDN we can see Beep takes two parameters:
BeepBOOL WINAPI Beep(
_In_ DWORD dwFreq,
_In_ DWORD dwDuration
);
The dwDuration is set to 0x012C for all three of them, and only the dwFreq is different.
Now let's see the "Match1" button code:
Match1 action.text:00405610 push ebp
.text:00405611 mov ebp, esp
.text:00405613 sub esp, 0Ch
.text:00405616 push offset loc_4010F6
.text:0040561B mov eax, large fs:0
.text:00405621 push eax
.text:00405622 mov large fs:0, esp
.text:00405629 sub esp, 8
.text:0040562C push ebx
.text:0040562D push esi
.text:0040562E push edi
.text:0040562F mov [ebp-0Ch], esp
.text:00405632 mov dword ptr [ebp-8], offset dword_4010C8
.text:00405639 mov esi, [ebp+8]
.text:0040563C mov eax, esi
.text:0040563E and eax, 1
.text:00405641 mov [ebp-4], eax
.text:00405644 and esi, 0FFFFFFFEh
.text:00405647 push esi
.text:00405648 mov [ebp+8], esi
.text:0040564B mov ecx, [esi]
.text:0040564D call dword ptr [ecx+4]
.text:00405650 mov edx, [esi+34h] ; where's this coming from?
.text:00405653 push 12Ch ;/ dwDuration
.text:00405658 push edx ;| dwFreq
.text:00405659 call sub_40519C ;\ DllFunctionCall(kernel32.Beep)
;trimmed
; edx=C8
; dword ptr[esi+34]=[0060051C]=C8
The dwDuration is again 0x012C, but the dwFreq here comes from a dynamic buffer instead from a hardcoded value and it's set to 0xC8.
Time to check that Main window initialization procedure and see where that 0xC8 came from:
Main window create procedure, IDA disassemble.text:004054C0 push ebp
.text:004054C1 mov ebp, esp
.text:004054C3 sub esp, 0Ch
.text:004054C6 push offset loc_4010F6
.text:004054CB mov eax, large fs:0
.text:004054D1 push eax
.text:004054D2 mov large fs:0, esp
.text:004054D9 sub esp, 0Ch
.text:004054DC push ebx
.text:004054DD push esi
.text:004054DE push edi
.text:004054DF mov [ebp-0Ch], esp
.text:004054E2 mov dword ptr [ebp-8], offset dword_4010B0
.text:004054E9 mov esi, [ebp+8]
.text:004054EC mov eax, esi
.text:004054EE and eax, 1
.text:004054F1 mov [ebp-4], eax
.text:004054F4 and esi, 0FFFFFFFEh
.text:004054F7 push esi
.text:004054F8 mov [ebp+8], esi
.text:004054FB mov ecx, [esi]
.text:004054FD call dword ptr [ecx+4]
.text:00405500 xor ebx, ebx
.text:00405502 mov edx, offset aHtsApplication ; "HTS Application Challenge Programmed by"...
.text:00405507 lea ecx, [ebp-18h]
.text:0040550A mov [ebp-18h], ebx
.text:0040550D call ds:__vbaStrCopy
.text:00405513 mov edi, ds:__vbaI4Str
.text:00405519 push offset a200 ;/ Parameter = "200"
.text:0040551E call edi ;\ __vbaI4Str (convert string to integer)
.text:00405520 push offset a600 ;"600"
.text:00405525 mov [esi+34h], eax ; result is stored here!
.text:00405528 call edi ; __vbaI4Str
.text:0040552A push offset a1100 ; "1100"
.text:0040552F mov [esi+38h], eax
.text:00405532 call edi ; __vbaI4Str
.text:00405534 mov [esi+3Ch], eax
.text:00405537 mov [ebp-4], ebx
.text:0040553A push offset loc_405549
.text:0040553F lea ecx, [ebp-18h]
.text:00405542 call ds:__vbaFreeStr
.text:00405548 retn
Got it! That string to integer thing actually stores the dwFreq values for the three buttons.
I checked the three buttons and they are playing sounds with dwFreq equal to 0x64 (Match1), 0x1F4 (Match2) and 0x3E8 (Match3), but the values here are 200 (0xC8), 600 (0x258) and 1100 (0x44C).
What I did here was to change these strings to 100, 500 and 1000, let the program run and BAM!:
It's interesting to mention that pressing the Play, Match1, Match2 or Match3 buttons does nothing, and the only way to solve this challenge is to patch it like I did.
The correct password is constructed pretty much like it was done in the previous VB6 challenges:
Site code construction, IDA disassembly.text:00405B9A push 0Fh ; first string index
.text:00405B9C lea edx, [ebp-48h]
.text:00405B9F mov ebx, 2
.text:00405BA4 mov esi, 4008h
.text:00405BA9 push ecx
.text:00405BAA push edx
.text:00405BAB mov dword ptr [ebp-240h], offset dword_4052C8
.text:00405BB5 mov dword ptr [ebp-248h], 8
.text:00405BBF mov dword ptr [ebp-30h], 1
.text:00405BC6 mov [ebp-38h], ebx
.text:00405BC9 mov [ebp-228h], esi
.text:00405BCF call edi ; rtcMidCharVar ; get character based on it's index in the charset string
.text:00405BD1 lea eax, [ebp-18h]
.text:00405BD4 lea ecx, [ebp-68h]
.text:00405BD7 mov [ebp-250h], eax
.text:00405BDD push ecx
.text:00405BDE lea edx, [ebp-258h]
.text:00405BE4 push 12h ; second string index
.text:00405BE6 lea eax, [ebp-78h]
.text:00405BE9 push edx
.text:00405BEA push eax
.text:00405BEB mov dword ptr [ebp-60h], 1
.text:00405BF2 mov [ebp-68h], ebx
.text:00405BF5 mov [ebp-258h], esi
.text:00405BFB call edi ; rtcMidCharVar ; get character based on it's index in the charset string
; trimmed