Introduction
Learning to deobfuscate code is a crucial skill for anyone working in cybersecurity, reverse engineering, or malware analysis. Obfuscation is a common technique attackers use to hide what their code is really doing. For example, during red team operations, you may encounter malicious JavaScript designed to look meaningless at first glance but actually used to fetch and execute a hidden payload. On the blue team side, defenders need to understand these scripts to detect, stop, and remediate such threats effectively.
If we cannot make sense of obfuscated code, we risk missing its true purpose — whether it’s data theft, persistence, or communication with a command-and-control server. That’s why deobfuscation is such a valuable skill: it transforms unreadable code into something understandable and actionable.
In this module, we will begin by revisiting the structure of an HTML page and then identify where JavaScript lives inside it. Once we can locate the code, we will dive into what obfuscation is, how it works, and why it is so widely used. From there, we will practice deobfuscation techniques, decode encoded strings, and finally analyze the cleaned-up code to understand its true behavior.
What You’ll Learn in This Module
- Locating JavaScript code – spotting where scripts hide within HTML.
- Intro to Code Obfuscation – understanding techniques attackers use.
- Deobfuscation methods – practical steps to clean up scripts.
- Decoding encoded messages – handling base64, hex, and other encodings.
- Basic Code Analysis – identifying functions, variables, and intent.
- Sending HTTP requests – simulating the recovered functionality to see how it operates.
Source Code
HTML defines structure, CSS handles appearance, and JavaScript provides behavior — the interactive logic that runs in the background and makes modern websites work. Even though the browser downloads this client-side code, we usually only interact with the finished UI and rarely inspect the source.
If you want to understand how a page behaves, start by viewing the client-side code. Inspecting the HTML, linked scripts, and inline JavaScript reveals the functions, event handlers, and network calls that power the site.
Quick ways to uncover client-side code
- View Page Source — shows the raw HTML and script tags.
- DevTools → Sources — browse loaded JavaScript files and set breakpoints.
- DevTools → Network — see which script files and dynamic requests are loaded at runtime.
Repeat what you learned in this section, and you should find a secret flag, what is it?
Source Code
Today, almost every website relies heavily on JavaScript. While:
- HTML defines the structure and main fields of a page,
- CSS determines its style and layout,
- JavaScript drives the functionality — everything from form validation and animations to dynamic API calls.
For the user, all of this happens in the background. What we see is only the polished front-end interface we interact with, while countless JavaScript functions execute behind the scenes to make the site responsive and interactive.
Why Inspect the Source Code?
Even though this code is fully available client-side, browsers render it seamlessly, so we rarely think about the underlying HTML and scripts. But if you want to understand how a web page actually works — especially its client-side logic — the best place to start is the page’s source code.
By examining the HTML and embedded or linked JavaScript, we can:
- Discover how input fields and events are handled.
- Identify API endpoints the page communicates with.
- Gain insight into client-side logic, which is crucial in security assessments and bug bounty testing.
Repeat what you learned in this section, and you should find a secret flag, what is it?
Open the page on your browser.

Press Ctrl + U to see the code of the page and get the flag.
</html>
<!DOCTYPE html>
<html>
<head>
<title>Secret Serial Generator</title>
<style>
html {
margin: 0;
padding: 0;
border: 0;
}
html {
width: 100%;
height: 100%;
}
body {
width: 100%;
height: 100%;
position: relative;
background-color: #6fb3eb;
}
.center {
width: 100%;
height: 50%;
margin: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-family: "Helvetica", Helvetica, sans-serif;
text-align: center;
}
h1 {
font-size: 144px;
}
p {
font-size: 64px;
}
</style>
[REDACTED]
</head>
<body>
<div class="center">
<h1>Secret Serial Generator</h1>
<p>This page generates secret serials!</p>
</div>
</body>
</html>
Deobfuscation
Now that we understand why code is obfuscated, it’s time to learn how to reverse it. Obfuscation is a deliberate attempt to make code hard to read or analyze; fortunately, the defensive side has an equally useful toolbox. Automated beautifiers and deobfuscators can rapidly turn tangled, minified, or encoded JavaScript into a far more understandable form — but success usually comes from applying tools in the right order and iterating.
What automated deobfuscation tools do (in practice)
- Beautify / format code: Reintroduce indentation, line breaks, and spacing so the code becomes readable.
- Decode common encodings: Recognize and decode Base64, hex, unicode escapes, and URL-encoded strings.
- Unwrap wrappers and evals: Detect patterns like
eval()or self-executing wrappers and attempt to resolve them to plain code. - Resolve string arrays: Many obfuscators replace identifiers and strings with array lookups (e.g.,
a[12]). Deobfuscators try to replace those lookups with the actual strings. - Simplify control flow: Some advanced tools attempt to undo simple control-flow flattening so conditionals and loops look natural again.
- Produce readable output: The final product is code where functions, calls, and data are visible and easier to analyze manually.
Typical workflow — step by step
- Make a safe copy of the original file. Always work from a copy so you can revert.
- Beautify the file to get consistent formatting (indentation, line breaks). This often reveals basic structure immediately.
- Search for obvious encodings (Base64, hex, escape sequences). Decode these strings and replace them inline.
- Identify eval-like constructs (e.g.,
eval,Function,setTimeoutwith code strings) and attempt to resolve them in a controlled environment or with a deobfuscator tool. - Replace string-array lookups by resolving the array and substituting literal strings where possible.
- Repeat beautification after each transformation — new structure often appears after decoding or substitution.
- Perform lightweight static analysis: rename obvious variables to meaningful names, add comments, and mark suspicious network calls or DOM operations.
- If needed, run dynamic analysis in an isolated environment (headless browser or sandbox) to observe runtime behavior and fetch reconstructed code pieces that only appear at execution time.
Tools & techniques (practical suggestions)
- Beautifiers / formatters:
js-beautify,prettieror online formatters — start here to make the file readable. - Interactive deobfuscators: tools like de4js (web-based) support many common obfuscation patterns and let you try decoders in one place.
- Node / headless browsers: use
nodeor Puppeteer/Playwright for safely executing non-malicious pieces or to let the page produce deobfuscated output at runtime (in a sandbox). - Custom scripts: small Node or Python scripts are often useful for repeated tasks (e.g., decode an array, replace lookups, or run a sequence of decoders).
- Debugger & DevTools: set breakpoints in the browser or use the Sources panel to inspect values at runtime and dump variables that reveal hidden strings.
Safety and methodology notes
- Never execute unknown code on your host machine without proper isolation. Use containers, VMs, or dedicated sandboxes.
- Iterate slowly: apply one transformation at a time and re-check output. Blind substitution can break code and hide what you’re trying to reveal.
- Document each change: keep notes or comments in the file so you know which step revealed which piece of logic — this helps when writing reports or reversing similar samples later.
Using what you learned in this section, try to deobfuscate ‘secret.js’ in order to get the content of the flag. What is the flag?
Open the page.

Press Ctrl + U to see the code of the page.
</html>
<!DOCTYPE html>
<html>
<head>
<title>Secret Serial Generator</title>
<style>
html {
margin: 0;
padding: 0;
border: 0;
}
html {
width: 100%;
height: 100%;
}
body {
width: 100%;
height: 100%;
position: relative;
background-color: #6fb3eb;
}
.center {
width: 100%;
height: 50%;
margin: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-family: "Helvetica", Helvetica, sans-serif;
text-align: center;
}
h1 {
font-size: 144px;
}
p {
font-size: 64px;
}
</style>
<script src="secret.js"></script>
[REDACTED]
</head>
<body>
<div class="center">
<h1>Secret Serial Generator</h1>
<p>This page generates secret serials!</p>
</div>
</body>
</html>
Click on the script to get the code.
eval(function(p,a,c,k,e,d){e=function(c){return c.toString(36)};if(!''.replace(/^/,String)){while(c--){d[c.toString(a)]=k[c]||c.toString(a)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('4 5=0;6.7=8(){9 a="b1b14_m3n_7h3_53r14l_9h3n3r470r";c.d("e/f",g);h.q(a,i,j)};r s=0,0,0]',19,19,'|var|xhr|url|null|generateSerial|flag|HTML|new|serial|XMLHttpRequest|send|php|open|POST|true|function|split'.split('|'),0,{}))
Open the site bellow in another browser tab.
https://matthewfl.com/unPacker.html
Copy the script and click on the button UnPack to get the flag.

HTTP Requests
In the previous section we discovered that secret.js’s main function sends an empty POST to /serial.php. Now we’ll replicate that same request using cURL so you can observe the server’s response and test the endpoint directly.
Why do this? Replaying requests with cURL is a fast way to confirm what client-side scripts do and to investigate server behavior without a browser.
Example cURL command (empty POST):
curl -i -X POST 'https://TARGET.example.com/serial.php' \
-H 'User-Agent: Mozilla/5.0' \
-H 'Accept: */*' \
--data ''
-ishows response headers.--data ''sends an empty POST body (same as the script).- Add
-kif testing an HTTPS server with a self-signed cert, or-vfor verbose debugging.
Try applying what you learned in this section by sending a ‘POST’ request to ‘/serial.php’. What is the response you get?
Just use the curl command bellow:
curl -X POST http://94.237.61.157:50386/serial.php
[REDACTED]
Decoding
After replaying the POST in the previous section you might see a weird block like this:
ZG8gdGhlIGV4ZXJjaXNlLCBkb24ndCBjb3B5IGFuZCBwYXN0ZSA7KQo=
That’s a encoded string — obfuscators often hide payloads or messages this way. Common encodings you’ll encounter are Base64, hex, and rot13. These strings are usually decoded at runtime by the script.
Quick decode examples
Base64 (example using the string above):
# decode with base64
echo 'ZG8gdGhlIGV4ZXJjaXNlLCBkb24ndCBjb3B5IGFuZCBwYXN0ZSA7KQo=' | base64 -d
# output:
# do the exercise, don't copy and paste ;)
Hex:
# decode hex to binary/text
echo '68656c6c6f' | xxd -r -p # -> "hello"
rot13:
# rot13 transform
echo 'uryyb' | tr 'A-Za-z' 'N-ZA-Mn-za-m' # -> "hello"
Using what you learned in this section, determine the type of encoding used in the string you got at previous exercise, and decode it. To get the flag, you can send a ‘POST’ request to ‘serial.php’, and set the data as “serial=YOUR_DECODED_OUTPUT”.
This was easy. Just copy the answer of the last execise and send it using a parameter and a POST method.
curl http://94.237.61.157:50386/serial.php -X POST -d "serial=7h15_15_a_s3cr37_m3554g3"
[REDACTED]
Skills Assessment
Got it. Here’s a focused, field-ready workflow to figure out what the site’s JavaScript and APIs do—and how they might hurt your customer. I’ll keep it practical with commands/snippets you can run on Kali.
1) Quick Recon & Mapping
Goal: inventory scripts, surface API docs, and list reachable endpoints.
# Grab root + common metadata
curl -i http://TARGET/
curl -i http://TARGET/robots.txt
curl -i http://TARGET/sitemap.xml
curl -i http://TARGET/.well-known/security.txt
curl -i http://TARGET/.well-known/openapi.json
curl -i http://TARGET/swagger.json
Find script files (from HTML):
# pull HTML
curl -s http://TARGET/ > index.html
# extract JS URLs (naive but effective)
grep -Eo '<script[^>]+src="[^"]+"' index.html | sed -E 's/.*src="([^"]+)".*/\1/' | sort -u
# download all JS for offline analysis
mkdir js && cd js
xargs -n1 -I{} curl -sS --path-as-is -O "http://TARGET{}" < ../js_urls.txt
2) JavaScript Analysis (what the code does)
Beautify/format (much easier to read):
# Option A: js-beautify
apt-get update && apt-get install -y node-js-beautify 2>/dev/null || true
find . -name "*.js" -maxdepth 1 -print0 | xargs -0 -I{} js-beautify -r {}
# Option B: prettier (if you have node)
npm i -g prettier
find . -name "*.js" -maxdepth 1 -print0 | xargs -0 -I{} prettier --write {}
Extract endpoints & secrets:
# URLs / endpoints
grep -RIEo "(https?://[a-zA-Z0-9\.\-:_/]+|/[a-zA-Z0-9\-_./?=&%]+)" . | sort -u > endpoints.txt
# API-ish patterns
grep -RInE "fetch\(|axios\.|XMLHttpRequest|\.ajax\(|baseURL|Authorization|Bearer |apiKey|secret|token|jwt" .
# Basic key/secret hunting (quick regex set)
grep -RInE "(api[_-]?key|secret|access[_-]?token|client[_-]?(id|secret)|Bearer [A-Za-z0-9\._-]+)" .
Tip: Don’t execute obfuscated JS. Read it statically; if it uses
atob,eval,Function, try to reconstruct strings by replacingatob('...')manually (use your base64 decode).
3) Build an API Map
Create a simple table (even a CSV) of:
- Method (GET/POST/PUT/DELETE)
- Path (/api/v1/…)
- Auth (None / Cookie / Bearer JWT / API key)
- Params (query/body)
- Notes (from JS comments, names, error strings)
Quickly probe allowed methods:
curl -i -X OPTIONS http://TARGET/api/thing
4) High-Impact Tests (fast checks with curl)
A) Authentication & Access Control
Unauthenticated access (try endpoints found in JS without creds):
curl -i http://TARGET/api/v1/users
curl -i http://TARGET/api/v1/admin/stats
IDOR (change IDs):
curl -i "http://TARGET/api/v1/users/1001"
curl -i "http://TARGET/api/v1/users/1002"
JWT sanity (if tokens appear in JS):
- Decode payload (base64url), check
role,exp,kid. - Try missing/invalid token and observe status differences (401 vs 403 vs 200).
B) CORS Misconfig
curl -i http://TARGET/api/v1/profile \
-H 'Origin: http://evil.example' \
-H 'Referer: http://evil.example'
# Red flags: Access-Control-Allow-Origin: * (or reflect evil) + Access-Control-Allow-Credentials: true
C) CSRF Exposure
If the app uses cookies for auth and lacks CSRF tokens/SameSite, that’s a likely finding. Confirm:
# Check Set-Cookie attributes
curl -I http://TARGET/ | grep -i set-cookie
# Look for SameSite=None|Lax and Secure; absence is a smell (context-dependent)
D) Rate Limiting
# 50 quick requests to the same endpoint
for i in $(seq 1 50); do curl -s -o /dev/null -w "%{http_code}\n" http://TARGET/api/v1/lookup?id=123; done | sort | uniq -c
# Look for lack of 429 after burst
E) Verb Tunneling / Method Confusion
If OPTIONS exposes PUT/DELETE, try them:
curl -i -X PUT http://TARGET/api/v1/resource/123 -d '{"name":"test"}' -H 'Content-Type: application/json'
curl -i -X DELETE http://TARGET/api/v1/resource/123
F) Input Validation (SQLi/NoSQLi basics)
SQLi quick probes (safe, read-only GET):
curl -i "http://TARGET/api/v1/search?q='"
curl -i "http://TARGET/api/v1/search?q=' OR '1'='1"
# Look for SQL errors or suspicious differences
NoSQLi (if backend looks like Mongo):
curl -i "http://TARGET/api/users?username[$ne]=x"
G) SSRF / File Fetchers
If JS references upload, import, or URL-fetch endpoints:
# Try internal address
curl -i -X POST http://TARGET/api/v1/fetch \
-H 'Content-Type: application/json' \
-d '{"url":"http://127.0.0.1:80/"}'
H) GraphQL (if present)
# Introspection test
curl -s http://TARGET/graphql \
-H 'Content-Type: application/json' \
-d '{"query":"{ __schema { types { name } } }"}'
If introspection is enabled in prod, call it out.
5) Headers & Browser Security
curl -I http://TARGET/
# Check for: Content-Security-Policy, X-Frame-Options/Frame-Options, Referrer-Policy,
# Permissions-Policy, Strict-Transport-Security (if HTTPS), X-Content-Type-Options
Missing or weak CSP + dangerous JS patterns → XSS risk.
6) Dependency/Client-Side Risks
From the JS files:
- Library versions (e.g., jQuery, Angular, React build banners). Note outdated/vuln versions.
- Hardcoded endpoints/keys (e.g., Firebase keys, third-party API tokens).
- Debug flags (
NODE_ENV,debug=true) or sourcemaps (.mapfiles) exposed.
7) Use Burp/Proxy for Depth
- Proxy the app, capture calls → Repeater/Intruder for parameter tampering.
- Auth flow: log in, then replay endpoints without auth headers/cookies.
- Change Content-Type:
application/json↔application/x-www-form-urlencoded↔text/plainto find parsing quirks.
8) Nuclei (quick wins)
If you use Nuclei, run safe templates for:
- CORS, missing security headers, exposed panels, swagger/openapi exposures, misconfig.
(Keep it scoped to the target and your engagement rules.)
9) Reporting (how it harms the customer)
For each issue you confirm, write:
- What: Clear description + affected endpoint(s)
- Why it matters: Business impact (data exposure, account takeover, lateral movement, brand damage, compliance)
- How to reproduce: Minimal steps + exact request/response (curl/Burp)
- Fix: Concrete mitigations (authz checks, rate limiting, strict CORS, CSRF tokens/SameSite, input validation, least privilege, turn off introspection, rotate secrets, pin library versions)
Try to study the HTML code of the webpage, and identify used JavaScript code within it. What is the name of the JavaScript file being used?
Open the page.

Press Ctrl + U to see the code of the page and get the answer.
</html>
<!DOCTYPE html>
<html>
<head>
<title>Secret Serial Generator</title>
<style>
html {
margin: 0;
padding: 0;
border: 0;
}
html {
width: 100%;
height: 100%;
}
body {
width: 100%;
height: 100%;
position: relative;
background-color: #6fb3eb;
}
.center {
width: 100%;
height: 50%;
margin: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-family: "Helvetica", Helvetica, sans-serif;
text-align: center;
}
h1 {
font-size: 144px;
}
p {
font-size: 64px;
}
</style>
<script src="api.min.js"></script>
</head>
<body>
<div class="center">
<h1>API Keys</h1>
<p>API Keys control panel</p>
</div>
</body>
</html>
Once you find the JavaScript code, try to run it to see if it does any interesting functions. Did you get something in return?
Open the script file clicking on it.
eval(function(p,a,c,k,e,d){e=function(c){return c.toString(36)};if(!''.replace(/^/,String)){while(c--){d[c.toString(a)]=k[c]||c.toString(a)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('5 2="[REDACTED]";6 g=3;7 1=8;9.a(\'b/c\',d);e.f(2,l,m)',30,30,'|xhr|flag|var|HTD|keys|apiKeys|new|XMLHttpRequest|open|php|send|null|function|console|log|45sc|rip|Jman3|470|function|split'.split('|'),0,{})
Copy the code above into a file named index.html
<!doctype html>
<html lang="pt-BR">
<body>
<script>
alert(function (p, a, c, k, e, d) { e = function (c) { return c.toString(36) }; if (!''.replace(/^/, String)) { while (c--) { d[c.toString(a)] = k[c] || c.toString(a) } k = [function (e) { return d[e] }]; e = function () { return >
</script>
</body>
</html>
Save and open on your browser

As you may have noticed, the JavaScript code is obfuscated. Try applying the skills you learned in this module to deobfuscate the code, and retrieve the ‘flag’ variable.
Copy the script on the site bellow:
https://matthewfl.com/unPacker.html

Try to Analyze the deobfuscated JavaScript code, and understand its main functionality. Once you do, try to replicate what it’s doing to get a secret key. What is the key?
Let’s analise the deofuscated code.

Analysing the request, we see that it was making a POST to this address. So we can construct a curl command.
curl -X POST http://83.136.250.223:55593/keys.php
[REDACTED]
Once you have the secret key, try to decide it’s encoding method, and decode it. Then send a ‘POST’ request to the same previous page with the decoded key as “key=DECODED_KEY”. What is the flag you got?
I used the site bellow to identify the hash type
https://www.boxentriq.com/code-breaking/cipher-identifier

So, I decoded with my kali terminal
echo '4150495f70336e5f37333537316e365f31355f66756e' | xxd -p -r
API_p3n_73571n6_15_fun
But then I realized I went beyond what was needed — the actual answer is just the encrypted code. Grab the secret key, work out how it’s encoded and decode it. Send a POST to the same page with key=DECODED_KEY and check which flag shows up. If you’ve already decrypted it, just use curl.
curl -X POST -d 'key=API_[REDACTED] http://83.136.250.223:55593/keys.php
[REDACTED]
