Last active
August 18, 2020 03:32
-
-
Save rrbutani/952c41508c304372542394b68fb881d1 to your computer and use it in GitHub Desktop.
Routers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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