diff --git a/Dockerfile b/Dockerfile index 88f2b25..004177b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,25 @@ -FROM python:2-alpine +# Build stage +FROM alpine:3.6 AS builder -COPY requirements.txt /usr/src/app/requirements.txt -WORKDIR /usr/src/app -RUN pip install --no-cache -r requirements.txt +RUN apk add --no-cache python3 + +RUN python3 -m venv /app -COPY . /usr/src/app/ +COPY . /app/src/ +RUN /app/bin/pip install -r /app/src/requirements.txt +RUN chown -R nobody:nobody /app + + +# Prod stage +FROM alpine:3.6 + +RUN apk add --no-cache python3 + +COPY --from=builder /app /app + +WORKDIR /app/src EXPOSE 8000 +ENV PYTHONUNBUFFERED=1 -CMD ['python', 'weblogic_server.py'] +CMD ["/app/bin/python3", "weblogic_server.py", "--verbose"] \ No newline at end of file diff --git a/README.md b/README.md index e177f20..37d053a 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,24 @@ It is released under the MIT license for the use of the community, pull requests # Usage -* Run without parameters to listen on default port (8080): - - > python weblogic_server.py - -* Run with --help to see other command line parameters + Usage: weblogic_server.py [OPTIONS] + + A low interaction honeypot for the Oracle Weblogic wls-wsat component + capable of detecting CVE-2017-10271, a remote code execution vulnerability + + Options: + -h, --host TEXT Host to listen + -p, --port INTEGER Port to listen + -v, --verbose Verbose logging + --hpfserver TEXT hpfeeds Server + --hpfport INTEGER hpfeeds Port + --hpfident TEXT hpfeeds Ident + --hpfsecret TEXT hpfeeds Secret + --hpfchannel TEXT hpfeeds Channel + --serverid TEXT hpfeeds ServerID/ServerName + --help Show this message and exit. + +Run without parameters to listen on default port (8000). See also diff --git a/requirements.txt b/requirements.txt index 3af12f5..acbcc5a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ click==6.7 +hpfeeds3==0.9.6 diff --git a/weblogic_server.py b/weblogic_server.py index 3e3922b..187828f 100644 --- a/weblogic_server.py +++ b/weblogic_server.py @@ -3,17 +3,44 @@ import os import socket import logging -from StringIO import StringIO +from io import BytesIO from xml.etree import ElementTree -from BaseHTTPServer import HTTPServer -from SocketServer import ThreadingMixIn -from SimpleHTTPServer import SimpleHTTPRequestHandler +from socketserver import ThreadingMixIn +from http.server import HTTPServer, SimpleHTTPRequestHandler +import datetime +import json class NonBlockingHTTPServer(ThreadingMixIn, HTTPServer): pass +class hpflogger: + def __init__(self, hpfserver, hpfport, hpfident, hpfsecret, hpfchannel, serverid, verbose): + self.hpfserver = hpfserver + self.hpfport = hpfport + self.hpfident = hpfident + self.hpfsecret = hpfsecret + self.hpfchannel = hpfchannel + self.serverid = serverid + self.hpc = None + self.verbose = verbose + if (self.hpfserver and self.hpfport and self.hpfident and self.hpfport and self.hpfchannel and self.serverid): + import hpfeeds + import hpfeeds + try: + self.hpc = hpfeeds.new(self.hpfserver, self.hpfport, self.hpfident, self.hpfsecret) + logger.debug("Logging to hpfeeds using server: {0}, channel {1}.".format(self.hpfserver, self.hpfchannel)) + except (hpfeeds.FeedException, socket.error, hpfeeds.Disconnect): + logger.critical('hpfeeds connection not successful') + def log(self, level, message): + if self.hpc: + if level in ['debug', 'info'] and not self.verbose: + return + message['serverid'] = self.serverid + self.hpc.publish(self.hpfchannel, json.dumps(message)) + + class WebLogicHandler(SimpleHTTPRequestHandler): logger = None @@ -32,6 +59,15 @@ class WebLogicHandler(SimpleHTTPRequestHandler): basepath = os.path.dirname(os.path.abspath(__file__)) alert_function = None + listening_port = None + + hpfl = None + data = None + timestamp = None + req_classification = 'request' + req_category = 'info' + vulnerability = None + payload = None def setup(self): SimpleHTTPRequestHandler.setup(self) @@ -57,15 +93,19 @@ def send_head(self): def do_POST(self): data_len = int(self.headers.get('Content-length', 0)) - data = self.rfile.read(data_len) if data_len else '' - if self.EXPLOIT_STRING in data: - xml = ElementTree.fromstring(data) + self.data = self.rfile.read(data_len) if data_len else '' + if self.EXPLOIT_STRING.encode() in self.data: + xml = ElementTree.fromstring(self.data) payload = [] for void in xml.iter('void'): for s in void.iter('string'): payload.append(s.text) - self.alert_function(self, payload) + self.req_classification = 'exploit' + self.req_category = 'critical' + self.vulnerability = 'CVE-2017-10271' + self.payload = ' '.join(payload) + self.alert_function(request=self) body = self.PATCHED_RESPONSE else: body = self.GENERIC_RESPONSE @@ -74,31 +114,55 @@ def do_POST(self): self.send_header('Content-Length', int(len(body))) self.send_header('Content-Type', 'text/html; charset=utf-8') self.end_headers() - self.wfile.write(body) + self.wfile.write(body.encode()) def send_file(self, filename, status_code=200): try: with open(os.path.join(self.basepath, 'wls-wsat', filename), 'rb') as fh: body = fh.read() - body = body.replace('%%HOST%%', self.headers.get('Host')) + body = body.replace(b'%%HOST%%', self.headers.get('Host').encode()) + body = body.replace(b'%%PORT%%', str(self.listening_port).encode('utf-8')) self.send_response(status_code) self.send_header('Content-Length', int(len(body))) self.send_header('Content-Type', 'text/html; charset=utf-8') self.end_headers() - return StringIO(body) + return BytesIO(body) except IOError: return self.send_file('404.html', 404) def log_message(self, format, *args): + postdata = None + if self.data: + postdata = self.data.decode('utf-8', 'ignore') + self.logger.debug("%s - - [%s] %s" % (self.client_address[0], self.log_date_time_string(), format % args)) + # hpfeeds logging + rheaders = {} + for k,v in self.headers._headers: + rheaders[k] = v + self.hpfl.log(self.req_category, { + 'classification': self.req_classification, + 'timestamp': self.timestamp, + 'vulnerability': self.vulnerability, + 'src_ip': self.client_address[0], + 'src_port': self.client_address[1], + 'dest_ip': self.connection.getsockname()[0], + 'dest_port': self.connection.getsockname()[1], + 'raw_requestline': self.raw_requestline.decode('utf-8'), + 'header': rheaders, + 'postdata': postdata, + 'exploit_command': self.payload + }) + def handle_one_request(self): """Handle a single HTTP request. Overriden to not send 501 errors """ + self.timestamp = datetime.datetime.now().isoformat() self.close_connection = True try: self.raw_requestline = self.rfile.readline(65537) @@ -122,7 +186,7 @@ def handle_one_request(self): method = getattr(self, mname) method() self.wfile.flush() # actually send the response if not already done. - except socket.timeout, e: + except socket.timeout as e: # a read or a write timed out. Discard this connection self.log_error("Request timed out: %r", e) self.close_connection = 1 @@ -138,27 +202,41 @@ def handle_one_request(self): @click.option('-h', '--host', default='0.0.0.0', help='Host to listen') @click.option('-p', '--port', default=8000, help='Port to listen', type=click.INT) @click.option('-v', '--verbose', default=False, help='Verbose logging', is_flag=True) - def start(host, port, verbose): + + # hpfeeds options + @click.option('--hpfserver', default=os.environ.get('HPFEEDS_SERVER'), help='hpfeeds Server') + @click.option('--hpfport', default=os.environ.get('HPFEEDS_PORT'), help='hpfeeds Port', type=click.INT) + @click.option('--hpfident', default=os.environ.get('HPFEEDS_IDENT'), help='hpfeeds Ident') + @click.option('--hpfsecret', default=os.environ.get('HPFEEDS_SECRET'), help='hpfeeds Secret') + @click.option('--hpfchannel', default=os.environ.get('HPFEEDS_CHANNEL'), help='hpfeeds Channel') + @click.option('--serverid', default=os.environ.get('SERVERID'), help='hpfeeds ServerID/ServerName') + + def start(host, port, verbose, hpfserver, hpfport, hpfident, hpfsecret, hpfchannel, serverid): """ A low interaction honeypot for the Oracle Weblogic wls-wsat component capable of detecting CVE-2017-10271, a remote code execution vulnerability """ - def alert(cls, request, payload): + + hpfl = hpflogger(hpfserver, hpfport, hpfident, hpfsecret, hpfchannel, serverid, verbose) + + def alert(cls, request): logger.critical({ 'src': request.client_address[0], 'spt': request.client_address[1], - 'destinationServiceName': ' '.join(payload), + 'destinationServiceName': request.payload, }) if verbose: logger.setLevel(logging.DEBUG) requestHandler = WebLogicHandler + requestHandler.listening_port = port requestHandler.alert_function = alert requestHandler.logger = logger + requestHandler.hpfl = hpfl - httpd = HTTPServer((host, port), requestHandler) - logger.info('Starting server on port {:d}, use to stop'.format(port)) + httpd = NonBlockingHTTPServer((host, port), requestHandler) + logger.info('Starting server on {:s}:{:d}, use to stop'.format(host, port)) try: httpd.serve_forever() except KeyboardInterrupt: diff --git a/wls-wsat/CoordinatorPortType b/wls-wsat/CoordinatorPortType index 4b3312c..4c20b4d 100644 --- a/wls-wsat/CoordinatorPortType +++ b/wls-wsat/CoordinatorPortType @@ -18,7 +18,7 @@ Information
Service Name:{http://schemas.xmlsoap.org/ws/2004/10/wsat}WSAT10Service
Port Name:{http://schemas.xmlsoap.org/ws/2004/10/wsat}CoordinatorPortTypePort
-
Address:http://%%HOST%%/wls-wsat/CoordinatorPortType
WSDL:http://%%HOST%%/wls-wsat/CoordinatorPortType?wsdl
Implementation class:weblogic.wsee.wstx.wsat.v10.endpoint.CoordinatorPortTypePortImpl
+
Address:http://%%HOST%%:%%PORT%%/wls-wsat/CoordinatorPortType
WSDL:http://%%HOST%%:%%PORT%%/wls-wsat/CoordinatorPortType?wsdl
Implementation class:weblogic.wsee.wstx.wsat.v10.endpoint.CoordinatorPortTypePortImpl
diff --git a/wls-wsat/CoordinatorPortType11 b/wls-wsat/CoordinatorPortType11 index ab1b391..c31a54e 100644 --- a/wls-wsat/CoordinatorPortType11 +++ b/wls-wsat/CoordinatorPortType11 @@ -18,7 +18,7 @@ Information
Service Name:{http://docs.oasis-open.org/ws-tx/wsat/2006/06}WSAT11Service
Port Name:{http://docs.oasis-open.org/ws-tx/wsat/2006/06}CoordinatorPort
-
Address:http://%%HOST%%/wls-wsat/CoordinatorPortType11
WSDL:http://%%HOST%%/wls-wsat/CoordinatorPortType11?wsdl
Implementation class:weblogic.wsee.wstx.wsat.v11.endpoint.CoordinatorPortImpl
+
Address:http://%%HOST%%:%%PORT%%/wls-wsat/CoordinatorPortType11
WSDL:http://%%HOST%%:%%PORT%%/wls-wsat/CoordinatorPortType11?wsdl
Implementation class:weblogic.wsee.wstx.wsat.v11.endpoint.CoordinatorPortImpl
diff --git a/wls-wsat/ParticipantPortType b/wls-wsat/ParticipantPortType index 2f5f97b..d11964b 100644 --- a/wls-wsat/ParticipantPortType +++ b/wls-wsat/ParticipantPortType @@ -18,7 +18,7 @@ Information
Service Name:{http://schemas.xmlsoap.org/ws/2004/10/wsat}WSAT10Service
Port Name:{http://schemas.xmlsoap.org/ws/2004/10/wsat}ParticipantPortTypePort
-
Address:http://%%HOST%%/wls-wsat/ParticipantPortType
WSDL:http://%%HOST%%/wls-wsat/ParticipantPortType?wsdl
Implementation class:weblogic.wsee.wstx.wsat.v10.endpoint.ParticipantPortTypePortImpl
+
Address:http://%%HOST%%:%%PORT%%/wls-wsat/ParticipantPortType
WSDL:http://%%HOST%%:%%PORT%%/wls-wsat/ParticipantPortType?wsdl
Implementation class:weblogic.wsee.wstx.wsat.v10.endpoint.ParticipantPortTypePortImpl
diff --git a/wls-wsat/ParticipantPortType11 b/wls-wsat/ParticipantPortType11 index bccc62a..49d49c4 100644 --- a/wls-wsat/ParticipantPortType11 +++ b/wls-wsat/ParticipantPortType11 @@ -18,7 +18,7 @@ Information
Service Name:{http://docs.oasis-open.org/ws-tx/wsat/2006/06}WSAT11Service
Port Name:{http://docs.oasis-open.org/ws-tx/wsat/2006/06}ParticipantPort
-
Address:http://%%HOST%%/wls-wsat/ParticipantPortType11
WSDL:http://%%HOST%%/wls-wsat/ParticipantPortType11?wsdl
Implementation class:weblogic.wsee.wstx.wsat.v11.endpoint.ParticipantPortImpl
+
Address:http://%%HOST%%:%%PORT%%/wls-wsat/ParticipantPortType11
WSDL:http://%%HOST%%:%%PORT%%/wls-wsat/ParticipantPortType11?wsdl
Implementation class:weblogic.wsee.wstx.wsat.v11.endpoint.ParticipantPortImpl
diff --git a/wls-wsat/RegistrationPortTypeRPC b/wls-wsat/RegistrationPortTypeRPC index 9899410..95cc045 100644 --- a/wls-wsat/RegistrationPortTypeRPC +++ b/wls-wsat/RegistrationPortTypeRPC @@ -18,7 +18,7 @@ Information
Service Name:{http://schemas.xmlsoap.org/ws/2004/10/wscoor}RegistrationService_V10
Port Name:{http://schemas.xmlsoap.org/ws/2004/10/wscoor}RegistrationPortTypeRPCPort
-
Address:http://%%HOST%%/wls-wsat/RegistrationPortTypeRPC
WSDL:http://%%HOST%%/wls-wsat/RegistrationPortTypeRPC?wsdl
Implementation class:weblogic.wsee.wstx.wsc.v10.endpoint.RegistrationPortTypeRPCPortImpl
+
Address:http://%%HOST%%:%%PORT%%/wls-wsat/RegistrationPortTypeRPC
WSDL:http://%%HOST%%:%%PORT%%/wls-wsat/RegistrationPortTypeRPC?wsdl
Implementation class:weblogic.wsee.wstx.wsc.v10.endpoint.RegistrationPortTypeRPCPortImpl
diff --git a/wls-wsat/RegistrationPortTypeRPC11 b/wls-wsat/RegistrationPortTypeRPC11 index 0eb9785..7f6faca 100644 --- a/wls-wsat/RegistrationPortTypeRPC11 +++ b/wls-wsat/RegistrationPortTypeRPC11 @@ -18,7 +18,7 @@ Information
Service Name:{http://docs.oasis-open.org/ws-tx/wscoor/2006/06}RegistrationService_V11
Port Name:{http://docs.oasis-open.org/ws-tx/wscoor/2006/06}RegistrationPort
-
Address:http://%%HOST%%/wls-wsat/RegistrationPortTypeRPC11
WSDL:http://%%HOST%%/wls-wsat/RegistrationPortTypeRPC11?wsdl
Implementation class:weblogic.wsee.wstx.wsc.v11.endpoint.RegistrationPortImpl
+
Address:http://%%HOST%%:%%PORT%%/wls-wsat/RegistrationPortTypeRPC11
WSDL:http://%%HOST%%:%%PORT%%/wls-wsat/RegistrationPortTypeRPC11?wsdl
Implementation class:weblogic.wsee.wstx.wsc.v11.endpoint.RegistrationPortImpl
diff --git a/wls-wsat/RegistrationRequesterPortType b/wls-wsat/RegistrationRequesterPortType index 6439eda..7908e51 100644 --- a/wls-wsat/RegistrationRequesterPortType +++ b/wls-wsat/RegistrationRequesterPortType @@ -18,7 +18,7 @@ Information
Service Name:{http://schemas.xmlsoap.org/ws/2004/10/wscoor}RegistrationService_V10
Port Name:{http://schemas.xmlsoap.org/ws/2004/10/wscoor}RegistrationRequesterPortTypePort
-
Address:http://%%HOST%%/wls-wsat/RegistrationRequesterPortType
WSDL:http://%%HOST%%/wls-wsat/RegistrationRequesterPortType?wsdl
Implementation class:weblogic.wsee.wstx.wsc.v10.endpoint.RegistrationRequesterPortTypePortImpl
+
Address:http://%%HOST%%:%%PORT%%/wls-wsat/RegistrationRequesterPortType
WSDL:http://%%HOST%%:%%PORT%%/wls-wsat/RegistrationRequesterPortType?wsdl
Implementation class:weblogic.wsee.wstx.wsc.v10.endpoint.RegistrationRequesterPortTypePortImpl
diff --git a/wls-wsat/RegistrationRequesterPortType11 b/wls-wsat/RegistrationRequesterPortType11 index 01da0fd..969fc5c 100644 --- a/wls-wsat/RegistrationRequesterPortType11 +++ b/wls-wsat/RegistrationRequesterPortType11 @@ -18,7 +18,7 @@ Information
Service Name:{http://docs.oasis-open.org/ws-tx/wscoor/2006/06}RegistrationService_V11
Port Name:{http://docs.oasis-open.org/ws-tx/wscoor/2006/06}RegistrationRequesterPort
-
Address:http://%%HOST%%/wls-wsat/RegistrationRequesterPortType11
WSDL:http://%%HOST%%/wls-wsat/RegistrationRequesterPortType11?wsdl
Implementation class:weblogic.wsee.wstx.wsc.v11.endpoint.RegistrationRequesterPortImpl
+
Address:http://%%HOST%%:%%PORT%%/wls-wsat/RegistrationRequesterPortType11
WSDL:http://%%HOST%%:%%PORT%%/wls-wsat/RegistrationRequesterPortType11?wsdl
Implementation class:weblogic.wsee.wstx.wsc.v11.endpoint.RegistrationRequesterPortImpl