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

001-scoreboard.png

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.

001-pestudio.png

001-pestudio.png

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.

001-error.png

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!

001-procmon.png

001-flare-desktop.png

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!

001-process-tree.png

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…

001-jh-github.png

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?

001-vbe-decoder.png

Awesome, now we have some regular JavaScript to work with. Let’s make it a bit easier to read.

001-source-code.png

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 theafunction on it, then uses the result as theLocalAdministrator` 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.

001-flag.png

488 points!