Factors in Unreliability

Countermeasures

There are many ways in which your exploit attempt can become unstable or fail to work altogether. However, there are also many ways in which you can compensate for these problems. It's important to remember that you are writing an application that should never have existed ”an exploit. Exploits exist only because of bugs in other software. Hence, creating reliable exploits is not a matter of simply using software engineering. At all times within the process, you should be constantly trying to find alternative methods of solving problems that crop up. When in doubt, think: "What would John McDonald do?" Here is an excerpt from Phrack (number 60, December 2002) which outlines his philosophy. Keep his words in mind whenever you run into trouble.

PHRACKSTAFF: You have found quite a lot of bugs in the past and developed exploit code for them. Some vulnerabilities required new creative exploitation concepts which were not known at that time. What drives you into challenging the exploitation of complicated bugs and what methods do you use?

John McDonald: Well, my motivations have definitely changed over time. I can come up with several ancillary reasons that have driven me at different times during my life, and they include both the selfish and the altruistic. But, I think it really comes down to a compulsion to figure all this stuff out. As far as methods, I try to be somewhat systematic in my approach. I budget a good portion of time for just reading through the program, trying to get a feel for its architecture and the mindset and techniques of its authors. This also seems to help prime my subconscious .

I like to start at the lower layers of a program or system and look for any kind of potential unexpected behavior that could percolate upwards. I will document each function and brainstorm any potential problems I see with it. I will occasionally take a break from documentation, and do the considerably more fun work of tracing back some of my theories to see if they pan out.

As far as writing exploits, I generally just try to reduce or eliminate the number of things that need to be guessed.

When your exploit is almost complete, but seems not to progress, become someone else. Write your exploit "Halvar"-style ” spend a great deal of time in IDA Pro examining in detail the exact location of failure and everything the program does from then on. Thrash at it madly with super-long strings. Examine what the program does when it's not dying because of your exploit. Perhaps you can find another bug that will be more reliable.

It's often useful to learn about exploitation techniques used on platforms other than those you are familiar with. Windows techniques can come in handy on Unix, and vice versa. Even when they don't come in handy, they can provide a needed inspiration for what your final exploit needs in order to be successful.

Preparation

Always be prepared. In fact, always have a stack of hard drives available with every OS in every language on them, with every service pack and patch available, and be prepared to cross reference addresses among them to determine which set of addresses works on all your targets. VMWare is a great help in this case, although VMWare and OllyDbg occasionally don't get along, which can be troublesome . Cross referencing the database of all possible addresses can cut down your brute forcing time as well.

Brute Forcing

Sometimes the best way to make your exploit robust is to exhaust the range of possible magic numbers . If you have a huge list of potential return to ebx addresses, perhaps you should simply run through them all. In any event, brute forcing is often a last resort, but it is a perfectly valid last resort.

There are, however, a few tricks that can keep you from wasting time and leaving more logs than you need to. Determine whether you can check more than one address at a time while you are brute forcing. Cache any valid results so that you can check for those first. Machines on any given network tend to all be set up in the same way, so if your technique worked once, it will probably work again.

Sending ludicrously large shellcode buffers sometimes can give you a reasonable chance of hitting your magic number correctly as well. And if possible, try to keep your magic numbers related . If you know that one address you'll need will always be near another, you will be much better off than when they are completely independent of each other.

Memory leaks can often make brute forcing much easier. Sometimes you don't even need a real memory leak in order to fill up memory with your shellcode. For example, in CANVAS's IIS ColdFusion exploit, we make 1,000 connections to the remote host, each of which sends 20,000 bytes of shellcode and NOPs. This procedure quickly fills up memory with copies of the shellcode. Finally, without disconnecting any of our other sockets, we send the heap overflow. It must guess the location of our shellcode, but it nearly always guesses correctly because most of the process's memory is filled up with it.

Filling up a process's memory is easy when the process is multithreaded as is IIS. Even when the process is not multithreaded, a memory leak can accomplish nearly the same thing. And if you can't find a memory leak, you may find a static variable that holds the last result of your query and is always in the same place. If you look at the entire program to see whether it has any operations you can manipulate to accomplish this sort of goal, you will almost always find something useful.

Local Exploits

There is no reason to have an unreliable local exploit. When you map yourself into the process space, you control nearly everything ”the memory space, signaling, what's on the disk, and the location of the current directory. Many people create more problems than they need to with local exploits; it's the sign of a beginner to have a local exploit that doesn't work every time.

For instance, when writing a simple Linux/Unix local buffer overflow, use exeve() to specify the exact environment for your target process. Now, you can calculate exactly where in memory your shellcode will be, and you can write your exploit as a return-into-libc attack without any guesswork. Personally, we like to return into strcpy () and copy our shellcode into the heap and then execute it there. We can use dlopen() and dlsym() to find strcpy() 's address during the exploit. This sort of sophistication will keep your exploits working in the wild.

As pointed out by coauthor Sinan Eren (known more widely as noir ), when attacking the kernel, you can map memory to any location needed, making it possible to set the return address to the exact place your shellcode begins, even if you can only use one character to which to return. (In other words, 0x00000000 can be a perfectly valid return address when you're writing a local kernel attack.)

OS/Application Fingerprinting

For many purposes, the fingerprints that tools like Nmap or Xprobe can provide are only one part of the picture. When you exploit an application, you need to know more than just what operating system you are targeting. You also need to know the following:

  • Architecture (x86/SPARC/other)

  • Application version

  • Application configuration

  • OS configuration (non- exec stack/PaX/etc.)

In many other cases, OS identification is completely useless, because you are being proxied from one host to the next . Or, perhaps you simply don't want to send bizarre OS identification packets to the host since that would fingerprint you to any listening network IDS. Therefore, to write reliable exploits, you must often find unique ways to fingerprint your remote host that lie within the bounds of completely normal traffic.

It's always best to be able to do your fingerprinting against the same port that you eventually will be attacking. The following example is used in CANVAS's MSRPC exploit. You can see that simply by using port 135 (the targeted service) we can finely narrow in on which OS we are targeting. First, we split XP and Windows 2003 from NT 4.0 and Windows 2003. Then we split 2003 from XP (using another function not shown here). Then we split Windows 2000 from NT 4.0. This entire function uses publicly available interfaces on port 135 (TCP), which is good because this may be the only open port. Using this technique, our exploit can narrow its targeting down to the correct platform with only a few simple connections.

 def runTest(self):         UUID2K3="1d55b526-c137-46c5-ab79-638f2a68e869"         callid=1         error,s=msrpcbind(UUID2K3,1,0,self.host,self.port,callid)                 if error==0:             errstr="Could not bind to the msrpc service for 2K3,XP -  assuming NT 4 or Win2K"             self.log(errstr)         else:             if self.testFor2003(): #Simple test not shown here.                 self.setVersion(15)                 self.log("Test indicated connection succeeded to msrpc  service.")                 self.log("Attacking using version %d:  %s"%(self.version,self.versions[self.version][0]))                 return 1                  self.setVersion(1) #default to Win2K or XP         UUID2K="000001a0-0000-0000-c000-000000000046"         #only provided by 2K and above         callid=1         error,s=msrpcbind(UUID2K,0,0,self.host,self.port,callid)                 if error==0:             errstr="Could not bind to the msrpc service for 2K and above  - assuming NT 4"             self.log(errstr)             self.setVersion(14) #NT4         else:             self.log("Test indicated connection succeeded to msrpc service.")             self.log("Attacking using version %d:  %s"%(self.version,self.versions[self.version][0]))             return 1  #Windows 2000 or XP                            callid=0         #IRemoteDispatch UUID         UUID="4d9f4ab8-7d1c-11cf-861e-0020af6e7c57"         error,s=msrpcbind(UUID,0,0,self.host,self.port,callid)         #error is reversed, sorry.         if error==0:             errstr="Could not bind to the msrpc service necessary to run  the attack"             self.log(errstr)             return 0         #we assume it's vulnerable if we can bind to it         self.log("Test indicated connection succeeded to msrpc service.")         self.log("Attacking using version %d:  %s"%(self.version,self.versions[self.version][0]))                      return 1 

Information Leaks

We are past the era in which every exploit was simply a fire-and-forget missile. These days, a good exploit writer looks for ways in which to guide his attack directly to the target. There are methods for obtaining information, often specific memory addresses, from your targets. We list some of these here:

  • Reading and interpreting the data the target sends to you. For example, MSRPC packets often contain pointers that are marshalled directly from memory. These pointers can be used to predict the memory space of your target process.

  • Using heap overflows to write into your data before it is sent back to you can tell you where in memory your buffer is.

  • Using frontlink() -style heap overflows to write the address of malloc internal variables into your data before it is sent back to you can tell you where in memory malloc's function pointers are via a simple calculation.

  • Overwriting a length field can often allow large parts of server memory to be sent to you. (Think BIND TSIG overflow.)

  • Utilizing an underflow or other similar attack can allow parts of server memory to be sent to you. FX of Phenoelit uses this method successfully with echo packets for his Cisco HTTPD exploit. His work is a stellar example of combining two exploits to produce one very reliable exploit.

Looking at timing information can be a valuable way in which to gain insight into what kinds of errors your exploit is running into. Did it send you a reset packet right away, or did it time out and then send you a reset?

Halvar Flake once said, "No good hacker just looks for one bug." An information leak can make even a difficult bug possible. Even PaX (the advanced kernel-based memory protection patch) is easily defeated with a good enough information leak.



The Shellcoder's Handbook. Discovering and Exploiting Security
Hacking Ubuntu: Serious Hacks Mods and Customizations (ExtremeTech)
ISBN: N/A
EAN: 2147483647
Year: 2003
Pages: 198
Authors: Neal Krawetz

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net