Secure C programming
Page 1 of 2 12 LastLast
Results 1 to 10 of 11

Thread: Secure C programming

  1. #1
    Junior Member
    Join Date
    Dec 2001

    Secure C programming

    slashdot obligates me to begin with a resounding:

    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:

    void function() {
      char buffer[20];
    our stack would look like this:

    |      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
    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
      cin >> buffer;
    Most people are unaware, but there is actually an optional third argument
    to cin.get(). Using this:
      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
      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).


    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.

  2. #2
    Senior Member
    Join Date
    Oct 2001
    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

  3. #3
    Fastest Thing Alive s0nIc's Avatar
    Join Date
    Sep 2001
    lol about time.. good post ee.. ill print this **** out and keep it as reference..

  4. #4
    AO Antique pwaring's Avatar
    Join Date
    Aug 2001
    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.
    Paul Waring - Web site design and development.

  5. #5
    Senior Member
    Join Date
    Sep 2001
    Hello There Evil!

    Good Post there!!!! Keep-up the good work

    -------- Arazel--------------

  6. #6
    Senior Member
    Join Date
    Oct 2001
    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!


  7. #7
    Senior Member
    Join Date
    Oct 2001
    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.

  8. #8
    Senior Member
    Join Date
    Nov 2001
    Oh cool! Wait a minute, I hope this posting doesn't cut into our quality IRC bonding time

  9. #9
    Senior Member
    Join Date
    Apr 2002
    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

  10. #10
    Senior Member
    Join Date
    Dec 2001
    holy post batman!!!

    killer tut, great detail in a compact and short tut.
    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