Open Source im professionellen Einsatz

Asterisk: Overflow mit vielen Nullen

Im Telefonie-Server Asterisk sind erneut einige Schwachstellen beim Verarbeiten von SIP-Paketen entdeckt worden.

Ein Angriff ist mit geschickt konstruierten SIP-Anfragen möglich, wodurch ein Buffer-Overflow ausgelöst wird. Damit kann ein entfernter Angreifer Speicherbereiche manipulieren und Asterisk zum Absturz bringen.

SIP-Anfragen landen im Asterisk-Programmcode zunächst in der Struktur "sip_request":

struct sip_request {
        ptrdiff_t rlPart1;      /*!< Offset of the SIP Method Name or "SIP/2.0" protocol version */
        ptrdiff_t rlPart2;      /*!< Offset of the Request URI or Response Status */
        int len;                /*!< bytes used in data[], excluding trailing null terminator. Rarely used. */
        int headers;            /*!< # of SIP Headers */
        int method;             /*!< Method of this request */
        int lines;              /*!< Body Content */
        unsigned int sdp_start; /*!< the line number where the SDP begins */
        unsigned int sdp_count; /*!< the number of lines of SDP */
        char debug;             /*!< print extra debugging if non zero */
        char has_to_tag;        /*!< non-zero if packet has To: tag */
        char ignore;            /*!< if non-zero This is a re-transmit, ignore it */
        char authenticated;     /*!< non-zero if this request was authenticated */
        ptrdiff_t header[SIP_MAX_HEADERS]; /*!< Array of offsets into the request string of each SIP header*/
        ptrdiff_t line[SIP_MAX_LINES];     /*!< Array of offsets into the request string of each SDP line*/
        struct ast_str *data;
        struct ast_str *content;
        /* XXX Do we need to unref socket.ser when the request goes away? */
        struct sip_socket socket;          /*!< The socket used for this request */
        AST_LIST_ENTRY(sip_request) next;
};

Der "len"-Eintrag speichert dabei die Byte-Länge der in "data" abgelegten Daten, wobei "ast_str" eine in der Header-Datei "include/asterisk/strings.h" definierte dynamische String-Struktur ist. Diese speichert für einen String noch ein paar zusätzliche Daten, um ein dynamischeres Speichermanagement zu erlauben, dient also als eine Art vereinfachter STL-String-Klasse.

Das eigentliche Einlesen von SIP-Anfragen und das Füllen der obigen Strukturen erledigt die Funktion "sipsock_read()" in "chan_sip.c":

static int sipsock_read(int *id, int fd, short events, void *ignore)
{
        struct sip_request req;
        struct ast_sockaddr addr;
        int res;
        static char readbuf[65535];

        memset(&req, 0, sizeof(req));
        res = ast_recvfrom(fd, readbuf, sizeof(readbuf) - 1, 0, &addr);
        if (res < 0) {
#if !defined(__FreeBSD__)
                if (errno == EAGAIN)
                        ast_log(LOG_NOTICE, "SIP: Received packet with bad UDP checksum\n");
                else
#endif
                if (errno != ECONNREFUSED)
                        ast_log(LOG_WARNING, "Recv error: %s\n", strerror(errno));
                return 1;
        }

        readbuf[res] = '\0';

        if (!(req.data = ast_str_create(SIP_MIN_PACKET))) {
                return 1;
        }

        if (ast_str_set(&req.data, 0, "%s", readbuf) == AST_DYNSTR_BUILD_FAILED)  {
                return -1;
         }

        req.len = res;
        req.socket.fd = sipsock;
        set_socket_transport(&req.socket, SIP_TRANSPORT_UDP);
        req.socket.tcptls_session       = NULL;
        req.socket.port = htons(ast_sockaddr_port(&bindaddr));

        handle_request_do(&req, &addr);
        deinit_req(&req);

        return 1;
}

Hier ist ein wenig Suchen notwendig, um den Fehler zu finden. Zunächst einmal kopiert die Netzwerkdaten via "ast_recvfrom()" in einen statischen Lesepuffer namens "readbuf" kopiert. Die Funktion "ast_recvfrom()" liefert in "res" hierbei die Länge der eingelesenen Zeichen zurück. Da der Lesepuffer gleich weiterverwendet wird, aber möglichweise nicht komplett gefüllt wurde, wird die darin gespeichert Zeichenkette mit "readbuf[res] = '\0'" terminiert. Nun müssen die eingelesenen Daten in die Struktur "req" kopiert werden, wobei die eigentlichen Daten in "req.data" abgelegt werden sollen. Dazu muss der dynamische String "req.data" zunächst mit "ast_str_create()" erzeugt werden, und anschließend werden die "readbuf"-Daten mit "ast_str_set()" dort hineinkopiert. Da zuvor der String terminiert wurde, und es sich hier um eine Stringkopie handelt, werden alle Zeichen bis zur "\0" kopiert. Zu diesem Zeitpunk befindet sich an der Adresse "&req.data" ein allozierter Speicherbereich, der genau so groß ist, dass er alle Zeichen des Puffers "readbuf" bis zur "\0" enthält. Am Ende der Funktion werden jetzt noch die anderen Felder der "req"-Struktur gesetzt, darunter auch die Länge des Dateneintrags, der von "res" übernommen wird.

Dieser Programmablauf funktioniert einwandfrei, solange die SIP-Daten beziehungsweise der "readbuf"-Puffer selbst keine "\0"-Zeichen enthalten. Enthält dieser Puffer nämlich eine große Anzahl dieses Terminierungszeichens, so wird "res" einen großen Wert zurückliefern. Die "ast_str_set"-Operation wird aber für "req.data" nur Speicher bis zur ersten Null reservieren, denn danach interpretiert diese Funktion die Zeichenkette als terminiert. Für "req.data" ist nun deutlich weniger Speicher alloziert als im eigentlichen "readbuf"-Puffer enthalten. Das Problem: Wird nun "req.len" auf "res" gesetzt, so ist dieser Wert unter Umständen viel größer als der für "req.data" allozierte Speicherbereich. Verwendet eine andere Asterisk-Funktion "req.len" und verlässt sich auf dessen Korrektheit, kann es zu einem Überlauf kommen.

Das Problem lässt sich korrigieren, indem man "req.len" nicht auf "res", sondern auf "ast_str_strlen(req.data)" setzt. Dies gibt die korrekte Stringlänge und damit auch die korrekte Größe des zuvor allozierten Speicherbereichs zurück.

Betroffen sind die Asterisk-Versionen 1.6 vor 1.6.2.18.1 sowie 1.8 vor 1.8.4.3.

comments powered by Disqus

Stellenmarkt

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.