Skip to content

Instantly share code, notes, and snippets.

@DexterHaslem
Last active August 8, 2024 00:55
Show Gist options
  • Save DexterHaslem/d0365dd4cbbcceac22a002fa981beaae to your computer and use it in GitHub Desktop.
Save DexterHaslem/d0365dd4cbbcceac22a002fa981beaae to your computer and use it in GitHub Desktop.
CM1000 modem log scraper thing
## this is a quick and dirty script to remotely login to a Netgear CM1000 modem,
# grab the docsis logs and format them as a csv for saving later. the webpage will only show so many
# and logs can be eventually be lost. this is also an easier way to grab/view them
# last tested on Firmware Version V7.01.01 jan 2 2022
import requests
import re
from time import time
from csv import DictWriter
from xml.dom.minidom import parseString
from datetime import datetime
# CONFIG SECTION
# i dont beleive this can be changed, or at least every ip leaves it provisioned the same
MODEM_ADDR = 'http://192.168.100.1/'
LOGIN_USERNAME = 'admin'
LOGIN_PASSWORD = 'password' # PROBABLY CHANGE ME
now = datetime.now()
def murl(endpoint):
return MODEM_ADDR + endpoint
def build_output_fn():
datestr = '{:02d}_{:02d}_{}'.format(now.month, now.day, now.year)
timestr = '{:02d}.{:02d}.{:02d}'.format(now.hour, now.minute, now.second)
return 'modem_log_{}-{}.csv'.format(datestr, timestr)
webtoken_regex = re.compile(r'<input type=\"hidden\" name=\"webToken\" (?:value=)([0-9]*)')
xmlformat_regex = re.compile(r'var xmlFormat = \'([^\']*)\'')
login = {
'loginUsername': {LOGIN_USERNAME},
'loginPassword': {LOGIN_PASSWORD},
'login': '1',
# webtoken will be grabbed from login page
}
# step 0: get webtoken value.. doesnt seem to be current ts.
loginpage = requests.get(murl('GenieLogin.asp'))
if loginpage.status_code != 200:
exit(1)
# # grab this element <input type="hidden" name="webToken" value=1641040522 />
# we need webtoken value for login form
for l in loginpage.text.splitlines():
l = l.strip()
if 'webToken' in l:
# luckily its only present once
g = webtoken_regex.match(l)
if g:
login['webToken'] = g.groups()[0]
break
if 'webToken' not in login:
exit(2)
# we need to follow the redirect manually. it slams socket shut after 302
x = requests.post(murl('goform/GenieLogin'), data = login, allow_redirects = False)
# now we need to post a plain get to index to get our session id cookie
# do this manually, cuz it leaves trailing %00 on the crap
# it has just /Index.asp in it
sc = requests.get(murl('Index.asp'))
if sc.status_code != 200:
exit(3)
sid = sc.cookies.get('SessionID')
x = requests.get(murl('EventLog.asp'), cookies={'SessionID':sid})
# now the worst part. the table entries are in the received js
# function InitTagValue()
# {
# var xmlFormat = '<docsDevEventTable><tr><docsDevEvIndex> .... 1000s
# so dig out that xml :/
results = xmlformat_regex.findall(x.text)
if len(results) > 0:
# note: unsafe against virtually every xml attack
doc = parseString(results[0])
# convert to csv and dump for this time ran
fn = build_output_fn()
with open(fn, 'w', newline='') as out_csv:
# TODO: maybe use less dumb names, but we used element names as is
d = DictWriter(out_csv, fieldnames=['docsDevEvIndex', 'docsDevEvFirstTime',
'docsDevEvLastTime', 'docsDevEvCounts', 'docsDevEvLevel', 'docsDevEvId', 'docsDevEvText'])
d.writeheader()
for tr in doc.documentElement.childNodes:
row = {}
for se in tr.childNodes:
v = se.childNodes[0].nodeValue
# HACK: remove commas in first/last time since we're using csv. if not done it will quote
if se.localName == 'docsDevEvFirstTime' or se.localName == 'docsDevEvLastTime':
v = v.replace(',', '')
row[se.nodeName] = v
d.writerow(row)
print('**DONE**')
@DexterHaslem
Copy link
Author

Thank you! I truly appreciate the hard-work you performed to create this. I created a Node.js version based on this.

cool thanks for sharing!

@discarn8
Copy link

discarn8 commented Apr 4, 2023

Dude! You saved me HOURS of work - thank you. I also "forked" a lesser version for the CM600 in Python2.7

@DexterHaslem
Copy link
Author

Dude! You saved me HOURS of work - thank you. I also "forked" a lesser version for the CM600 in Python2.7

I banged this out kinda quick and dirty as it appears many moons ago and thought "should i throw it in a repo in case anyone else wants to do this? naw a public gist is probably good enough", glad to hear people are still finding it and its useful

@dustinfike
Copy link

Any chance someone could update this to work with a CM3000?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment