Slashdot is powered by your submissions, so send in your scoop

 



Forgot your password?
typodupeerror
×
Bug

Libsafe: Protecting Critical Elements of Stacks 114

pomac writes "Check out Libsafe for a great effort in protecting outdated systems against stack attacks or buffer overflows. I would love to see some constructive opinions on this. " You and me both.
This discussion has been archived. No new comments can be posted.

Libsafe: Protecting Critical Elements of Stacks

Comments Filter:
  • bUt w1th0uT bUff3r 0v3rf10wz, 1 c4n'7 d0 mY k-n34t-0 h4x0r tr1x! w04h 1z m3!

  • ... having the initial implementation on Linux which would not require such a library, being Open Source. I suppose for systems that need 99.999% uptime... and Linux being open source it's probably easier to work out the library calls. Excellent concept though, wish them well.
  • libsafe is basically a rehash of snarskii's libparanoia, but using shared library redirection to wrap libc instead.

    this isn't research, it's a gross hack. solar designer's work is much better.

    www.openwall.com [openwall.com]

  • OK, so they've put wrappers around calls that are known to be vulnerable. Why not:

    1) Contact the glibc people with patches for those functions (assuming the problems are patchable, which not all of them are)

    2) Check for bad args in the wrapper and log it so defective programs are easier to find.
    --
  • No.
    You and I both does not make sense in this situation, or many for that matter.

    You and me both is the correct way of saying this. Just take the comparative word out, and the "you", and you are left with: Me too.

    Gotta love the English language. :-) My grandfather is an English teacher, and he's always teaching me grammar.

    And MODEREATORS, PLEASE DO NOT MODERATE ME DOWN! I was trying to promote good English. Thanks. :-)
    Fran Frisina (franf@hhs.net)
    http://www.zero-productions.com/money
  • ...not the MickyMouse themePark B.S. 'innovation' of those other guys.

    "...the performance overhead of libsafe is negligible. "

    It doesn't slow you down much either.
  • 1. No fixing of source code
    2. Intercepts all function calls (yeah I know it says only the ones that are vulnerable. but it still has to check.)
    3. Replaces said function call with one of it's own that does the same damned thing.
    4. Will handle all existing problems PLUS any future problems that may pop up.
    5. Negligible performance overhead.

    Ya know... my grandfather used to tell me stories about travelling Snake Oil salesmen. I never thought I'd see one though...

  • Your description suggests that libsafe is for protecting *outdated* systems against *stack* overflows.

    Actually it provides wrappers around potentially dangerous system calls ( e.g. strcpy() ), so it helps protect against buffer overruns in all the (dynamically linked) software running on the system.

    IMHO, the biggest feature is, that it does not require code instrumentation or even re-linking of the software, something that other solutions do usually require.

    This means that problems can be prevented in many cases.

    All in all, a nice piece of work...

    --
    Greetings,
    Ed.
  • Receiving a DNS hick-up with the above link. These work for me: http://www.bell-labs.com/news/2000/april/20/1.html with source code here: http://www.bell-labs.com/org/11356/libsafe.html Moderator, Is this constructive?? Sig's, cant live with them, cant shoot them.
  • by Anonymous Coward
    I asked some people why this isn't integrated into the glic code and they said that this patch does things which are not compatible with every program out there so it wouldn't work good as a default. Regarding logging, this wrapper does log to syslog before terminating the program. It can even email you if so configured.
  • If this library is broken would that in itself pose a risk?

    How much lag in system time would such a library add? (on a low end system)in real terms ie: my busy little 486 that runs too many services

    Why bother using something that you cant fix the buffer overflow in?

    Why does my http:// in my sig not work. Wahhhhhh

    --sig--

    .sig = .plan = NULL;
    http://www.alladvantage.com/go.asp?refid=HVZ895
  • Option 1 is not that relevant, I think. It's not glibc itself, but the programs that use certain library functions that are defective.

    Option 2 would be *very* nice to have though...

    --
    Greetings,
    Ed.
  • by zCyl ( 14362 ) on Tuesday April 25, 2000 @06:16AM (#1111422)
    We've had a tool that protects open source programs from calling known bad calls for some time now. It's called grep. Grep your code for bad calls, find them, fix them, then don't waste 4 metric tons of overhead running all your software with a wrapper.

    The ideal solution would be to make a tiny little perl script that pattern matched code for all these known bad calls, and then printed out the file, line number, and reason why the call is known to be bad.
  • There's better solutions out there - like StackGuard. Basic theory is this - buffer overflows write over the data segment and then write into the adjacent data segment. By placing a small amount of space between the two and writing a known value to it, you can detect when a buffer overflow occurs before executing that segment. Of course, this method has it's drawbacks in the form of additional memory, alittle more latency, and trusting that the attacker doesn't know the "magic numbers" in the mystery segment.

    The best method is simply to code your programs from the ground up with security in mind to begin with - and then have alot of people audit your code. Is it foolproof? No. Then again, is anything?

    Libsafe is a nice idea.. but a simple 'grep -H "strcpy(" *' would do the same thing.

  • We had some software here at work that was suffering from overflows caused by new employees who were entering data "incorrectly"; i.e., the system wasn't idiotproofed.

    What I did was program a new interface to the system, which had buffer overflow checking. The users entered the data, the program warned them if it was too long or the wrong format, and then when everything worked we passed it to the actual system.

    A bit tricky to program in this particular case, but the idea could work very well for some systems.

  • Why not: 1) Contact the glibc people with patches for those functions (assuming the problems are patchable, which not all of them are)

    Because buffer overruns is a problem on all UNIX systems and not only Linux? :-)

    I haven't checked it, but I guess they use LD_PRELOAD to load their versions of the calls? In that case it should work on any UNIX that supports some kind of LD_PRELOAD, which makes it much more usable than if the changes were made solely to glibc.

  • by technos ( 73414 ) on Tuesday April 25, 2000 @06:24AM (#1111426) Homepage Journal
    Okay.. Let's put it this way. I have a mission critical program. We'll call it Midnight Modeller. Now, this program is the only one with the particular feature set I need. Unfortunatly, its been so long unmaintained that all I can get functioning is a binary that is known vunerable.

    I have two options; Install the wrappers, or slog through 18,000 lines of code to modernize it.

    Or another:
    I have a commercial application. Let's call it Microsoft Office 2001 for Linux. Am I to trust that in that huge, behemoth of Microsoftian bloat there isn't some exploitable code?

    I have two options; Install the wrappers, or pay the vendor BIG MONEY for a copy of the source and audit it.
  • by Anonymous Coward
    StackGuard [ogi.edu]

    and

    SecureLinux [freshmeat.net]

  • I kind of agree. Id rather see the programs fixed myself than not. However not all software is open source. And not all software will ever be open source. This provides a manner in which to protect against stack smash, and any concievable manner in which to plug a security problem is cool beans in my court. I would hope that this would not delegate to lower, less Quality Controlled software though. Thats something that tools like this can lead to. "oh I've got protection against that so I don't have to be so worried about auditing my code." Using this as additional protection is one thing. Using it as a substitute for stringent code auditing is another all together. Lets hope that won't happen.
  • I must say that this seems like a very intresting approach to deal with this issue. I'd be intrested in finding out how well it works. My only concern is will it create a level of laziness when it comes to security.

    When I'm writting code, why should I worry about such trivial issues such as buffer overflows. I mean, libsafe will cover my butt now. Also, why should I upgrade my ftp server, my machine is a fortress now.

    Security is not something I consider tangible. It doesn't come down to the system being secure or not secure, it comes down to the people who run the system being secure or not secure. Part of that is the people running the system need to stay on top of issues and resolve them. My biggest concern with this package is people getting a false sense of security with it. Having worked with people who believed that a system was unbreakable because it was using ssh, I'm very afraid about what they will believe with this package.

    That said, I do plan on checking it out, I will consider it a tool too help make my system(s) more secure.

    A very wise man once said the most secure machine is one incased in cement at the bottom of the ocean.

  • normally i wouldn't reply to a troll, but what the hell...

    the difference is that on the linux side, there are people trying to make the system better. On the Windows side, you only have Microsoft... and if they want to make it better they will, but most of the time they don't.

    Sure, Linux isn't perfect (much to the surprise to some on /.) but, it has much more potential than Windows currently does.

    So, as I read your comment, you'd rather have Windows, where the libraries are in just as bad shape as Linux (or worse), than to have Linux where at least someone is trying to address the problem with the libraries. Fine by me... I don't want to dictate your OS choice.
  • According to the man page, libsafe implements versions of several library functions (strcpy, etc.) in such a way that any data written is limited to the current stack frame. Yes, that does prevent overwriting return addresses and the like. However, it does not prevent overwriting other arguments to the same function. If the object of the attack is merely denial of service, that may well be sufficient. Data that you can access, you can corrupt, this just limits your reach somewhat.
  • Why did they GPL it, and not LGPL it? This does not seem to be very sensible. Only GPL code can use GPL. And GPLed programs have the source available, so buffer overruns can be fixed in the original source, This is a more sensible approach than hacking up libsafe. It seems that the programs which need these protection are precisely the binary only, closed-source, proprietry programs. By GPLing it, it becomes illegal to link against libsafe, so where's the benefit of that? IMHO, it should have been LPGL!
  • Sorry about my mistake! It is LPGL! I misread!
  • by Pike ( 52876 )
    You can keep the attacker from knowing the magic numbers by using a randomly generated value and writing it into the stackguard space. Then do a one way hash on it and store that in a seperate place to check the original against...also, make the size of the spacer vary slightly (randomly) to keep them from knowing exactly how much farther they have to go to cross into the stack.

    -JD
  • I note that the main install method is to use LD_PRELOAD, but this won't protect against all overflow attacks. All a cracker has to do is unset LD_PRELOAD before running the exploit.

    Using /etc/ld.so.conf (which the man page admits is dangerous) will prevent this, though.

    Secondly, it only works on certain system calls...
    --

  • Being open source does not automatically make a program bug free. Look at XMMS for instance (why on Earth does it suddenly become silent when I turn on the equalizer?). Or ext2fs (why is it I lose a partition every time I pull the plug?). Or KDE (the window manager should not be using more resources than the windows it managers...yes, that is a bug). Or GAIM (why does my computer lock up for 10 minutes or so when I shut gaim down?). Do I need to continue?
  • Of course, no reason. That is, if you don't mind that your software crashes. I don't think this is here to make buffer overflows just 'go away' silently - they just get killed as they ought to - with a syslog-message to make it easier to catch it. Also, for those who say that this is obsolete for opensource.. Well, how is it that there ARE many (compared to zero) buffer overflow-bugs? I can see no reason why this piece of software shouldn't exist. In fact, I think it's great. I expect it to get into Woody some day..
  • If you read their white paper, you will see (claimed) advantages to libsafe over stackguard. In particular, libsafe does not require source/recompilation. A second, minor benefit is that in their tests at least, libsafe was faster, taking essentially no extra time, whereas stackguard's effect was at least measurable in two of their four real-world-program benchmarks.

    Regarding the point that many are making here about grepping for strcpy: well, that is all fine and good for open source thingies. But we need to keep in mind our ultimate goal: Linux world domination! Among other things, that means:

    • Closed source games running on Linux
    • Closed source "productivity" programs, like Office 2005, running on Linux
    • Open source thingies running on Linux, used by people like my gramma, who will not run grep by themselves and not even get updated software when security problems are found.
    • More closed source games running on Linux!
    In all these cases, it is unrealistic to expect to be able to grep or recompile when needed.

  • I have a commercial application. Let's call it Microsoft Office 2001 for Linux. Am I to trust that in that huge, behemoth of Microsoftian bloat there isn't some exploitable code?

    Why are you running it is root? :)
  • by Blue Lang ( 13117 ) on Tuesday April 25, 2000 @06:55AM (#1111440) Homepage
    Well, the docs say the impact is negligible, so I decided to give it a shot.

    Box is a celery, 450mhz, 128MB RAM, intel i810 chipset, kernel is 2.2.12-20 (stock RH 6.1)

    Apache is 1.3.12, freshly compiled just for this benchmark.

    Without:

    Running 15,000 requests over three iterations:

    1: 373 req/sec - 677 kb/sec
    2: 374 req/sec - 675 kb/sec
    3: 375 req/sec - 679 kb/sec

    With libsafe:

    1: 334 req/sec - 605 kb/sec
    2: 343 req/sec - 621 kb/sec
    3: 355 req/sec - 642 kb/sec

    Benchmark command line was:

    ./ab -n5000 -c100 -k http://cobalt/index.html

    So, not bad, it it does what it says it does. The box was loaded to the gills, so every little extra makes a difference.

    I'll probly be putting this to at least some serious testing in our production environment.

    --
    blue
  • libsafe could only be effective if it wasn't a statically-linked executable. Otherwise, you'd need to modify the kernel itself.
  • Ok. I learned C in college. But in the real world I only write Perl and Java. So, if/when I want to write something in C, how do I know which system calls are bad? I was never taught that. Where would one go to find such information.
  • Linux may be a work in progress, but at least its progressing. Windows has been stagnating for a long time. I mean, Microsoft is the only party--at least til the penalty phase is complete--that can modify and check its source for bugs and vulnerablities. Linux on the other hand has a small army of people making it better with each revision. All that seems to happen when I upgrade windows is that it takes up more drive space and reserves more memory for itself.

  • I don't think that that was quite his point. I think he was referring to the fact that one could implement the same features *directly into* the source instead of creating a seperate library.
  • Ya know... my grandfather used to tell me stories about travelling Snake Oil salesmen. I never thought I'd see one though...

    Aww c'mon! Apart from point 4:

    4. Will handle all existing problems PLUS any future problems that may pop up.

    the claims are plausible.

  • On Digital UNIX I believe it's possible to make the stack non-executable when a process is running as root.

    Can this be done in Linux?

    -E
  • Uh, why would it have to run as root to have an exploit? Being able to run arbitrary code as a priviledged user (as opposed to unpriviledged, like "nobody") gives much greater access to the system than many network services, also it is often trivial to elevate your privileges once you have shell access to a system (rootkits anyone?). And how devistating would it be to have your home directory blown away, sure the "system" is ok but your data has gone to heaven. Systems can be reinstalled from CD very easily, data cannot.

    I am playing with libsafe right now and haven't as yet suffered any adverse effects. It is a good stopgap measure until libc is fixed and/or the development environment is fixed because asking every programmer, big and small, to manually check these function calls every time is just asking for things to be missed through incompetance, ignorance or just plain mistakes. I mean how come we still have buffer overflow problems in this day and age, this should have been fixed 10 years ago. The current system of every program manually having to implement this stuff for every call of a function doesn't work.

    Rant mode off please. . . please.

  • Insightfull??? The poster displayed a horrible lack of horrible lack of knowledge about how linux's dynamic loader works. libsafe works by LD_PRELOAD, what this means is that while normally the program would look in libc.so.whatever for the function, it will first look in this program for functions to resolve. Also, it only has to do this once per program execution, once it resolves a function to the new libsafe library, it knows to use that for the rest of the program's execution. Therefore, the performance overhead is only the overhead of the extra code in the functions.
  • by nil ( 26268 )
    (*a)(b, c, d);

    Grep that!
  • by Cramer ( 69040 ) on Tuesday April 25, 2000 @07:15AM (#1111450) Homepage
    Actually, it's much simpler than that... Linux has had, in one form or another, a "non-executable stack" patch for some time now. There has been heated debates on it's usefulness, however. Most buffer overflow tricks overwrite the return address on the stack as well as stick there own instructions there. If the stack is not executable then it's a hell of alot harder to get the program to do what you want it to -- unless "crash" is what you want it to do. It would be very difficult to implant your own instructions; and very few programs have something like 'system("/bin/sh");' in them.

    Such patches also cause a problem for programs that legitimately alter their code -- self-modifying code. Granted, there are very few such programs and there are ways to make them work. (The Distributed.Net client has a self-modifying core.)
  • Download the RPM [sourceforge.net]


    ----
  • The first and obvious problems is that it only protects these library functions. It will not prevent the case of someone doing a roll-your-own strcpy() and overflowing a buffer that way.

    Another problem is that it caps the length of the buffer at the size of the stack frame. While I can appreciate why this is the only realistic way to figure out the buffer size without incuring penalties, it doesn't prevent attacks against other data (e.g. pointers, function pointers) in the same stack frame as the buffer.

    It also doesn't prevent heap overflows.

    Also, I've got some concerns about the way that they've re-implimented some of these function calls. I haven't seen any reports yet from people that have thoroughly audited their functions for compatibility against the libc functions. Since they've reimplimented them differently, they may not have exactly the same side effects.

    Still, since non-exec stacks on x86 are worthless and StackGuard has not yet been ported to egcs/RH6.x I'm seriously considering deploying this library to crucial systems.

  • You and me both is the correct way of saying this. Just take the comparative word out, and the "you", and you are left with: Me too. If you take the comparative word out, and the "you", wouldn't you be left with: me both? How much sense does that make?
  • Open Source means that the security bugs in most programs are fixable, not that they don't happen. For some people who have the time to audit every line of code of every program installed on the system this may just be a belt-and-suspenders approach.

    Look at the errata or security page for any Linux or UNIX distro, look at the history of BIND or Sendmail. It is better/easier to wrap these library calls than audit every line of software (including closed sourse stuff you may have??) or blindly trust that every programmer is perfect and never makes a mistake that comprimises security.

  • I'll have to agree. I wasn't going for insightful at all. Funny if anything.

    OK fine so I'm on self-imposed posting probation for a week.

    And you're right I don't have the knowledge.. yet. But I'm learning. :)

  • by rlk ( 1089 ) on Tuesday April 25, 2000 @07:30AM (#1111456)
    Grep's great for finding calls to gets that are basically unsafe under virtually any conditions. It can find calls to other dangerous functions (strcat, strcpy, sprintf for example) if you have the source handy, but it doesn't help you decide whether each call is dangerous or not. For example, strcpy is dangerous, but if the length of the string being copied is provably bounded, it may be quite safe.

    I consider gets to be essentially unsafe in all circumstances because it relies upon good external behavior. I suppose one can always come up with a safe example -- a program pipes, forks, and the child rebinds stdin to the pipe -- but that's contrived. It would be nice to replace the substitute function for gets with one that aborts if gets (and probably also scanf) is ever called.

    The real advantage of libsafe is that it catches problems that happen at runtime with calls that might otherwise be reasonable. It's a backstop. The fact that stack smashes still happen is clear evidence that grepping source isn't sufficient.
  • I won't even bother responding to the first part of your posting (the ability to audit code is not the same as the actual auditing code). But your "ideal solution" is so utterly unrealistic that I think it deserves some comment. Are you aware of the halting problem? Then I suppose you realize that the determination of whether a specific invocation of a black box function (ie. we can neglect the behavior/side effects of the function and the result still holds) is "safe" or "unsafe" is reducable to a more general decision problem that is known to be undecidable in the deterministic Turing machine model of computation. Translation: if you think this "tiny little perl script" is so easy, please write it (or even a plausible prototype) and earn everlasting fame for disproving the Church-Turing thesis. Just remember, your perl script does not know anything more about the flow of the program than your C compiler (and probably a good deal less). It cannot determine what input will be presented to a given function or program unless you resort to alternate or supplemental descriptive formal languges, but if you're going to go the route to theorem proving and validation, why bother with C?
  • 1. create your own function called "strcpy".

    2. have it reference a declared but undefined external

    3. always link against it

    4. just stop calling it.

    it is a sickness. how many fucking security holes do we have to patch one at a fucking time because we are asking every new person who comes along not to make the same mistakes we made. It's stupid.

    Too much old code to fix? email it to me. I will fix it for you. It is not difficult, there is no excuse.

  • I tried running with it and Netscape bus errored
    until I undefined the LD_PRELOAD env variable they
    have you set. When I undefined it netscape worked
    fine. Although it was probably netscapes problem.


    ********************************************
    Superstition is a word the ignorant use to describe their ignorance. -Sifu
  • Giving incorrect advice isn't a way of promoting good English.

    "You and me both." is obviously in the context "We both would like to see..."

    and since "We" is the subject, the corresponding singular pronouns are "You" and "I".

    "Us both" does not equate to "Us too", as you seem to suggest.
  • The attack it tries to protect against is where the attacker tries to feed very specific bad data to a running program. This data is actually code, with some stack hackery to force the executable to run the desired code. If libsafe prevents the necessary action from taking place (specifically, smashing the stack frame in the inappropriate fashion), then the attacker can never run the code in the first place.

    Unsetting LD_PRELOAD won't affect a running executable even if it could be done by the attacker, which hopefully it can't do now anyway.

    Libsafe is not designed to protect against someone who already has independent means of running an executable (although it might protect against someone who tries to crack a local setuid program, issues of forcing the LD_PRELOAD aside). Someone who has a shell can create their own executable anyway, and doesn't need a stack smash. It's intended to protect network daemons, where the attacker doesn't already have system access.
  • Something has gone seriously wrong, what with all these different versions of libc, and glibc etc.
    The expected behaviour of any library function is defined by a specification, and the function should do exactly what it is meant to. If, later on, someone decides that a function should behave differently, then they should create a new function rather than changing the behaviour of the old one.

    Specifications often leave many things up to the implementation, and when a programmer uses a library function, he or she should ensure that only the specified behaviour is relied on, and not anything else.

    There is too much coding done these days where people will hack out some code that ought to work, and then test and tweak it by trial-and-error until it seems to do what they want.... until they have to maintain and support the code a few months (or years) down the track.

  • My UNIX programming experience is limited to a couple of college courses as well, by my understanding is this: any call you don't have code to that takes arguments of the form char[], or int [] or even char*.

    The important concept is that of a buffer overflow, wherein an arbitrarily long argument runs over a fixed length buffer to overwrite code and execute arbitrary instructions. The system calls are convenient as having a predictable location near the kernel. So, unless you know that the strings you're passing to strcpy are of reasonable length, you're risking allowing a malicious user to take over control of your system.

    Thus, you don't want to pass unchecked arrays into any system call you think might have a fixed buffer, which should be all of them that take arrays unless you know better.

    Again, I'm mainly replying because no one else has, and I think it's sort of snotty that /. people have let an honest question go for two hours without answering it. My expertese probably includes at least one gap, and I'm sure someone will happily show me up.

  • Any call that does copying without requiring a copy of what's counted. Best examples are sprintf() and strcpy(). sprintf is particularly insideous since it's a great temptation to use it, and you might not think of its fundemental function as copying.

    D

    ----
  • Many errors cannot be detected by trivial source code inspection, which is all that grep could ever accomplish. Show me a regexp that will tell me whether

    printf(fmtString, a, b, c);

    is valid without knowing what fmtString is at run time.

  • All the time we hear about new buffer overruns discovered in supposedly stable software. Then at patch comes out, and people praise open source for its efficacy at fixing bugs found.

    However, a buffer overrun is inexcusable in the first place, and is a direct result of sloppy coding.

    Ask yourself, how often do you allocate a char[] buffer that "should be large enough" ? Or, more insidiously, how often do you write a function that takes a (char *), or even worse, (char **) but you do not document clearly how big a buffer it must be pointing to, or how big a buffer you are going to return, or what you are going to change.

    C is a low-level language and requires the programmer to fully understand the memory layout and usage of the code they write -- and to clearly document a function they expose to other programmers.

    So when a buffer overrun is discovered (either by chance, or by somebody combing the source for sloppy buffers), it is then easy to make an exploit that gives root, etc., because the code is open-source.

    So, what to do about this? If you do not wish to spend the time involved in writing robust C code... use C++ !
    Use a string class or template set that will prevent a buffer overrun (or underrun, for that matter).
    Why are buffer overruns so very rare in Windows code? Because most Windows code is written in object-oriented languages, using components or string containers that have effectively had billions of man-hours of testing. Not to mention the fact that due to lack of source, it is difficult to find an overrun, let alone exploit it once found.

    For people who baulk at C++ ? You can still use your old C coding style and practises, you know. Just learn how to use a C++ string, and incorporate it in your code. A small change, but with priceless benefits.

  • There are several different types of errors; the most commonly abused ones are all library functions that use a user-specified buffer of unspecified size. This is why strncpy() and vnprintf() safe (they can truncate the copy once the buffer is full), but strcpy() and vprintf() are not.

    A more subtle version of the same error can occur if you pass the wrong structure to a function that fills out a passed structure. This can happen if the user redefines the structure locally, or forces the issue with an explicit cast.

    In all of these cases, the library call will overwrite unrelated data. If it's in the heap, some of your data will be bogus and if you're lucky you'll soon SEGV on a bad pointer. If it's on the stack (as an "auto" variable) it can overwrite the return address, possibly resulting in execution of arbitrary code.
  • But an error in a format string couldn't lead
    to corruption of control flow. Could it?

    Alex.
  • this is a fairly common trick which is used for all sorts of things: getting around licensing, tracing system calls, error checking and garbage collection. it's unfortunate that the authors didn't look to see how it could be implemented on a few more systems - it's considerably more difficult to find stack frame information on other systems; they could also have explored ways of making the run time loader load their stuff first on other platforms too. this is slightly too linux specific, and linux is a bad example since libc is publicly available. the same trick is used in many commercially available error checking tools, e.g. purify & the like.
  • Even by overriding the library functions, this does not preclude the possibility of other overflow conditions in code. There are many occasions in C code when a pointer is referenced outside of a standard library function.

    For example, how would libsafe stop an overreach condition, like x[i], where x is a char pointer?

    It's certainly an interesting approach that they are taking, but I would rather have access to the source, and be able to audit for myself. I don't like black boxes.

  • by Animats ( 122034 ) on Tuesday April 25, 2000 @08:35AM (#1111471) Homepage
    The concept seems useful, but hokey. It only checks for a few standard bugs, and even then, the checking isn't airtight. It won't catch non-library overflows. It may encourage people to think unsafe programs are safe. Incidentally, if you're not familiar with how buffer overflow bugs are found and exploited, the classic The Tao of Windows Buffer Overflow [cultdeadcow.com] from Cult of the Dead Cow is a good tutorial.

    I'm amazed that people are still using the old unchecked C library functions. There are checked versions for all of them. I stopped using the unchecked versions in the early 1980s. As was suggested in 1995 [netsys.com], it's time to pull all those functions out of the standard library and move them to something called "deprecated". The open-source community should try this; it would break lots of programs at compile time, but they'd be easy to fix. And you've got the source.

  • IINM (the link is apparently bad), libsafe is some kind of library, which means that it would have to be included at link time. This means that you would either need the source or the objects, *not* the binaries. It would seem unlikely that one would have the objects without the source.
  • Too much old code to fix? email it to me. I will fix it for you. It is not difficult, there is no excuse.

    On the other hand, if you've got a number of old programs which you either bought as off-the-shelf solutions *or* have lost the source code for (in the real world, this happens more often than it should), a tool like this is a *wonderful* thing.

    It's not a panacea --- the code in question should be rewritten anyway. But as a stopgap bandaid to plug the security hole during the six months that it takes to recreate some important program from scratch, it's a godsend.
  • <me too&gt&lt/me too&gt

    I never did understand why people insist on using old programming ideas for new code. Consider the following arenas:

    • System calls
    • Large apps
    • Device drivers or other time-critical code

    Theoretically, libc et al are well-checked and "safe". At the other extreme, it should be pretty easy to write a stripped-down and low-overhead set of functions for a device driver with minimalist parameter checking while maintaining a set of hard limits on input data.

    That leaves larger programs, such as mailers, browsers, editors, etc. In those systems, why are so many unwilling to use the tools already in front of them? Why roll your own string handler when C++ has it already? Need a stack class? Got it! Array? Check! I can't imagine one single time, ever, when your average coder would need (or want!) to be using libraries with potentially unsafe calls. Wake up, fellow hackers! You are free to not be bothered with these problems anymore if only you'll be willing to answer the call and learn about the options that available to you.

  • OK first off, I think you mean snprintf() and sprintf(), not vnprintf() and vprintf(). Secondly, strcpy() and sprintf() are very useful, and I don't find them dangerous at all.

    In fact, strncpy() I'd say is one of the most useless functions described in the standard. There are contexts where strcpy() makes sense, and there are contexsts where memcpy() makes sense, but never have I found a situation where strncpy() makes sense.

    snprintf() is also pretty bad BTW. Using snprintf() is so awkward that it's far easier to just use sprintf() (properly, though!).

    Anyway, gets() and scanf() seem to be the worst for buffer corruption. There are many other ways to kill yourself with standard calls, but not too many that will cause buffer corruption.
  • Or ext2fs (why is it I lose a partition every time I pull the plug?).

    I wouldn't exactly call this a bug. You shouldn't do this on any system that hasn't been specifically designed for it (and yes, that includes ones with journalling filesystems and MS-DOS). Besides which, maybe I'm just lucky, but fsck has cleaned up nicely every time I've failed to do a clean shutdown. Obviously it depends how busy the system is at the time, and the alignment of the stars, but if you're trashing your filesystem every time I can't help wondering if you're not just doing something wrong (in addition to pulling the plug in the first place, that is).

  • On Digital UNIX I believe it's possible to make the stack non-executable when a process is running as root. Can this be done in Linux?

    No, and this is the topic of seemingly continual discussion on the kernel development list. See http://kt.linuxcare.com/ kernel-traffic/kt20000117_51.epl#1 [linuxcare.com].

    Those for a non-executable stack argue it is one more level of protection on the system. Those against it argue the protection is illusionary (it just changes the form the attack takes) so all a non-executable stack adds is kernel bloat. I won't go further into either side here. Please see the aforementioned link for more detail.

  • It is a library, but no. Libsafe uses the LD_PRELOAD mechanism to subvert the normal calls.. Binaries are fine!
  • "OK first off, I think you mean snprintf() and sprintf(), not vnprintf() and vprintf(). "

    I think that you'll find that they're all valid. Here is the information from MSDN [microsoft.com]

    "but never have I found a situation where strncpy() makes sense. "

    I guess you haven't needed it for copying a sub-string, or when you're dealing with binary data that you know is a valid string but might not be termintated.
  • by Deven ( 13090 ) <deven@ties.org> on Tuesday April 25, 2000 @09:14AM (#1111480) Homepage
    This is great stuff! (Bell Labs rocks!)

    I downloaded the code and looked through their whitepaper and the source code a bit. Here's the basic technique:
    • Install the libsafe library in /lib/libsafe.so.1.3. (Red Hat Linux 6.2 users might want to get the RPM [linuxfort.com].)
    • Set the environment variable LD_PRELOAD to point to the libsafe library. (The RPM installs shell init scripts into /etc/profile.d for Red Hat 6.2 shells to do this.)
    • Calls to "unsafe" C library routines (e.g. strcpy(3)) will be trapped by libsafe, which will figure out the size of the stack frame and reimplement the "unsafe" function safely (e.g. with memcpy(3)).
    • If the program attempts to overflow the stack frame (presumably from a stack-smashing attack), a message is logged with syslog (into /var/log/secure) and the process group is killed with the SIGKILL signal.
    • This interception is done at runtime, and works without source code or recompiling
    • This technique can guard against stack-smashing exploits that haven't even been discovered yet. (And might even help discover them earlier!)
    There are a few caveats here:
    • There is a performance impact, but the overhead appears to be relatively low.
    • Since this technique relies on the dynamic linker (ld.so) to intercept the unsafe calls, statically-linked programs cannot be protected with this technique.
    • If LD_PRELOAD is used, setuid programs will not be protected because ld.so (properly) doesn't trust the environment variables when running setuid, and thus ignores LD_PRELOAD.
    • For system-wide protection, it is safer to use /etc/ld.so.preload instead of LD_PRELOAD; it will apply to all (dynamically-linked) programs, including setuid programs, and cannot be accidently lost from the environment.
    To the packager of the RPM: how about using /etc/ld.so.preload instead of LD_PRELOAD? Maybe in libsafe-1.3-3.*.rpm?
  • Perry Wagle [ogi.edu] (principle StackGuard [immunix.org] developer) has done some analysis comparing libsafe to stackguard. Here's the short version:

    • Use StackGuard when you can, because it's safer:
      • Libsafe only protects selected library string functions, while StackGuard protects all potential sources of stack overflow.
      • Libsafe depends on the existance of the frame pointer in the stack frame to parse/detect the stack frame. Unfortunately, the frame pointer may not be there, either because of a compile option to remove it, or because the optimizer took it out.
    • Use libsafe where you cannot use StackGuard. It's better than nothing, and it can protect closed-source apps where StackGuard cannot.
    The long version of the analysis is here [ox.ac.uk].

    My further comment on libsafe: the paper that the authors will be presenting at USENIX [usenix.org] in June presents two forms of defense ("library intercept" and binary-rewrite (BRW)) and only the library intercept appears to be embodied in the publicly available libsafe, which is why libsafe only protects against overflows that use particular string library functions.

    The BRW method is a pseudo-compiler that can transform binaries into "safe" programs by transforming the binary. It copies program onto the heap, inserting checks as it goes. The copy-to-the-heap is to make space for the additional checks. I really like the BRW method, and hope it becomes available.

    If my understanding is mistaken, and BRW is actually in the distributed libsafe, please correct me.

    Crispin
    -------
    CTO, WireX Communciations, Inc. [wirex.com]
    Immunix: [immunix.org] Free Hardened Linux

  • I remember there being a non-executable stack patch for the 2.0.x kernel, but for one reason or another it was upgraded for newer kernels. Anyone know if anyone has been working on something like this? There were some problems with it, for example, I believe JDK wouldn't work, and other such things that manipulate the stack in weird ways would have the same problem. I don't know if they were just issues that could be worked out, or if the system was fundamentally flawed in that area.
  • I was recently having problems with inexplicable access violations. I tracked it down to an sprintf and a buffer on the heap that wasn't big enough (I had tried to allocate the correct buffer size but I mixed up a couple of variables when calculating the lengths). This should have been the first point in the code that I looked as I use very few news/deletes... everywhere else I have STL and experienced no problems. I guess I assumed it was safe as I had already fixed a more obvious problem for the same sprintf about a month before. The whole experience just reinforces my believe that we're better off allowing the system to handle memory... or reuseing code such as the STL.
  • by Deven ( 13090 ) <deven@ties.org> on Tuesday April 25, 2000 @09:30AM (#1111484) Homepage
    Making the stack non-executable can intefere with valid code (e.g. signal handlers, nested function calls and trampoline functions) while leaving other stack-smashing exploits open to exploit. The libsafe library comes with an example exploit that will give you a shell despite a non-executable stack. Using libsafe will protect you from this attack without harming valid code. Making the stack non-executable remains vulnerable to this attack while breaking otherwise-valid code; the Linux kernel folks were right to reject the non-executable-stack "fix" to the problem.
  • I need a refresher course in C. How does a buffer overrun on the stack cause invalid return addresses? Are the return addresses pushed onto the stack last, after all of locals variables?
  • This isn't going to do much good, is it ?

    You're only protecting the return address, not the remainder of the stack frame. Since a large proportion of the variables in any C program are pointers, there's a very good chance that you have a pointer later in the frame, and libsafe isn't going to prevent anyone from overflowing the buffer and overwriting it.

    If you have access to a pointer to a buffer, or to a pointer which, after a some number of dereferences, leads to a buffer, then you have control over the address of that buffer, which permits you to then perform a subsequent stack smashing without the need for buffer overruns. In fact, the second target doesn't even really need to be a buffer, just any variable you have the power to set.

    Not the great advance in security we were hoping for, I think.
    --
  • <snip>..., then don't waste 4 metric tons of overhead running all your software with a wrapper

    Well, the overhead is unnoticeable in my machine, a state-of-the art 486sx with 8Mb of ram... I would hardly call that 4 metric tons.

    Hal Duston

  • Ahhh, so if the implementation were reversed, buffer overruns on the stack would overflow into unallocated memory (or if enough memory were in use, into the heap.)
  • Bell Labs has a press release [bell-labs.com] from April 20 about this...
  • IMHO: Libsafe augments non-executable stacks. Take a look at the libsafe/exploits directory, there is a sample program that escapes non-executable stacks to execute /bin/sh, but libsafe catches the overflow.
  • As I understand how the thing works, it calculates the frame size of the calling function, and uses that as the upper bound. But what happens if the buffer that you are copying into is a stack variable from a *previous* frame? That is, if I write a function:

    char *strcpy_wrapper(char *s1, char *s2) {
    printf("You just called strcpy()\n");
    return strcpy(s1, s2);
    }

    When strcpy gets called, libsafe will calculate the frame size of strcpy_wrapper to be about zero bytes, then declare that the call to strcpy would overrun the stack.

    A similar problem would occur if I did:

    char *squirrel(char *s) {
    char *c;
    c = malloc(4 * 1024 * 1024);
    strcpy(s, c);
    return c;
    }

    Or, am I missing something?
  • I'm well aware of the halting problem. I went back to reread my post to make sure it wasn't worded as stupidly as you said, and it wasn't. I am not referring to solving the halting problem, and if I were attempting to even present a close approximate solution that can sometimes solve the halting problem, perl wouldn't be my first choice for such. No, what I said was "these known bad calls". There are a number of things that are clearly stupid to use in a program which a simple grep would catch, that end up in some open source programs and a lot of commercial products.
  • > If my understanding is mistaken, and BRW > is actually in the distributed libsafe, > please correct me. No. The USENIX paper talks about libsafe and libverify. The *old* name for libverify was libbrw (brw = binary rewrite), but that changed now. IMHO, I would say to use libsafe everwhere performance matters, where you don't have access to source code, and where stackguard does not exist (eg, RH 6.0 and later versions where stackguard chages have not been proted to egcs compiler).
  • Malcom, Read the white paper available at http://www.bell-labs.com/org/11356/libsafe.html. The white pager describes how a simple buffer overflow can be used to break into machines.
  • printf(fmtString, a, b, c);

    If you screw up your fmtString, you will get bad output, but that's all. This isn't dangerous, just annoying. What you could have brought up is:

    sprintf(dest, fmtString, a, b, c);

    Here, however, libsafe could not help anyway, because I really doubt they've found a way to make a library that can determine how much you really want to store in dest before overwriting something important nearby on your data segment, such as a function pointer, etc.
  • When fixing the code is an option, there is a solution that eliminates most of the issues involved with buffer sizes. It can allow values of arbitrary length to be handled in cases where you don't want to set an upper limit, such as free-form text fields. And it has been around in glibc for ages. Obstacks [gnu.org]! If libsafe is an airbag, then obstacks is a tank. Each makes the road safer (for you) in its own way.
  • > But what happens if the buffer that you are > copying into is a stack variable from a > *previous* frame? No problem: libsafe catches that too. Take a look at their white paper decsribing how it work. Even better, down load the software and give it a try.
  • Instead of sticking libsafe in /etc/ld.so.preload and risking a bug taking out applications that you don't really want libsafe'd (I'm ignoring for the moment entirely valid arguments that all code is potentially privaleged and should be protected) there is another way to protect SUIDs. If you put libsafe in /lib with appropriate symlinks, and recompile suid programs with -lsafe then the programs will be dynamically linked against libsafe and will be protected without having to libsafe your entire distribution.

    Ex:

    % ls -1F /lib/libsafe*
    /lib/libsafe.so@
    /lib/libsafe.so.1@
    /lib/libsafe.so.1.3*
    % cat foo.c
    #include

    void foo(char *string) {
    char buffer[1024];
    strcpy(buffer, string);
    }

    int main(int argc, char *argv[]) {
    foo(argv[1]);
    }
    % gcc -o foo foo.c -lsafe
    % ldd foo
    libsafe.so.1 => /lib/libsafe.so.1 (0x4001c000)
    libc.so.6 => /lib/libc.so.6 (0x40021000)
    libdl.so.2 => /lib/libdl.so.2 (0x40116000)
    /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
    % ./foo `perl -e '{ print "A" x 1040 }'`
    Detected an attempt to write across stack boundary.
    Terminating /home/lamont/security/non-exec/foo.

    You can make that executable suid root and it works the same.

  • by p3d0 ( 42270 )
    Making the stack non-executable can intefere with valid code (e.g. signal handlers, nested function calls and trampoline functions)

    What are you talking about? None of these things need an executable stack. And the example exploit does need an executable stack. The code is loaded into a local variable (ie. on the stack) and then executed.

    Normal C code does not provide any way to execute the stack. You have to do abnormal things like cast the address of a local variable as a function pointer and execute it; not something that normal C programs do. Or (of course) overrun the return address.
    --
    Patrick Doyle
  • Here, however, libsafe could not help anyway, because I really doubt they've found a way to make a library that can determine how much you really want to store in dest before overwriting something important nearby on your data segment, such as a function pointer, etc.

    Hey, moderate this up! Even though libsafe can prevent you from overwriting the return address, it can't prevent you from overwriting a function pointer in the current stack frame. The latter could be just as good as the former for buffer overflow attacks.

    --
    Patrick Doyle
  • Secondly, strcpy() and sprintf() are very useful, and I don't find them dangerous at all.

    Then you haven't given the issue much thought. Strcpy() has no idea how big the destination buffer is, so you can easily corrupt memory. sprintf(x, "%s", y) is the same as strcpy(x,y), so same problem.

    Here's some code for ya:


    void func(char *arg){
    char buf[1024];
    strcpy(buf, arg);
    }


    If arg happens to point to more than 1024 characters of data, you're screwed. Here's the fixed version:


    void func(char *arg){
    char buf[1024];
    strncpy(buf, arg, sizeof(buf)-1);
    buf[sizeof(buf)-1] = 0;
    }


    This will safely copy the data into the buffer, and make sure it's null-terminated (which strncpy does not guarantee).

    The sprintf version looks like this:


    void func(char *arg){
    char buf[1024];
    sprintf(buf, "%.*s", sizeof(buf)-1, arg);
    }


    Of course, you probably know this already. It must seem quite easy to fix an example this small, and wonder what's so unsafe about strcpy. All I can say to this is that you must be smarter than the average coder, because code like the first example is all over the place.

    --
    Patrick Doyle
  • non-exec stacks on x86 are worthless

    Why is that?
    --
    Patrick Doyle
  • ...as the majority of bugs, it would seem, resolves around bad programming of the finished product, not bad programming of the generic libs used in the various programs out there. What I mean is that, their solution plainly states that they do _NOT_ touch both the source code NOR the binaries of these programs. Rather, it's just a lib wrapper that catches all lib calls. Well not all exploits... in fact IMHO i think very few exploits revolve around calls to libc and other such generic, public libraries. The exploitable code is in the source, in the binaries, static, not dynamically linked. All those bad sprintf() calls... can't fix those with a lib wrapper. Same with bad pointer handling, ect.. ect... BLAH! -Bug

    -
    "There is no off position on the genius switch." --Dave Letterman
    -
  • Nah, Solaris has this functionality built-in already. Just add the following in /etc/system:

    set noexec_user_stack = 1
    set noexec_user_stack_log = 1

  • True, but what can wrappers fix that grep could not, in this instance?

    I wish I had a nickel for every time someone said "Information wants to be free".
  • Thanks You
  • FreeBSD has a pretty good solution here, much like your suggestion of "aborts if gets":

    {"/home/green"}$ cat foo.c
    #include
    void foo(void) { char x[1]; gets(x); }
    int main(void) { fclose(stdin); foo(); exit(0); }
    {"/home/green"}$ cc -o foo foo.c
    /home/green/tmp/ccMD6705.o: In function `foo':
    /home/green/tmp/ccMD6705.o(.text+0xe): warning: this program uses gets(), which is unsafe.
    {"/home/green"}$ ./foo
    warning: this program uses gets(), which is unsafe.

    This is something you can easily make use of in libc, with a nice __warn_references() macro to define them :)

    It's not a perfect solution, but it certainly lets people know that they're REALLY out of line with their code =)

    --

  • But you suggested things like libsafe are unnecessary, because you can grep programs for things that are known to be unsafe.

    That isn't true. More complex uses of functions that are *sometimes* unsafe cannot be found in this way, and as the poster points out, that is a fundamental law of nature, not just a requirement for a cleverer perl script.
  • I think what I did wrong was installing Linux. After I replaced it with another Unix, life was good :)

What is research but a blind date with knowledge? -- Will Harvey

Working...