Agent: Wrap log4shell LDAP server in a process
A Twisted reactor can only be started and stopped once. It cannot be restarted after it has been stopped. To work around this, the reactor is configured and run in a separate process. This allows us to run multiple LDAP servers sequentially or simultaneously and stop each one when we're done with it.
This commit is contained in:
parent
aef7beedb3
commit
6b934d6de5
|
@ -1,3 +1,5 @@
|
|||
import logging
|
||||
import multiprocessing
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
|
@ -10,6 +12,8 @@ from twisted.internet.protocol import ServerFactory
|
|||
from twisted.python import log
|
||||
from twisted.python.components import registerAdapter
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
EXPLOIT_RDN = "dn=Exploit"
|
||||
|
||||
|
||||
|
@ -63,33 +67,50 @@ class LDAPExploitServer:
|
|||
def __init__(
|
||||
self, ldap_server_port: int, http_server_ip: str, http_server_port: int, storage_dir: Path
|
||||
):
|
||||
LDAPExploitServer.output_twisted_logs_to_python_logger()
|
||||
self._ldap_server_port = ldap_server_port
|
||||
self._http_server_ip = http_server_ip
|
||||
self._http_server_port = http_server_port
|
||||
self._storage_dir = storage_dir
|
||||
|
||||
# A Twisted reactor can only be started and stopped once. It cannot be restarted after it
|
||||
# has been stopped. To work around this, the reactor is configured and run in a separate
|
||||
# process. This allows us to run multiple LDAP servers sequentially or simultaneously and
|
||||
# stop each one when we're done with it.
|
||||
self._server_process = multiprocessing.Process(
|
||||
target=self._run_twisted_reactor, daemon=True
|
||||
)
|
||||
|
||||
def run(self):
|
||||
self._server_process.start()
|
||||
self._server_process.join()
|
||||
|
||||
def _run_twisted_reactor(self):
|
||||
self._configure_twisted_reactor()
|
||||
logger.debug(f"Starting log4shell LDAP server on port {self._ldap_server_port}")
|
||||
reactor.run()
|
||||
|
||||
def _configure_twisted_reactor(self):
|
||||
LDAPExploitServer._output_twisted_logs_to_python_logger()
|
||||
|
||||
registerAdapter(lambda x: x.root, LDAPServerFactory, IConnectedLDAPEntry)
|
||||
|
||||
tree = Tree(http_server_ip, http_server_port, storage_dir)
|
||||
tree = Tree(self._http_server_ip, self._http_server_port, self._storage_dir)
|
||||
factory = LDAPServerFactory(tree.db)
|
||||
factory.debug = True
|
||||
|
||||
application = service.Application("ldaptor-server")
|
||||
service.IServiceCollection(application)
|
||||
reactor.listenTCP(ldap_server_port, factory)
|
||||
reactor.listenTCP(self._ldap_server_port, factory)
|
||||
|
||||
@staticmethod
|
||||
def output_twisted_logs_to_python_logger():
|
||||
def _output_twisted_logs_to_python_logger():
|
||||
# Configures Twisted to output its logs using the standard python logging module instead of
|
||||
# the Twisted logging module.
|
||||
# https://twistedmatrix.com/documents/current/api/twisted.python.log.PythonLoggingObserver.html
|
||||
log_observer = log.PythonLoggingObserver()
|
||||
log_observer.start()
|
||||
|
||||
def run(self):
|
||||
# For an explaination of installSignalHandlers=0, see
|
||||
# https://twistedmatrix.com/trac/wiki/FrequentlyAskedQuestions#Igetexceptions.ValueError:signalonlyworksinmainthreadwhenItrytorunmyTwistedprogramWhatswrong
|
||||
reactor.run(installSignalHandlers=0)
|
||||
|
||||
def stop(self):
|
||||
# Since `reactor.run()` may not be running on the main thread, reactor.stop() must be
|
||||
# invoked with reactor.callFromThread()
|
||||
# https://twistedmatrix.com/documents/12.2.0/core/howto/threading.html
|
||||
reactor.callFromThread(reactor.stop)
|
||||
# The Twisted reactor registers signal handlers so it can catch SIGTERM and gracefully
|
||||
# shutdown.
|
||||
self._server_process.terminate()
|
||||
|
|
Loading…
Reference in New Issue