Date: 10 Feb 1997 07:49:14 -0000 From: tqbf@enteract.com To: roberto@keltia.freenix.fr, freebsd-security@freebsd.org Subject: Re: buffer overruns Message-ID: <19970210074914.22012.qmail@char-star.rdist.org> In-Reply-To: <19970209231433.QS19404@keltia.freenix.fr>
next in thread | previous in thread | raw e-mail | index | archive | help
In article <19970209231433.QS19404@keltia.freenix.fr>, you wrote: >If you put enough characters in $SOME_VARIABLE, you could trash, the return >address that is usually just before the automatic variables... Usually? =) You're overwriting (everyone seems to have a different term for this) your call-stack-frame/procedure-activation-record, which is a structure describing the routine's caller's state. One of the bits of information in that state is the address to return to after the procedure ends. What's a situation in which the information from the stack frame is not adjacent to the procedure's automatic variables? >So if you able to calculate how many bytes the automatic variables will >take and put enough data in tmp to overrun all variables *and* the return >address, you'll be able to change that return address, making it pointing This is much simpler than you're making it sound. I don't need to calculate anything, and, if I take the time to calculate anything, it takes a trivial amount of data to accomplish this. First, if I know roughly how large the buffer I'm overflowing is (by reading the source, or by trial and error looking for segfaults), I can ensure the stack frame is overwritten entirely with forged addresses simply by filling the end of the buffer, along with a couple hundred bytes after the buffer, with return addresses. I don't usually need to calculate where these return addresses point to. If I'm exploiting a typical stack overflow, I already know that the data I've spewed at the program is sitting somewhere in the stack segment, which starts at a specific address regardless of the program. If I make an offset from some sane base-stack-address value a configurable option in my exploit, trial and error will eventually show me a good address to jump to to take over the program. Recall now that I'm almost always jumping to some address pointing into the buffer that I've thrown at the program (this is where I usually have the most control over what's in the memory space of the process I'm trying to take over). Obviously, if I have to jump *exactly* to the first machine instruction of my real exploit code (whatever is generating a shell for me), I've got a real challenge - it'll take thousands of attempts to find the right address without the ability to pull the program up in a debugger. However, I can maximize my acceptable margin of error by filling most of my buffer up with valid instructions. If I hit *any* of these instructions, the processor will simply execute the instruction, fetch the next one, execute it, etc, etc, until eventually I've "slid" into my real exploit code. If the buffer I'm overflowing is 2048 bytes long, I can fill it with about 2000 1 byte null-ops, then 40 bytes of real exploit code (execve()), and then enough return addresses to ensure that the stack frame is trashed. I now have a 2000-byte margin of error. Of course, if I'm in a situation with a limited amount of space in which to inject my code, I can always pull the program up in a debugger, find out *exactly* where the buffer I'm overflowing starts in memory, and engineer a more careful exploit. Often, this isn't even necessary, as I've got enough space in the stack to trample after a few procedures have been called that I can simply fill the buffer entirely with return addresses, and tack a few K worth of opcodes into the stack after the buffer. This technique (which, along with some other ingenious tricks, was first published in the Linux i386 splitvt exploit) almost *always* works without modification. In fact, because most programs don't use too much stack space, if I have a valid stack address, more often than not, I'll be able to get an exploit working without significant trial and error. crt0 worked for me, by cutting and pasting the exploit code from lpr's card() routine, on the first try. Really, the only thing that varies between programs is the number of bytes I have to pad the return addresses with, as they're often misaligned by a byte or two. These methods are documented, exceedingly simple to execute once you're familiar with them, and aren't time consuming. Stack overruns are, at this point, trivial to exploit for most people with elementary C coding ability. >The easiest way to close all this bugs is to make the stack non executable >(from a processor standpoint) but I'm not sure you can do it in Intel >processors. You'll have to make every other region of memory that a calling process could potentially control non-executable as well. It's a gross assumption to say that I, being the caller of any given program, only have influence over the contents of that program's stack. -- ---------------- Thomas Ptacek at EnterAct, L.L.C., Chicago, IL [tqbf@enteract.com] ---------------- exit(main(kfp->kargc, argv, environ));
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?19970210074914.22012.qmail>