SCTP FORWARD-TSN OVERFLOW

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
routineis 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_streamwhich is the first argument in 
this function. An invalid stream ID canlead to a denial of service because of 
segmentation violation but ifthe attacker is able to construct malicious data on
the location of SSN+1then 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():
 
    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. :)
 
 
 
 


Category Article

2 Responses to “c0decstuff”

What's on Your Mind...

Thank f' u C0mment