Windows Local Shellcode Injection WLSI
Windows Local Shellcode Injection WLSI
Windows Local
Shellcode Injection
Author:
Cesar Cerrudo
(cesar>.at.<argeniss>.dot.<com)
Argeniss – Information Security
Abstract:
This paper describes a new technique to create 100% reliable local exploits for Windows
operating systems, the technique uses some Windows operating systems design weaknesses
that allow low privileged processes to insert data on almost any Windows processes no matter
if they are running under high privileges. We all know that local exploitation is much easier
than remote exploitation but it has some difficulties. After a brief introduction and a description
of the technique, a couple of samples will be provided so the reader will be able to write
his/her own exploits.
Introduction:
When writing a local Windows exploit you can face many problems:
-Different return addresses:
-Because different Windows versions.
-Because different Windows service pack level.
-Because different Windows languages.
-Limited space for shellcode.
-Null byte restrictions.
-Character set restrictions.
-Buffer overflows/exploits protections.
-Etc.
To bypass those restrictions an exploit has to use many different return addresses and/or
techniques. After you finish reading this paper you won't have to worry any more about that
because it will be very easy to write a 100% reliable exploit that will work on any Windows
version, service pack level, language, etc. and could bypass buffer overflows/exploits
protections since the code won't be executed from the stack nor the heap and it won't use a
fixed return address.
This technique relies in the use of Windows LPC (Local/Lightweight Procedure Call), this is an
inter-process communication mechanism, RPC (Remote Procedure Call) uses LPC as a transport
for local communications. LPC allow processes to communicate by "messages" using LPC ports.
LPC is not well documented and here won't be detailed but you can learn more at the links
listed on references section. LPC ports are Windows objects, servers (processes) can create
named LPC ports to which clients (processes) can connect by referencing their names. You can
see processes LPC ports using Process Explorer from www.sysinternals.com, by selecting a
process in the upper panel and then looking at the lower panel at the Type column, they are
identified by the word Port, you can see the port name, handle and by double clicking you can
see additional information like permissions, etc.
LPC is heavily used by Windows internals, also by OLE/COM, etc. this means that almost every
Windows process has a LPC port. LPC ports can be protected by ACLs so sometimes a
connection can not be established if the client process doesn't have proper permissions.
To use this technique we will need to use a couple of APIs that will be detailed below.
In order to establish a connection to a LPC port the next native API NtConnectPort from
Ntdll.dll is used.
-2- www.argeniss.com
Argeniss – Information Security
NtConnectPort(
OUT PHANDLE ClientPortHandle,
IN PUNICODE_STRING ServerPortName,
IN PSECURITY_QUALITY_OF_SERVICE SecurityQos,
IN OUT PLPCSECTIONINFO ClientSharedMemory OPTIONAL,
OUT PLPCSECTIONMAPINFO ServerSharedMemory OPTIONAL,
OUT PULONG MaximumMessageLength OPTIONAL,
IN OUT PVOID ConnectionInfo OPTIONAL,
IN OUT PULONG ConnectionInfoLength OPTIONAL );
There are others LPC APIs but they won't be detailed here because they won't be used by this
technique, if you want to learn more look at the references section.
to fill this structure a shared section (see [1] for more info on shared sections) has to be
created, this shared section will be mapped on both processes (the one which we are
connecting from and the target process we are connecting to) after a successful connection.
-3- www.argeniss.com
Argeniss – Information Security
SECURITY_QUALITY_OF_SERVICE structure can have any value, we don't have to worry about
it:
for ConnectionInfo data we can use a buffer with 100 null elements, ConnectionInfoLength
should have the length of the buffer.
For using this technique before a connection to a LPC port is established we need to create a
shared section. To create a shared section the next native API NtCreateSection from Ntdll.dll is
used.
NtCreateSection(
The technique:
Now that we know the APIs needed to establish a LPC connection let's see how this technique
works. As I said most Windows processes have LPC ports to which we can connect (if we have
proper permissions), as you have seen on the NtConnectPort API parameters we can supply a
shared section on one of the structures, this shared section will be mapped on both processes
that are part of the communication, this is really good news, why this is so good? because it
means that “all” the stuff we put on our process shared section will be instantly mapped on the
-4- www.argeniss.com
Argeniss – Information Security
other process, this is really crazy we can inject any data (shellcode of course!!!) on any
process we want no matter if the process is running with higher privileges than our process!.
What is more amazing is that the address where the shared section is mapped at the target
process will be returned by the function!!!, if you don't know yet why this is so good you
should go and learn how to write exploits before continuing reading this :). Basically when
exploiting a vulnerability using LPC we will be able to put shellcode on target process and we
will know exactly were the shellcode is located, so we only have to make the vulnerable
process to jump to that address and voila!, that's all.
For instance if you want to put code on smss.exe process you have to create a shared section,
connect to \DbgSsApiPort LPC port, then put the code on the shared section and that code will
be instantly mapped on smss.exe address space, or maybe you want to put code on
services.exe process, do the same as described before but connecting to \RPC
Control\DNSResolver LPC port.
Building an exploit:
Let's see a simple sample exploit for a fictitious vulnerability on service XYZ where
VulnerableFunction() takes a Unicode string buffer and sends it to XYZ service where the buffer
length is not properly validated. While this sample is based on a buffer overflow vulnerability
this technique is not limited to this kind of bugs, it can be used on any kind of vulnerabilities
as you can see on the exploits available with this paper (see Sample Exploits).
The next code creates a committed shared memory section of 0x10000 bytes with all access
-5- www.argeniss.com
Argeniss – Information Security
(read, write, execute, etc.) and with read and write page attributes:
-----Code begins------
HANDLE hSection=0;
LARGE_INTEGER SecSize;
SecSize.LowPart=0x10000;
SecSize.HighPart=0x0;
if(NtCreateSection(&hSection,SECTION_ALL_ACCESS,NULL,&SecSize,
PAGE_READWRITE,SEC_COMMIT ,NULL))
printf(“Could not create shared section. \n”);
-----Code ends------
The following code connects to a LPC Port named LPCPortName, passing the handle and size of
a previously created shared section, this section will be mapped on both processes
participating on the connection after a successful connection:
-----Code begins------
HANDLE hPort;
LPCSECTIONINFO sectionInfo;
LPCSECTIONMAPINFO mapInfo;
DWORD Size = sizeof(ConnectDataBuffer);
UNICODE_STRING uStr;
WCHAR * uString=L"\\LPCPortName";
DWORD maxSize;
SECURITY_QUALITY_OF_SERVICE qos;
byte ConnectDataBuffer[0x100];
for (i=0;i<0x100;i++)
ConnectDataBuffer[i]=0x0;
memset(§ionInfo, 0, sizeof(sectionInfo));
memset(&mapInfo, 0, sizeof(mapInfo));
sectionInfo.Length = 0x18;
sectionInfo.SectionHandle =hSection;
sectionInfo.SectionSize = 0x10000;
mapInfo.Length = 0x0C;
uStr.Length = wcslen(uString)*2;
uStr.MaximumLength = wcslen(uString)*2+2;
uStr.Buffer =uString;
-----Code ends------
After a successful connection pointers to the beginning of the mapped shared section on client
process and the server process is returned on sectionInfo.ClientBaseAddress and
sectionInfo.ServerBaseAddress respectively.
-6- www.argeniss.com
Argeniss – Information Security
The next code copies the shellcode to the client mapped shared section:
-----Code begins------
_asm {
pushad
jmp Done
Shellcode:
End:
Done:
popad
}
-----Code ends------
The next code triggers the vulnerability making vulnerable process jump to the server mapped
shared section:
-----Code begins------
_asm{
pushad
popad
}
-----Code ends------
-7- www.argeniss.com
Argeniss – Information Security
2. A few LPC ports have strong ACL and won't let us to connect unless we have enough
permissions.
3. Some LPC ports need some specific data to be passed on ConnectionInfo parameter in order
to let us establish a connection.
To solve problem #1 we have 2 alternatives, the first one is to reverse engineering how LPC
port names are resolved but this is very time consuming and I'm very lazy :) so we have the
second alternative (the easy one ;)) which is to hook certain function to get the port name.
When working with automation (OLE/COM) before connecting to the port the client process
resolves the name of target server LPC port by some black magic, this is all done automatically
by COM/OLE functionality, reverse engineering all this seems complicated, but what we can do
is to hook the NtConnectPort API so we can get the target port name when the function tries to
connect to the port. This method can be seen on one of the exploits available with this paper
(see Sample Exploits).
Problem #2 seems impossible to solve, did I say impossible, sorry that word doesn't exist on
hacker dictionary :), right now it seems it can't be solved but LPC is so obscure and I have
seen some weird things on LPC that I'm not 100% sure. It's possible to connect indirectly to an
LPC port “bypassing” permissions but it seems difficult to have a shared section created, I
should go deep on this when I have some free time :).
Problem #3 can be easily solved by reverse engineering how the connection to the problematic
port is established. Just debug, set a breakpoint on NtConnectPort API and look at parameters
values and then try to use the same values on the exploit.
Sample Exploits:
To see this technique in action take a look at the exploits available with this paper:
• SSExploit2
• MS05-012 - COM Structured Storage Vulnerability - CAN-2005-0047
• TapiExploit
• MS05-040 - Telephony Service Vulnerability – CAN-2005-0058
Conclusion:
As you have seen it is very easy to build almost 100% reliable (I'm saying “almost” because
not all vulnerabilities are easy to exploit and a few are complex to exploit in a reliable way)
exploits by using this technique, building a simple local stack overflow multi language and
service pack independent exploit will take you no more than 5-10 minutes, at least that was
what it took me to build the local TAPI (MS05-040) exploit :)
Spam:
Looking for 0days, check out Argeniss Ultimate 0day Exploits Pack (to be
released soon)
http://www.argeniss.com/products.html
-8- www.argeniss.com
Argeniss – Information Security
References:
-9- www.argeniss.com
Argeniss – Information Security
About Argeniss
Contact us
Corrientes 240
Parana, Entre Rios
Argentina
E-mail: info>.at.<argeniss>.dot.<com
Tel: +54-343-4231076
Fax: 1-801-4545614
-10- www.argeniss.com