/
screenz.py
executable file
·280 lines (236 loc) · 9.19 KB
/
screenz.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Small screenshoting script.
# Requires Ghost.py for quick screenshoting
# pip install -e git+https://github.com/jeanphix/Ghost.py#egg=Ghost.py
# Requires python-libnmap for nmap report parsing
# pip install python-libnmap
# author: @090h
from os import path, makedirs, walk
from subprocess import Popen
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
from threading import Thread
from datetime import datetime
from Queue import Queue
from time import sleep
from sys import exit, stdout, exc_info
from pprint import pprint
import logging
#External dependencies
from libnmap.parser import NmapParser
from ghost import Ghost
class UrlManager(object):
@staticmethod
def files_from_dir(directory, ext=None):
print('Parsing dir: %s' % directory)
file_list = []
for root, dirs, files in walk(directory):
for filename in files:
fname = path.join(root, filename)
#print(fname)
if ext is None or fname.lower().endswith(ext):
file_list.append(fname)
print('Files found:')
for f in file_list:
print(f)
return file_list
@staticmethod
def urls_from_file(filename):
if not path.exists(filename):
return
return filter(lambda x: x.find('http') != -1, open(filename, 'rb').read().split('\n'))
@staticmethod
def urls_from_dir(directory):
files = UrlManager.files_from_dir(directory)
urls = []
for f in files:
us = UrlManager.urls_from_file(f)
urls.extend(us)
return urls
@staticmethod
def urls_from_nmap_xml(nmap_file):
if not path.exists(nmap_file):
return
report = NmapParser.parse_fromfile(nmap_file)
urls = []
for host in report.hosts:
#Skip hosts with errors
if host.address.find(':') != -1:
continue
if len(host.hostnames):
tmp_host = host.hostnames.pop()
else:
tmp_host = host.address
# print("Nmap scan report for {0} ({1})".format(tmp_host, host.address))
# print("Host is {0}.".format(host.status))
# print(" PORT STATE SERVICE")
for serv in host.services:
# pserv = "{0:>5s}/{1:3s} {2:12s} {3}".format(
# str(serv.port),
# serv.protocol,
# serv.state,
# serv.service)
# if len(serv.banner):
# pserv += " ({0})".format(serv.banner)
#print(pserv)
svc = serv.service.lower()
if serv.state == 'open' and svc.find('http') != -1:
if svc.find('ssl'):
proto = 'https'
else:
proto = 'http'
urls.append('%s://%s:%i/' % (proto, host.address, serv.port))
if tmp_host != host.address:
urls.append('%s://%s:%i/' % (proto, tmp_host, serv.port))
return urls
@staticmethod
def urls_from_nmap_dir(directory):
files = UrlManager.files_from_dir(directory, '.xml')
urls = []
for f in files:
us = UrlManager.urls_from_nmap_xml(f)
urls.extend(us)
return urls
class Shotter(object):
def __init__(self,):
try:
#self.ghost = Ghost(wait_timeout=10)
self.ghost = Ghost()
except ImportError:
self.ghost = None
def cuty_shot(self, url, filename, x11=True, width=1024, height=768, colorbits=24):
#TODO: add check if cutycapt installed
if x11:
cmd = 'cutycapt --url="%s" --out=%s' % (url, filename)
else:
cmd = 'xvfb-run --server-args="-screen 0, %ix%ix%i" cutycapt --url="%s" --out=%s' % \
(url, filename, width, height, colorbits)
try:
Popen(cmd, shell=True).wait()
return True
except:
return False
def ghost_shot(self, url, filename, ignore_errors=True):
#print('ghost_shot(%s)' % url)
try:
page, resources = self.ghost.open(url)
if ignore_errors:
self.ghost.capture_to(filename)
return True
elif page.http_status == 200 and page.totalBytes() != 0:
self.ghost.capture_to(filename)
return True
else:
return False
except:
print(exc_info())
return False
def screenshot(self, url, filename, overwrite=False):
if path.exists(filename) and not overwrite:
print('%s exists, skipping' % filename)
return
print('[SCREENSHOT] %s -> %s' % (url, filename))
if self.ghost is not None:
self.ghost_shot(url, filename)
else:
self.cuty_shot(url, filename)
class ShotterThread(Thread):
def __init__(self, queue, output, prefix=None):
super(ShotterThread, self).__init__()
self.queue, self.output, self.prefix = queue, output, prefix
self.daemon = True
self.shotter = Shotter()
def url_to_filename(self, url):
filename = '%s.png' % url.replace('://', '_').replace('/', '').replace(':', '_')
if self.prefix is not None:
filename = self.prefix + filename
return path.join(self.output, filename)
def run(self, ):
while True:
url = self.queue.get()
filename = self.url_to_filename(url)
self.shotter.screenshot(url, filename)
self.queue.task_done()
class MassShotter():
def __init__(self, urls, output, prefix=None, thread_count=5):
self.urls = urls
self.output = output
self.prefix = prefix
self.thread_count = thread_count
self.queue = Queue()
self.threads = []
def run(self):
if not path.exists(self.output):
makedirs(self.output)
print('Filling queue with %i urls and deduplicating' % len(self.urls))
seen = set()
seen_add = seen.add
for url in [x for x in urls if x not in seen and not seen_add(x)]:
self.queue.put(url)
init_size = self.queue.qsize()
print('Only %i urls to screen.' % init_size)
# Fill threads list
for i in xrange(0, self.thread_count):
t = ShotterThread(self.queue, self.output)
self.threads.append(t)
# Start all threads
[x.start() for x in self.threads]
# Wait for all of them to finish
[x.join() for x in self.threads]
# for i in xrange(0, self.thread_count):
# t = ShotterThread(self.queue, self.output)
# t.start()
#
# current_count = 0
# while not self.queue.empty():
# q = init_size - self.queue.qsize()
# stdout.write("\r%i/%i urls screened. Screenshot speed %i per sec. " % \
# (q, init_size, q - current_count))
# stdout.flush()
# current_count = q
# sleep(1)
if __name__ == '__main__':
parser = ArgumentParser(description='screenz.py - small screenshot script for nmap report', formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument('-n', '--nmap', help='nmap xml report file or directory')
parser.add_argument('-u', '--urls', help='url list files or dirs')
parser.add_argument('-p', '--prefix', default=None, help='prefix for output')
parser.add_argument('-o', '--output', default='output', help='output directory')
parser.add_argument('-d', '--debug', action='store_true', help='debug mode')
parser.add_argument('-t', '--threads', type=int, default=5, help='threads count')
# parser.add_argument('-T', '--imeout', type=int, help='timeout in seconds')
parser.add_argument('-v', action='version', version='%(prog)s 0.2')
args = parser.parse_args()
pprint(args)
if args.debug:
root = logging.getLogger()
root.setLevel(logging.DEBUG)
#if 'nmap' not in args and 'url' not in args:
if args.nmap is None and args.urls is None:
parser.print_help()
print('Please use -n or -u with argument!')
exit(1)
start_time = datetime.now()
urls = []
um = UrlManager()
if args.nmap is not None:
if path.isfile(args.nmap):
urls.extend(um.urls_from_nmap_xml(args.nmap))
elif path.isdir(args.nmap):
urls.extend(um.urls_from_nmap_dir(args.nmap))
if args.urls is not None:
if path.isfile(args.urls):
urls.extend(um.urls_from_file(args.urls))
elif path.isdir(args.urls):
urls.extend(um.urls_from_dir(args.urls))
print('Urls:')
for u in urls:
pprint(u)
print('Start screenshoting. Press Ctrl+C to abort.')
try:
msh = MassShotter(urls, args.output, args.prefix, args.threads)
msh.run()
except KeyboardInterrupt:
print('Screenshoting aborted.')
exit(1)
print "Start time: " + start_time.strftime('%Y-%m-%d %H:%M:%S')
print "Finish time: " + datetime.now().strftime('%Y-%m-%d %H:%M:%S')