Skip to content

Commit

Permalink
Add detailed usage
Browse files Browse the repository at this point in the history
  • Loading branch information
llccd committed Jul 20, 2021
1 parent 576ceb2 commit e1b5991
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 38 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.obj
*.exe
57 changes: 49 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,58 @@ WinSU allows you to run program as any user. It uses undocumented ntdll api `ZwC

## Usage

If you start WinSU without any commandline options, it will execute `%SYSTEMROOT%\System32\cmd.exe` as user `NT AUTHORITY\SYSTEM`, with `NT SERVICE\TrustedInstaller` added to groups, and all privileges enabled.

## Compile
```text
winsu.exe [-acl SDDL] [-d desktop] [-p priv_present] [-P priv_enabled]
[-s session_id] [-nw] [-c | -C] [-m integrity] [-M mandatory_policy]
[-g count sid_group1 sid_group2 ...] [user_sid] [-- cmdline]
-acl SDDL
Changes default DACL of the process.
-d desktop
Create process in specified desktop, default "WinSta0\Default".
-p priv_present
The privileges hold by the process, in bitmap form.
Default is 0xFFFFFFFFE (all avaliable privileges).
-P priv_enabled
The effective privileges hold by the process, in bitmap form.
Default is 0xFFFFFFFFE (all avaliable privileges).
-s session_id
Create process in specified session, default to
the session of current process (winsu.exe).
-nw
Do not wait for process to end.
-c
Create new console.
-C
Do not create console window.
-m integrity
Changes integrity level. One of:
UT: Untrusted
LW: Low
ME: Medium
MP: Medium plus
HI: High
SI: System
-M mandatory_policy
Whteher integrity level is enforced. One of:
0: TOKEN_MANDATORY_POLICY_OFF
1: TOKEN_MANDATORY_POLICY_NO_WRITE_UP
2: TOKEN_MANDATORY_POLICY_NEW_PROCESS_MIN
3: TOKEN_MANDATORY_POLICY_VALID_MASK
Consult docs.microsoft.com for more details, default is 0.
-g count sid_group1 sid_group2 ...
Add additional groups to the token.
user_sid
User SID of the token, default "S-1-5-18".
-- cmdline
Command line to execute, default "%ComSpec% /K".
```

with C runtime
If you start WinSU without any commandline options, it will execute `%ComSpec%` as user `NT AUTHORITY\SYSTEM`, with `NT SERVICE\TrustedInstaller` added to groups, and all privileges enabled.

```shell
cl winsu.cpp /GS- /Gy /GL /O2 /link /OPT:REF
```
## Compile

without C runtime (smaller binary size)
Use MSVC compilers

```shell
cl winsu.cpp /GS- /Gy /GL /O2 /link /ENTRY:main /OPT:REF
Expand Down
121 changes: 91 additions & 30 deletions winsu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ typedef wchar_t* (NTAPI* _wcsstr)(wchar_t* wcs1, const wchar_t* wcs2);
typedef unsigned __int64 (NTAPI* _wcstoui64_ntdll)(const wchar_t* strSource, wchar_t** endptr, int base);
typedef unsigned long (NTAPI* _wcstoul)(const wchar_t* strSource, wchar_t** endptr, int base);

typedef struct _SID_2
{
UCHAR Revision;
UCHAR SubAuthorityCount;
SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
ULONG SubAuthority[2];
} SID_2;

static _wcsstr wcsstr_ntdll;
static _wcstoui64_ntdll wcstoui64_ntdll;
static _wcstoul wcstoul_ntdll;
Expand All @@ -41,9 +49,12 @@ static SID ConsoleLogon = { 1, 1, {SECURITY_LOCAL_SID_AUTHORITY}, {SECURITY_LOCA
static SID INTERACTIVE = { 1, 1, {SECURITY_NT_AUTHORITY}, {SECURITY_INTERACTIVE_RID} };
static SID AuthenticatedUsers = { 1, 1, {SECURITY_NT_AUTHORITY}, {SECURITY_AUTHENTICATED_USER_RID} };
static SID ThisOrganization = { 1, 1, {SECURITY_NT_AUTHORITY}, {SECURITY_THIS_ORGANIZATION_RID} };
static SID LocalSystem = { 1, 1, {SECURITY_NT_AUTHORITY}, {SECURITY_LOCAL_SYSTEM_RID} };
static SID LocalAccount = { 1, 1, {SECURITY_NT_AUTHORITY}, {SECURITY_LOCAL_ACCOUNT_RID} };
static SID LocalAccountAndAdmin = { 1, 1, {SECURITY_NT_AUTHORITY}, {SECURITY_LOCAL_ACCOUNT_AND_ADMIN_RID} };
static SID mandatory = { 1, 1, {SECURITY_MANDATORY_LABEL_AUTHORITY}, {SECURITY_MANDATORY_SYSTEM_RID} };
static SID_2 Administrators = { 1, 2, {SECURITY_NT_AUTHORITY}, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS} };
static SID_2 Users = { 1, 2, {SECURITY_NT_AUTHORITY}, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS} };

DWORD count_one(unsigned __int64 x)
{
Expand All @@ -56,7 +67,7 @@ DWORD count_one(unsigned __int64 x)
return static_cast<DWORD>(x);
}

PTOKEN_PRIVILEGES generate_privilege(const unsigned __int64& priv)
PTOKEN_PRIVILEGES generate_privilege(const unsigned __int64& priv, const unsigned __int64& enabled)
{
const DWORD privilege_count = count_one(priv);
const DWORD size = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[privilege_count]);
Expand All @@ -67,8 +78,10 @@ PTOKEN_PRIVILEGES generate_privilege(const unsigned __int64& priv)
{
if (priv & static_cast<unsigned __int64>(1) << i)
{
privileges->Privileges[j].Attributes = SE_PRIVILEGE_ENABLED;
if (enabled & static_cast<unsigned __int64>(1) << i)
privileges->Privileges[j].Attributes = SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT;
privileges->Privileges[j++].Luid.LowPart = i + 1;

if (j >= privilege_count) break;
}
}
Expand Down Expand Up @@ -102,9 +115,9 @@ PTOKEN_GROUPS generate_groups(const PSID& logon_sid, PSID* add_groups, const DWO
groups->Groups[7].Attributes = SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY;
groups->Groups[8].Sid = &LocalAccountAndAdmin;
groups->Groups[8].Attributes = SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY;
ConvertStringSidToSidA("S-1-5-32-545", &groups->Groups[9].Sid); //BUILTIN\Users
groups->Groups[9].Sid = static_cast<PSID>(&Users);
groups->Groups[9].Attributes = SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY;
ConvertStringSidToSidA("S-1-5-32-544", &groups->Groups[10].Sid); //BUILTIN\Administrators
groups->Groups[10].Sid = static_cast<PSID>(&Administrators);
groups->Groups[10].Attributes = SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY |
SE_GROUP_OWNER;
//NT SERVICE\TrustedInstaller
Expand All @@ -128,7 +141,7 @@ PTOKEN_GROUPS generate_groups(const PSID& logon_sid, PSID* add_groups, const DWO

void free_groups(PTOKEN_GROUPS groups)
{
for (DWORD i = 9; i < groups->GroupCount; ++i) LocalFree(groups->Groups[i].Sid);
for (DWORD i = 11; i < groups->GroupCount; ++i) LocalFree(groups->Groups[i].Sid);
LocalFree(groups);
}

Expand Down Expand Up @@ -210,7 +223,7 @@ LUID get_auth_id(const PSID uid, const DWORD session_id)

BOOL get_token_pid(DWORD& ProcessId, PHANDLE TokenHandle)
{
const auto process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, ProcessId);
const auto process = OpenProcess(MAXIMUM_ALLOWED, FALSE, ProcessId);
if (!process) return false;
const auto res = OpenProcessToken(process, MAXIMUM_ALLOWED, TokenHandle);
CloseHandle(process);
Expand All @@ -219,10 +232,10 @@ BOOL get_token_pid(DWORD& ProcessId, PHANDLE TokenHandle)

DWORD get_lsass_pid()
{
const auto scm = OpenSCManagerA(nullptr, nullptr, SC_MANAGER_CONNECT);
const auto scm = OpenSCManagerA(nullptr, nullptr, MAXIMUM_ALLOWED);
if (!scm) return -1;

const auto service = OpenServiceA(scm, "SamSs", SERVICE_QUERY_STATUS);
const auto service = OpenServiceA(scm, "SamSs", MAXIMUM_ALLOWED);
if (!service)
{
CloseServiceHandle(scm);
Expand Down Expand Up @@ -258,32 +271,56 @@ BOOL load_ntdll()
return true;
}

HANDLE create_token(const PSID& uid, const unsigned __int64& priv_present, const PSID& logon_sid, LUID authid,
LPCWSTR dacl, PSID* add_groups, DWORD add_count, const PSID& mandatory)
HANDLE create_token(const PSID& uid, const unsigned __int64& priv_present, const unsigned __int64& priv_enabled,
const PSID& logon_sid, LUID authid, LPCWSTR dacl, PSID* add_groups, DWORD add_count, const PSID& mandatory)
{
SECURITY_QUALITY_OF_SERVICE sqos = {sizeof(sqos), SecurityDelegation, SECURITY_STATIC_TRACKING, FALSE};
OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, 0, 0, 0, &sqos};
LARGE_INTEGER li = {{0xFFFFFFFF, -1}};
TOKEN_SOURCE source = { {'F', 'r', 'e', 'e', 'H', 'K', 0}, {0x99996E2F, 0x51495FA9} };
TOKEN_USER user = {{uid, 0}};
PTOKEN_GROUPS groups = generate_groups(logon_sid, add_groups, add_count, mandatory);
PTOKEN_PRIVILEGES privileges = generate_privilege(priv_present);
TOKEN_OWNER owner = {uid};

PSECURITY_DESCRIPTOR sd;
ConvertStringSecurityDescriptorToSecurityDescriptorW(dacl, SDDL_REVISION_1, &sd,
nullptr);
if(!ConvertStringSecurityDescriptorToSecurityDescriptorW(dacl, SDDL_REVISION_1, &sd, nullptr))
return INVALID_HANDLE_VALUE;

TOKEN_OWNER owner;
TOKEN_PRIMARY_GROUP primary_group;
TOKEN_DEFAULT_DACL default_dacl;
BOOL present = false, defaulted = false;
GetSecurityDescriptorOwner(sd, &owner.Owner, &defaulted);
if (!owner.Owner) owner.Owner = uid;
GetSecurityDescriptorGroup(sd, &primary_group.PrimaryGroup, &defaulted);
if (!primary_group.PrimaryGroup) primary_group.PrimaryGroup = static_cast<PSID>(&Administrators);
GetSecurityDescriptorDacl(sd, &present, &default_dacl.DefaultDacl, &defaulted);
LocalFree(sd);

if (!present) {
DWORD size = sizeof(ACL) + (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) + sizeof(LocalSystem);
size += (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) + sizeof(Administrators);
if (logon_sid) size += (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) + GetLengthSid(logon_sid);
size = (size + (sizeof(DWORD) - 1)) & 0xfffffffc;
default_dacl.DefaultDacl = (ACL*)LocalAlloc(LPTR, size);
if (!default_dacl.DefaultDacl) {
LocalFree(sd);
return INVALID_HANDLE_VALUE;
}

InitializeAcl(default_dacl.DefaultDacl, size, ACL_REVISION);
AddAccessAllowedAce(default_dacl.DefaultDacl, ACL_REVISION, GENERIC_ALL, &LocalSystem);
AddAccessAllowedAce(default_dacl.DefaultDacl, ACL_REVISION, GENERIC_ALL, static_cast<PSID>(&Administrators));
if (logon_sid)
AddAccessAllowedAce(default_dacl.DefaultDacl, ACL_REVISION, GENERIC_READ | GENERIC_EXECUTE, logon_sid);
}

TOKEN_USER user = { {uid, 0} };
PTOKEN_GROUPS groups = generate_groups(logon_sid, add_groups, add_count, mandatory);
PTOKEN_PRIVILEGES privileges = generate_privilege(priv_present, priv_enabled);

HANDLE elevated_token = INVALID_HANDLE_VALUE;
ZwCreateToken(&elevated_token, TOKEN_ALL_ACCESS, &oa, TokenPrimary, &authid, &li, &user, groups, privileges,
&owner, &primary_group, &default_dacl, &source);

if (!present) LocalFree(default_dacl.DefaultDacl);
LocalFree(sd);
LocalFree(privileges);
free_groups(groups);
return elevated_token;
Expand Down Expand Up @@ -322,7 +359,7 @@ _declspec(noreturn) void main()

auto user = L"S-1-5-18";
auto cmd = L"%ComSpec% /K";
auto dacl = L"G:BAD:(A;;GA;;;SY)(A;;GA;;;BA)";
auto dacl = L"G:BA";
STARTUPINFOW startup_info = {sizeof(STARTUPINFOW)};
startup_info.wShowWindow = SW_SHOWDEFAULT;
startup_info.dwFlags = STARTF_USESHOWWINDOW;
Expand All @@ -331,7 +368,8 @@ _declspec(noreturn) void main()
BOOL wait = true;
DWORD add_count = 0;
PSID* add_groups = nullptr;
unsigned __int64 priv_present = 0xFFFFFFFFE;
unsigned __int64 priv_present = 0xFFFFFFFFE, priv_enabled = 0xFFFFFFFFE;
TOKEN_MANDATORY_POLICY mandatory_policy = { 0 };
DWORD session_id = -1, lsass_pid = get_lsass_pid();
if (lsass_pid == -1) ExitProcess(0x102);

Expand All @@ -350,7 +388,8 @@ _declspec(noreturn) void main()
else if (!lstrcmpiW(argv[i], L"-p"))
{
if (++i >= argc) ExitProcess(0x103);
priv_present = wcstoui64_ntdll(argv[i], nullptr, 16);
if (*(argv[i - 1] + 1) == L'P') priv_enabled = wcstoui64_ntdll(argv[i], nullptr, 16);
else priv_present = wcstoui64_ntdll(argv[i], nullptr, 16);
}
else if (!lstrcmpiW(argv[i], L"-s"))
{
Expand All @@ -363,18 +402,38 @@ _declspec(noreturn) void main()
}
else if (!lstrcmpiW(argv[i], L"-c"))
{
creation_flags |= CREATE_NEW_CONSOLE;
if (*(argv[i] + 1) == L'C') creation_flags |= CREATE_NO_WINDOW;
else creation_flags |= CREATE_NEW_CONSOLE;
}
else if (!lstrcmpiW(argv[i], L"-m"))
{
if (++i >= argc) ExitProcess(0x103);
if (!lstrcmpiW(argv[i], L"UT")) mandatory.SubAuthority[0] = SECURITY_MANDATORY_UNTRUSTED_RID;
else if (!lstrcmpiW(argv[i], L"LW")) mandatory.SubAuthority[0] = SECURITY_MANDATORY_LOW_RID;
else if (!lstrcmpiW(argv[i], L"ME")) mandatory.SubAuthority[0] = SECURITY_MANDATORY_MEDIUM_RID;
else if (!lstrcmpiW(argv[i], L"MP")) mandatory.SubAuthority[0] = SECURITY_MANDATORY_MEDIUM_PLUS_RID;
else if (!lstrcmpiW(argv[i], L"HI")) mandatory.SubAuthority[0] = SECURITY_MANDATORY_HIGH_RID;
else if (!lstrcmpiW(argv[i], L"SI")) mandatory.SubAuthority[0] = SECURITY_MANDATORY_SYSTEM_RID;
else mandatory.SubAuthority[0] = wcstoul_ntdll(argv[i], nullptr, 10);
if (*(argv[i - 1] + 1) == L'M') mandatory_policy.Policy = wcstoul_ntdll(argv[i], nullptr, 10);
else switch (*argv[i]) {
case L'U':
case L'u':
mandatory.SubAuthority[0] = SECURITY_MANDATORY_UNTRUSTED_RID;
break;
case L'L':
case L'l':
mandatory.SubAuthority[0] = SECURITY_MANDATORY_LOW_RID;
break;
case L'M':
case L'm':
if (!lstrcmpiW(argv[i], L"MP")) mandatory.SubAuthority[0] = SECURITY_MANDATORY_MEDIUM_PLUS_RID;
else mandatory.SubAuthority[0] = SECURITY_MANDATORY_MEDIUM_RID;
break;
case L'H':
case L'h':
mandatory.SubAuthority[0] = SECURITY_MANDATORY_HIGH_RID;
break;
case L'S':
case L's':
mandatory.SubAuthority[0] = SECURITY_MANDATORY_SYSTEM_RID;
break;
default:
mandatory.SubAuthority[0] = wcstoul_ntdll(argv[i], nullptr, 10);
}
}
else if (!lstrcmpiW(argv[i], L"-g"))
{
Expand Down Expand Up @@ -420,12 +479,14 @@ _declspec(noreturn) void main()
if (!ret) ExitProcess(0x110);
CloseHandle(token);
enable_all_privileges(dup_token);
if (!SetThreadToken(nullptr, dup_token)) ExitProcess(0x111);
if (!(SetThreadToken(nullptr, dup_token) || ImpersonateLoggedOnUser(dup_token))) ExitProcess(0x111);
CloseHandle(dup_token);

token = create_token(uid, priv_present, logon_sid, authid, dacl, add_groups, add_count, &mandatory);
token = create_token(uid, priv_present, priv_enabled, logon_sid, authid, dacl, add_groups, add_count, &mandatory);
if (!token) ExitProcess(0x112);
LocalFree(uid);

SetTokenInformation(token, TokenMandatoryPolicy, static_cast<PVOID>(&mandatory_policy), sizeof(TOKEN_MANDATORY_POLICY));
SetTokenInformation(token, TokenSessionId, static_cast<PVOID>(&session_id), sizeof(DWORD));

PROCESS_INFORMATION process_info = {0};
Expand Down

0 comments on commit e1b5991

Please sign in to comment.