Friday, November 5, 2010

Monitoring ActiveX execution

Intro

My project this year involved hooking the load of ActiveX modules, and the next step is to actually monitor its execution and check for exploits. Let's take a step back first and have a recap of the ways in which a piece of software can be exploited.

Exploits

To exploit an application, there are two steps - injecting code, and altering the flow of execution. There are many ways of injecting code, and these are often hard to distinguish from legitimate data provisioning, so we will look at ways in which the flow of data can be altered. They are as follows:
  1. Overwriting a segment of code. This is the hammer in our knife set, as not only is it almost impossible (without having first exploited the application anyway), it destroys the underlying data.
  2. Overwriting a pointer which is used by a JMP or CALL command. This is slightly more precise, but requires somehow overwriting a specific part of memory, which is also quite difficult, but is much more feasible than #1
  3. Overwriting the pointer to the previous stack frame. This is traditionally done through an overflow of a stack-based buffer, but can also be done by a heap overflow exploit. This used to be quite common, but there is a fourth option that has come into play recently
  4. Overwriting the pointer to an exception handler. This requires that an exception be somehow triggered, and that the handler is in a module that doesn't have safeSEH activated. It's a bit of a stretch, but it is one of the ways that DEP is bypassed in modern exploits.
Now I'm by no means an expert here, but I think it's safe to rule out #1 due to code sections generally not having write privileges, and tentatively rule out #2, although a clever heap overflow exploit could take advantage of something like this (although the reason I can think of is in a non-DEP environment while trying to evade detection techniques such as the one described below, since in a non-DEP environment, #3 is much easier).

This leaves us with #3 and #4, each of which requires very different approaches. #3 should theoretically be disabled by DEP, however applications which have memory spaces that are both writeable and executable are still vulnerable, since an overflow would load the following addresses in the stack frame (memcpy, [padding DWORDs for each argument of the original function], address of w/x memory, address of w/x memory, address of shellcode, pointer to length) which would make the return instruction jump to the memcpy function, copy the shellcode to the w/x buffer, and then on return execute that memory. Given also that not every computer has DEP enabled (even windows 7 it is a per-application opt-in by default), and old hardware may not be DEP capable, this is still an area where protection is required.

An approach for protecting against attacks in category #4 requires that either modifications to the SEH chain are monitored, or that each SEH handler is tested when an exception is raised. The first option means breakpointing every SEH record in the chain (easy, but requires lots of breakpoints). The advantage of this is that the instruction pointer can be checked for every modification to the SEH chain, and suspicious modifications can be flagged (eg writes from within HeapFree and the like are probably a result of a heap overflow). The second option is a little easier but possibly not as effective, however the advantage lies in how non-invasive it is - the KiDispatchUserException function is patched to add extra checks, and these checks can include checking the page that the handler is executing from - if it's writeable (which is normally not the case for executable memory), then mark as suspicious.

Category #3 attacks are still important to block, even on modern systems. As mentioned earlier, even windows 7 uses the optin DEP rather than optout scheme for most apps, and apps that do take advantage of this can be circumvented by a return-to-libc attack, although with aslr this becomes more difficult. Since these (and most of category #2 except JMP[] ) all alter the stack, it is sufficient to breakpoint stack memory, and then either keep a separate collection of stack frames to compare after each RET instruction, or just check the permissions of the memory that the instruction pointer references.

ActiveX

Applying these to ActiveX modules is fairly straightforward. For performance reaspns it's best not to trace through the whole execution, but rather activate monitors when they're needed. Fortunately, the ActiveX interface makes this easy for us.

Each ActiveX object is instantiated by the CoGetClassObject() function, which returns an IClassFactory object, which exports a CreateInstance function. The first step then, is to hook CoGetClassObject() and then the CreateInstance function of every IClassFactory which is returned.

After this, we can detect the instantiation of every object, each of which contains a reference to its vtable, which is a structure containing references to functions and data objects. The function references point to functions in code memory, while the object references point to data memory, so these are easy to distinguish. A memory breakpoint across the whole structure allows us to hook every function that is called, and every object that is referenced.

Cunning plan
  1. Hook the CoGetClassObject function
  2. Hook the IClassFactory::CreateInstance function on every IClassFactory returned
  3. Breakpoint the vtable of every IUnknown returned
  4. Implement stack breakpoints whenever ActiveX functions are called

Once this is set up, every RET instruction will invoke a check of the stack to see if the destination memory is writeable and/or in data memory (covering #3), and at some stage this same function needs to be patched into KiDispatchUserException (covering #4) giving us fairly high confidence that any low level exploits will be detected.

Sunday, October 24, 2010

ARP reloaded: The IPv6 ping replay attack

I've been thinking about my neighbour solicitation replay concept today, and thought I would clarify a few points
  1. Sometimes (more rarely than on IPv4) these are sent to multicast/broadcast, and this can be detected by checking the destination MAC (FFFFFFFF for broadcast, 33:33:xx:xx:xx:xx for multicast)
  2. Unicast neighbour solicitation and neighbour advertisement packets can't be distinguished just from packet length (although if an obvious exchange of 2 packets occurs this can be inferred)
  3. Neighbour advertisement packets don't elicit replies
  4. The ICMPv6 packet does LOTS of things, some of which also demand a reply.
So I had a look at RFC4443 again, checked my dumps, and realised that the neighbour advertisement packets are far from useless. Let's have a look at the NA packet format:

       0                   1                   2                   3
       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |     Type      |     Code      |          Checksum             |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |R|S|O|                     Reserved                            |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                                                               |
      +                                                               +
      |                                                               |
      +                       Target Address                          +
      |                                                               |
      +                                                               +
      |                                                               |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |   Options ...
      +-+-+-+-+-+-+-+-+-+-+-+-
And then compare this to the echo request packet format

       0                   1                   2                   3
       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |     Type      |     Code      |          Checksum             |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |           Identifier          |        Sequence Number        |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |     Data ...
      +-+-+-+-+-

The NA packet has type=136, code=0 and a checksum which is merely a sum of the packet as a set of 16-bit words. The echo request has type=128, code=0, same checksum style. It also has an identifier (can be anything) and a sequence number (can be anything) and room for any amount of data afterwards.

What does this mean? This means changing the type field from 136 to 128 (flipping bit 4) and adding 0x800 to the checksum converts this packet from a neighbour advertisement packet destined to a node which specifically requested it, into an echo request packet aimed at a node which we know to be alive and which is obliged to respond!

It works fine in plaintext, but what extra challenges are added when manipulating WEP encrypted packets?
  1. The ICV is a value appended to the end of a WEP encrypted packet. It is essentially the CRC-32 of the packet's contents, and due to its calculation by interpreting the packet data as polynomials rather than integers, this can be easily altered by XORing it with the CRC-32 of the changes made to the packet.
  2. The ICMPv6 checksum is slightly harder than this. Because the packet has been XORed against the keystream, an addition of 0x800 to the checksum may require flipping more than just bit 12. For example, if the checksum was 0x3800, an addition of 0x800 would result in a value of 0x4000, which requires bits 12, 13, 14 and 15 be flipped. Since the checksum is a ones-compliment sum, the worst case of a checksum initially being 0xFFFF would require 17 flips (with bit 12 being flipped once at the start and at the end). Fortunately this is unlikely, and in actual fact 50% of the time only 1 bit would need to be flipped, so one could easily discard half the NA packets, or could continue flipping bits until replies are sent.
I haven't tested this yet, but will try to get some code together soon. If nothing else, this just shows how easy it is to modify existing attacks for WEP, but it also shows the advantage (from an attacker's point of view) of having ARP functionality built into IPv6.

Friday, October 22, 2010

IPv6 Neighbour solicitation replay attack

After setting up an ad-hoc WEP network (with the totally secure key of 12:34:56:78:90 - not actually that uncommon in practise unfortunately) I set up airodump and tested aireplay-ng's replay feature (which only does ARP packets, so gave me no results), and from the amount of traffic generated (cat /dev/urandom over ssh) aircrack-ng managed to somehow retrieve the key! I had a bit more of a search and a look through the aircrack code, and I can only assume that aircrack got lucky with its bruteforcing stage, as there is no reference anywhere to it actively cracking from IPv6 packets. This isn't totally unbelievable, given that the first 8 bytes (the LLC header) are the same between IPv4 and IPv6, but was still surprising.

Instead of trying to implement an IPv6 extension to aircrack at this stage, I thought it would be interesting to modify aireplay-ng to allow it to replay neighbour solicitation requests. This turned out to only require a couple of modifications (removing the last check from the filter_packet function for broadcast MAC addresses at line 590ish and adding checks for lengths 112 and 104 at line 2500ish), and I was ready to go. The test didn't work quite as well as I wanted it to - it only seemed to generate single advertisements a few seconds apart, but I'll put this down to the linux and bsd boxes I was using (rfc4861 makes no mention of waiting between packets) - a windows box (aka ARP farm) should give the the results I want.

Next steps:
  1. Test if my ARP farm windows boxes also work as NA farms
  2. Possibly add some code to aireplay to distinguish between NA and NS packets based on the responses elicited
  3. Look deeper into the aircrack code and find where it parses IPv4 packets, and add IPv6 functionality (and a -6 switch?)

IPv6 WEP cracking

Those of you with interests in security will no doubt know how trivial it is to break WEP keys using a known-plaintext attack to recover the first 16 bytes of keystream for each packet, and then executes the PTW attack once there are about 20,000-80,000 packets captured. The original known-plaintext attack for IPv4 relied on the predictability of the first 16 bytes of ARP packets, the first 8 being the LLC header (always the same) and the first 8 bytes of the ARP header (the same except for the last byte, but this is predictable based on the MAC addresses - ARP requests are broadcast, ARP replies are unicast). Gathering ARP packets was made even easier due to the ARP replay attack - where a captured ARP request is replayed to quickly generate large numbers of ARP replies, each encrypted with a different IV.

The PTW attack with its ability to determine non-consecutive key bytes made a passive attack more feasible too - normal IP packets can be used, and the two bytes which are assumed to be random (the identification field) can be bruteforced.

Unfortunately, WEP was more or less left untouched after 2007, with a few extensions to the PTW attack, and reapplication of KoreK's correlations, but there doesn't appear to be an IPv6 version of this yet (EDIT: aircrack actually decrypted the dump of my ipv6 test). IPv6 requires that we find ways of implementing both a replay attack, and a passive attack, however it appears that they have a lot more in common on IPv6 than they did under IPv4.

Because IPv6 uses ICMPv6 instead of ARP for its neighbour discovery (RFC 4861), only an attack on the IPv6 header is required, which makes life easier. So assuming the LLC header stays the same (will confirm later), let's have a look at the first 8 bytes:

Byte 0.5: Version (6) - this is trivial if we know the network is running IPv6 (can infer this from IPv6 multicast MAC addresses)
Bytes 1-4:  Traffic class and Flowlabel - 0 if no QoS/MPLS is implemented
Bytes 5-6: Payload length (can be deduced from packet length)
Byte 7: Next header (is probably going to be TCP, UDP or ICMP)
Byte 8: Hop limit (I've seen 255 and 1 for local packets, 128 for leaving packets, and various numbers for packets received from outside the LAN).

As we can see, quite a bit of this can be easily guessed. If we're simply intercepting LAN traffic, then it's unlikely that the traffic class and flow label fields are giong to be used, making our first 4 bytes 0, the payload byte is guessable (total 6 predictable bytes so far). The next header field is in practise only going to be one of 3 values (and if the length matches can be assumed to be ICMP, or if it is too long can be assumed to be TCP/UDP), which leaves us with the hop limit field, which can easily be bruteforced (as the current IPv4 attack shows that bruteforcing 2 bytes is feasible).

For a replay attack, an ICMPv6 Neighbour solicitation packet is required. Unfortunately for us, there is no obvious way to distinguish between solicitation and advertisement packets, so it is somewhat trial and error, at least when unicast solicitations happen. From what I've seen in my (incredibly limited) tests, these happen more frequently than ARP exchanges on IPv4, which should make it easier to capture neighbour solicitations.

Neighbour solicitation/advertisement packets are always 64/72 bytes long (only counting the IPv6 and ICMP headers + payload), so packets of this length can be assumed (possibly incorrectly) to be neighbour solicitation/advertisements, which gives us a 50/50 chance of getting a solicitation packet. But what if we have an advertisement, and want to convert it into a solicitation, what do we need to do?

Firstly, the type needs to be changed from 136 to 135. Secondly, the field that was used for flags is reserved in the solicitation packet, so this should be zeroed (trial and error on the 3 currently used bits, or just assume the recipient is going to ignore this field). Thirdly, if the option is set (dest link-layer address) this needs to be changed, which can easily be done by xoring the MAC addresses from the unencrypted link layer header. This leaves us with one final problem - swapping the source and destination IP addresses from the IP header. To do this would require inside knowledge, and it is probably easier to break the WEP key passively than try and guess these, but what would we need to do if we could figure them out? The checksum used here isn't cryptographically secure, and can be updated in the same way as the WEP ICV because we know which bits have been altered, so the packet can then be used for replaying... but this isn't realistic.

In summary:
  • WEP is still a stupid idea
  • If QoS and MPLS aren't being used, the passive attack is easier than under IPv4
  • The new "ARP" replay attack should work just as well as the old one


References:
  1. Breaking 104 bit WEP in less than 60 seconds 
  2. http://tools.ietf.org/html/rfc4861
  3. http://tools.ietf.org/html/rfc4443

    Wednesday, October 20, 2010

    IPv6 touchup

    My home network is largely sorted for IPv6 with just a couple of problems

    1. My windows boxes don't get default routes
    2. My linux boxes use the prefix from the route advertisement rather than from DHCPv6
    It turns out you need to explicitly tell radvd to send out a default route or else windows won't pick it up, whereas linux does this automatically. The following radvd.conf file fixed this for me:

    interface eth1
    {
       AdvSendAdvert on;
       AdvManagedFlag on;
       MinRtrAdvInterval 3;
       MaxRtrAdvInterval 10;
       prefix 2001:1234:5678:1::/64
       {
          AdvOnLink on;
          AdvAutonomous on;
          AdvRouterAddr on;
       };
       route ::/0{
       };
    };  


    Meanwhile the DHCPv6 problem was due to debian not having a DHCPv6 client by default. I downloaded wide-dhcpv6-client, played with the config for a bit, and finally got something that worked:

    interface eth0
    {
      send ia-na 1;
      request domain-name-servers;
      request domain-name;
    };

    id-assoc na 1 {
    };


    One point to remember is that you need to manually create a duid, and this can be done with wide_mkduid.pl (which you can find easily on google), and this allows you to query DHCPv6 from debian.

    This still doesn't fix windows xp however, but it's getting old now, so hopefully it won't be an issue once people finally start migrating properly to IPv6.

    In other news, the gsm729 codec worked well with my SIP server, allowing me  to make softphone calls to cellphones, but when using my n95 through the sip server I still couldn't hear anything back... so a bit more investigation to be done yet.

    Sunday, October 17, 2010

    79228162514264337593543950336 addresses

    My subnet request got approved! Let's go through some of the details.

    Naturally, the first thing to set up was the firewall (because the best way to implement security is from the start). After hacking together bits and pieces from various scripts on the net, I managed to put together something that let everything out and only ssh in.

    # Always accept loopback traffic
    ip6tables -A INPUT -i lo -j ACCEPT

    # Always accept traffic from the inside
    ip6tables -A INPUT -i eth1 -j ACCEPT
    # Allow all ICMP traffic
    ip6tables -A INPUT -p icmpv6 -j ACCEPT

    # make sure in-new chain is open
    ip6tables -N in-new
    ip6tables -N fw-new

    # Strict sixxs
    ip6tables -A INPUT -i sixxs -m state --state RELATED,ESTABLISHED -j ACCEPT
    ip6tables -A INPUT -i sixxs -m state --state NEW -j in-new
    ip6tables -A FORWARD -i sixxs -o eth1 -m state --state NEW -j fw-new

    # Inbound sixxs connections
    ip6tables -A in-new -p tcp -m tcp --dport 22 --syn -j ACCEPT
    ip6tables -A in-new -p udp -m udp --dport 53 -j ACCEPT
    ip6tables -A in-new -j REJECT
    ip6tables -A fw-new -j REJECT

    # Reject everything else
    ip6tables -A INPUT -i sixxs -j REJECT


    With this set up, the next step was to get DHCP going. Unfortunately it wasn't that simple - address, router, and DNS setup is split into two separate categories, with router advertisement and IP autoconf done through ICMPv6 (requiring a router advertisement server) and DNS and some IP hints done through DHCPv6.

    I installed radvd, and after some playing ended up with the following conf file:

    interface eth1
    {
       AdvSendAdvert on;
       AdvManagedFlag on;
       MinRtrAdvInterval 3;
       MaxRtrAdvInterval 10;
       prefix 2001:1234:5678:1::/64
       {
          AdvOnLink on;
          AdvAutonomous on;
          AdvRouterAddr on;
       };
    };


    The AdvManagedFlag was something that tripped me up, it tells hosts to still look for DHCPv6 for DNS and further IP hints, while AdvAutonomous meant that they would do global IPv6 autoconf.

    Next step was DHCPv6. There were a couple of packages, but I ended up settling for wide-dhcpv6. The config for this looked like so:

    option domain-name-servers fe80::4428:200:94:20;

    interface eth1 {
            address-pool pool1 3600;
            send domain-name-servers;
    };

    pool pool1{
            range
    2001:1234:5678:1:cafe:f00d::1000 to 2001:1234:5678:1:cafe:f00d::2000;
    };

    host gary {
            duid 00:03:00:07:12:7a:34:02:38:a0:9a:47:7f:09;
            address
    2001:1234:5678:1:cafe:f00d::123 1800 7200;
    };


    I was surprised with the DUID entries, I have yet to find out why this is used instead of MAC address for stateful DHCPv6 setup, but nonetheless it's working now.

    Next step was to get ipv6 set up on all my machines. My windows 7 box proved most difficult, however once it was sorted it even picked up the DHCPv6 IP hints without any problem, although I still had to manually set a route in netsh.

    My debian laptop worked fine, and as with my archlinux virtual machine, the ipv6 module needed to be loaded (added to /etc/modules to auto load on boot).

    Windows XP was just as easy - netsh interface ipv6 install, and it picked up everything fine.

    I'm enjoying this tunnel, and as you can see from the configs, I've already started finding ways to play with my addresses. I can now see the dancing kame.net turtle, and interestingly the list of friends in chat on facebook looks different on their ipv6 page (tiled by profile photo, rather than listed).

    Next step: start using ipv6 versions of things, and finding ipv6 services to use!

    Untl then though, I need to get my codecs sorted, so I can start calling mobile phones through my VOIP setup...

    Friday, October 15, 2010

    IPv6: The final frontier

    My lecturer Andy Linton was going into detail about IPv6 the other day and suggested I sign up to sixxs.net and get my home network running IPv6, so I did just that.

    They use a system where you get given a certain amount of credits when you sign up, spend them to make changes to your account (adding/deleting tunnels and subnets etc) or when your connection times out, and slowly accumulate them for keeping your tunnel open for a long period of time. There are three different types of tunnel - static IP with straight 6-in-4, dynamic IP with the heartbeat protocol to notify of any IP changes which also uses straight 6-in-4, and a system called AYIYA (anything in anything) which encapsulates IPv6 in UDP.

    Thinking that my brand new router would be able to route IPv6 without any issues (or at least 6-in-4) I mistakenly signed up for a heartbeat tunnel, only to find out that short of explicit routing I would need to have a DMZ server set up... so I settled on the AYIYA protocol in the end.

    I got their AICCU client set up, it has a package in the debian repository which made everything really easy, and the only moderately hard bit was making sure I removed all the autoconf IPv6 addresses from eth1 (long story why I don't have eth0 but it's a testament to my unsuitability as a system admin) before adding new ones, only to find that all the original addresses were fe80::/64 anyway so the whole exercise was almost a total waste of time.

    After configuring my addresses on my windows 7 box (todo: install DHCPv6 server) and rebooting bind, everything was pinging fine, but none of my browsers would go to IPv6 websites. I assume it's to do with not having a global subnet yet, so that'll have to wait until after my subnet request gets approved.

    Todo:

    Choose a firewall (ufw? modify my old iptables script?)
    Firewall server (because a global IPv6 address is going to take me back to dialup days where no password = free access to SMB shares, but that's another story for another day)
    Install DHCPv6 (I've ALWAYS wanted to have a DHCP server that hands out global IP addresses)
    Configure DNS (I'm thinking I'll keep my dummy 10.1.1.* records for my domains that are hosted locally, but add some reverse DNS entries once I get that delegated)

    Thursday, October 7, 2010

    Explicit buffer overflow detection

    I've spent the bulk of this year working on my Honours project of explicitly detecting the loading of ActiveX modules in Internet Explorer, and the obvious follow-on is explicit detection of the the exploit within the module.

    That would take quite a bit of work however, but I had another idea after seeing Microsoft's recent out-of-band patch release. One of the exploits was for the native font handler in Windows, and looking at the code I could see stack cookies within each function, but these appeared to be hardcoded (and thus predictable from an attacker's point of view). This got me thinking - why not take advantage of exception handlers and breakpoints to create a better overflow detection and add that to the security cookie stuff that's already present in modern compilers?

    For those of you that spend more time finding bugs in your own code rather than searching through somebody else's work of art for something exploitable, here's a quick intro to buffer overflows.

    void vulnerable(char* in){
        char test[10];
        strcpy(test, in);
        printf("argv[1] = %s\n", test);
    }

    What's wrong with this picture? If you've written something like this before, you've probably been presented with a "Segmentation Fault" error and an abrupt crash. The bug here is fairly straight-forward - the buffer is only 10 bytes long (which will hold 9 chars + terminating null char), so passing an argument with more than 9 characters will start overwriting other memory.... but what exactly?

    When you call a function, the return address (next instruction after your function call) is saved onto the stack, and the stack pointer is decreased (stack memory grows downwards). Declaring local variables like this makes the compiler reserve space on the stack, giving you a buffer just below your return address in memory. See where this is going? If you overflow your buffer (string copies go from low to high in memory) you'll start overwriting your other stack variables until you eventually overwrite the return address, and when the function ends, it attempts to relocate to where it was called from, but actually ends up somewhere completely different. If you do it accidentally, you'll probably end up in unexecutable memory, triggering a segfault. If you craft your input well, you can execute anything you like!

    Compiler designers got around this by setting stack cookies, making the compiled code look more like this:

    void vulnerable(char* in){
        char test[10];
        int cookie=34531;
        strcpy(test, in);
        printf("argv[1] = %s\n", test);
        if(cookie!=34531) ThrowException();
    }

    The cookie sits just below the return address, so will be overwritten before the return address is. Then when the function is about to return, the cookie is checked, and if it isn't the expected value, then we know an overflow has occurred, and can crash the program.

    There are two problems with this though:
    1. This only detects the overflow just before the function exits
    2. Hardcoded stack cookies can be accounted for by hackers, so really only detect unintentional overflows
    We can fix #2 by randomly generating these at runtime, but we're still being quite passive about it. This is where the exception handlers and breakpoints come in. Since we're already wrapping our functions (or at least our compilers are doing it for us), it's quite reasonable to want to extend these.

    Our new framework has the following things then:
    1. A preamble, which installs an exception handler and breakpoints the return address
    2. An exception handler, which explicitly detects modifications to the return address
    3. A postamble, which removes the breakpoints before leaving.
    I've said enough, it's time for the code.

    // oflow.cpp : Defines the entry point for the console application.
    // thanks to http://www.yashks.com/breakpoints.html for an excellent reference
    // heavily based on SimpleSEH http://www.oldschoolhack.de/tutorials/seh.pdf

    #define WIN32_LEAN_AND_MEAN        // Exclude rarely-used stuff from Windows headers
    #include <stdio.h>
    #include <tchar.h>
    #include <windows.h>

    // dirty global var
    DWORD retval=0;
    DWORD byteswritten=0;

    LONG WINAPI TopExceptionFilter(EXCEPTION_POINTERS* ep){
        if(ep->ExceptionRecord->ExceptionCode==EXCEPTION_SINGLE_STEP){
            if(ep->ContextRecord->Dr6 & 1){
                // overflow
                printf("Overflow detected... ");
                // overwrite
                if(WriteProcessMemory(GetCurrentProcess(), (LPVOID) (ep->ContextRecord->Dr0), &retval, 4, &byteswritten))
                    printf("Return address patched\n");
                else
                    printf("Patch failed\n");
                return EXCEPTION_CONTINUE_EXECUTION;
            }   
        }
        return EXCEPTION_CONTINUE_SEARCH;
    }

    void Preamble(PDWORD d){
        // install handler
        DWORD prev;
        printf("Return address of last function: %X\n", *d);
        retval=*d;
        SetUnhandledExceptionFilter(TopExceptionFilter);

        CONTEXT ContextRecord={CONTEXT_DEBUG_REGISTERS};
        printf("Add h/w breakpoint at %X\n", d);
        // add hw breakpoint
        ContextRecord.Dr0=(DWORD)d;
        ContextRecord.Dr6=0;
        ContextRecord.Dr7 |= 0xD0001; // 4 bytes wide, break on writes only
        printf("DR0 set to %X\nDR7 set to %X\n", ContextRecord.Dr0, ContextRecord.Dr7);
       
        if(!SetThreadContext(GetCurrentThread(), &ContextRecord)){
            printf("Couldn't write context\n");
        }
    }

    void Postamble(){
        // remove hook
        CONTEXT ContextRecord={CONTEXT_DEBUG_REGISTERS};
        printf("Leaving, remove breakpoint\n");
        // add hw breakpoint
        ContextRecord.Dr0=0;
        ContextRecord.Dr6=0;
        ContextRecord.Dr7=0; // 4 bytes wide
        printf("DR0 set to %X\nDR7 set to %X\n", ContextRecord.Dr0, ContextRecord.Dr7);
       
        if(!SetThreadContext(GetCurrentThread(), &ContextRecord)){
            printf("Couldn't write context\n");
        }
    }

    _declspec(noinline) void vulnerable(char* in){
        char test[10];

        PDWORD stack;
        _asm{
            mov stack,ebp
        }
        stack++;
        Preamble(stack);
        printf("Doing strcpy\n");
        strcpy(test, in);
        printf("argv[1] = %s\n", test);
        Postamble();
    }

    int _tmain(int argc, _TCHAR* argv[])
    {
        if(argc>1)
            vulnerable(argv[1]);
        else
            printf("Takes an argument\n");
        printf("done\n");
        return 0;
    }



    That didn't hurt now, did it? Here's the output

    C:\Documents and Settings\Driverz\My Documents\Visual Studio 2005\Projects\oflow
    \release>oflow garygarygarygarygarygary
    Return address of last function: 40121B
    Add h/w breakpoint at 12FF74
    DR0 set to 12FF74
    DR7 set to D0001
    Doing strcpy
    Overflow detected... Return address patched
    Overflow detected... Return address patched
    Overflow detected... Return address patched
    Overflow detected... Return address patched
    argv[1] = garygarygarygarygary←↕@
    Leaving, remove breakpoint
    DR0 set to 0
    DR7 set to 0
    done


    And we're done. You'll see that each time the return address was overwritten, it was immediately returned to its original value, so the function could return normally. In a lot of circumstances this isn't going to be enough - so much of the stack is going to be totally mangled - but you could get a just-in-time debugger to be invoked here to patch up the memory properly, rather than when the security cookie kicks in, or when a poorly coded exploit fails and crashes your application further down the line.

    There are a few other issues to consider here, so we'll look at them one by one
    1. How do I debug it? Debuggers use the hardware breakpoints themselves, so the detection breaks (making it fun trying to iron out the bugs). The simplest way is to formally prove that it does what it needs to, and leave it out until you've finished developing your code
    2. There are only 4 hardware breakpoints, what if I have more than 4 nested functions? This shouldn't be an issue - each invocation of the Preamble() function can store the previous hook in a Stack-type collection, since each function's call stack is going to be below its parent in memory, so overwriting of the current return address will be detected before the stacks of other functions are mangled.
    3. Will this fix all exploits? No. It only detects stack overflows, not heap overflows (which are another story and are probably too extensive to protect with hardware breakpoints, but memory breakpoints are a possibility).
    4. Can this be realistically applied to compiled binary code? Not easily. This is the principle I would like to use, but it would have to be done alongside a debugger which traces through each instruction, and manually invoking preamble and postamble before each CALL and RET, which is going to be really slow, but should be okay for research projects.
    All done! Big thanks to:
    http://www.yashks.com/breakpoints.html - good breakpoint reference
    http://www.oldschoolhack.de/tutorials/seh.pdf - where the bulk of my code was stolen from

    PS: Make sure you build the code with /GS- in compiler commandline and /SAFESEH:NO in linker commandline if building with visual studio

    Wednesday, October 6, 2010

    VOIP up and running

    It's been a big afternoon, where do I start?

    My asterisk config isn't perfect yet, but I've got users set up at least. My first choice of VOIP company wanted to play silly buggers by charging me more and then asking me how much extra they had charged as a security measure, but wouldn't accept a value in New Zealand dollars, and wanted a 30 day turn-around before letting me call my own country, so I figured I'd let it slide and try somewhere else. I ended up signing up with voipvoip.com, eventually got the worst of the config set up (made more amusing by the config being modularised, reminded me of trying to configure freeradius and apache after they changed their config structure), with some basic outgoing extensions, and managed to get calls going through to my cellphone.


    I had two main problems though:
    1. The sound was really jittery
    2. I could hear from my cellphone to my softphone but not vice-versa
     It turns out these were both related, but we'll get back to that in a second.

    The next fun part was configuring my N95 to register with my server. I found a ton of guides, the simplest one being here. My phone wanted to register as a user, so one of these had to be set up too (best tute here), and then everything was all hunky dory. But then I came across the fun part - when I called my home number from my cellphone, the connection was fine, but no sound was going through either way! I had a look at my asterisk output, and it was complaining about conversion paths between ulaw and g729. Turns out you need a license to use it, or at least the codec, and I had neither. However, removing g729 from my config and letting it default to ulaw not only made the sound work, but killed the jitter I was getting!

    The only thing stopping me using this now for cheap calls (apart from the lag from going to america and back) is how much of a pain it is to get Asterisk to handle a plus sign at the start of the extension. Otherwise, I'm really happy with this setup for now.

    [quick edit] Got it parsing + signs now (thanks), but it turns out the sound doesn't work with calls to cellphones, only landlines... I'll have another look tomorrow.

    Tuesday, October 5, 2010

    Venturing into telephony

    Coming to the end of my degree, I figured now would be a good time to finally find out what a network engineer can actually do for a job. I had a look at what jobs Nokia Siemens had on offer in Wrocław, and realised I don't know anything about VOIP, let alone SS7 and other suites, so lacking a direct connection to a telephone network I figured it would be best to download and install Asterisk.

    The install went smoothly on my old debian box, and now I'm just waiting for the SIP provider to inform me that no, I haven't stolen my own credit card, and that yes, they are happy to take my money off me so I can make 20 second test calls to people all around the world until the novelty finally wears off.

    Oddly enough there's one practical reason for this apart from the experience - my n95 has SIP capabilities, so I'm hoping this means cheap cellphone calls when I'm at home - because I still can't be bothered plugging my old skype phone back in.

    Hopefully it'll be ready for testing, further config, and a security tidy-up when I get home tomorrow!