New Hack Exploits Common Programming Error
buzzardsbay writes "TechTarget's security editor, Dennis Fisher is reporting that researchers at Watchfire Inc. have discovered a reliable method for exploiting a common programming error, which until now had been considered simply a quality problem and not a security vulnerability. According to the article, the researchers stumbled upon the method for remotely exploiting dangling pointers by chance while they were running the company's AppScan software against a Web server. The good folks at Watchfire will detail the technique in a presentation at the Black Hat Briefings in Las Vegas in August, Fisher writes."
Who would have thought that invalid pointers and buffer overruns might be exploitable as a security hole?
Quick, someone alert Bill Gates!
OK, you load the code into memory with the dangling pointer, now, how to you get the instruction pointer to go and execute said code?
..is down to dangly bits.
Microsoft seems to have fixed it in IIS and pushed the patch out already. I wonder how many of these Apache has exposed...?
Enough with all of this talk of "dangling pointers" you perverts.
Dedicated Cthulhu Cultist since 4523 BC.
I found that if I stop programming every 15 minutes or so and look up some pr0n, I significantly reduced my chances of having a "dangling pointer."
However, I thought that the compiler took care of destroying pointers when variables went out of scope? Aren't destructors implicit clean up calls? I'm fairly sure that Borland 16-bit Turbo C++ did this, back in the day.
If I mod you up, it doesn't necessarily mean I agree with what you've said, sorry.
I can see this as fodder for the argument that it's safer to run software in sandbox like Java's VM.
...which is why all my dangling pointers have unfree'd memory at the end of them just in case ;)
biopowered.co.uk - catalytically cracking triglycerides for home automotive use since 2008. Just say no to big oil!
Finally, an indisputable reason for choosing Java over C++.
occultae nullus est respectus musicae - originally a Greek proverb
Women have been exploiting dangling pointers for centuries... millennia even! :)
"Hello security hole, wanna meet my dangling pointer?"
"When Watchfire first alerted Microsoft's security response team to what Afek and Sharabani had found, they were met with skepticism, and understandably so, Allan said. The company had known since 2005 about the IIS bug that caused the crash, but it was considered a simple denial-of-service problem and not remotely exploitable."
Worded a little ambiguously, but I presume it's Microsoft their talking about... How can a bug like this get through the QA process since 2005 and multiple product versions without getting fixed?
And this isn't a "use Python" or "use Java" rant, either. I will say, however, UNIT TEST YOUR SHIT! EVERY LINE! Even the little inline function, you need to test it all! Repeat after me: Resource Acquisition Is Initialization. Resource Release Is Destruction. -Wall -Werror, no, warnings aren't OK. No, not even signed vs unsigned comparison warnings, you need to either get your data types straight or wrap that in a partial-specialization template functor that correctly checks that you won't be killed by sign-promotion when you compare int and unsigned long long. strncpy(), not strcpy()! -fprofile-arcs -ftest-coverage! Valgrind!
I dunno. I manage to write C++ and never overflow a buffer, always release all resources when I'm done with them, and never throw away an error. Why can't the other 95% of the programmers out there do the same thing?
<xml><I><am><so><damn>Web 2.0</damn></so></am></I></xml>
it's one thing to find a major exploit, but a whole new class of exploits?
welcome back to the days of sql slammer and code red folks. buffer overflows have been analyzed to death, but this is just the beginning
intellectual property law is philosophically incoherent. it is your moral duty to ignore it or sabotage it
This is a story about a company that says they have a story.
Let's just wait until the actual story next time? (since it doesn't seem likely there will be a real one, here, anyway)
I have written a bunch of C code, and a little C++ code. I have made it a habit to set a pointer to NULL after I free the pointer's data. If I had code that allocates a FOO structure, I would make a function to free the FOO structure; in C, my FreeFoo() function would not take a pointer to a FOO, but a pointer to a pointer to a FOO, and after freeing the FOO it would set the pointer to NULL. Like so:
/* C code */
void
FreeFoo(PFOO *ppfoo)
{
PFOO pfoo;
assert(NULL != ppfoo);
if (NULL == ppfoo)
return;
pfoo = *ppfoo;
assert(NULL != pfoo);
if (NULL == pfoo)
return;
free(pfoo);
*ppfoo = NULL;
}
/* typical use:
PFOO pfoo = PfooNew(args);
...do something with FOO object...
FreeFoo(&pfoo);
*/
Note that if you acidentally try to double-free the FOO, the above code will not crash; the first free sets the FOO pointer to NULL, and the second one notices that the pointer is already NULL and exits early. It does assert() when you try to free a NULL pointer, so you can catch the error and see what else you might have messed up.
For C++ you should be able to write a template that takes a reference to any pointer type and applies the above logic.
I once had to maintain a legacy code base, a whole bunch of C implementing a fairly complicated application. The app had a whole bunch of crashing bugs. I went through and applied the above logic everywhere the app was calling free() and suddenly the app stopped crashing. I wonder if the previous developers were using a different compiler or something, and the dangling pointers just happened to work for them?
steveha
lf(1): it's like ls(1) but sorts filenames by extension, tersely
And this is somehow a newly discovered hitherto unknown class of exploit?
People have been 'sploiting this kind of thing for years as far as I know.
Not that there's any thing wrong with that! (Except that maybe there is a kit now for diddling the danglers.)
Give a man a fish and you have fed him for today. Teach a man to fish, and he'll say "WHERE'S MY FISH, YOU IDIOT?"
....programming?
Let's ignore plugins or modules, because those are frequently C/C++; however, it's been my experience that writing modules is far less common than just implementing code in PHP or Perl. I'm talking about basic application logic, generally form processing and serving HTML content.
I have implemented barebones C and C++ HTTP servers because high performance was a critical requirement, but the percent gain wasn't really worth the effort. PHP with caching (apc, memcached, db pooling) is almost as fast as a bare C server. Debating 1,000 connections per second versus 10,000 connections per second is a bit moot when your system isn't topping 300 req/second, even at system peak.
So. Does anyone have a reason for C/C++ web code that's not based on performance? Say, a library wrapper for accessing a legacy data source for which there's no scripted library?
Camping on quad since 1996.
You have a pointer.
You free the the object the pointer is pointing too.
The bad guy figures out where the pointer is pointing and writes his code at that location. The next time the pointer is called the bad code is executed.
This is like saying you lock a door and put the key away somewhere. The bad guy finds the key, unlocks the door and takes what he wants.
Why is this suddenly a major security issue and what am I missing?
Apache doesn't really work in a way that leaves dangling pointers to exploit in the first place (resource pools). And since this requires code to be loaded at a specific point in memory, which then must be executed, it's going to be webserver-build and OS-specific, which leaves Apache in a good position since that varies across distributions and versions; the attack will be useless if grsecurity or other address randomization technique is used.
IIS 5.1 and 6.0 is a smaller target space of possibilities.
THIS THING CAN TURN ON A DIME, MACROSSZERO STYLE ALSO FUCK BETA, ~NYORON
From the article:
Dangling pointers are quite common, but security experts and developers have said for years that there is no practical way to exploit them, so they've been considered quality-assurance problems and not security flaws.Any security expert with at least half a brain is going to assume that a remotely-triggered crash might be exploitable, unless he can actually prove otherwise.
That said, I've known plenty "security experts" who weren't.
http://outcampaign.org/
The article is a bit thin on the details of how the exploit works. Without some kind of explanation, I'm skeptical of the claim that this works for "any application in which there is a dangling pointer." In particular, note the quote that "[t]he long and short of it is, if you can determine the value of the pointer, it's game over." Okay, but I'm not in the habit of exposing pointer values to users, so if a user-visible pointer value is required for the exploit, that significantly reduces the set of apps to which this attack applies. The apparent overstatement degrades the overall credibility of the related claims. Also, I'm really curious why on earth it would matter that the pointer is dangling. A dangling pointer is a pointer that points to a resource that no longer exists -- and, therefore, to garbage memory. I'm assuming the attack involves writing arbitrary code to that garbage memory. The question is, why would such an attack rely on the memory being garbage, as opposed to a valid resource? If we're overwriting it anyway, why does it matter? Perhaps what really happens is that when you crash due to a dangling pointer, the resulting dialogue shows the pointer value, and this tells the attacker which location to overwrite for the next time. If that's the case, it strikes me as being just as obvious as, say, buffer overflow, and that dangling pointers are rather incidental. I guess we'll have to await the details to determine if there really is meat here, a legitimate new class of attacks, or just overstatement of something not at all new an exciting.
"Common programming error"?
The foo! Just tell us WHICH error, will ya?!?
Ok, I know a few lines further on they actually did, but the beginning just ticked me off. This is a bloody tech site, I'd think that some people here _would_ know what you're talking about when you say "dangling pointer".
Please correct me if I got my facts wrong.
If a dangling pointer is followed into 'random memory' what are the odds it'll hit exploit code? Sometimes very low...
Oftentimes, however, the original value was off of the heap, so if one can make the app allocate a whole bunch of memory there might be even odds that the pointer will now point into that memory. If that memory consists of a huge header which can be *entered at any point* (eg: big pile of nops) and forwards execution along to the end where one has the exploit code proper... then you have an exploit.
See some other post about how to get from an ordinary pointer to an executable one (eg: a function pointer)... how rare are function pointers?
How do they know its an dangling pointer?
Could by anyone of a hundred possible bugs.
Those guys used the wrong term. When I hear dangling pointers, I often think structs that have pointers to other stuff and the struct has been freed before its pointers are freed. What these people say is dangling pointers are merely pointers to stuff that's been freed but the references have not been reset to NULL. Resetting them to NULL is basic C-programming 101.
Is this a problem with Dangling Pointers alone or don't you also need some kind of buffer overflow to fill the place pointed to by the buffer?
On Linux, I just ran "valgrind ls" and I saw the following...
ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 1)
malloc/free: in use at exit: 23,673 bytes in 84 blocks.
malloc/free: 136 allocs, 52 frees, 57,621 bytes allocated.
Looks like ls has 84 allocs without free's....is this a problem?
Great, so you find a dangling pointer. So what?
Now you have to somehow fill that garbage location with code. How are you going to do that, except with another exploit? And if you've got an exploit to insert your own code into execution memory, why bother with a dangling pointer at all?
The only way you're going to exploit a dangling pointer is if it happens to point to a memory location whose contents you can overwrite by some other means. That's a highly unlikely scenario.
In OO languages a pointer to an object works almost as well. The object pointed to in many implementations begins with a type field. This is usually a pointer to the class's virtual function table - usually implemented as a table of function pointers.
That is to say - if the object is referenced through a bad pointer, *and executes* any methods of that object's type - then it could be used to run someone elses code. They'll need to have filled some memory with something that can be interpreted as a virtual function table that points at something that can be interpreted as code. Which is doable.
If the processor/OS has set an app to able to write to it's executable memory, then it is vulnerable to this class of vulnerability.
Many OS's and C++, Objective C and *java* implementations default to this.
Pascal and perl used (maybe they still do) stubby things that required that the *stack* be executable, nevermind just data... *buffer overruns* are much easier when the stack is executable.
Java is interesting. Modern VMs do a lot of dynamic optimization - this means that they write on code that is actually running. They need OS permission to do so (in decent OSs?) so now you *have* to give the VM's process that permission in order to run Java. Now any dangling pointers in the VM implemention are potentially exploitable. Or if the memory manager has a bug and improperly deallocates an object... Or if the application has to call a library and that library accidentally accesses a reference to an object that was already released by java. Or maybe the app calls the OS - and the OS has a dangling pointer (say to a data structure that the Java VM needed to allocate). If you can fill the Java heap with executable exploit data, then if someone, anyone, jumps into it - they are toast.
I hope this helps. There is likely an actual paper that they will present. It will document one or several of the myriad ways to exploit dangling pointers - hopefully more efficiently than previously.
After discussing it with a fellow developer, here's what we thought might be happening:
1. Application allocates a C++ object, deletes it, but continues to point to it. The exploit code is likely to force this condition. The attacker has to know the type (and size) of the C++ object.
2. Exploit code sends a packet to the server causing it to allocate memory of exactly the same size as the C++ object that was deleted and store the exploit payload in that memory. For example, in case of a web server it might be an HTTP request of size X or an HTTP request with multiple HTTP headers of size X, depending how the implementation stores whatever it receives on port 80.
3. The heap allocation algorithm would presumably re-use the space that was deallocated when the C++ object was deleted. The exploit payload is copied over into the allocated buffer, examined and discarded (e.g. the payload is not valid HTTP). This is OK since the dangling pointer is still pointing to the memory area with the exploit payload.
4. Now the exploit causes the dangling pointer to be used to reference a virtual function and it's game over.
The exploit payload needs to act like a virtual table. It can reference exploit code in itself or jump somewhere in the running process which would make it exploitable (e.g. "DeinitializeSecurity" function).
Hopefully the paper is more interesting than this.
I'm guessing you've never worked on a large product that's released to end-users and has release deadlines that should avoid slipping. Bug triage is exceptionally common on such projects. Nobody fixes every last bug before release, and I've had five year old low-priority bugs that were still on my to-do list when I left the last company I worked on that had such a product.
What you spend your time on is finding and fixing the critical bugs, followed by the high priority bugs, follow by the mediu-- oh, hey wait, someone added new features and there's a host of new bugs to fix. Yay!
I wish I was wrong, but this is how the real world works most of the time. I'm kind of glad to have spent the past few years on internal-use only applications.
If it's for-profit but free, you're not the customer -- you're the product (e.g., the Slashdot Beta's "audience").
IIS 6.0 came out in 2003 (Windows Server 2003, Windows XP Pro x64) (and appears to be unaffected).
IIS 7.0 was just released in Vista, and the Upcoming Windows Server 2008 (and appears to be unaffected).
MS released a patch for IIS 5.1, which is only on Windows XP Pro (32Bit) Service Pack 2.
"This is a bit of a Pandora's box and once we open it, it will be just the tip of the iceberg."
Did anyone else think:
"If we hit that bullseye, the rest of the dominoes will fall like a house of cards! Checkmate." - Zapp Brannigan
https://www.accountkiller.com/removal-requested
Can anybody hazard a guess as to how this exploit is done? As it says in the article... once you know what the value of the dangling pointer is, how do you (a) get your root-shell code into that address, and (b) get the target computer to start executing code at that address?
I don't care if it's 90,000 hectares. That lake was not my doing.
Wow, a slashdotter doesn't understand an article but still feels the need to post about how he's much smarter than everyone else.
It's not unheard of to not bother zeroing out a pointer if you're never going to look at it again without reinitializing it.
What I'm not able to conceive of is any possible code path where:
1. This would be exploitable.
2. It wouldn't just crash if you'd zeroed out the pointer.
I don't generally dereference pointers after freeing them. So far as I can tell, you'd have to for this to work.
My blog: http://www.seebs.net/log/ --- My iPhone/iPad app: http://www.seebs.net/seebsfrac/
> I dunno. I manage to write C++ and never overflow a buffer, always release all resources
> when I'm done with them, and never throw away an error. Why can't the other 95% of the
> programmers out there do the same thing?
Now please go into your phone booth and change back into civilian clothes!
Thank You!
Mozilla has considered dangling pointer use to be "probably exploitable to run arbitrary code" for a long time. I even blogged about that fact, describing what types of dangling point use are most likely to be exploitable. If other software companies refuse to prioritize those bugs until the reporter supplies a demonstration exploit that launches calc.exe or Calculator.app, they've been asking for trouble for years.
The shareholder is always right.
Anyone know how C# works, and if it will also have the problem?
GC does eliminate a few classes of bugs:
If you forgot the length/size, you can't be safe.
If you do have the length/size, you have the info needed to use memcpy. This is faster then strcpy, and often much faster than strcat or sprintf.
Given the moderation on your post I'd say it worked quite well...
The reason you don't follow this is because a good programmer will know when the memory is released that the pointer is not going to be used again so its contents become a don't care.
A bad programmer releases the memory but doesn't really check if the pointer will or will not be used.
A crash occurs if some function tries to use the pointer when it is not valid to use it. An exploit occurs if you load the proper data via normal load procedures such as asking a web server to say load a jpeg and the jpeg is coded as a jpeg and looks like a jpeg but contains a segment of code in a location which is determined by the particular buggy program in question.
Once the code is in the machine then the next step is to cause the errant program to execute the improperly coded function which is often an error handler which typically get written and never checked out properly.
One way around this is trivially simple. Free the memory and set the pointer to null. Of course there can be other pointers which are set. One can have several pointers to a chunk of memory. Sometimes this is the _only_ way to do a task efficiently. A for instance is scanning a list and returning the address of the item found.
Of course this can easily be handled by prudent programming practices. One needs to understand that a list is an object. As such its FIRST word should be its status. This is ALWAYS the case. If zero then the status is normal. If no-zero then its an error or a warning and its programmer's glory how this is set up.
Then, Any function that needs to work with the list needs the list address and the address of say an item in the list. Any code using the address of an item must check the list status first.
A simple way to do this with a dynamic list in say shared memory would be to put an interator on the list which increments each time there is a modification to the list. Then the address of an item which should also be seen as an object would have the list iteration number as part included with the address of the item in question. By doing this any code needing to process the item which was for instance looked up by helper functions will know if the list is valid for the item being referenced.
Of course. Most programmers I know don't do this. They write buggy code.
Most people I know go by the idea that if something works its good enough. A serious and professional programmer must change this to the mathematical standard which is we must be able to prove it cannot fail.
I am a manager and an employer and a businessman. This is probably pretty close to the correct order but the vast majority of businessmen are salesmen and they look for the money first. Any programmer working for one will be constantly under pressure to do it fast and we can fix it later.
Think of this as brain surgery. Would you want the surgeon to do it fast and suggest it can be fixed later?
Mistake #2: AC isn't a slashdotter, he's less than nobody.
Mistake #3: Arguing on the internet.
I think that's enough points to prove I'm smarter than everyone else, too! Isn't the Internet fun?
So... You never had a correct unit test that fail to find a problem in its unit after the change was made in seemingly unrelated piece of code? You program C++ and deal with str(n)cpy? What kind of 80's C++ is that? You only need to deal with type correctness, never with integral overflows? Get off the high horse, man!
I do not disagree with folks that say that programs are as good as their programmers. It's true, no one can dispute that. But it takes God-like programmers to make a C/C++ application of many thousands of lines of code without errors like buffer overflows and dangling pointers. In other words, only 10% of the programmers out there are capable of producing very good C/C++ programs. With Java, the number rises to maybe 50%, thanks to garbage collection.
The article mentions that Java apps are not targets for this exploit because Java has built-in garbage collection. Wouldn't this also be true for .Net apps?
Too true - for most purposes the programmer should not have to worry about such things. This is a failing of C/C++ which makes it inappropriate for most software development. Using languages more appropriate for typical applications, it is not impossible to go for years without a segfault. There are reasons why so many programmers work in interpreted environments such as Perl, PHP, Ruby, Lisp, Haskell, etc. - beyond each language's particular strengths these languages provide fundamental productivity and security advantages over low-level languages.
...
I used to teach a software quality assurance workshop, and in the course of collecting information for the class found substantial research showing that, regardless of programming language (from assembler to 4G languages), given 'good' programming practice (structured design and programming and walkthroughs among other things - this was a while back), on average there was one bug every 200 lines of production, released code. About 70% of these bugs were in the original design. That means 30% were actual coding errors. The research was performed in large scale, corporate, government and military related projects. I've probably still got the book somewhere
At that time it was also found that a team could produce, on average, about one line of production-release code per person-hour (this includes planning, design, testing, documentation, etc.). Actual coding accounted for about 15% of the total number of person-hours IIRC.
Many modern languages provide much better support for the programmer, and any possible performance advantage of bare-metal programming is no longer very relevant. Today even 'bare-metal' programming usually isn't, because processors now have their own memory management, CPU load management, etc. and the OS and auto-loaded libraries take up 95% of the slack. Much of the original domain of C (described once as "structure PDP-11 Macro assembler", I think by Kernighan or Richie) is now the domain of Intel and AMD processor hardware design teams, and to some extent Linus and friends.
Embedded processor systems may or may not require this low level access; operating system kernel programming may or may not. There is an arguable case for an OS to be built as a microkernel written close to the hardware, with a userland programming environment based a modern interpreted language - Mach + Java, or Mach + LISP could be exemplars.
[I am not a LISPer, but I am reminded of the remark that "all languages evolve over time to emulate LISP - badly." (source??) Maybe it's true.]
Therefore IMHO, C (or C++) is no longer the most appropriate tool for most programming applications. It may be necessary for coding to hardware, but for general applications there is no longer any excuse for using a language that does not prevent such basic problems, and requires so much of the programmer's mindshare for such low-level problem prevention. This link provides some perspective. (I was unable to find the original citation for the phrase "programming without a net".) If I can perform a complete HTTP access of a document and retrieve all the ancillary files using a single function call, then I have saved hundreds of lines of code, each with its bug probability, and saved many hours of coding.
By way of analogy, at one time it was necessary to adjust the spark gap differently for starting vs. running an automobile engine (Ford Model T). Tire patching was an every-20-miles event. As things advanced, shifting gears no longer required double clutching due to synchromesh, then automatic transmissions took gear management out of the problem space. As automobiles advanced, the number of things that had to be manually dealt with has continually been reduced, or rather moved up the scale of complexity - nowadays we spend more time managing our traffic environment and looking at the moving map GPS display than we spend manag
It's easier to be a result of the past, but more fun to be a cause of the future! http://www.spacefinancegroup.com/
heyya, this exploit or feature?
maybe core A.I. routine can use this?
you know that "it's on the tip of my tongue, but i can't utter it" feeling?
http://www.boost.org/libs/smart_ptr/smart_ptr.htm