From 235479a38b124f82a884a57300c4b2fc44c78ada Mon Sep 17 00:00:00 2001 From: Jakob Friedl <71284620+jakobfriedl@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:44:26 +0100 Subject: [PATCH] Included user information in 'ps' command. --- README.md | 3 ++- src/agent/core/token.nim | 6 ++--- src/modules/systeminfo.nim | 47 +++++++++++++++++++++++++++++++++----- 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7f82b44..c641502 100644 --- a/README.md +++ b/README.md @@ -57,4 +57,5 @@ The following projects and people have significantly inspired and/or helped with - Existing C2's written (partially) in Nim - [NimPlant](https://github.com/chvancooten/NimPlant) - [Nimhawk](https://github.com/hdbreaker/Nimhawk) - - [grc2](https://github.com/andreiverse/grc2) \ No newline at end of file + - [grc2](https://github.com/andreiverse/grc2) + - [Nimbo-C2](https://github.com/itaymigdal/Nimbo-C2) \ No newline at end of file diff --git a/src/agent/core/token.nim b/src/agent/core/token.nim index be9c903..382438a 100644 --- a/src/agent/core/token.nim +++ b/src/agent/core/token.nim @@ -75,7 +75,7 @@ proc sidToString(apis: Apis, sid: PSID): string = discard apis.ConvertSidToStringSidA(sid, addr stringSid) return $stringSid -proc sidToName(apis: Apis, sid: PSID): string = +proc sidToName*(sid: PSID): string = var usernameSize: DWORD = 0 domainSize: DWORD = 0 @@ -139,7 +139,7 @@ proc getTokenUser(apis: Apis, hToken: HANDLE): tuple[username, sid: string] = if status != STATUS_SUCCESS: raise newException(CatchableError, status.getNtError()) - return (apis.sidToName(pUser.User.Sid), apis.sidToString(pUser.User.Sid)) + return (sidToName(pUser.User.Sid), apis.sidToString(pUser.User.Sid)) proc getTokenElevation(apis: Apis, hToken: HANDLE): bool = var @@ -178,7 +178,7 @@ proc getTokenGroups(apis: Apis, hToken: HANDLE): string = result &= fmt"Group memberships ({groupCount})" & "\n" for i, group in groups.toOpenArray(0, int(groupCount) - 1): - result &= fmt" - {apis.sidToString(group.Sid):<50} {apis.sidToName(group.Sid)}" & "\n" + result &= fmt" - {apis.sidToString(group.Sid):<50} {sidToName(group.Sid)}" & "\n" proc getTokenPrivileges(apis: Apis, hToken: HANDLE): string = var diff --git a/src/modules/systeminfo.nim b/src/modules/systeminfo.nim index 8106e66..afcc21e 100644 --- a/src/modules/systeminfo.nim +++ b/src/modules/systeminfo.nim @@ -40,6 +40,7 @@ when defined(agent): import os, strutils, strformat, tables, algorithm import ../agent/utils/io import ../agent/protocol/result + import ../agent/core/token # TODO: Add user context to process information type @@ -47,8 +48,11 @@ when defined(agent): pid: DWORD ppid: DWORD name: string + user: string children: seq[DWORD] + NtQueryInformationToken = proc(hToken: HANDLE, tokenInformationClass: TOKEN_INFORMATION_CLASS, tokenInformation: PVOID, tokenInformationLength: ULONG, returnLength: PULONG): NTSTATUS {.stdcall.} + proc executePs(ctx: AgentCtx, task: Task): TaskResult = print " [>] Listing running processes." @@ -74,13 +78,44 @@ when defined(agent): raise newException(CatchableError, GetLastError().getError) while true: + # Retrieve information about the process + var + hToken: HANDLE + hProcess: HANDLE + user: string + + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID) + if hProcess != 0: + if OpenProcessToken(hProcess, TOKEN_QUERY, addr hToken): + var + 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) + if status != STATUS_SUCCESS and status != STATUS_BUFFER_TOO_SMALL: + raise newException(CatchableError, status.getNtError()) + + pUser = cast[PTOKEN_USER](LocalAlloc(LMEM_FIXED, returnLength)) + if pUser == NULL: + raise newException(CatchableError, GetLastError().getError()) + defer: LocalFree(cast[HLOCAL](pUser)) + + status = pNtQueryInformationToken(hToken, tokenUser, cast[PVOID](pUser), returnLength, addr returnLength) + if status != STATUS_SUCCESS: + raise newException(CatchableError, status.getNtError()) + + user = sidToName(pUser.User.Sid) + var procInfo = ProcessInfo( pid: pe32.th32ProcessID, ppid: pe32.th32ParentProcessID, name: $cast[WideCString](addr pe32.szExeFile[0]), + user: user, children: @[] ) - procMap[pe32.th32ProcessID] = procInfo if Process32Next(hSnapshot, addr pe32) == FALSE: @@ -94,9 +129,9 @@ when defined(agent): processes.add(pid) # Add header row - let headers = @[protect("PID"), protect("PPID"), protect("Process")] - output &= fmt"{headers[0]:<10}{headers[1]:<10}{headers[2]:<25}" & "\n" - output &= "-".repeat(len(headers[0])).alignLeft(10) & "-".repeat(len(headers[1])).alignLeft(10) & "-".repeat(len(headers[2])).alignLeft(25) & "\n" + let headers = @[protect("PID"), protect("PPID"), protect("Process"), protect("Username")] + output &= fmt"{headers[0]:<10}{headers[1]:<10}{headers[2]:<40}{headers[3]}" & "\n" + output &= "-".repeat(len(headers[0])).alignLeft(10) & "-".repeat(len(headers[1])).alignLeft(10) & "-".repeat(len(headers[2])).alignLeft(40) & "-".repeat(len(headers[3])) & "\n" # Format and print process proc printProcess(pid: DWORD, indentSpaces: int = 0) = @@ -104,9 +139,9 @@ when defined(agent): return var process = procMap[pid] - let indent = " ".repeat(indentSpaces) + let processName = " ".repeat(indentSpaces) & process.name - output &= fmt"{process.pid:<10}{process.ppid:<10}{indent}{process.name:<25}" & "\n" + output &= fmt"{process.pid:<10}{process.ppid:<10}{processName:<40}{process.user}" & "\n" # Recursively print child processes with indentation process.children.sort()