4 minutes
HuntressCTF: Strange Calc
This is my writeup for the “Strange Calc” challenge I solved during the HuntressCTF 2024 event. This was a medium difficulty problem which got my team (Hack Smarter) 488 points, propelling us to 40th place after the first day of the event! Considering this challenge was created by the great John Hammond and its category is Malware, I was certain this would be a good one.
Challenge Description
To start off, I booted up my Flare VM and extracted the calc.exe
file from the provided calc.zip
archive. My go-to tool for starting an investigation is PEStudio, which gives us some high-level information about the executable.
At first glance, not much is standing out here. The import table has some notables, like LoadLibraryA
, which we can keep in mind as we continue down the rabbit hole. The strings tab also didn’t provide any strings of note.
Moving onto dynamic analysis, I fire up wireshark and Procmon and run the executable. Nothing shows up on the first run in either tools’ outputs (besides standard LLMNDR, MDNS, and events generated by us executing the file from explorer), and I see the following error dialog on the screen. This makes sense, since the problem description mentioned the executable needing admin permissions to run.
Running the executable with admin permissions turns out to have a lot more going on. The end result is another dialog box saying “Failed to open the calculator”, but what we’re interested in is some events shown in Procmon. In particular, the JSE file being created on my Desktop. I was so preoccupied in Procmon that I didn’t even notice it!
Let’s see if this file is being executed. We can look at the process tree in Procmon to see what processes are children of the calc.exe
process. Sure enough, there it is!
This is definitely interesting. This JSE file is being invoked by WScript, which then spawns child processes of it’s own, which use the net
utility to create a new LocalAdministrator
user and add that user to the administrators
group. I expected to see a password here, and honestly expected that password to be the flag, but it looks like the user was added without a password.
Let’s see if we can learn a bit more about the JSE file that performed these actions. Before this, I hadn’t run into JSE files, but after some research I learned that it’s just a Microsoft way of encoding Javascript files. Basically “Javascript-Encoded” == “JSE”, nice. I tried to find some online decoders or deobfuscators, but none of them seemed to exist. Well, except one…
John Hammonds own tool to solve his own challenge! Everywhere I read about decoding JSE files seemed to reference the same encoding mechanism for VBE files, so I was pretty confident this tool would work. I mean, it makes sense, right?
Awesome, now we have some regular JavaScript to work with. Let’s make it a bit easier to read.
There’s the command we saw within Procmon! We see that the n
variable represents the password for the user, but for some reason it wasn’t actually used. It looks like the function takes the input string "begin 644 -\nG9FQA9WLY.3(R9F(R,6%A9C$W-3=E,V9D8C(X9#<X.3!A-60Y,WT*\n
\nend", runs the
afunction on it, then uses the result as the
LocalAdministrator` user’s password.
Digging into the function a bit, it looks like it splits the string by newlines and only uses the middle piece: G9FQA9WLY.3(R9F(R,6%A9C$W-3=E,V9D8C(X9#<X.3!A-60Y,WT*
. Then it performs a bunch of character manipulation. I started to dig into this, but then I noticed that g
is a variable that is local to the for
loop and it is being referenced outside of the for
loop. JavaScript being JavaScript will interpret this null
variable as a 0
when it is used in the c.substring(0, g)
call. Let’s just print out c
after the for
loop exits and see what was supposed to be output by the function.
488 points!