-
Notifications
You must be signed in to change notification settings - Fork 11
/
wiper.py
332 lines (302 loc) · 19.1 KB
/
wiper.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
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# r3nt0n
# https://github.com/r3nt0n/wiper
"""
wiper.py - wiper run script
EXIT CODE 0 - OK
EXIT CODE 3 - KEYBOARD INTERRUPT
EXIT CODE 4 - FEW/INCORRECT ARGUMENTS PROVIDED
EXIT CODE 5 - INCOMPATIBLE OPERATIVE SYSTEM
Wiper is a set of tools to perform secure destruction of sensitive virtual data, temporary files and swap memories.
## Features
+ MANUAL wipe selection:
You can wipe: single files, whole directories, whole free space in partitions.
+ AUTO wipe selection:
An automatic selection of paths to wipe, relatives to personal directories and swap data.
If you run it with the OS target unmounted, you should provide the root path/mount point to that system.
"""
name = 'wiper.py'
__author__ = 'r3nt0n'
__version__ = '0.6~beta'
__status__ = 'Development'
################################################################################
import os, sys, platform, time
import argparse
from shutil import rmtree
from socket import gethostname
from r3ntlib import wiper_ops
from r3ntlib import os_ops
from r3ntlib.color import color
################################################################################
# ARGS DEFINITION
################################################################################
def check_methods(method, interactive):
for char in method:
if char not in ('o', 'z', 'r'):
print(u'{} [!]{} ERROR: {} method not found'.format(color.RED, color.END, char))
if not interactive:
sys.exit(4)
return True
def read_args():
parser = argparse.ArgumentParser(description='set of tools to perform secure destruction of sensitive virtual data, \
temporary files and swap memories.. Absolute and relative paths are \
allowed, but no wildcards. Find more info at https://github.com/r3nt0n/wiper')
parser.add_argument('-i', '--interactive',action='store_true',dest='interactive',default=False,
help='interactive mode, the script will guide you')
parser.add_argument('-f', '--free',action='store',dest='free',type=str,default=False,metavar='path',
help='wipe all free space on given path')
parser.add_argument('-p', '--path',action='store',dest='path',type=str, default=False,metavar='path',
help='path to dir/file you want to wipe')
parser.add_argument('-m', '--method', action='store', dest='method', type=str, default='r', metavar='ozr',
help='overwrite methods to apply (o: ones, z: zeros, r: random), you can combine it and choose the order')
parser.add_argument('-r', '--root',action='store',dest='root_path',type=str,default=False,
metavar='path',help='set a custom root path if you want to wipe with auto-search modes an unbooted system (e.g. /media/drive)')
# parser.add_argument('-t', '--temp', action='store_true',dest='temp',default=False,
# help='auto-search mode: locate actual user temp directory and wipes it')
parser.add_argument('-u', '--home', action='store_true',dest='home',default=False,
help='auto-search mode: locate actual user home directory and wipes it')
# parser.add_argument('-T', '--temp-all', action='store_true',dest='temp_all',default=False,
# help='auto-search mode: locate all users temp directory and wipes it')
parser.add_argument('-U', '--home-all', action='store_true',dest='home_all',default=False,
help='auto-search mode: locate all users home directory and wipes it')
parser.add_argument('-s', '--swaps', action='store_true',dest='swaps',default=False,
help='auto-search mode: locate swap partitions/pagefiles and wipes it (be careful: UUID swap partitions also will be wiped)')
args = parser.parse_args()
interactive = args.interactive
wipe_free_arg = args.free
path = args.path
method = args.method
custom_root_path = args.root_path
#wipe_temp_arg = args.temp
wipe_home_arg = args.home
#wipe_temp_all_arg = args.temp_all
wipe_home_all_arg = args.home_all
wipe_swaps_arg = args.swaps
# Print help and exit when runs without args
if len(sys.argv) == 1: parser.print_help(sys.stdout); sys.exit(4)
# Print help and exit when runs non-interactive without path
if (not interactive and not wipe_free_arg and not path and not wipe_home_arg and not wipe_home_all_arg and not wipe_swaps_arg):
parser.print_help(sys.stdout)
sys.exit(4)
# Check if methods introduced are right
check_methods(method, interactive)
return interactive, wipe_free_arg, path, method, custom_root_path, wipe_home_arg, wipe_home_all_arg, wipe_swaps_arg
def print_methods(method):
print(u'\r\n {}[+]{} Current configuration:'.format(color.ORANGE, color.END))
for char in method:
if char == 'o': method_name = 'One-pass with ones'
if char == 'z': method_name = 'One-pass with zeros'
if char == 'r': method_name = 'One-pass with random data'
print(u' {}[-]{} {}'.format(color.PURPLE, color.END, method_name))
def banner():
headers_color = color.PURPLE
options_color = color.ORANGE
appname_color = color.PURPLE
author_color = color.ORANGE
delay_per_line = 0.02
time.sleep(delay_per_line*2)
print(u" __________ "); time.sleep(delay_per_line)
print(u" .'----------`. +------+ "); time.sleep(delay_per_line)
print(u" | .--------. | | -- - | "); time.sleep(delay_per_line)
print(u" | |########| | _|______|_ "); time.sleep(delay_per_line)
print(u" | |########| | /__________\ "); time.sleep(delay_per_line)
print(u" .--------| `--------' |------|1 -=====- |----------------."); time.sleep(delay_per_line)
print(u" | `----,-.-----' |o =w1p3r= | |"); time.sleep(delay_per_line)
print(u" | ______|_|_______ |__________| |"); time.sleep(delay_per_line)
print(u" | / %%%%%%%%%%%% \ | | | | | |"); time.sleep(delay_per_line)
print(u" | / %%%%%%%%%%%%%% \ | | | | | wiper.py {}{}{} |".format(appname_color,(__version__)[0:3],color.END)); time.sleep(delay_per_line)
print(u" | ^^^^^^^^^^^^^^^^^^^^ {}{}{} |".format(author_color,__author__,color.END)); time.sleep(delay_per_line)
print(u" +--------------------------------------------------------+"); time.sleep(delay_per_line)
print(u' +-- {}MANUAL SELECTION MODE{} -------------------------------+'.format(headers_color,color.END)); time.sleep(delay_per_line)
print(u' | [{}1{}] Wipe all free space in a choosen partition |'.format(options_color,color.END)); time.sleep(delay_per_line)
print(u' | [{}2{}] Wipe a single file/all files under a choosen path |'.format(options_color,color.END)); time.sleep(delay_per_line)
print(u' +-- {}AUTO SEARCH MODE{} ------------------------------------+'.format(headers_color,color.END)); time.sleep(delay_per_line)
print(u' | [{}3{}] Wipe my personal directory |'.format(options_color,color.END)); time.sleep(delay_per_line)
print(u' +-- {}AUTO SEARCH MODE (elevated privileges){} --------------+'.format(headers_color,color.END)); time.sleep(delay_per_line)
print(u' | [{}4{}] Wipe all users personal directories |'.format(options_color,color.END)); time.sleep(delay_per_line)
print(u' | [{}5{}] Wipe all swap partitions/pagination files |'.format(options_color,color.END)); time.sleep(delay_per_line)
print(u' +-- {}OVERWRITE METHODS{} -----------------------------------+'.format(headers_color, color.END));time.sleep(delay_per_line)
print(u' | [{}6{}] Change overwrite method |'.format(options_color, color.END));time.sleep(delay_per_line)
print(u' +--------------------------------------------------------+'); time.sleep(delay_per_line)
print(u' | [{}0{}] Exit |'.format(options_color,color.END)); time.sleep(delay_per_line)
print(u' +--------------------------------------------------------+\r\n'); time.sleep(delay_per_line)
def is_empty(string):
"""Checks if a string is empty.
Returns True or False
"""
empty = False
if len(str(string)) == 0: empty = True
return empty
def set_root_path():
print(u' {}[!]{} System detected: {}{}{} (running {}{}{}) '.format(color.ORANGE, color.END,
color.ORANGE, gethostname(), color.END,
color.PURPLE, platform.system(), color.END))
print(u' {}[!]{} By default, {}auto-search{} mode targets the booted OS.'.format(color.ORANGE, color.END,
color.PURPLE,color.END))
print(u' {}[!]{} To wipe an unbooted system, you can provide the root'.format(color.ORANGE,color.END))
print(u' {}[!]{} path where is mounted (e.g.: /media/mydrive).'.format(color.ORANGE, color.END))
print(u' {}[!]{} SET a custom root path or PRESS ENTER to continue...'.format(color.ORANGE, color.END))
root_path = False
user_input = input(u' {}[?]{} '.format(color.PURPLE, color.END))
if os.path.isdir(user_input):
root_path = user_input
if not is_empty(user_input):
try: os.chdir(user_input)
except Exception as exception: print('{} [!]{} ERROR: {}'.format(color.RED, color.END, exception))
return root_path
def wipe(path, method='r', exclude_script=True):
status = 3
if not exclude_script:
files_to_wipe = os_ops.find_files(path)
else:
# Get actual script absolute path to exclude from deletion task
script_abspath = os.path.abspath(__file__)
# Get all files included in the given path
files_to_wipe = os_ops.find_files(path, [script_abspath])
if not files_to_wipe:
print(u' {}[!]{} ERROR: No files found'.format(color.RED, color.END))
status = 2
else:
print(u' {}[+]{} Files found: {}{}{}'.format(color.GREEN, color.END, color.PURPLE, len(files_to_wipe), color.END))
#if method == 'z':
#print(u' {}[+]{} Starting one-pass zeros wipe...'.format(color.ORANGE, color.END))
#elif method == 'o':
#print(u' {}[+]{} Starting one-pass ones wipe...'.format(color.ORANGE, color.END))
#else:
#print(u' {}[+]{} Starting one-pass random wipe...'.format(color.ORANGE, color.END))
# Wipe each file
counter = 0
for f in files_to_wipe:
wiped_status = False
try:
bytesize_to_write = os.stat(f).st_size
print(u'\r\n {}[+]{} Overwriting {}{}{} ({}{}{} bytes)'.format(color.ORANGE, color.END,
color.ORANGE, f, color.END,
color.PURPLE, bytesize_to_write, color.END))
wiped_status = wiper_ops.wipe_bytes(f, 'wb', bytesize_to_write, method)
except Exception as exception:
print(u'{} [!]{} ERROR: {}'.format(color.RED, color.END, exception))
status = 1
if wiped_status == 0: counter += 1
print(u' {}[+]{} Files wiped: {}{}{}'.format(color.GREEN, color.END,color.PURPLE,counter,color.END))
# Remove the empty tree if path given was a dir AFTER ALL OVERWRITES
if (os.path.isdir(path) and status == 3):
try:
print(u' {}[+]{} Empty tree removed (path given was a dir)'.format(color.GREEN, color.END))
rmtree(path)
status = 0
except Exception as exception: print('{} [!]{} ERROR: {}'.format(color.RED,color.END,exception)); status=5
return status
def main():
if (os.name != 'posix' and os.name != 'nt'):
print(u'{}[!]{} Incompatible Operative System detected. Exiting...'.format(color.RED, color.END))
sys.exit(5)
else:
# Read CLI arguments
interactive, wipe_free_arg, path, method, custom_root_path, wipe_home_arg, wipe_home_all_arg, wipe_swaps_arg = read_args()
if interactive:
# Clear screen and print banner with options in interactive mode
os_ops.clear()
banner()
custom_root_path = set_root_path()
print(u'\r\n {}[+]{} Your current working directory is:'.format(color.ORANGE, color.END))
print(u' {}[+]{} {}{}{}'.format(color.ORANGE, color.END, color.ORANGE, os.getcwd(), color.END))
while True:
opt = ''
if interactive:
# Get opt and check it
path = ''
opt = input(u'\r\n {}[?]{} Choose an option (help to show again) [{}0-9{}]: '.format(color.PURPLE,color.END,color.ORANGE,color.END))
# 0. Wipes all free space in the given paths
if (opt == '1' or wipe_free_arg):
# Get the path from user input
if wipe_free_arg:
path = wipe_free_arg
else:
path = input(u' {}[?]{} Enter the path you want to wipe all free space: '.format(color.PURPLE,color.END))
status = wiper_ops.wipe_free_space(path, method)
if status == 0:
print(u' {}[+]{} The free space was succesfully wiped.'.format(color.GREEN, color.END))
else:
print(u' {}[!]{} An error has occurred'.format(color.RED, color.END))
# 1. Secure delete all data in the given path
elif (opt == '2' or path):
# Get the path from user input
if not path: path = input(u' {}[?]{} Enter the path you want to wipe: '.format(color.PURPLE, color.END))
wipe(path, method)
# elif (opt == '3' or wipe_temp_arg):
# print(u' {}[x]{} This feature is not still implemented.'.format(color.ORANGE, color.END))
# continue
# Wipes user personal dir
elif (opt == '3' or wipe_home_arg):
personal_dirs = os_ops.get_personal_dirs()
if not personal_dirs:
print(u' {}[!]{} ERROR: Any home directory found'.format(color.RED, color.END))
else:
print(u' {}[+]{} {}{}{} personal dirs found\r\n'.format(color.ORANGE, color.END, color.ORANGE,
len(personal_dirs), color.END))
for pdir in personal_dirs:
print(u' {}[+]{} [{}{}{}] {}'.format(color.GREEN, color.END,
color.PURPLE,personal_dirs.index(pdir),
color.END,pdir))
if len(personal_dirs) > 0:
if len(personal_dirs) == 1:
pdir = personal_dirs[0]
else:
pdir = input(u'\r\n {}[?]{} Choose the one you want to wipe [{}0-{}{}] '.format(color.PURPLE,color.END,
color.ORANGE,str(len(personal_dirs)-1),
color.END))
if (type(pdir) is not int or int(pdir) >= len(personal_dirs)):
print(u' {}[!]{} ERROR: Bad option choosen'.format(color.RED, color.END))
continue # Back to menu
wipe(pdir, method)
# elif (opt == '5' or wipe_temp_all_arg):
# print(u' {}[x]{} This feature is not still implemented.'.format(color.ORANGE, color.END))
# continue
elif (opt == '4' or wipe_home_all_arg):
print(u' {}[x]{} This feature is not still implemented.'.format(color.ORANGE, color.END))
continue
# Wipes swaps/pagefiles
elif (opt == '5' or wipe_swaps_arg):
print(u' {}[-]{} Searching swap/pagefiles...'.format(color.ORANGE, color.END))
swaplist = os_ops.get_swaps(custom_root_path)
if not swaplist:
print(u' {}[!]{} ERROR: Any swap partition or pagefile found'.format(color.RED, color.END))
if custom_root_path:
print(u' {}[!]{} Try to run wiper booted on the target system'.format(color.ORANGE, color.END))
if (type(swaplist) == str and swaplist.startswith(':ERR:')):
msg = swaplist.split(':ERR:')[1]
print(u' {}[!]{} ERROR: {}'.format(color.ORANGE, color.END, msg))
else:
print(u' {}[+]{} {} swap/pagefiles found\r\n'.format(color.PURPLE, color.END,len(swaplist)))
for swap in swaplist:
print(u' {}[+]{} {}'.format(color.ORANGE, color.END,swap))
confirm = input(u' {}[?]{} Do you want to confirm? (y/n) > '.format(color.PURPLE,color.END))
if (confirm.lower().startswith('y')):
if (os.name == 'nt' or custom_root_path):
wipe(swaplist, method)
elif (os.name == 'posix'):
for swap in swaplist:
status = wiper_ops.dd_linux_wipe(swap, method)
if str(status) == '0':
print(u' {}[+]{} {}{}{} was succesfully wiped.'.format(color.GREEN,color.END,
color.PURPLE,swap,color.END))
elif (opt == '6'):
print_methods(method)
m = input(u'\r\n {}[?]{} Set the new configuration (examples: o, zr, ozozr): '.format(color.PURPLE,color.END))
if check_methods(m, interactive):
method = m
print_methods(method)
elif (opt == '0' or opt == 'quit' or opt == 'exit'):
interactive = False
elif (opt.lower() == 'help'):
banner()
else:
print(u' {}[!]{} Incorrect option.'.format(color.RED, color.END))
continue
if not interactive:
break
if __name__ == '__main__':
try: main()
except KeyboardInterrupt: print(u'\n\n {}[!]{} Exiting...\n'.format(color.RED, color.END)); sys.exit(3)