|
Page 5 of 5
5b. Further tips on writing exploits
There
are many programs that are tough to exploit, but nonetheless
vulnerable. However, there are many tricks you can do to get behind
filtering and such. Also other overflow techniques do not
necessarily include changing the return address at all or only the
return address. There are so-called pointer overflows, where a
pointer that a function allocates can be overwritten by an
overflow, altering the programs execution flow (an example is the
RoTShB bind 4.9 exploit), and exploits where the return address
points to the shells environment pointer, where the shellcode is
located instead of being on the stack (this defeats very small
buffers, and Non-executable stack patches, and can fool some
security programs, though it can only be performed locally).
Another
important subject for the skilled shellcode author is radically
self-modifying code, which initially only consists of printable,
non-white upper case characters, and then modifies itself to put
functional shellcode on the stack which it executes, etc. You
should never, ever have any binary zeroes in your shell code,
because it will most possibly not work if it contains any. However,
discussing how to sublimate certain assembler commands with others
would go beyond the scope of this paper. We also suggest reading
the other great overflow howto's out there, written by aleph1,
Taeoh Oh and mudge.
6.
Conclusions
We have
learned, that once an overflow is present which is user dependent,
it can be exploited about 90% of the time, even though exploiting
some situations is difficult and takes some skill. Why is it
important to write exploits? Because ignorance is omniscient in the
software industry. There have already been reports of
vulnerabilities due to buffer overflows in software, though the
software has not been updated, or the majority of users did not
update, because the vulnerability was hard to exploit and nobody
believed it created a security risk. Then, an exploit actually
comes out, proves, and practically enables a program to be
exploitable, and there is usually a big (necessary) hurry to update
it.
As for
the programmer (you), it is a hard task to write secure programs,
but it should be taken very serious. This is an especially large
concern when writing servers, any type of security programs, or
programs that are suid root, or designed to be run by root, any
special accounts, or the system itself. Apply bounds checking
(strn*, sn*, functions instead of sprintf etc.), prefer allocating
buffers of a dynamic, input-dependent, size, be careful on
for/while/etc. loops that gather data and stuff it into a buffer,
and generally handle user input with very much care are the main
principles we suggested.
There
has also been made notable effort of the security industry to
prevent overflow problems with techniques like non-executable
stack, suid wrappers, guard programs that check return addresses,
bounds checking compilers, and so on. You should make use of those
techniques where possible, but do not fully rely on them. Do not
assume to be safe at all, if you run a vanilla two-year old UNIX
distribution without updates, but overflow protection or (even more
stupid) firewalling/IDS. It cannot assure security, if you continue
to use insecure programs because _all_ security programs are
_software_ and can contain vulnerabilities themselves, or at least
not be perfect. If you apply frequent updates _and_ security
measures, you can still not expect to be secure, _but_ you can
hope.
|