Many thanks to the moderator who pointed out errors and suggested the
correct information on this post. Over half the ideas are due to him. ;)
Don't forget this is about writing your own daemon or TCP program. Some
of the ideas here will not happen with today's software that has been
hardened. It is presented to prevent your software from being taken
advantage of.
Most often, hackers get in through several simple ways:
1. Programs put data in local variables in functions. These variables
are on the computer stack. Feeding data to those variables (usually
string variables) causes the stack to be corrupt. Most often the
perpetrator will put code into the string buffer and put the return
address of the code in the string buffer into the correct location in the
stack. When the program exits, it does a return from subroutine call into
the code the perp placed there. Instant access as the user that runs the
daemon, running any code the perpetrator puts there.
Usually that code runs one of the shell programs, with the stdin/stdout
connected through the current tcp connection, or starting up its own tcp
connection.
In one case I saw, a perp deleted /etc/hosts.deny, ran adduser to create
user rewt, then telnetted into the system. /etc/hosts.deny is now "chattr
-i". What stopped the perp in that case was that /etc/skel/.bashrc had an
exit at the end of the script. He was immediately logged out and went
away. He was using the buffer overflow in named 4.9.6 to do it.
In the past, I have had a situation where a shell was running the
program, and the executable did a GPF. The shell was left standing
with a prompt, as the root user. In this case, the perp used the
open communication link to run commands as root. Don't run your
executable this way. This may not work today, but since you are rolling
your own, don't let it happen. Don't execute from a shell. Or if you
do,
then run the shell with a command that makes it bail out immediately after
the program it runs finishes "sh -c" (thanks to REW).
2. Daemons can sometimes be tricked into copying or sending the wrong
file to the wrong location if that is part of their normal function.
Sometimes through the same sort of overflow process. As was pointed out
to me, many times daemon writers are sloppy and don't check permissions
good enough. If a perp can create a symlink to a file that a daemon is
about to delete (such as in a tmp directory), he may get /etc/hosts.deny
deleted through the daemon not checking.
A race condition can exist that can cause the daemon to copy or send a
file to the wrong location. A race condition is called that due to the
winner being the first one to win the "race." Basically, if your
program
checks permissions and then decides to do something with the information
it gathered, then does it, it will be vunerable to a race.
The reason is that on Linux, the basic time slice is 10 milliseconds, or
100 time per second. If a program checks permissions, makes a decision,
then executes the decision, it can be swapped out (cease execution) just
before the decision is executed. At that point you can change a file
the program was getting ready to deal with, and the program can be tricked
into doing what you want. I quote verbatim from the moderator:
"--------------------------
In other cases, a deamon may check permissions for itself and then
decide to do something. This is insecure, because the permission could
change inbetween.
xterm used to have something like:
if (user_is_allowed_to_write (logfile)) {
open_the_logfile_for_writing (logfile);
chown (my_user, logfile);
}
when you asked it to log the session to a file.
If I know the filename and have a program do:
ln -s /etc/passwd logfile2
while (1)
mv logfile2 logfile
mv logfile logfile2
done
then there is a chance where the xterm will simply find no file and
xterm will simply create it. There also a chance that xterm will find
that it is ok to open the file (it doesn't exist), but then end up
writing to /etc/passwd and chowning it to the user running
xterm. Bingo. These are called "races".
The implementation of various things on older Unices makes these even
more likely to succeed than they appear at first. Linux is not one
of those "older Unices".
---------------------------------------------"
To handle this, you must put in a lot of thought. Generally, a file
operation is a serial resource that is being used in a multitasking
environment. The traditional ways to deal with serial resources are 1)
use gatekeepers to enforce serial access to a serial resource, and 2) lock
the system down so that you own all of the cpu cycles until completion of
your operation on a serial resource, or 3) don't use the serial resource
at the multitasking level.
In a multitasking system with gatekeepers, you can close the gate,
operate on the resource, and then open the gate. This is usually done
with flags that are tested and set (with an indivisible instruction that
operates on the test and set without being interrupted) and then cleared
when finished. Linux provides semaphores to give you this ability.
[REW: However, this doesn't help if you're up against a malicious user
who is free not to honour your lock]
To lock down cpu cycles in a multitasking system, generally, you disable
interrupts. No interrupts means you don't get scheduled out. No
interrupts also means disk operations hang, TCP/IP operations hang, etc.
But for short, critical pieces of code, this is acceptable.
To not use serial resources at the multitasking level, push them into
device drivers which run atomically. They are not interrupted (if they
disable interrupts) and can finish dealing with one request at a time.
At the user level, however, the best thing to do is be very careful
with your files. If possible create the new file and fill it up
before deleting the old ones. Try not to change ownership of any
files that have been moved. Try not to move files.
[REW: Most importantly, open the file, so that you have a file
descriptor to the file, and operate on that. You can fchown it to
correct the ownership and fstat it to see if your "normal" users
would've been allowed to access it.]
You can also create a crc of your input file(s) while you can trust
them. Move your input files(s). Verify the crc still is the same on the
resulting output files. If it isn't, then you can panic. Panic may take
the form of creating a safe configuration file, or writing to the terminal
console and locking the machine down by disabling interrupts, or simply
stop doing what you would normally do. Of course, this would lead to a
Denial Of Service.
3. TCP/ip programs can be given packets they think is from a trusted
source, and send the reply to the same location. The program on the
other end never gets the reply, but anticipates the next correct tcp packet
and feeds it to the program. This is called spoofing.
This allows an untrusted machine to cause the tcp/ip program to carry
out its function as if it came from the trusted machine. You may get
files copied to you or deleted or modified.
The moderator came up with a good way of using this:
"----
rsh -l root somehost "echo bla::0:0:hacker:/:/bin/sh
>>/etc/passwd"
or whatever is needed.
------"
Also pointed out to me was the fact that the spoofed machine will reply
back "I don't know about this connection" in many cases. This
will stop
[...] [mod: the kernel] from continuing with the dialog. [...]
Many times a spoofer will step in during the middle of an exchanges,
where authentication has already occurred and the "connection" is set
up.
You need to make sure your connection sequence follows good rules of
engagement that can be hard to ascertain from the outside. Don't allow
your daemon to backtrack, if backtracking can cause undesirable effects.
The connection should be terminated if any of the desired steps are
skipped or re-run. The entire connection sequence can the be restarted
from the beginning, starting with authentication. Of course, this may not
be what you need to do with your daemon. One shoe does not fit all.
4. Denial of Service (DOS) attacks can be generated by sending the
wrong kind of data or a large amount of data to a TCP/IP daemon,
causing it to freeze on a port or zombie or become a runaway process
chewing up all cpu cycles. As the moderator pointed out, this stop
servicing of legitimate users.
The moderator comments are interleaved with > before them.
Simple Fixes:
1. Never put string variables on a stack.
>No. Although exploiting overflows isn't as easy when they are not on
>the stack, you can exploit overflowable static string variables!
2. Always monitor how big a string would be before you stick the next
byte into it to prevent string overflows.
>Right.
3. Always run a daemon in a "jail." Chroot it to a subdirectory
with no>If possible run a deamon....
subdirectories under it except exactly what it needs to perform its duty.
> Note that a program running as "root" in a jail will be able to
> break out of the "jail". This is NOT a bug in the chroot
> system. Chroot protects against nonprivilidged users acessing the
> rest of the system, not against root-users.
4. Use inetd.conf and tcp wrappers to enable only certain hosts access
to your program's output so the whole world does not get a crack at it.
5. Set up a good unguessable (random) method of authentication so that
remote programs can't easily guess the next proper move before you
program does something potentially harmful.
>>From cryptography: Assume that the adversary knows the mechanism, but
>not the passwords/keys. So: Use a password or something like that.
6. Throttle the number of possible children or the amount of data you
will accept to prevent a DOS attack.
>Right.
>This is tricky. If you limit the number of possible deamons and reject
>further attempts, you get a DOS when the bad guy manages to establish
>the max number of connections, and the deamon starts refusing legit
>connections....
>If you don't limit the number of deamons, the attacker can bring your
>system to a halt by using unlimited amounts of RAM/swap on your
>machine....
The level and type of exposure you allow yourself for a DOS attack will
be determined by the type of situation. If you _must_ run quickly and
handle many connections simultaneously, you must design your hardware and
software and live with the possibility of a DOS attack locking your
system.
If you must remain standing at all times, limit the number of daemons and
their duration. Put automatic reject mechanisms on information coming
from the same ip faster than a certain rate.
For example, to limit spam, some system admins will disable the route
from their machine to ip addresses that are connecting to Sendmail when
those addresses start delivering over a certain number of messages per
second. This technique can be used for your own daemon.
wade