-
June 13th, 2002, 08:06 AM
#1
Secure C programming
slashdot obligates me to begin with a resounding:
fp!
Now that that's over with...
This is a short tutorial on secure programming in C. It's targetted
towards *nix, but most of these issues exist in all systems.
I've yet to see a book or tutorial for a beginning programmer
mention any of these issues, which is probably why many of them
continue to come up in software. I've separated it into 3 parts:
buffer overflows, format string vulnerabilities, and race conditions.
Buffer overflows:
Buffer overflows are really only an issue if your code will be running
as setuid. However, if you plan on releasing your code, it's possible that
somebody, somewhere, will use it setuid, so these techniques can never hurt.
To know how to prevent buffer overflows, we first need to understand
them. Most of the papers out there describe how to exploit them, not
how they work, so I'll give a little background here:
When a local variable is created, the space for it is allocated on
the stack. This is no big surprise, but the issue is that there is
other important data on the stack as well. When a function is called,
there are two (or possibly 3) registers that get pushed onto the stack.
I'll use the 16-bit x86 names here for clarity. Depending on the
size model that the program is compiled as, the first thing to be
pushed is the IP register (instruction pointer) and possibly the
CS register (code segment). Then after this, a standard entry dictates
that the BP register be saved (BP stands for base pointer - in a
function, this gets overwritten to provide access to the arguments,
which coincidentally are also on the stack). After all of this, the space
for local variables gets allocated. So, if we have a function:
Code:
void function() {
char buffer[20];
}
our stack would look like this:
Code:
|--------------|
| CS |
|--------------|
| IP |
|--------------|
| BP |
|--------------|
| buffer | ^
| (20 bytes) | | memory addresses
|--------------| | increasing
Its important to note that on an x86, the stack grows down. Of course,
other platforms do things differently. The specific size of CS and IP
depends on the processor and the model (some programs may not use CS,
and the diagram above would look the same except only have 3 blocks).
Modern x86 processors use 2 bytes for CS and 4 bytes for EIP (the 32-bit
version of the IP register). EBP (extended BP) is also a 32-bit register.
The problem with this system is that most functions that fill a buffer
work with increasing memory addresses. That is, if we look at our figure
above, they would fill the buffer from bottom to top. However, someone
could specify malicious code that would put 28 bytes into the 20-byte
buffer! If the stuff that gets written over the IP (and CS) registers
is a valid address, when the function returns the processor will start
executing whatever code is located there!
The solution to preventing buffer overflows is simple: never allow the
buffer to have excess data written into it. There are a few functions
to specifically look out for.
strcpy: This function is without fail listed in every basic C or C++ book
there is. The problem is that it will happy keep copying data from
the source string until it sees a null terminator. Instead, always use
Code:
strncpy(char *dest, const char *src, size_t n)
This will only copy n bytes from src into dest. Passing the size of
your buffer as n, you can never copy in too much data.
gets: This function (and its twin fgets) should never be used, even
when the file you are reading from is supposedly trusted. In place of
gets, use read() and fread(). These functions take in the amount of
data to read, again preventing accidental overflow.
sprintf: If you're formatting something including a user-entered string
(or argument), use snprintf(char *str, size_t size, char *format, ...)
cin: For those C++'ers out there, make sure when reading in a word that
you don't just
Most people are unaware, but there is actually an optional third argument
to cin.get(). Using this:
Code:
cin.get(buffer, length, ' '); cin.ignore(length, '\n');
will read in one word, up to a space (or newline).
These are the most common errors to cause buffer overflows, but they are
by far not the only ones. If you find yourself calling a function that will
blindly copy data until a certain condition is met, look for an alternative
that will allow you to specify a maximum number of characters to copy.
Format strings:
The way a format string exploit executes its own code is very similar to
a buffer overflow - by overwriting the saved EIP/CS values on the stack.
However, the method is different.
The issue arises because of the format atom "%n", which will write the
number of characters printed so far to a pointer designated as its argument.
This can sometimes be manipulated into writing specific characters to
a specific address. In general, using %n yourself can be avoided by using
other format characters, or strlen() and tricky use of '\t'
The real problem is when a programmer wants to print back certain user-entered
data, but decides to be lazy and uses
instead of
Code:
printf("%s", buffer);
using this, a malicious code writer can specify their own format string, get
it to actually print the address of the saved CS and IP for them, and overwrite
them with their shellcode address. Some of the more talented people have even
written programs that will do this entire process for them, making the exploit
automatic. Of course, the simple solution is to always provide your own
format string to printf (and sprintf).
Race conditions:
The last subject I'll touch on is race conditions. These can be the hardest
exploits to prevent because of how they work. The idea behind a race
condition is that the program depends on something it did before to be
the same at a later time. For example, a race condition would exist if a program wrote some
data to a file, then read that data back, expecting it to be the same.
If a malicious programmer gets there first ('races' the program), they
could introduce bad data.
The only OS-introduced race condition exists is in the functions mktemp(),
mkdtemp(), tmpnam(), and tempnam(). The issue is that there exists a
delay between when the filename is computed and when the file is opened.
mkstemp() and tmpfile() both open the file at the same time, and are
therefore a safe alternative.
Other race conditions can exist. You should be extremely careful when
using IPC (interprocess communcation) techniques like shared memory.
Try to avoid temporary files when you can (or use mkstemp).
Conclusion:
The most important thing when aiming for security is to never trust user-supplied
data. If you read in arguments, check them to make sure they are valid. If
you're reading in data, verify the length.
No medium or large program is inherently secure, and people are coming up with
new exploit techniques all the time, but by avoiding some simple pitfalls, we can
ensure that our code will not have vulnerability after vulnerability.
-
June 13th, 2002, 08:24 AM
#2
The great linux god evil_enchilada finally makes his first post and an excellent one at that. You definately deserve antipoints, too bad Im over my limit of positives.....Just kidding.
Wine maketh merry: but money answereth all things.
--Ecclesiastes 10:19
-
June 13th, 2002, 08:32 AM
#3
lol about time.. good post ee.. ill print this **** out and keep it as reference..
-
June 13th, 2002, 08:33 AM
#4
Excellent tutorial on a subject that all programmers need to take into account. Even though it's mainly about C, the concepts can be applied to most compiled languages.
-
June 13th, 2002, 09:10 AM
#5
Hello There Evil!
Good Post there!!!! Keep-up the good work
-------- Arazel--------------
-
June 13th, 2002, 01:39 PM
#6
EE, I thought you posted all the time, being in IRC a lot, etc... This comes as a supprise with this being your first post...
Great post!
-Tim_axe
-
June 13th, 2002, 02:35 PM
#7
Hehe, I knew that you'd post something sooner or later evil. Let's hope it's the first of many good posts.
OpenBSD - The proactively secure operating system.
-
June 13th, 2002, 08:37 PM
#8
Oh cool! Wait a minute, I hope this posting doesn't cut into our quality IRC bonding time
-
June 14th, 2002, 03:43 PM
#9
nice 1 m8 kick a$$ tut
By the sacred **** of the sacred psychedelic tibetan yeti ....We\'ll smoke the chinese out
The 20th century pharoes have the slaves demanding work
http://muaythaiscotland.com/
-
June 17th, 2002, 07:53 AM
#10
holy post batman!!!
killer tut, great detail in a compact and short tut.
~THEJRC~
I\'ll preach my pessimism right out loud to anyone that listens!
I\'m not afraid to be alive.... I\'m afraid to be alone.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
|