aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile13
-rw-r--r--build.bat45
-rw-r--r--golang-runtime-dll-injection.patch316
3 files changed, 354 insertions, 20 deletions
diff --git a/Makefile b/Makefile
index 720c0099..8a93a700 100644
--- a/Makefile
+++ b/Makefile
@@ -4,17 +4,26 @@ WINDRES := x86_64-w64-mingw32-windres
export CGO_ENABLED := 1
export GOOS := windows
export GOARCH := amd64
+REAL_GOROOT := $(shell go env GOROOT)
+export GOROOT := $(PWD)/deps/go
+export PATH := $(GOROOT)/bin:$(PATH)
DEPLOYMENT_HOST ?= winvm
DEPLOYMENT_PATH ?= Desktop
all: wireguard.exe
+deps/.prepared:
+ mkdir -p deps
+ rsync -a --delete --exclude=pkg/obj/go-build "$(REAL_GOROOT)/" "$(GOROOT)/"
+ patch -f -N -r- -d deps/go -p1 < golang-runtime-dll-injection.patch
+ touch "$@"
+
resources.syso: resources.rc manifest.xml ui/icon/icon.ico
$(WINDRES) -i $< -o $@ -O coff
rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
-wireguard.exe: resources.syso $(call rwildcard,,*.go *.c *.h)
+wireguard.exe: resources.syso $(call rwildcard,,*.go *.c *.h) deps/.prepared
go build -ldflags="-H windowsgui -s -w" -v -o $@
deploy: wireguard.exe
@@ -22,6 +31,6 @@ deploy: wireguard.exe
scp wireguard.exe $(DEPLOYMENT_HOST):$(DEPLOYMENT_PATH)
clean:
- rm -rf resources.syso wireguard.exe
+ rm -rf resources.syso wireguard.exe deps
.PHONY: deploy clean all
diff --git a/build.bat b/build.bat
index 0c8f1831..082310d2 100644
--- a/build.bat
+++ b/build.bat
@@ -1,21 +1,8 @@
@echo off
set STARTDIR=%cd%
set OLDPATH=%PATH%
-if not exist deps\.prepared call :installdeps
-set PATH=%STARTDIR%\deps\x86_64-w64-mingw32-native\bin\;%STARTDIR%\deps\go\bin\;%PATH%
-set CC=x86_64-w64-mingw32-gcc.exe
-set CFLAGS=-O3 -Wall -std=gnu11
-set GOOS=windows
-set GOARCH=amd64
-set GOPATH=%STARTDIR%\deps\gopath
-set GOROOT=%STARTDIR%\deps\go
-set CGO_ENABLED=1
-echo Assembling resources
-windres.exe -i resources.rc -o resources.syso -O coff || goto :error
-echo Building program
-go build -ldflags="-H windowsgui -s -w" -v -o wireguard.exe || goto :error
-goto :out
+if exist deps\.prepared goto :build
:installdeps
rmdir /s /q deps 2> NUL
mkdir deps || goto :error
@@ -24,19 +11,41 @@ goto :out
curl -#fo go.zip https://dl.google.com/go/go1.12.windows-amd64.zip || goto :error
echo Downloading mingw
curl -#fo mingw.zip https://musl.cc/x86_64-w64-mingw32-native.zip || goto :error
+ echo Downloading patch
+ curl -L#fo patch.zip https://sourceforge.net/projects/gnuwin32/files/patch/2.5.9-7/patch-2.5.9-7-bin.zip || goto :error
echo Extracting golang
tar -xf go.zip || goto :error
echo Extracting mingw
tar -xf mingw.zip || goto :error
+ echo Extracting patch
+ tar -xf patch.zip --strip-components 1 bin || goto :error
+ echo Patching golang
+ .\patch.exe -f -N -r- -d go -p1 --binary < ..\golang-runtime-dll-injection.patch || goto :error
echo Cleaning up
- del go.zip mingw.zip || goto :error
+ del patch.exe patch.zip go.zip mingw.zip || goto :error
copy /y NUL .prepared > NUL || goto :error
cd .. || goto :error
- exit /b
-:error
- echo Failed with error #%errorlevel%.
+:build
+ set PATH=%STARTDIR%\deps\x86_64-w64-mingw32-native\bin\;%STARTDIR%\deps\go\bin\;%PATH%
+ set CC=x86_64-w64-mingw32-gcc.exe
+ set CFLAGS=-O3 -Wall -std=gnu11
+ set GOOS=windows
+ set GOARCH=amd64
+ set GOPATH=%STARTDIR%\deps\gopath
+ set GOROOT=%STARTDIR%\deps\go
+ set CGO_ENABLED=1
+ echo Assembling resources
+ windres.exe -i resources.rc -o resources.syso -O coff || goto :error
+ echo Building program
+ go build -ldflags="-H windowsgui -s -w" -v -o wireguard.exe || goto :error
+ echo Success. Launch wireguard.exe.
+
:out
set PATH=%OLDPATH%
cd %STARTDIR%
exit /b %errorlevel%
+
+:error
+ echo Failed with error #%errorlevel%.
+ goto :out
diff --git a/golang-runtime-dll-injection.patch b/golang-runtime-dll-injection.patch
new file mode 100644
index 00000000..af0fc87b
--- /dev/null
+++ b/golang-runtime-dll-injection.patch
@@ -0,0 +1,316 @@
+From a5dacf9b3b54bc24733776a42e7907964f23cd3b Mon Sep 17 00:00:00 2001
+From: "Jason A. Donenfeld" <Jason@zx2c4.com>
+Date: Wed, 6 Mar 2019 19:26:29 +0100
+Subject: [PATCH] runtime: safely load DLLs
+
+While many other call sites have been moved to using the proper
+higher-level system loading, these areas were left out. This prevents
+DLL directory injection attacks. This includes both the runtime load
+calls (using LoadLibrary prior) and the implicitly linked ones via
+cgo_import_dynamic, which we move to our LoadLibraryEx. The goal is to
+only loosely load kernel32.dll and strictly load all others.
+
+Meanwhile we make sure that we never fallback to insecure loading on
+older or unpatched systems.
+
+This is CVE-2019-9634.
+
+Fixes #14959
+Fixes #28978
+Fixes #30642
+
+Change-Id: I401a13ed8db248ab1bb5039bf2d31915cac72b93
+---
+ src/runtime/os_windows.go | 64 +++++++++++++++++++++++++++------
+ src/runtime/syscall_windows.go | 14 ++++----
+ src/syscall/dll_windows.go | 28 +++++++++++++--
+ src/syscall/security_windows.go | 1 +
+ src/syscall/zsyscall_windows.go | 14 ++++++++
+ 5 files changed, 101 insertions(+), 20 deletions(-)
+
+diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go
+index 2e1ec58a0d..d3e84fe3dc 100644
+--- a/src/runtime/os_windows.go
++++ b/src/runtime/os_windows.go
+@@ -29,6 +29,7 @@ const (
+ //go:cgo_import_dynamic runtime._GetProcessAffinityMask GetProcessAffinityMask%3 "kernel32.dll"
+ //go:cgo_import_dynamic runtime._GetQueuedCompletionStatus GetQueuedCompletionStatus%5 "kernel32.dll"
+ //go:cgo_import_dynamic runtime._GetStdHandle GetStdHandle%1 "kernel32.dll"
++//go:cgo_import_dynamic runtime._GetSystemDirectoryA GetSystemDirectoryA%2 "kernel32.dll"
+ //go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 "kernel32.dll"
+ //go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 "kernel32.dll"
+ //go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll"
+@@ -47,12 +48,9 @@ const (
+ //go:cgo_import_dynamic runtime._VirtualAlloc VirtualAlloc%4 "kernel32.dll"
+ //go:cgo_import_dynamic runtime._VirtualFree VirtualFree%3 "kernel32.dll"
+ //go:cgo_import_dynamic runtime._VirtualQuery VirtualQuery%3 "kernel32.dll"
+-//go:cgo_import_dynamic runtime._WSAGetOverlappedResult WSAGetOverlappedResult%5 "ws2_32.dll"
+ //go:cgo_import_dynamic runtime._WaitForSingleObject WaitForSingleObject%2 "kernel32.dll"
+ //go:cgo_import_dynamic runtime._WriteConsoleW WriteConsoleW%5 "kernel32.dll"
+ //go:cgo_import_dynamic runtime._WriteFile WriteFile%5 "kernel32.dll"
+-//go:cgo_import_dynamic runtime._timeBeginPeriod timeBeginPeriod%1 "winmm.dll"
+-//go:cgo_import_dynamic runtime._timeEndPeriod timeEndPeriod%1 "winmm.dll"
+
+ type stdFunction unsafe.Pointer
+
+@@ -75,6 +73,7 @@ var (
+ _GetProcessAffinityMask,
+ _GetQueuedCompletionStatus,
+ _GetStdHandle,
++ _GetSystemDirectoryA,
+ _GetSystemInfo,
+ _GetSystemTimeAsFileTime,
+ _GetThreadContext,
+@@ -96,12 +95,9 @@ var (
+ _VirtualAlloc,
+ _VirtualFree,
+ _VirtualQuery,
+- _WSAGetOverlappedResult,
+ _WaitForSingleObject,
+ _WriteConsoleW,
+ _WriteFile,
+- _timeBeginPeriod,
+- _timeEndPeriod,
+ _ stdFunction
+
+ // Following syscalls are only available on some Windows PCs.
+@@ -109,6 +105,7 @@ var (
+ _AddDllDirectory,
+ _AddVectoredContinueHandler,
+ _GetQueuedCompletionStatusEx,
++ _LoadLibraryExA,
+ _LoadLibraryExW,
+ _ stdFunction
+
+@@ -126,6 +123,12 @@ var (
+ // links wrong printf function to cgo executable (see issue
+ // 12030 for details).
+ _NtWaitForSingleObject stdFunction
++
++ // These are from non-kernel32.dll, so we prefer to LoadLibraryEx them.
++ _timeBeginPeriod,
++ _timeEndPeriod,
++ _WSAGetOverlappedResult,
++ _ stdFunction
+ )
+
+ // Function to be called by windows CreateThread
+@@ -173,6 +176,26 @@ func windowsFindfunc(lib uintptr, name []byte) stdFunction {
+ return stdFunction(unsafe.Pointer(f))
+ }
+
++var sysDirectory [521]byte
++var sysDirectoryLen uintptr
++
++func windowsLoadSystemLib(name []byte) uintptr {
++ if useLoadLibraryEx {
++ return stdcall3(_LoadLibraryExA, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)
++ } else {
++ if sysDirectoryLen == 0 {
++ l := stdcall2(_GetSystemDirectoryA, uintptr(unsafe.Pointer(&sysDirectory[0])), uintptr(len(sysDirectory)-1))
++ if l == 0 || l > uintptr(len(sysDirectory)-1) {
++ throw("Unable to determine system directory")
++ }
++ sysDirectory[l] = '\\'
++ sysDirectoryLen = l + 1
++ }
++ absName := append(sysDirectory[:sysDirectoryLen], name...)
++ return stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&absName[0])))
++ }
++}
++
+ func loadOptionalSyscalls() {
+ var kernel32dll = []byte("kernel32.dll\000")
+ k32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0])))
+@@ -182,17 +205,19 @@ func loadOptionalSyscalls() {
+ _AddDllDirectory = windowsFindfunc(k32, []byte("AddDllDirectory\000"))
+ _AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000"))
+ _GetQueuedCompletionStatusEx = windowsFindfunc(k32, []byte("GetQueuedCompletionStatusEx\000"))
++ _LoadLibraryExA = windowsFindfunc(k32, []byte("LoadLibraryExA\000"))
+ _LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000"))
++ useLoadLibraryEx = (_LoadLibraryExW != nil && _LoadLibraryExA != nil && _AddDllDirectory != nil)
+
+ var advapi32dll = []byte("advapi32.dll\000")
+- a32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&advapi32dll[0])))
++ a32 := windowsLoadSystemLib(advapi32dll)
+ if a32 == 0 {
+ throw("advapi32.dll not found")
+ }
+ _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000"))
+
+ var ntdll = []byte("ntdll.dll\000")
+- n32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&ntdll[0])))
++ n32 := windowsLoadSystemLib(ntdll)
+ if n32 == 0 {
+ throw("ntdll.dll not found")
+ }
+@@ -205,6 +230,27 @@ func loadOptionalSyscalls() {
+ }
+ }
+
++ var winmmdll = []byte("winmm.dll\000")
++ m32 := windowsLoadSystemLib(winmmdll)
++ if m32 == 0 {
++ throw("winmm.dll not found")
++ }
++ _timeBeginPeriod = windowsFindfunc(m32, []byte("timeBeginPeriod\000"))
++ _timeEndPeriod = windowsFindfunc(m32, []byte("timeEndPeriod\000"))
++ if _timeBeginPeriod == nil || _timeEndPeriod == nil {
++ throw("timeBegin/EndPeriod not found")
++ }
++
++ var ws232dll = []byte("ws2_32.dll\000")
++ ws232 := windowsLoadSystemLib(ws232dll)
++ if ws232 == 0 {
++ throw("ws2_32.dll not found")
++ }
++ _WSAGetOverlappedResult = windowsFindfunc(ws232, []byte("WSAGetOverlappedResult\000"))
++ if _WSAGetOverlappedResult == nil {
++ throw("WSAGetOverlappedResult not found")
++ }
++
+ if windowsFindfunc(n32, []byte("wine_get_version\000")) != nil {
+ // running on Wine
+ initWine(k32)
+@@ -311,8 +357,6 @@ func osinit() {
+
+ loadOptionalSyscalls()
+
+- useLoadLibraryEx = (_LoadLibraryExW != nil && _AddDllDirectory != nil)
+-
+ disableWER()
+
+ initExceptionHandler()
+diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go
+index 8cfc71124a..36ad7511af 100644
+--- a/src/runtime/syscall_windows.go
++++ b/src/runtime/syscall_windows.go
+@@ -104,9 +104,13 @@ func compileCallback(fn eface, cleanstack bool) (code uintptr) {
+
+ const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
+
++// When available, this function will use LoadLibraryEx with the filename
++// parameter and the important SEARCH_SYSTEM32 argument. But on systems that
++// do not have that option, absoluteFilepath should contain a fallback
++// to the full path inside of system32 for use with vanilla LoadLibrary.
+ //go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary
+ //go:nosplit
+-func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) {
++func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) {
+ lockOSThread()
+ defer unlockOSThread()
+ c := &getg().m.syscall
+@@ -121,15 +125,9 @@ func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) {
+ }{filename, 0, _LOAD_LIBRARY_SEARCH_SYSTEM32}
+ c.args = uintptr(noescape(unsafe.Pointer(&args)))
+ } else {
+- // User doesn't have KB2533623 installed. The caller
+- // wanted to only load the filename DLL from the
+- // System32 directory but that facility doesn't exist,
+- // so just load it the normal way. This is a potential
+- // security risk, but so is not installing security
+- // updates.
+ c.fn = getLoadLibrary()
+ c.n = 1
+- c.args = uintptr(noescape(unsafe.Pointer(&filename)))
++ c.args = uintptr(noescape(unsafe.Pointer(&absoluteFilepath)))
+ }
+
+ cgocall(asmstdcallAddr, unsafe.Pointer(c))
+diff --git a/src/syscall/dll_windows.go b/src/syscall/dll_windows.go
+index c57cd34f82..34925f74a4 100644
+--- a/src/syscall/dll_windows.go
++++ b/src/syscall/dll_windows.go
+@@ -28,7 +28,7 @@ func Syscall12(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 ui
+ func Syscall15(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2 uintptr, err Errno)
+ func Syscall18(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 uintptr) (r1, r2 uintptr, err Errno)
+ func loadlibrary(filename *uint16) (handle uintptr, err Errno)
+-func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno)
++func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno)
+ func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno)
+
+ // A DLL implements access to a single DLL.
+@@ -37,6 +37,26 @@ type DLL struct {
+ Handle Handle
+ }
+
++// We use this for computing the absolute path for system DLLs on systems
++// where SEARCH_SYSTEM32 is not available.
++var systemDirectoryPrefix string
++
++func init() {
++ n := uint32(MAX_PATH)
++ for {
++ b := make([]uint16, n)
++ l, e := getSystemDirectory(&b[0], n)
++ if e != nil {
++ panic("Unable to determine system directory: " + e.Error())
++ }
++ if l <= n {
++ systemDirectoryPrefix = UTF16ToString(b[:l]) + "\\"
++ break
++ }
++ n = l
++ }
++}
++
+ // LoadDLL loads the named DLL file into memory.
+ //
+ // If name is not an absolute path and is not a known system DLL used by
+@@ -53,7 +73,11 @@ func LoadDLL(name string) (*DLL, error) {
+ var h uintptr
+ var e Errno
+ if sysdll.IsSystemDLL[name] {
+- h, e = loadsystemlibrary(namep)
++ absoluteFilepathp, err := UTF16PtrFromString(systemDirectoryPrefix + name)
++ if err != nil {
++ return nil, err
++ }
++ h, e = loadsystemlibrary(namep, absoluteFilepathp)
+ } else {
+ h, e = loadlibrary(namep)
+ }
+diff --git a/src/syscall/security_windows.go b/src/syscall/security_windows.go
+index ae8b3a17bf..db80d98a08 100644
+--- a/src/syscall/security_windows.go
++++ b/src/syscall/security_windows.go
+@@ -290,6 +290,7 @@ type Tokenprimarygroup struct {
+ //sys OpenProcessToken(h Handle, access uint32, token *Token) (err error) = advapi32.OpenProcessToken
+ //sys GetTokenInformation(t Token, infoClass uint32, info *byte, infoLen uint32, returnedLen *uint32) (err error) = advapi32.GetTokenInformation
+ //sys GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) = userenv.GetUserProfileDirectoryW
++//sys getSystemDirectory(dir *uint16, dirLen uint32) (len uint32, err error) = kernel32.GetSystemDirectoryW
+
+ // An access token contains the security information for a logon session.
+ // The system creates an access token when a user logs on, and every
+diff --git a/src/syscall/zsyscall_windows.go b/src/syscall/zsyscall_windows.go
+index de2d4f3adb..2348f6534f 100644
+--- a/src/syscall/zsyscall_windows.go
++++ b/src/syscall/zsyscall_windows.go
+@@ -190,6 +190,7 @@ var (
+ procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken")
+ procGetTokenInformation = modadvapi32.NewProc("GetTokenInformation")
+ procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW")
++ procGetSystemDirectoryW = modkernel32.NewProc("GetSystemDirectoryW")
+ )
+
+ func GetLastError() (lasterr error) {
+@@ -1916,3 +1917,16 @@ func GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) {
+ }
+ return
+ }
++
++func getSystemDirectory(dir *uint16, dirLen uint32) (len uint32, err error) {
++ r0, _, e1 := Syscall(procGetSystemDirectoryW.Addr(), 2, uintptr(unsafe.Pointer(dir)), uintptr(dirLen), 0)
++ len = uint32(r0)
++ if len == 0 {
++ if e1 != 0 {
++ err = errnoErr(e1)
++ } else {
++ err = EINVAL
++ }
++ }
++ return
++}
+--
+2.20.1
+