Slashdot Mirror


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."

13 of 255 comments (clear)

  1. That's nice and everything but.... by Anonymous Coward · · Score: 1, Interesting
    ...The problem before was, you had to override the exact location that the pointer was pointing to. It was considered impossible. But we discovered a way to do this with generic dangling pointers and run our own shell code."

    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?

    1. Re:That's nice and everything but.... by lgw · · Score: 3, Interesting

      I betting it's the C++ vtables exploit suggested by the previous thread. If you have a C++ class with a virtual function, the freed memory has a pointer to a table of pointers to executable code. Changing that vtable pointer to point to a malicious vtable might work.

      I'm not sure why that would work better with dangling pointer than pointers to live code, however - how do you change the memory that the pointer points to without already having access? Presumably that's what these guys discovered: a way to get a buffer allocated and filled with user data at the spot the dangling pointer points.

      --
      Socialism: a lie told by totalitarians and believed by fools.
  2. Re:Well duhhhh. by Anonymous Coward · · Score: 1, Interesting

    well duh yourself... none of the smart minds looking at this, obviously a lot more retarded than you are, thought it was possible to do this for years.

    But I guess you have no clue about programming and what dangling pointers really are, and you're just replying cause it's slashdot..

  3. Known since 2005 by drspliff · · Score: 4, Interesting

    "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?

    1. Re:Known since 2005 by jimicus · · Score: 2, Interesting

      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?

      Very easily. Every bug gets a priority assigned to it, and from the sound of things this one was ranked pretty low.

      The sheer number of bugs in any large product means it's not really practical to fix every one before a release so the higher priority ones get concentrated on. Time passes, products are realeased because there are deadlines and business needs and it's quite possible that something could be ranked at such a low priority that it never gets any real attention.

  4. Re:Pleasantly surprised! by Wavicle · · Score: 5, Interesting

    Yes and no. The pointer in question may have a lifetime greater than that of the object being pointed to. Example:


    void (*myFuncPtr)() = NULL;

    void cleanUp() {
        item listItem = firstItem;

        while ( listItem != NULL ) {

            myFuncPtr = listItem->fcn;
            myFuncPtr();

            tempItem = listItem->next;
            free(listItem);
            listItem = tempItem;
        }
    } // myFuncPtr is now dangling!


    A little contrived, sure, but it is an example of how a pointer might get left dangling.

    --
    Education is a better safeguard of liberty than a standing army.
    Edward Everett (1794 - 1865)
  5. Re:Pleasantly surprised! by Anonymous Coward · · Score: 1, Interesting

    Destroying a pointer isn't the same as destroying the object-pointed-to.

    C++ gives you a lot of choices, and thus lots of ways to shoot yourself in the foot if you don't understand those choices and make the correct ones.

    The backwards-compatible C-style "dumb" pointers don't do any reference counting, so there's no way for a heap object to know that it's no longer needed. There are other tools to do this job (auto_ptr and others) if that's what you want and need. And you could add GC to your C++ system if you want, though that still leaves you a window of vulnerability between end-of-life and actual cleanup. (Few heap management libraries zero out freed memory, so pointers inside deallocated objects can still be read; they may or may not point to still-valid objects. Even if these internal pointers are not "dangling", they're still a security hole if you hand them to other malicious code in the same address space.)

  6. NULL those pointers, folks by steveha · · Score: 3, Interesting

    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
  7. - It doesn't have to be a function pointer. by DogFacedJo · · Score: 5, Interesting

    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.

    1. Re: - It doesn't have to be a function pointer. by DogFacedJo · · Score: 2, Interesting

      Definitely an implementation choice for Pascal - but it (according to legend) was not a *rare* one - especially on x86 era implementations. 'Trampolines' I heard they called those stubs... didn't recheck this, so feel free to enlighten me.

          BPVs... I had thought Pascal still used something similar, but double checking, it seems those were just a VMS-iness. Pascal's scoping certainly enabled some profound hells in my life ... it is truly amazing what an incompetent developer can do with what was at first glance merely an 'interesting' language feature...

          As for Java - in the context of wild jumps and security vulnerabilities, the crazy VM implementations floating around are _surely_ interesting. ;}

  8. Exploit by Anonymous Coward · · Score: 3, Interesting

    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.

  9. Re:Dangling Pointers alone?? by Chirs · · Score: 2, Interesting

    Nope, because they get freed when it exits.

    In short-running programs with no persistant side effects (sysv shared memory, semas, or msg queues, for instance) there's really nothing wrong with letting the OS clean up after you.

  10. GC does eliminate bugs by Peaker · · Score: 3, Interesting
    Leaks are logical bugs, not memory management bugs, so ofcourse GC does not eliminate them.

    GC does eliminate a few classes of bugs:
    1. A specific kind of memory leak: Of ceasing to hold references to an object, but not freeing it.
    2. Pointer arithmetic and forging is impossible, so objects cannot override each others' memory. This kind of bug creates cryptic problems that are pretty hard to debug, as they are not easily contained in a single component.
    3. In manually-managed languages, leaks can cause crashes, security problems and memory overruns, while GC converts this to an "object leak". Object leaks usually translate to a memory leak (hog problem) and only rarely cause very serious problems (as in your example of leaking /etc/passwd).