-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathreset.py
More file actions
299 lines (205 loc) · 7.54 KB
/
Copy pathreset.py
File metadata and controls
299 lines (205 loc) · 7.54 KB
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
"""
Performs a factory reset on a Cisco IR800 router through a serial connection.
Automatically finds the correct USB port, logs in using environment variables,
runs write erase, cleans flash and nvram, and reloads the router. After the
reload, the router enters ROMMON-2 mode and fix_rommon.py must be run.
"""
import serial
import time
import os
import glob
import subprocess
import re
from dotenv import load_dotenv
# Load the router login credentials from the .env file
load_dotenv()
username = os.getenv("ROUTER_USERNAME")
password = os.getenv("ROUTER_PASSWORD")
enable_password = os.getenv("ROUTER_ENABLE_PASSWORD")
def close_screen_sessions():
"""
Closes all active screen sessions to free the serial ports.
Runs `screen -list` to check for active sessions.
If any sessions are found, they are closed using `screen -X quit`.
"""
result = subprocess.run(["screen", "-list"], capture_output=True, text=True)
if "No Sockets found" not in result.stdout:
subprocess.run(["screen", "-X", "quit"])
time.sleep(1)
print("Screen sessions closed")
else:
print("No screen sessions found")
def find_port():
"""
Searches for and returns an active USB serial port.
Loops until an active port is found by sending a newline
and checking if the router responds. Retries every 5 seconds
if no active port is found.
Returns:
str: The path to the active port, for example '/dev/ttyUSB0'.
"""
print("Searching for USB port...")
while True:
ports = glob.glob("/dev/ttyUSB*")
for port in ports:
try:
test = serial.Serial(port=port, baudrate=9600, timeout=2)
test.write(b"\r\n")
time.sleep(2)
output = test.read(test.in_waiting).decode("utf-8", errors="ignore")
test.close()
if output:
print(f"Found active port: {port}")
return port
except:
continue
print("No active port found, retrying in 5 seconds...")
time.sleep(5)
def send_and_wait(command, wait=3):
"""
Sends a command through the serial connection and waits for a response
from the router. Reads the output until a known prompt or keyword
is found, or until the 15 second timeout is reached.
Arguments:
command (str): The command to send to the router.
wait (float): Seconds to wait before reading output. Default is 3.
Returns:
str: The decoded output from the router.
"""
ser.write(f"{command}\r\n".encode())
time.sleep(wait)
output = ""
deadline = time.time() + 15
while time.time() < deadline:
if ser.in_waiting:
output += ser.read(ser.in_waiting).decode("utf-8", errors="ignore")
time.sleep(0.2)
if any(p in output for p in ["#", ">", "confirm", "save", "[OK]", "dialog", "complete", "erase", "yes/no", "filename", "rommon", "deleted", "No such", "Error"]):
break
return output
def login_and_enable():
"""
Logs in to the router and activates enable mode.
Attempts up to 10 times to log in using the username and password,
then activates enable mode using the enable password.
Returns:
bool: True if enable mode was activated, otherwise False.
"""
MAX_ATTEMPTS = 10
for attempt in range(MAX_ATTEMPTS):
print(f"Login attempt {attempt + 1} of {MAX_ATTEMPTS}...")
ser.write(b"\r\n")
time.sleep(6)
output = ser.read(ser.in_waiting).decode("utf-8", errors="ignore")
if "(config)" in output:
send_and_wait("end")
if "Username" in output:
output = send_and_wait(username)
if "Password" in output:
output = send_and_wait(password)
if ">" in output:
output = send_and_wait("enable")
if "Password" in output:
output = send_and_wait(enable_password)
if "(config)" in output:
send_and_wait("end")
if "#" in output:
print("Enable mode activated!")
return True
print("Could not reach enable mode, retrying in 5 seconds...")
time.sleep(5)
print("Maximum number of attempts reached, exiting")
return False
def clean_flash():
"""
Cleans the flash memory and removes all files except protected system files.
Lists the flash contents and removes all files that do not match
the protected file list. Short numeric filenames are also skipped.
"""
print("Cleaning flash...")
ser.write(b"dir flash:\r\n")
output = ""
deadline = time.time() + 20
while time.time() < deadline:
if ser.in_waiting:
output += ser.read(ser.in_waiting).decode("utf-8", errors="ignore")
if "#" in output:
break
time.sleep(0.3)
# Files that should never be deleted
never_delete = [
"eem",
"managed",
"ir800-universalk9",
"ir800-hv",
"ir800-uk9",
"gemboa",
"MC7304",
"no_usb_emul"
]
# Finds all files and folders in flash
all_files = re.findall(r"(?:^|\s)([\w\-\.]+)(?:\s|$)", output, re.MULTILINE)
for fil in all_files:
if not fil or any(skip in fil for skip in never_delete):
continue
if re.match(r"^\d+$", fil) and len(fil) < 6:
continue
send_and_wait(f"delete /force /recursive flash:{fil}", wait=3)
print("Flash cleaned!")
def clean_nvram():
"""
Cleans nvram and removes all files.
Lists the nvram contents and removes all files that are not
short numeric filenames.
"""
print("Cleaning nvram...")
ser.write(b"dir nvram:\r\n")
output = ""
deadline = time.time() + 20
while time.time() < deadline:
if ser.in_waiting:
output += ser.read(ser.in_waiting).decode("utf-8", errors="ignore")
if "#" in output:
break
time.sleep(0.3)
# Files that should never be deleted in nvram
never_delete_nvram = []
# Finds all files in nvram
all_files = re.findall(r"(?:^|\s)([\w\-\.]+)(?:\s|$)", output, re.MULTILINE)
for fil in all_files:
if not fil or any(skip in fil for skip in never_delete_nvram):
continue
if re.match(r"^\d+$", fil) and len(fil) < 6:
continue
send_and_wait(f"delete /force nvram:{fil}", wait=3)
print("Nvram cleaned!")
# Closes screen sessions, finds the port, and connects to the router
close_screen_sessions()
time.sleep(2)
port = find_port()
ser = serial.Serial(port=port, baudrate=9600, timeout=5)
# Exit if login fails
if not login_and_enable():
ser.close()
exit()
# Clears startup configuration
print("Running write erase...")
output = send_and_wait("write erase", wait=5)
if "confirm" in output.lower() or "erase" in output.lower():
send_and_wait("", wait=8)
print("Write erase completed!")
# Cleans flash and nvram
clean_flash()
clean_nvram()
# Reloads the router without saving the configuration
print("Reloading the router...")
output = send_and_wait("reload", wait=5)
if "yes/no" in output.lower() or "save" in output.lower() or "modified" in output.lower():
send_and_wait("no", wait=5)
if "confirm" in output.lower() or "proceed" in output.lower():
send_and_wait("", wait=3)
if "confirm" in output.lower() or "proceed" in output.lower():
send_and_wait("", wait=3)
print("Factory reset completed! The router is rebooting into ROMMON-2.")
print("Run fix_rommon.py to boot the router.")
ser.close()