xorl %eax, %eax

CVE-2009-0065: SCTP FORWARD-TSN Overflow

leave a comment »

On 5 January 2009, Wei Yongjun of Fujitsu disclosed this vulnerability which was patched by D. Miller on 26 December 2008. This security bug was found on the SCTP (Stream Control Transmission Protocol) subsystem of the Linux kernel and it affects kernel releases prior to 2.6.28-git8. It is classified as a critical vulnerability since it is remotely exploitable and present on the default Linux kernel. In addition, it can lead to remote code execution! As we read from the SCTP’s RFC that FORWARD-TSN is used to send TSN (Transmission Sequence Number) so that you can forward in a session. The following code snippets were taken from Linux kernel 2.6.28 release. As Eugene Teo explained, the bug can be reached through the following path:

1079 /* This is the side-effect interpreter.  */
1080 static int sctp_cmd_interpreter(sctp_event_t event_type,
1081                                sctp_subtype_t subtype,
1082                                sctp_state_t state,
1083                                struct sctp_endpoint *ep,
1084                                struct sctp_association *asoc,
1085                                void *event_arg,
1086                                sctp_disposition_t status,
1087                                sctp_cmd_seq_t *commands,
1088                                gfp_t gfp)
1089 {

This function is part of net/sctp/sm_sideeffect.c and is used along with state routines for SCTP traffic. The above function is used to interpret the requested command on an SCTP packet. It continues like:

       ...
1092        sctp_cmd_t *cmd;
       ...
1112        while (NULL != (cmd = sctp_next_cmd(commands))) {
1113                switch (cmd->verb) {
       ...
1169
1170                case SCTP_CMD_PROCESS_FWDTSN:
1171                        sctp_cmd_process_fwdtsn(&asoc->ulpq, cmd->obj.ptr);
1172                        break;
       ...
1582 }
1583

At line 1092 an sctp_cmd_t structure is defined, this structure can be found in net/sctp/commands.h and is a simple linked list structure that contains SCTP commands, here is its type definition at commands.h:

196 typedef struct {
197        sctp_cmd_t cmds[SCTP_MAX_NUM_COMMANDS];
198        __u8 next_free_slot;
199        __u8 next_cmd;
200 } sctp_cmd_seq_t;

Next, at line 1112 we can see that the while loop iterates through all of the commands stored in the linked list and using conditions it transfers code execution to the appropriate function. If the command is SCTP_CMD_PROCESS_FWDTSN it’s simply translates to:

46 typedef enum {
47        SCTP_CMD_NOP = 0,       /* Do nothing. */
      ...
97        SCTP_CMD_PROCESS_FWDTSN, /* Skips were reported, so process further. */
      ...
109 } sctp_verb_t;
110

As seen on include/net/sctp/command.h. If this is the case, it passes the execution flow to sctp_cmd_process_fwdtsn() with two arguments that we’ll discuss later. The latter routine is found at the same source code file (sm_sideeffect.c) and it performs the following operations:

823 /* Process variable FWDTSN chunk information. */
824 static void sctp_cmd_process_fwdtsn(struct sctp_ulpq *ulpq,
825                                     struct sctp_chunk *chunk)
826 {
827        struct sctp_fwdtsn_skip *skip;
828        /* Walk through all the skipped SSNs */
829        sctp_walk_fwdtsn(skip, chunk) {
830                sctp_ulpq_skip(ulpq, ntohs(skip->stream), ntohs(skip->ssn));
831        }
832
833        return;
834 } 

The first argument (sctp_ulpq structure) contains the ULP (Upper Layer Protocol) sockets API information where the second one (sctp_chunk structure), is a chunk of an SCTP packet as it is defined on RFC2960. It includes a chunk header and chunk specific content. Then, at line 830, it jumps to sctp_ulpq_skip() routine and passes th three arguments, the ULP structure, the SCTP stream ID and finally, the SSN (Stream Sequence Number) of the stream both in network byte order (which means big endian to little endian when used on x86). Let’s move to this function:

936 /* Skip over an SSN. This is used during the processing of
937  * Forwared TSN chunk to skip over the abandoned ordered data
938  */
939 void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn)
940 {
941        struct sctp_stream *in;
      ...
943        /* Note: The stream ID must be verified before this routine.  */
944        in  = &ulpq->asoc->ssnmap->in;
      ...
950        /* Mark that we are no longer expecting this SSN or lower. */
951        sctp_ssn_skip(in, sid, ssn);

If we don’t have an old SSN then the function at line 951 is called and passes our stream, stream ID and SSN directly to sctp_ssn_skip(). The above function is located at net/sctp/ulpqueue.c and the routine called at line 951 is an inline function from include/net/sctp/structs.h which does the following:

515 static inline void sctp_ssn_skip(struct sctp_stream *stream, __u16 id, 
516                                 __u16 ssn)
517 {
518        stream->ssn[id] = ssn+1;
519 }

Since we control the ID and the SSN we can write an arbitrary value to a user controlled offset of the structure sctp_stream which is the first argument in this function. An invalid stream ID can lead to a denial of service because of segmentation violation but if the attacker is able to construct malicious data on the location of SSN+1 then code execution is possible. For example, if the attacker stores the shellcode somewhere on the userspace and gets its address, then using the above vulnerability constructs a packet with SSN+1 equal to the address of the shellcode and stream ID equal to a stored pointer that gets executed either by JMP, or CALL, RET etc. he can gain code execution in the context of the kernel. To try this, use your favorite packet generator to construct a FORWARD-TSN chunk of an SCTP packet with these values:

FORWARD-TSN chunk
  Type             = 192
  Flags            = 0
  Length           = 172
  NewTSN           = 99
  Stream           = 10000
  StreamSequence   = 0xffff

Since ID and the SSN are __u16 we are limited to only 2 bytes overwrite which makes the exploitation more difficult however still feasible. To fix this vulnerability the final patch was to add the following:

        struct sctp_fwdtsn_hdr *fwdtsn_hdr;
+       struct sctp_fwdtsn_skip *skip;
        __u16 len;

This structure was added to sctp_disposition_t sctp_sf_eat_fwd_tsn() which can be found at net/sctp/sm_statefuns.c in order to perform the following check a few lines later:

                goto discard_noforce;
 
+       /* Silently discard the chunk if stream-id is not valid */
+       sctp_walk_fwdtsn(skip, chunk) {
+               if (ntohs(skip->stream) >= asoc->c.sinit_max_instreams)
+                       goto discard_noforce;
+       }
+
        sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_FWDTSN, SCTP_U32(tsn));

And similarly, addition of the same segments to sctp_disposition_t sctp_sf_eat_fwd_tsn_fast() like this:

        struct sctp_fwdtsn_hdr *fwdtsn_hdr;
+       struct sctp_fwdtsn_skip *skip;
        __u16 len;

And the bounds check:

               goto gen_shutdown;

+       /* Silently discard the chunk if stream-id is not valid */
+       sctp_walk_fwdtsn(skip, chunk) {
+               if (ntohs(skip->stream) >= asoc->c.sinit_max_instreams)
+                       goto gen_shutdown;
+       }
+
        sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_FWDTSN, SCTP_U32(tsn));

This was an interesting vulnerability but don’t panic, if you don’t have SCTP networking enabled then you are not vulnerable to this security bug. :)

Written by xorl

January 28, 2009 at 14:03

Posted in bugs, linux

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s