VULNARCHIVE

GCVE-1988-2026-0389

[NotCVE-2026-0014] Input Leap 3.0.3 Drag-and-Drop File Transfer Path Traversal Allows Arbitrary File Write Outside the Drop Directory

Description (en)

----------------------------------------------------------------------------
NotCVE Advisory — NotCVE-2026-0014
----------------------------------------------------------------------------

[-] Summary:
Improper limitation of a pathname in the drag-and-drop file transfer
feature of Input Leap, the open-source keyboard and mouse sharing tool,
allows a connected peer to write a file outside the configured drop-target
directory. Writing into the per-user Startup folder turns this into code
execution as the receiving user at the next login. CVSS:3.1 5.3
(AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:N).

[-] Affected:
Input Leap through 3.0.3 (the final release) and master, on Windows and
macOS. The input-leap/input-leap repository was archived read-only on
26 July 2026; no fixed version exists or is expected.

[-] Technical Description:
DragInformation::parseDragInfo() in src/lib/inputleap/DragInformation.cpp
reduces each received filename to its basename. It picks one separator for
the whole received blob ('/' if the data contains a '/' anywhere, otherwise
'\') and then locates the last separator at or before each entry's comma:

  findResult2 = data.find_last_of(slash, findResult1);
  ...
  if (findResult1 - findResult2 > 1) {
      auto filename = data.substr(findResult2 + 1,
                                  findResult1 - findResult2 - 1);

find_last_of() returns std::string::npos when no separator is present, and
that value is never tested. The unsigned arithmetic wraps: findResult2 + 1
becomes 0, the guard still passes, and the call degrades to
data.substr(0, findResult1) - the whole attacker-supplied string up to the
comma, with any ".." sequences intact.

Because the separator is chosen once per blob, a peer can force the
mismatch: a single stray '/' after the first comma selects '/', while a
filename written with '\' separators contains no '/' before that comma.

DropHelper::writeToDir() in src/lib/inputleap/DropHelper.cpp appends the
result to the drop-target directory and opens it for writing, with no
normalisation, no check for ".." or absolute prefixes, and no check that
the path stays beneath the drop target. A name such as
..\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\ escapes
the configured folder, and a file written there runs as the receiving user
at the next login.

Preconditions: the feature is opt-in (--enable-drag-drop, not the
default), and ArgParser refuses the flag on Linux, so only Windows and
macOS targets are affected. Both directions are reachable (malicious
client against a server, malicious server against a client). Under the
default ENCRYPTED_AUTHENTICATED level the sender must already be a paired
peer; a target started with --disable-crypto has no such requirement.

The same unchecked find_last_of() result is present in the ancestor
debauchee/barrier codebase. The sibling fork Deskflow removed drag-and-drop
file transfer and does not appear to carry this code path.

Weaknesses:
CWE-22: Improper Limitation of a Pathname to a Restricted Directory
CWE-191: Integer Underflow (Wrap or Wraparound)
CAPEC-126: Path Traversal
CAPEC-139: Relative Path Traversal

[-] Credit:
Discovered by Christopher Duram
(https://www.linkedin.com/in/christopherduram/).

[-] Full Details and Updates:
https://notcve.org/notcve/NotCVE-2026-0014

[-] Main References:
https://github.com/input-leap/input-leap
https://github.com/input-leap/input-leap/blob/v3.0.3/src/lib/inputleap/DragInformation.cpp

[-] About NotCVE:
NotCVE (https://notcve.org) assigns public, timestamped NotCVE IDs to
vulnerabilities not acknowledged by vendors. Vendor will not assign a CVE?
Request a NotCVE: https://notcve.org/form/ · Contributors:
https://notcve.org/hall/
_______________________________________________
Sent through the Full Disclosure mailing list
https://nmap.org/mailman/listinfo/fulldisclosure
Web Archives & RSS: https://seclists.org/fulldisclosure/

References

Raw JSON
{
  "containers": {
    "cna": {
      "affected": [
        {
          "product": "Input Leap",
          "vendor": "unknown",
          "versions": [
            {
              "status": "affected",
              "version": "unknown"
            }
          ]
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "finder",
          "value": "advisories"
        }
      ],
      "descriptions": [
        {
          "lang": "en",
          "value": "----------------------------------------------------------------------------\nNotCVE Advisory — NotCVE-2026-0014\n----------------------------------------------------------------------------\n\n[-] Summary:\nImproper limitation of a pathname in the drag-and-drop file transfer\nfeature of Input Leap, the open-source keyboard and mouse sharing tool,\nallows a connected peer to write a file outside the configured drop-target\ndirectory. Writing into the per-user Startup folder turns this into code\nexecution as the receiving user at the next login. CVSS:3.1 5.3\n(AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:N).\n\n[-] Affected:\nInput Leap through 3.0.3 (the final release) and master, on Windows and\nmacOS. The input-leap/input-leap repository was archived read-only on\n26 July 2026; no fixed version exists or is expected.\n\n[-] Technical Description:\nDragInformation::parseDragInfo() in src/lib/inputleap/DragInformation.cpp\nreduces each received filename to its basename. It picks one separator for\nthe whole received blob ('/' if the data contains a '/' anywhere, otherwise\n'\\') and then locates the last separator at or before each entry's comma:\n\n  findResult2 = data.find_last_of(slash, findResult1);\n  ...\n  if (findResult1 - findResult2 > 1) {\n      auto filename = data.substr(findResult2 + 1,\n                                  findResult1 - findResult2 - 1);\n\nfind_last_of() returns std::string::npos when no separator is present, and\nthat value is never tested. The unsigned arithmetic wraps: findResult2 + 1\nbecomes 0, the guard still passes, and the call degrades to\ndata.substr(0, findResult1) - the whole attacker-supplied string up to the\ncomma, with any \"..\" sequences intact.\n\nBecause the separator is chosen once per blob, a peer can force the\nmismatch: a single stray '/' after the first comma selects '/', while a\nfilename written with '\\' separators contains no '/' before that comma.\n\nDropHelper::writeToDir() in src/lib/inputleap/DropHelper.cpp appends the\nresult to the drop-target directory and opens it for writing, with no\nnormalisation, no check for \"..\" or absolute prefixes, and no check that\nthe path stays beneath the drop target. A name such as\n..\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\ escapes\nthe configured folder, and a file written there runs as the receiving user\nat the next login.\n\nPreconditions: the feature is opt-in (--enable-drag-drop, not the\ndefault), and ArgParser refuses the flag on Linux, so only Windows and\nmacOS targets are affected. Both directions are reachable (malicious\nclient against a server, malicious server against a client). Under the\ndefault ENCRYPTED_AUTHENTICATED level the sender must already be a paired\npeer; a target started with --disable-crypto has no such requirement.\n\nThe same unchecked find_last_of() result is present in the ancestor\ndebauchee/barrier codebase. The sibling fork Deskflow removed drag-and-drop\nfile transfer and does not appear to carry this code path.\n\nWeaknesses:\nCWE-22: Improper Limitation of a Pathname to a Restricted Directory\nCWE-191: Integer Underflow (Wrap or Wraparound)\nCAPEC-126: Path Traversal\nCAPEC-139: Relative Path Traversal\n\n[-] Credit:\nDiscovered by Christopher Duram\n(https://www.linkedin.com/in/christopherduram/).\n\n[-] Full Details and Updates:\nhttps://notcve.org/notcve/NotCVE-2026-0014\n\n[-] Main References:\nhttps://github.com/input-leap/input-leap\nhttps://github.com/input-leap/input-leap/blob/v3.0.3/src/lib/inputleap/DragInformation.cpp\n\n[-] About NotCVE:\nNotCVE (https://notcve.org) assigns public, timestamped NotCVE IDs to\nvulnerabilities not acknowledged by vendors. Vendor will not assign a CVE?\nRequest a NotCVE: https://notcve.org/form/ · Contributors:\nhttps://notcve.org/hall/\n_______________________________________________\nSent through the Full Disclosure mailing list\nhttps://nmap.org/mailman/listinfo/fulldisclosure\nWeb Archives & RSS: https://seclists.org/fulldisclosure/"
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-191",
              "description": "CWE-191",
              "lang": "en",
              "type": "CWE"
            },
            {
              "cweId": "CWE-22",
              "description": "CWE-22",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-10-02T04:57:26Z",
        "orgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
        "shortName": "VULNARCHIVE"
      },
      "references": [
        {
          "tags": [
            "technical-description"
          ],
          "url": "https://vuln.freearchive.org/archive/full-disclosure/2026/Sep/67"
        },
        {
          "tags": [
            "technical-description"
          ],
          "url": "https://seclists.org/fulldisclosure/2026/Sep/67"
        },
        {
          "url": "https://github.com/input-leap/input-leap"
        },
        {
          "url": "https://github.com/input-leap/input-leap/blob/v3.0.3/src/lib/inputleap/DragInformation.cpp"
        },
        {
          "url": "https://nmap.org/mailman/listinfo/fulldisclosure"
        },
        {
          "url": "https://notcve.org"
        },
        {
          "url": "https://notcve.org/form/"
        },
        {
          "url": "https://notcve.org/hall/"
        },
        {
          "url": "https://notcve.org/notcve/NotCVE-2026-0014"
        },
        {
          "url": "https://seclists.org/fulldisclosure/"
        },
        {
          "url": "https://www.linkedin.com/in/christopherduram/"
        }
      ],
      "source": {
        "defect": [
          "https://seclists.org/fulldisclosure/2026/Sep/67"
        ],
        "discovery": "EXTERNAL"
      },
      "title": "[NotCVE-2026-0014] Input Leap 3.0.3 Drag-and-Drop File Transfer Path Traversal Allows Arbitrary File Write Outside the Drop Directory",
      "x_gcve": [
        {
          "recordType": "advisory",
          "relationships": [],
          "vulnId": "GCVE-1988-2026-0389",
          "x_vulnarchive": {
            "archiveUrl": "https://vuln.freearchive.org/archive/full-disclosure/2026/Sep/67",
            "automated": true,
            "contentSha256": "eef29a444ff1983adf3b6e2aff80da54644b68e28ab6ade228df090afd1a5e2b",
            "evidenceScore": 8,
            "messageId": "",
            "originalUrl": "https://seclists.org/fulldisclosure/2026/Sep/67",
            "policy": "vulnarchive-1",
            "sourceFormat": "text/html",
            "sourcePublishedAt": "2026-09-25T09:00:49Z"
          }
        }
      ]
    }
  },
  "cveMetadata": {
    "assignerOrgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
    "assignerShortName": "VULNARCHIVE",
    "datePublished": "2026-10-02T04:57:26Z",
    "dateUpdated": "2026-10-02T04:57:26Z",
    "state": "PUBLISHED",
    "vulnId": "GCVE-1988-2026-0389"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}