HTB Academy - Deobfuscation , Encode and decode
HTB Academy - Deobfuscation , Encode and decode: Take an obfuscated JavaScript P.A.C.K.E.R snippet and turn it into readable code, then cover quick ways to decode embedded strings (base64, hex, rot13). Keep everything practical and copy‑pasteable. • HTB Academy • base64, decode
🎯 Objective
Take an obfuscated JavaScript P.A.C.K.E.R snippet and turn it into readable code, then cover quick ways to decode embedded strings (base64, hex, rot13). Keep everything practical and copy‑pasteable.
🧩 What I’m analyzing
A classic eval‑packer payload (Dean Edwards P.A.C.K.E.R style), where an eval(function(p,a,c,k,e,d){...})(...) unpacks itself at runtime and hides intent, endpoints, and strings.
Original snippet
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 }('g 4(){0 5="6{7!}";0 1=8 a();0 2="/9.c";1.d("e",2,f);1.b(3)}', 17, 17, 'var|xhr|url|null|generateSerial|flag|HTB|flag|new|serial|XMLHttpRequest|send|php|open|POST|true|function'.split('|'), 0, {}))Safety note: Never run unknown
evalon a real machine/page. Work offline and replaceeval(withconsole.log(to print the unpacked code instead of executing it.
🧭 Steps I Took
Beautify first (makes the wrapper readable)
- Tools: Prettier Playground, Beautifier.io
- Quick trick: replace leading
eval(withconsole.log(, then run in a throwaway browser console/Node REPL to print the unpacked JS.
JSNice pass for names & types
- Paste the result into JSNice and click Nicify JavaScript to get better variable names and a clearer function.
Read the plain intent
- The unpacked code shows a function that POSTs to
/serial.phpand contains aflagstring.
- The unpacked code shows a function that POSTs to
Deobfuscated output (from JSNice)
'use strict';
function generateSerial() { // formerly: g 4()
var flag = "HTB{flag!}"; // formerly: 0 5="6{7!}"
var xhr = new XMLHttpRequest(); // formerly: 0 1=8 a()
var url = "/serial.php"; // formerly: 0 2="/9.c"
xhr.open("POST", url, true); // formerly: 1.d("e",2,f)
xhr.send(null); // formerly: 1.b(3)
}📎 Copy‑paste helpers
1) Print instead of execute (browser/Node)
// Replace eval( with console.log(, then run in a safe REPL
const src = `eval(function(p,a,c,k,e,d){/* ... */})`;
console.log(src.replace(/^eval\(/, 'console.log('));2) Minimal “packer” unwrap (conceptual)
If you can’t use online tools, the packer mechanism is just a dictionary substitution:
k.split('|')gives the dictionary of words.- Tokens like
g, 4, 0, 5, ...map back to real identifiers via base‑aconversion (toString(36)in this case). - The wrapper replaces each
\btoken\bwith the dictionary entry.
Use an offline packer‑unpacker script (there are many small ones) or a JS sandbox that logs the output instead of executing it.
3) Curl sample (from the snippet’s endpoint)
curl http://SERVER_IP:PORT/serial.php -X POST -d "param1=sample"
# Example base64 response:
# ZG8gdGhlIGV4ZXJjaXNlLCBkb24ndCBjb3B5IGFuZCBwYXN0ZSA7KQo=🔐 Decoding embedded strings
Base64
- Spot: alphanumeric +
+/and often=padding; length multiple of 4. - Decode
echo 'aHR0cHM6Ly93d3cuaGFja3RoZWJveC5ldS8K' | base64 -d
# https://www.hackthebox.eu/- From the curl example
echo 'ZG8gdGhlIGV4ZXJjaXNlLCBkb24ndCBjb3B5IGFuZCBwYXN0ZSA7KQo=' | base64 -d
# do the exercise, don't copy and paste ;)Hex
- Spot: only 0–9 and a–f.
- Encode
echo 'https://www.hackthebox.eu/' | xxd -p- Decode
echo '68747470733a2f2f7777772e6861636b746865626f782e65752f0a' | xxd -p -rrot13 (Caesar)
- Encode/Decode
echo 'https://www.hackthebox.eu/' | tr 'A-Za-z' 'N-ZA-Mn-za-m'
# uggcf://jjj.unpxgurobk.rh/
echo 'uggcf://jjj.unpxgurobk.rh/' | tr 'A-Za-z' 'N-ZA-Mn-za-m'
# https://www.hackthebox.eu/🧪 Troubleshooting
- Blank output after replacing
eval? Some payloads perform environment checks. Evaluate in a fresh Node REPL or a blank HTML page. - Tool blocked / offline? Keep local copies of a beautifier/unpacker, or use simple regex/logging to dump the inner string.
- Still unreadable? Layers are common (packer + base64 + hex). Decode strings first, then beautify again.
- Danger: don’t execute unknown code; always log/print the unpacked result, not run it.
🔒 Defense (dev notes)
- Avoid
eval/Function/setTimeout("<string>"); minify ≠ obfuscate. - Treat third‑party scripts as untrusted; pin with SRI & CSP; host locally where possible.
- Monitor for unexpected POST targets (e.g.,
/serial.php) and anomalous base64/hex blobs in code.
✅ Result
- The packer payload unpacks to a readable function
generateSerial()that POSTs to/serial.phpand embeds a string"HTB{flag!}". - The sample base64 blob decodes to:
do the exercise, don't copy and paste ;)