Skip to content

Instantly share code, notes, and snippets.

@rrbutani
Last active August 18, 2020 03:32
Show Gist options
  • Save rrbutani/952c41508c304372542394b68fb881d1 to your computer and use it in GitHub Desktop.
Save rrbutani/952c41508c304372542394b68fb881d1 to your computer and use it in GitHub Desktop.
Routers
#!/usr/bin/env bash
# Alright so, this hotel decided to put up separate SSIDs for each room. This
# is kind of annoying since we've been moved 3 times and since it's a large
# property. T-Mobile also doesn't have coverage here so the moment we step out
# of our rooms we can't make or receive calls even though we're still in range
# of the hotel's wifi networks.
#
# Also interesting is that the hotel decided to use a few models of router (as
# far as I can tell they're all cable + wifi router devices) *and* decided to
# use the default username + password. The hotel also decided make the network
# passwords essentially the same as the SSIDs (for example, SSID: Name123,
# pass: 123Name).
#
# All of this means that I get to write a fun script!
#
# Note: this requires curl, nmcli, jq, xxd, grep, base64, a modern-ish version
# of bash and the usual built-ins.
# First let's get a list of the routers (SSIDs) that we want to target:
# To make sure that we get a good list, let's scan a few times and collate the
# results:
scan() { nmcli dev wifi rescan && nmcli -t -f SSID dev wifi; }
read -ra ssids <<< $({ scan; sleep 5; scan; sleep 5; scan; } \
| sort \
| uniq \
| grep -E "^Kiahuna[0-9]{3}$")
# Repeat something N times (or until it first succeeds)
# shellcheck disable=SC2068
repeatN() { for _ in $(seq "$1"); do ${@:2} && return; done; }
# Exit (if $exit_on_err is set) or continue
# shellcheck disable=SC2015,SC2104,SC2154
exit() { [ ! -z "${exit_on_err+x}" ] && command exit || continue; }
# Some constants:
BASE_URL="http://192.168.0.1"
SSID="<>"
PASS="<>"
encode_ssid() { echo -n "%$(echo -n "\$${1}" | xxd -p -u)"; }
count=0
# Now for the each network:
# shellcheck disable=SC2015
for ssid in "${ssids[@]}"; do
# Try to connect to it:
m=$(repeatN 3 nmcli dev wifi con "${ssid}" password "${ssid:7:3}<>") \
&& ! grep -q "Error" <<< "${m}" \
|| { echo "Failed to connect to ${ssid}!! Got: \`$m\`"; exit; }
echo "Connected to ${ssid}!"
# Now try to login:
# Let's assume an ARRIS GW (with the default username/password) for now:
# (so, I was hoping the value of arg below was a salted hash or something
# - I realize this doesn't _really_ help since it's client side and the
# salt if there is one is static meaning it's really just security by
# obscurity - but, unfortunately (in case the '=' at the end isn't enough
# of a clue):
# `base64 -d <<< "YWRtaW46cGFzc3dvcmQ="` => "admin:password"
response=$(curl -s "${BASE_URL}/login?arg=YWRtaW46cGFzc3dvcmQ=&_n=1")
# The response is a base64 encoded JSON blob that has some information
# about the device and, most importantly for us, a unique field that (as
# far as I can tell) is the only non-static part of the response.
# The base64 encoded blob is *also* the credential cookie which is kind of
# cute.
# So now, let's check that this really is an ARRIS GW router thing:
[ ! "$(base64 -d <<< "${response}" | jq -cr '.modelname')" = "DG1670A" ] \
&& { echo "Warning: This isn't a supported router!! Skipping!" \
&& base64 -d <<< "${response}" | jq \
&& exit; } \
|| true
# Now we can go set the SSID and password for the 5GHz network:
# So there's lots of stuff here that we don't really have to understand, but
# just for fun, here's what I've gathered:
# - this is really just a thin layer over SNMP, probably (sidenote: what
# are oids?)
# - each field (i.e. 5GHz SSID) has an oid (i.e. 1.3.6.1.4.1.4115.1.20.1.1.3.26.1.2.10101)
# - the web UI seems to keep track of what fields have been modified and
# then go set those fields using the snmp wrapper interface (at least
# I _think_ that's what this is: `http://192.168.0.1/snmpGet?oids=<>`
# - the way setting a field works is (again, I'm guessing):
# * first, the client (web UI) goes and fires a GET (snmpGET) for the
# oid pertaining to the fields that are being modified. I'm not
# sure, but I think this oid is really a lock of sorts for the
# fields. If the server responses with a "0" (really a JSON blob
# with the oid as the single key a "0" as a value for the key):
# * then the client (the web UI) goes and fires off `snmpSet`s for
# each field that changed (sequentially, not sure what happens if
# one of these fail):
# ~ password is plaintext, SSID is encoded in a slightly wonky
# way: start with %24 and then for each character of the password
# take the ASCII hex representation of the char and append it to
# the string; for the curious 0x24 -> '$'
# ~ nvm, this is just the http thing; I'm a dumb
# %2461 -> a
# %2470617373776F7264 -> "password"
# %244B696168756E6134 -> "Kiahuna4"
# ~ this is also the right way to do this (emoji SSIDs!)
# * additionally, the client fires an snmpSet for the original oid (I
# think this release a lock or applies the settings or commits to
# NVRAM or something)
# * finally, now the client goes and fires a waterfall of snmpGets
# against a different oid altogether until the router responds.
# What's fun about this is that the web UI sticks the milliseconds
# since epoch into each request so you can actually *see* the web UI
# waiting (6 seconds between requests, btw)
# * once it gets a 200, the web UI goes and reloads the page
curl -s "${BASE_URL}/snmpSet?oid=1.3.6.1.4.1.4115.1.20.1.1.3.22.1.2.10101=$(encode_ssid "${SSID}");4;&_n=1" -H "Cookie: credential=${response}" > /dev/null
curl -s "${BASE_URL}/snmpSet?oid=1.3.6.1.4.1.4115.1.20.1.1.3.26.1.2.10101=${PASS};4;&_n=1" -H "Cookie: credential=${response}" > /dev/null
curl -s "${BASE_URL}/snmpSet?oid=1.3.6.1.4.1.4115.1.20.1.1.9.0=1;2;&_n=1" -H "Cookie: credential=${response}" > /dev/null
blob="$(curl -s "${BASE_URL}/snmpGet?oids=1.3.6.1.4.1.4115.1.20.1.1.1.2.0;1.3.6.1.4.1.4115.1.20.1.1.2.2.1.21.200;1.3.6.1.4.1.4115.1.20.1.1.3.22.1.3.10001;1.3.6.1.4.1.4115.1.20.1.1.3.22.1.2.10001;1.3.6.1.4.1.4115.1.20.1.1.3.22.1.5.10001;1.3.6.1.4.1.4115.1.20.1.1.3.26.1.2.10001;1.3.6.1.4.1.4115.1.20.1.1.3.30.1.0;1.3.6.1.4.1.4115.1.20.1.1.3.30.2.0;1.3.6.1.4.1.4115.1.20.1.1.3.30.3.0;1.3.6.1.4.1.4115.1.20.1.1.3.22.1.3.10101;1.3.6.1.4.1.4115.1.20.1.1.3.22.1.2.10101;1.3.6.1.4.1.4115.1.20.1.1.3.22.1.5.10101;1.3.6.1.4.1.4115.1.20.1.1.3.26.1.2.10101;1.3.6.1.4.1.4115.1.20.1.1.3.65.1.0;1.3.6.1.4.1.4115.1.20.1.1.3.65.2.0;1.3.6.1.4.1.4115.1.20.1.1.3.65.3.0;&_n=1" -H "Cookie: credential=${response}")"
[ "$(jq -cr '."1.3.6.1.4.1.4115.1.20.1.1.3.22.1.2.10101"' <<< "${blob}")" = "${SSID}" ] \
&& [ "$(jq -cr '."1.3.6.1.4.1.4115.1.20.1.1.3.26.1.2.10101"' <<< "${blob}")" = "${PASS}" ] \
&& { echo 'Success!'; ((count++)) || true; } \
|| { echo "Something went wrong!" && jq <<< "${blob}"; }
done
nmcli dev wifi con "${SSID}" password "${PASS}" \
&& echo "Great success! You're now on ${SSID}, a network with at least ${count} routers!"
# Some misc things:
#
# The routers in the hotel don't support broadcasting multiple SSIDs so, to make
# sure I didn't inconvenience anyone too much (by making the SSID they connected
# to disappear altogether), I only changed the SSID/pass for the 5GHz network.
# I don't know of any devices that ship with only 5GHz support, so in theory any
# devices connected to a 5GHz network that was modified should fall back to the
# 2.4GHz network by the same name without too much fuss.
#
# There definitely are routers in the hotel that *aren't* DG1670As, but I didn't
# seem to run into many of them while testing this script.
#
# Unfortunately the routers (to my knowledge) don't have SSH/telnet support so I
# can't, without moving around the hotel, modify the networks of all the routers
# on the property.
#
# The routers seem to have some kind of dyndns thing set up? They're all cable
# modem + wireless router combos so maybe this has something to do with it.
#
# I encountered at least one router with a different network password but none
# with a different web UI password. Also, there's a field in the JSON blob that
# the router gives you on auth saying whether the password is still the default.
#
# It's worth noting that since the routers aren't just in AP mode (i.e. they
# each have their own DHCP servers) switchover between networks isn't seamless.
# I could, however, if so motivated, configure all the routers to be in AP mode
# and use a DHCP server hosted somewhere in the public Internet. For what I'm
# trying to do though (not lose all Internet access once I leave my room)
# changing the SSID names + passwords is good enough.
#
# Native IPv6! And good speeds! (On Spectrum)
#
# It's very easy to abuse all this for evil; using super predictable passwords
# for their networks is negligent, keeping the default password on the web UI is
# just incompetent.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment