Skip to content

Instantly share code, notes, and snippets.

@DexterHaslem
Last active April 6, 2023 15:59
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • 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**')
@ErinsMatthew
Copy link

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

@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

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