Prevent DLL hijacking by setting default DLL directories on initialization
Description
This PR fixes a DLL hijacking vulnerability identified during a VAPT assessment. When the application tries to load certain DLLs from the application directory instead of the system directories, it creates a potential security risk where an attacker could place malicious DLLs in the application directory.
The issue affects several DLLs including:
- uxtheme.dll
- WindowsCodecs.dll
- dwmapi.dll
- PROPSYS.dll
- CRYPTBASE.dll
- Secur32.dll
- SSPICLI.DLL
- MLANG.dll
- WININET.dll
While most DLLs can be fixed by adding windows.SetDefaultDllDirectories(windows.LOAD_LIBRARY_SEARCH_SYSTEM32) in application code, the uxtheme.dll is loaded by the Wails before application initialization. This PR addresses this issue by adding the secure DLL loading directive at the framework level.
Additional Background Information
Our security team recently discovered a DLL hijacking vulnerability in our application. After digging into it, I found that this is actually a common issue in Go applications - there's even a similar bug report in the Go repository (https://github.com/golang/go/issues/64411).
Here's what's happening: When Windows looks for DLLs, it first checks the application directory before checking system directories. This creates a security risk where someone could drop malicious versions of these DLLs into our application folder.
I initially tried fixing this in our code by adding windows.SetDefaultDllDirectories(windows.LOAD_LIBRARY_SEARCH_SYSTEM32), which tells Windows to prioritize loading DLLs from the System32 directory. This worked for most DLLs, but not for uxtheme.dll, it loaded by Wails before application initialization, so our fix never gets a chance to take effect.It's an important security improvement that protects applications from a potentially serious attack vector where bad actors could execute arbitrary code through fake DLLs.
Type of change
Please select the option that is relevant.
- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
Steps to Reproduce
- Create a basic Wails application
- Build the application for Windows
- Run Process Monitor while launching the application and Filter the PATH to
<Application Directory>\uxtheme.dlland observe
How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration using wails doctor.
- [x] Windows
- [ ] macOS
- [ ] Linux
If you checked Linux, please specify the distro and version.
Test Configuration
PS C:\Users\DELL\wails\v2\cmd\wails> .\wails.exe doctor
# Wails
Version | v2.10.1
Revision | 194b0586ba63b3003bb6f5c3b5bc48f8c84720fc
Modified | true
# System
┌─────────────────────────────────────────────────────────────────────────────────────┐
| OS | Windows 10 Pro |
| Version | 2009 (Build: 19045) |
| ID | 22H2 |
| Branding | Windows 10 Pro |
| Go Version | go1.23.0 |
| Platform | windows |
| Architecture | amd64 |
| CPU | Intel(R) Core(TM) i7-7600U CPU @ 2.80GHz |
| GPU | Intel(R) HD Graphics 620 (Intel Corporation) - Driver: 31.0.101.2127 |
| Memory | 16GB |
└─────────────────────────────────────────────────────────────────────────────────────┘
# Dependencies
┌───────────────────────────────────────────────────────┐
| Dependency | Package Name | Status | Version |
| WebView2 | N/A | Installed | 134.0.3124.93 |
| Nodejs | N/A | Installed | 22.14.0 |
| npm | N/A | Installed | 10.9.2 |
| *upx | N/A | Available | |
| *nsis | N/A | Available | |
| |
└─────────────── * - Optional Dependency ───────────────┘
# Diagnosis
Optional package(s) installation details:
- upx : Available at https://upx.github.io/
- nsis : More info at https://wails.io/docs/guides/windows-installer/
SUCCESS Your system is ready for Wails development!
Checklist:
- [x] I have updated
website/src/pages/changelog.mdxwith details of this PR - [x] My code follows the general coding style of this project
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
Summary by CodeRabbit
-
New Features
- Introduced a new option for Windows users to control DLL search paths, enhancing the application's configurability.
- Expanded DLL loading settings with advanced controls, allowing for more detailed management of how DLL files are located on Windows platforms.
-
Improvements
- Updated function visibility for improved accessibility and initialization within the application.
[!CAUTION]
Review failed
The pull request is closed.
Walkthrough
Adds a Windows DLL search-path option and wiring: changelog entry, new constants and DLLSearchPaths option, conditional call to SetDefaultDllDirectories during frontend init, and makes several platform init functions exported for external initialization. No other behavior changes.
Changes
| Cohort / File(s) | Change Summary |
|---|---|
Changelogwebsite/src/pages/changelog.mdx |
Added changelog entry: "Added DLLSearchPaths option to control DLL search paths on Windows" under v2.10.2. |
Windows optionsv2/pkg/options/windows/windows.go |
Added Windows DLL search flag constants (mapped to golang.org/x/sys/windows) and new DLLSearchPaths uint32 field to Options. |
Frontend wiringv2/internal/frontend/desktop/windows/frontend.go |
Imported w32consts and golang.org/x/sys/windows as w; in NewFrontend conditionally call w.SetDefaultDllDirectories(appoptions.Windows.DLLSearchPaths) when non-zero and initialize w32/w32consts before constructing the Frontend. |
Win32 init exportsv2/internal/frontend/desktop/windows/winc/w32/uxtheme.go, v2/internal/platform/win32/consts.go |
Renamed package-local init() functions to exported Init() to allow invocation from other packages; internal logic unchanged. |
Sequence Diagram(s)
sequenceDiagram
participant App as Application
participant Options as AppOptions.Windows
participant WinSys as golang.org/x/sys/windows (w)
participant W32 as w32 / w32consts
App->>Options: Read Windows options
alt DLLSearchPaths != 0
App->>WinSys: SetDefaultDllDirectories(DLLSearchPaths)
WinSys-->>App: success / error
App->>W32: Init()
W32-->>App: loaded DLLs
else DLLSearchPaths == 0
App-->>App: Skip SetDefaultDllDirectories
App->>W32: Init()
W32-->>App: loaded DLLs
end
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
- wailsapp/wails#3713 — Modifies the same
winc/w32/uxtheme.goand relates to making initialization and syscall usage changes; likely related to exported init and theme syscall updates.
Suggested labels
Windows, Documentation, go, Bug
Poem
I'm a rabbit in code,
Hopping through DLL lanes so wide,
Flags and paths now mark the tide,
Init is called where shadows hide,
Changelog cheers — I twitch with pride! 🐰✨
✨ Finishing touches
- [ ] 📝 Generate Docstrings
🧪 Generate unit tests
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
📜 Recent review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📥 Commits
Reviewing files that changed from the base of the PR and between 5d75d08138e4b34519aa61455989509a044286fc and 8746a975f717e9b1431a59a5814e4266ada86786.
📒 Files selected for processing (1)
-
v2/internal/frontend/desktop/windows/frontend.go(2 hunks)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
Thanks for taking the time to open this 🙏
I absolutely see the need for this but the solution will need to be more flexible to accommodate everyone's use cases. Some people ship DLLs with their binaries using NSIS and this will break their apps in a way they can't fix.
Proposal: create a windows specific application setting that lists the set of search paths. Let's call it DLLSearchPaths. If this is blank, we do nothing and default to Go's defaults. If given, then we set them at application startup. This will mean updating most of the win32 package as you've noted.
Thoughts?
Do you think we need to change func init() { in uxtheme.go to func Init() { and call this manually after setting the DLL search paths? Alternatively, you could try lazy loading the DLL but I'm guessing there's a reason this is being loaded differently.
Do you think we need to change
func init() {inuxtheme.gotofunc Init() {and call this manually after setting the DLL search paths? Alternatively, you could try lazy loading the DLL but I'm guessing there's a reason this is being loaded differently.
Thanks for highlighting this. I didn’t get a chance to test the new changes as my Windows laptop is having some issues. I would prefer changing func init() to func Init() in both uxtheme.go and consts.go for a minimal impact.
Sounds good. Can you please make sure that the changes work correctly and they don't flicker windows when they start up or anything? That is my only concern with converting the init function.
Sounds good. Can you please make sure that the changes work correctly and they don't flicker windows when they start up or anything? That is my only concern with converting the init function.
Sure i will do proper testing and update you here.
Tested on Windows 10, I did not observe any issues.
Quality Gate passed
Issues
0 New issues
0 Accepted issues
Measures
0 Security Hotspots
0.0% Coverage on New Code
0.0% Duplication on New Code
Quality Gate passed
Issues
0 New issues
0 Accepted issues
Measures
0 Security Hotspots
0.0% Coverage on New Code
0.0% Duplication on New Code
Quality Gate passed
Issues
0 New issues
0 Accepted issues
Measures
0 Security Hotspots
0.0% Coverage on New Code
0.0% Duplication on New Code
Quality Gate passed
Issues
0 New issues
0 Accepted issues
Measures
0 Security Hotspots
0.0% Coverage on New Code
0.0% Duplication on New Code
Documentation Updates
Checked 1 published document(s). No updates required.
You have 2 draft document(s). Publish docs to keep them always up-to-date