Skip to content

Commit

Permalink
Release Package
Browse files Browse the repository at this point in the history
  • Loading branch information
Swapnil committed Jul 28, 2022
0 parents commit a1ad6b1
Show file tree
Hide file tree
Showing 7 changed files with 683 additions and 0 deletions.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2022 FourCore

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
108 changes: 108 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# wintoken
Windows Token Manipulation GOlang Library

Wintoken abstracts away windows token manipulation functions with functions you are more likely to use. The library exposes easy-to-use functions to steal tokens, enable/disable privileges, and grab interactive and linked tokens.

## Install

- Go
- Requires Go to be installed on system. Tested on Go1.16+.
- `go get github.com/fourcorelabs/wintoken`

## Usage
- To steal a token from a process, you can use OpenProcessToken and supply the PID and the type of token that you want

```go
package main

import (
"os/exec"
"syscall"

"github.com/fourcorelabs/wintoken"
)

func main() {
token, err := wintoken.OpenProcessToken(1234, wintoken.TokenPrimary) //pass 0 for own process
if err != nil {
panic(err)
}
defer token.Close()

//Now you can use the token anywhere you would like
cmd := exec.Command("/path/to/binary")
cmd.SysProcAttr = &syscall.SysProcAttr{Token: syscall.Token(token.Token())}
}
```

- If you want the elevated interactive token for the currently logged in user, you can call GetInteractiveToken with TokenLinked as parameter

```go
package main

import (
"os/exec"
"syscall"

"github.com/fourcorelabs/wintoken"
)

func main() {
//You can get an interactive token(if you are running as a service)
//and specify that you want the linked token(elevated) in the same line
token, err := wintoken.GetInteractiveToken(wintoken.TokenLinked)
if err != nil {
panic(err)
}
defer token.Close()

//Now you can use the token anywhere you would like
cmd := exec.Command("/path/to/binary")
cmd.SysProcAttr = &syscall.SysProcAttr{Token: syscall.Token(token.Token())}
}
```

- Once you have a token, you can query information from this token such as its privileges, integrity levels, associated user details, etc.

```go
package main

import (
"fmt"

"github.com/fourcorelabs/wintoken"
)

func main() {
token, err := wintoken.OpenProcessToken(1234, wintoken.TokenPrimary)
if err != nil {
panic(err)
}
defer token.Close()

fmt.Println(token.GetPrivileges())
fmt.Println(token.GetIntegrityLevel())
fmt.Println(token.UserDetails())
}
```

- You can Enable, Disable, and Remove privileges in a simple manner

```go
package main

import(
"github.com/fourcorelabs/wintoken"
)

func main(){
token, err := wintoken.OpenProcessToken(1234, wintoken.TokenPrimary)
if err != nil {
panic(err)
}
//Enable, Disable, or Remove privileges in one line
token.EnableAllPrivileges()
token.DisableTokenPrivileges([]string{"SeShutdownPrivilege", "SeTimeZonePrivilege"})
token.RemoveTokenPrivilege("SeUndockPrivilege")
}
```
135 changes: 135 additions & 0 deletions gettoken.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package wintoken

import (
"fmt"
"unsafe"

"golang.org/x/sys/windows"
)

const (
WTS_CURRENT_SERVER_HANDLE windows.Handle = 0
)

//Open Process Token using PID, pass 0 as PID for self token
func OpenProcessToken(pid int, tokenType tokenType) (*Token, error) {
var (
t windows.Token
duplicatedToken windows.Token
procHandle windows.Handle
err error
)

if pid == 0 {
procHandle = windows.CurrentProcess()
} else {
procHandle, err = windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION, false, uint32(pid))
}
if err != nil {
return nil, err
}

if err = windows.OpenProcessToken(procHandle, windows.TOKEN_ALL_ACCESS, &t); err != nil {
return nil, err
}

defer windows.CloseHandle(windows.Handle(t))

switch tokenType {
case TokenPrimary:
if err := windows.DuplicateTokenEx(t, windows.MAXIMUM_ALLOWED, nil, windows.SecurityDelegation, windows.TokenPrimary, &duplicatedToken); err != nil {
return nil, fmt.Errorf("error while DuplicateTokenEx: %w", err)
}
case TokenImpersonation:
if err := windows.DuplicateTokenEx(t, windows.MAXIMUM_ALLOWED, nil, windows.SecurityImpersonation, windows.TokenImpersonation, &duplicatedToken); err != nil {
return nil, fmt.Errorf("error while DuplicateTokenEx: %w", err)
}

case TokenLinked:
if err := windows.DuplicateTokenEx(t, windows.MAXIMUM_ALLOWED, nil, windows.SecurityDelegation, windows.TokenPrimary, &duplicatedToken); err != nil {
return nil, fmt.Errorf("error while DuplicateTokenEx: %w", err)
}
dt, err := duplicatedToken.GetLinkedToken()
windows.CloseHandle(windows.Handle(duplicatedToken))
if err != nil {
return nil, fmt.Errorf("error while getting LinkedToken: %w", err)
}
duplicatedToken = dt
}

return &Token{token: duplicatedToken, typ: tokenType}, nil
}

// Get the Interactive token associated with current logged in user
func GetInteractiveToken(tokenType tokenType) (*Token, error) {

switch tokenType {
case TokenPrimary, TokenImpersonation, TokenLinked:
default:
return nil, ErrOnlyPrimaryImpersonationTokenAllowed
}

var (
sessionPointer uintptr
sessionCount uint32
interactiveToken windows.Token
duplicatedToken windows.Token
sessionID uint32
)

err := windows.WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, (**windows.WTS_SESSION_INFO)(unsafe.Pointer(&sessionPointer)), &sessionCount)
if err != nil {
return nil, fmt.Errorf("error while enumerating sessions: %v", err)
}
defer windows.WTSFreeMemory(sessionPointer)

sessions := make([]*windows.WTS_SESSION_INFO, sessionCount)
size := unsafe.Sizeof(windows.WTS_SESSION_INFO{})

for i := range sessions {
sessions[i] = (*windows.WTS_SESSION_INFO)(unsafe.Pointer(sessionPointer + (size * uintptr(i))))
}

for i := range sessions {
if sessions[i].State == windows.WTSActive {
sessionID = sessions[i].SessionID
break
}
}
if sessionID == 0 {
return nil, ErrNoActiveSession
}

if err := windows.WTSQueryUserToken(sessionID, &interactiveToken); err != nil {
return nil, fmt.Errorf("error while WTSQueryUserToken: %w", err)
}

defer windows.CloseHandle(windows.Handle(interactiveToken))

switch tokenType {
case TokenPrimary:
if err := windows.DuplicateTokenEx(interactiveToken, windows.MAXIMUM_ALLOWED, nil, windows.SecurityDelegation, windows.TokenPrimary, &duplicatedToken); err != nil {
return nil, fmt.Errorf("error while DuplicateTokenEx: %w", err)
}
case TokenImpersonation:
if err := windows.DuplicateTokenEx(interactiveToken, windows.MAXIMUM_ALLOWED, nil, windows.SecurityImpersonation, windows.TokenImpersonation, &duplicatedToken); err != nil {
return nil, fmt.Errorf("error while DuplicateTokenEx: %w", err)
}
case TokenLinked:
if err := windows.DuplicateTokenEx(interactiveToken, windows.MAXIMUM_ALLOWED, nil, windows.SecurityDelegation, windows.TokenPrimary, &duplicatedToken); err != nil {
return nil, fmt.Errorf("error while DuplicateTokenEx: %w", err)
}
dt, err := duplicatedToken.GetLinkedToken()
windows.CloseHandle(windows.Handle(duplicatedToken))
if err != nil {
return nil, fmt.Errorf("error while getting LinkedToken: %w", err)
}
duplicatedToken = dt
}

if windows.Handle(duplicatedToken) == windows.InvalidHandle {
return nil, ErrInvalidDuplicatedToken
}

return &Token{typ: tokenType, token: duplicatedToken}, nil
}
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/fourcorelabs/wintoken

go 1.16

require golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
11 changes: 11 additions & 0 deletions vars.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package wintoken

import "fmt"

var (
ErrNoActiveSession error = fmt.Errorf("no active session found")
ErrInvalidDuplicatedToken error = fmt.Errorf("invalid duplicated token")
ErrOnlyPrimaryImpersonationTokenAllowed error = fmt.Errorf("only primary or impersonation token types allowed")
ErrNoPrivilegesSpecified error = fmt.Errorf("no privileges specified")
ErrTokenClosed error = fmt.Errorf("token has been closed")
)
Loading

0 comments on commit a1ad6b1

Please sign in to comment.