aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--attacksurface.md2
-rw-r--r--main.go25
-rw-r--r--service/securityapi.go62
-rw-r--r--service/service_manager.go2
-rw-r--r--service/service_tunnel.go14
-rw-r--r--service/zsyscall_windows.go32
6 files changed, 26 insertions, 111 deletions
diff --git a/attacksurface.md b/attacksurface.md
index 9b58339e..d9b32f5a 100644
--- a/attacksurface.md
+++ b/attacksurface.md
@@ -19,7 +19,7 @@ The tunnel service is a userspace service running as Local System, responsible f
- A listening pipe in `\\.\pipe\WireGuard\%s`, where `%s` is some basename of an already valid filename. Its permissions are set to `O:SYD:(A;;GA;;;SY)`, which presumably means only the "Local System" user can access it and do things, but it might be worth double checking that. This pipe gives access to private keys and allows for reconfiguration of the interface, as well as rebinding to different ports (below 1024, even).
- It handles data from its two UDP sockets, accessible to the public Internet.
- It handles data from Wintun, accessible to all users who can do anything with the network stack.
- - It does not yet drop privileges.
+ - After some initial setup, it uses `AdjustTokenPrivileges` to remove all privileges.
### Manager Service
diff --git a/main.go b/main.go
index c0dc5c49..0ed7bfa8 100644
--- a/main.go
+++ b/main.go
@@ -8,13 +8,12 @@ package main
import (
"fmt"
"os"
- "runtime"
"strconv"
"strings"
"time"
- "unsafe"
"golang.org/x/sys/windows"
+
"golang.zx2c4.com/wireguard/windows/ringlogger"
"golang.zx2c4.com/wireguard/windows/service"
"golang.zx2c4.com/wireguard/windows/ui"
@@ -64,28 +63,12 @@ func checkForWow64() {
func checkForAdminGroup() {
// This is not a security check, but rather a user-confusion one.
- adminSid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid)
- if err != nil {
- fatal("Unable to create well-known SID for Builtin Administrators: ", err)
- }
- token, err := windows.OpenCurrentProcessToken()
+ processToken, err := windows.OpenCurrentProcessToken()
if err != nil {
fatal("Unable to open current process token: ", err)
}
- gs, err := token.GetTokenGroups()
- if err != nil {
- fatal("Unable to get groups of current process token: ", err)
- }
- groups := (*[(1 << 28) - 1]windows.SIDAndAttributes)(unsafe.Pointer(&gs.Groups[0]))[:gs.GroupCount]
- isAdmin := false
- for _, g := range groups {
- if windows.EqualSid(g.Sid, adminSid) {
- isAdmin = true
- break
- }
- }
- runtime.KeepAlive(gs)
- if !isAdmin {
+ defer processToken.Close()
+ if !service.TokenIsMemberOfBuiltInAdministrator(processToken) {
fatal("WireGuard may only be used by users who are a member of the Builtin Administrators group.")
}
}
diff --git a/service/securityapi.go b/service/securityapi.go
index 989b4ac3..a7e6072c 100644
--- a/service/securityapi.go
+++ b/service/securityapi.go
@@ -55,40 +55,6 @@ type WTS_SESSION_INFO struct {
//sys wtsEnumerateSessions(handle windows.Handle, reserved uint32, version uint32, sessions **WTS_SESSION_INFO, count *uint32) (err error) = wtsapi32.WTSEnumerateSessionsW
//sys wtsFreeMemory(ptr uintptr) = wtsapi32.WTSFreeMemory
-// TEMP //
-
-type LUID struct {
- LowPart uint32
- HighPart int32
-}
-
-type LUID_AND_ATTRIBUTES struct {
- Luid LUID
- Attributes uint32
-}
-
-type TOKEN_PRIVILEGES struct {
- PrivilegeCount uint32
- Privileges [1]LUID_AND_ATTRIBUTES
-}
-
-const (
- SE_PRIVILEGE_REMOVED uint32 = 0X00000004
- TOKEN_READ uint32 = 0x00020008
- TOKEN_WRITE uint32 = 0x000200e0
- TokenPrivileges uint32 = 3
-)
-
-//sys adjustTokenPrivileges(token windows.Token, disableAllPrivileges bool, newstate *TOKEN_PRIVILEGES, buflen uint32, prevstate *TOKEN_PRIVILEGES, returnlen *uint32) (err error) = advapi32.AdjustTokenPrivileges
-//sys openProcessToken(processHandle windows.Handle, accessFlags uint32, token *windows.Token) (err error) = advapi32.OpenProcessToken
-
-// END TEMP //
-
-const (
- SE_GROUP_ENABLED = 0x00000004
- SE_GROUP_USE_FOR_DENY_ONLY = 0x00000010
-)
-
func tokenIsElevated(token windows.Token) bool {
var isElevated uint32
var outLen uint32
@@ -116,7 +82,7 @@ func getElevatedToken(token windows.Token) (windows.Token, error) {
return windows.Token(0), errors.New("the linked token is not elevated")
}
-func tokenIsMemberOfBuiltInAdministrator(token windows.Token) bool {
+func TokenIsMemberOfBuiltInAdministrator(token windows.Token) bool {
adminSid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid)
if err != nil {
return false
@@ -128,7 +94,7 @@ func tokenIsMemberOfBuiltInAdministrator(token windows.Token) bool {
groups := (*[(1 << 28) - 1]windows.SIDAndAttributes)(unsafe.Pointer(&gs.Groups[0]))[:gs.GroupCount]
isAdmin := false
for _, g := range groups {
- if (g.Attributes&SE_GROUP_USE_FOR_DENY_ONLY != 0 || g.Attributes&SE_GROUP_ENABLED != 0) && windows.EqualSid(g.Sid, adminSid) {
+ if (g.Attributes&windows.SE_GROUP_USE_FOR_DENY_ONLY != 0 || g.Attributes&windows.SE_GROUP_ENABLED != 0) && windows.EqualSid(g.Sid, adminSid) {
isAdmin = true
break
}
@@ -143,33 +109,31 @@ func dropAllPrivileges() error {
return err
}
var processToken windows.Token
- err = openProcessToken(processHandle, TOKEN_READ | TOKEN_WRITE, (*windows.Token)(unsafe.Pointer(&processToken)))
+ err = windows.OpenProcessToken(processHandle, windows.TOKEN_READ|windows.TOKEN_WRITE, &processToken)
if err != nil {
return err
}
defer processToken.Close()
+
var bufferSizeRequired uint32
- _ = windows.GetTokenInformation(processToken, TokenPrivileges, nil, 0, (*uint32)(unsafe.Pointer(&bufferSizeRequired)))
- if bufferSizeRequired == 0 {
+ windows.GetTokenInformation(processToken, windows.TokenPrivileges, nil, 0, &bufferSizeRequired)
+ if bufferSizeRequired == 0 || bufferSizeRequired < uint32(unsafe.Sizeof(windows.Tokenprivileges{}.PrivilegeCount)) {
return errors.New("GetTokenInformation failed to provide a buffer size")
}
- buffer := make([]uint8, bufferSizeRequired)
+ buffer := make([]byte, bufferSizeRequired)
var bytesWritten uint32
- err = windows.GetTokenInformation(processToken, TokenPrivileges, (*uint8)(unsafe.Pointer(&buffer[0])), (uint32)(len(buffer)), (*uint32)(unsafe.Pointer(&bytesWritten)))
+ err = windows.GetTokenInformation(processToken, windows.TokenPrivileges, &buffer[0], uint32(len(buffer)), &bytesWritten)
if err != nil {
return err
}
if bytesWritten != bufferSizeRequired {
return errors.New("GetTokenInformation returned incomplete data")
}
- tokenPrivileges := (*TOKEN_PRIVILEGES)(unsafe.Pointer(&buffer[0]))
- privs := (*[1024]LUID_AND_ATTRIBUTES)(unsafe.Pointer(&buffer[unsafe.Sizeof(tokenPrivileges.PrivilegeCount)]))
+ tokenPrivileges := (*windows.Tokenprivileges)(unsafe.Pointer(&buffer[0]))
for i := uint32(0); i < tokenPrivileges.PrivilegeCount; i++ {
- privs[i].Attributes = SE_PRIVILEGE_REMOVED
- }
- err = adjustTokenPrivileges(processToken, false, tokenPrivileges, 0, nil, nil)
- if err != nil {
- return err
+ (*windows.LUIDAndAttributes)(unsafe.Pointer(uintptr(unsafe.Pointer(&tokenPrivileges.Privileges[0])) + unsafe.Sizeof(tokenPrivileges.Privileges[0])*uintptr(i))).Attributes = windows.SE_PRIVILEGE_REMOVED
}
- return nil
+ err = windows.AdjustTokenPrivileges(processToken, false, tokenPrivileges, 0, nil, nil)
+ runtime.KeepAlive(buffer)
+ return err
}
diff --git a/service/service_manager.go b/service/service_manager.go
index fae8800a..b2d93385 100644
--- a/service/service_manager.go
+++ b/service/service_manager.go
@@ -91,7 +91,7 @@ func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest
if err != nil {
return
}
- if !tokenIsMemberOfBuiltInAdministrator(userToken) {
+ if !TokenIsMemberOfBuiltInAdministrator(userToken) {
userToken.Close()
return
}
diff --git a/service/service_tunnel.go b/service/service_tunnel.go
index 0af7c733..3c09ed77 100644
--- a/service/service_tunnel.go
+++ b/service/service_tunnel.go
@@ -150,6 +150,13 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest,
return
}
+ logger.Info.Println("Dropping all privileges")
+ err = dropAllPrivileges()
+ if err != nil {
+ serviceError = ErrorDropPrivileges
+ return
+ }
+
logger.Info.Println("Creating interface instance")
dev = device.NewDevice(wintun, logger)
@@ -194,13 +201,6 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest,
}
}()
- logger.Info.Println("Dropping all privileges")
- err = dropAllPrivileges()
- if err != nil {
- serviceError = ErrorDropPrivileges
- return
- }
-
changes <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop}
logger.Info.Println("Startup complete")
diff --git a/service/zsyscall_windows.go b/service/zsyscall_windows.go
index a269d3a8..cc40ddea 100644
--- a/service/zsyscall_windows.go
+++ b/service/zsyscall_windows.go
@@ -44,8 +44,6 @@ var (
procWTSQueryUserToken = modwtsapi32.NewProc("WTSQueryUserToken")
procWTSEnumerateSessionsW = modwtsapi32.NewProc("WTSEnumerateSessionsW")
procWTSFreeMemory = modwtsapi32.NewProc("WTSFreeMemory")
- procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
- procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken")
procNotifyServiceStatusChangeW = modadvapi32.NewProc("NotifyServiceStatusChangeW")
procSleepEx = modkernel32.NewProc("SleepEx")
)
@@ -79,36 +77,6 @@ func wtsFreeMemory(ptr uintptr) {
return
}
-func adjustTokenPrivileges(token windows.Token, disableAllPrivileges bool, newstate *TOKEN_PRIVILEGES, buflen uint32, prevstate *TOKEN_PRIVILEGES, returnlen *uint32) (err error) {
- var _p0 uint32
- if disableAllPrivileges {
- _p0 = 1
- } else {
- _p0 = 0
- }
- r1, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(newstate)), uintptr(buflen), uintptr(unsafe.Pointer(prevstate)), uintptr(unsafe.Pointer(returnlen)))
- if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- return
-}
-
-func openProcessToken(processHandle windows.Handle, accessFlags uint32, token *windows.Token) (err error) {
- r1, _, e1 := syscall.Syscall(procOpenProcessToken.Addr(), 3, uintptr(processHandle), uintptr(accessFlags), uintptr(unsafe.Pointer(token)))
- if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- return
-}
-
func notifyServiceStatusChange(service windows.Handle, notifyMask uint32, notifier *SERVICE_NOTIFY) (ret error) {
r0, _, _ := syscall.Syscall(procNotifyServiceStatusChangeW.Addr(), 3, uintptr(service), uintptr(notifyMask), uintptr(unsafe.Pointer(notifier)))
if r0 != 0 {