In this post, I will be writing a new toolkit for red team operators that will assist in compromising Cisco IOS routers and the networks they are protecting. The tool will contain its own command interpreter and multiple modules for brute-forcing SSH and the Cisco secret password, as well as modules for building local and/or remote malicious file servers to exploit legacy tools built-into the Cisco IOS image itself.
My goal with this post, is to give you some insight into the process of tool development for assisting in offensive security operations. I will do my best to explain each step in the process, from planning, to development, to testing, and then a demonstration video using the tool on some lab infrastructure setup to closely match the intended target.
I will also preface this post with the fact that I am 100% self-taught, and some of my methods or development techniques may not be “expert” level, however, at Malicious Group we all help one another achieve a great foundation to enter almost any cyber field as a career, and we are all hackers.
Before I can start writing the tool itself, I need to setup a testing environment that will be using a Cisco IOS router to route the traffic, and 2 or 3 internal machines to simulate enough of a network to demonstrate the tool. I will use one of my lab servers to set everything up using VirtualBox and/or VMWare Workstation, and the latest version of GNS3 in order to virtualize multiple Cisco networking devices during the testing process.
Note: If you are using Arch like I was when testing, you can follow this post to install GNS3 on Arch Linux.
For this lab setup, I will be using my DELL laptop, as well as my DELL PowerEdge R620 rack-mounted server, that currently has 96GB of RAM, and 40 CPU Cores available for setting up the target infrastructure. I will also be using the latest version of VirtualBox and VMWare Workstation to utilize VM topology within GNS3.
🠒 Virtual Hardware
It is time to create some virtual machines on the server to assist with the testing process. The first thing I need to do is setup the GNS3 VM on a supported hypervisor by downloading the ISO from their official download link. The next step is to create some infrastructure, so for this I will be using a Windows Server 2016 Standard release for the Active Directory server, and some Windows 7 workstations connected to the AD domain. Lastly, I will setup a Cisco 2691 edge router to route all the traffic to the internal network.
After creating the resources mentioned above, the current lab infrastructure topology looks like the image below.
In this scenario, the Cloud1 resource can be either an internal network or the internet depending on your situation, but both scenarios will work using the same tool. At this point, all devices have been setup and configured, including Windows AD and DNS services, and the Windows 7 machine is joined to the AD domain. The Cisco device has a minimal configuration, including an SSH server, 2 users, 2 interfaces and the service password-encryption policy enabled to encrypt all passwords on the device. In a real life scenario, the attacker would not know the internal network settings, subnets or VLAN data ahead of time, so I will pretend to not know them either and use discovery techniques using the tool I am currently developing.
My goal is to reach the internal Windows AD server and to explore the internal network(s) by going through the Cisco IOS edge router, however, I have a few security blocks in my way as mentioned below.
🠒 SSH Server
The router is running an SSH server, which will be my entry point for this attack. This means I will need to write a coroutine that will brute-force SSH logins to Cisco devices as part of the tool.
🠒 Enable Password
There is a chance the SSH user is not a privilege 15 user, and will require a secret password to enter privileged mode. This means I will need to write a coroutine that will brute-force the Cisco secret password if the user is not a privilege 15 user.
Once the above security challenges are taken care of, I still need access to the internal network but without modifying the running configuration which may alert administrators. I also have the hurdle of running malicious code on the Cisco device without leaving artifacts on the file system, however, thanks to a little known legacy support feature within Cisco IOS images, you can take advantage of TCL scripting language to utilize sockets on the Cisco device itself without writing anything to the running configuration. This means that I will need to write custom TCL scripts to exploit some of the legacy features within Cisco IOS to access the internal network via TCL scripted sockets.
🠒 TCL Scripts
Having access to a sockets-like object within the Cisco’s tclsh command interpreter will allow me to write scripts using sockets to setup a SOCKS4a proxy, as well as port scanning capabilities among other things. This will allow a new layer of communication bypassing Cisco protocols and security and will allow access directly into the network.
🠒 Custom Interpreter
I want to write a custom command interpreter for this tool, because I want to allow more on-demand flexibility using an easy to understand system of setting and changing variables to suit your current operational needs without having a huge list of command arguments to remember.
I have decided to write this tool in Python 3 using Asynchronous coroutines and a few modules for network communication and pretty output. The command interpreter will have some basic settings for logging and prompt output, but will require loading modules to carry out specific tasks like SSH brute-forcing. It will be a metasploit-like command interpreter with a limited command set when complete.
Now that I have a plan in place, it is time to work out the programming logic to achieve all the goals listed above. For this specific tool, I will be using PyCharm as my development IDE and the latest version of Python 3.8 (3.8.2)
🠒 Custom Interpreter
The interpreter will use an event loop with asynchronous coroutines and a registry for storing and retrieving values. The command interpreter framework is fairly simple, and requires a JSON configuration to load some initial options for the user. At this point in the development process, the JSON that gets loaded only contains very basic options for the prompt name and timestamp, but this changes later.
To write the basic command interpreter, I started a new project and created the following file structure.
Note: Don't get overwhelmed, I go over the code in each file below
If you are following along, at this point I simply created the file structure you see above with the PyCharm IDE, and then started to code everything in order, as seen below. (At this point, no coding has been done yet)
There are a few python modules I need to install (pip install <package>):
- prompt-toolkit – to provide an asynchronous prompt object
- Ptable – to provide pretty output to terminal
- ruamel.yaml – to parse YAML configuration data
Once the above modules are installed, I started with coding the command interpreter as seen below. The first thing I did was to code the utility files first.
The colors.py file contains some simple colored output if the terminal allows for it.
The tables.py file contains a function for creating tables when given name and value lists
The next thing I needed before writing more complex data structures, was the Option and Command registry.
The CommandRegistry.py file contains a CommandRegistry class that when called, will register the passed class instance into a global command registry.
The OptionRegistry.py file contains multiple functions for storing and retrieving data from the OptionRegistry object, including a single value, a pair, or the entire dictionary depending on need.
Next in the coding order, was the base and abstract classes for the Command and Console objects that are used heavily in the next few steps. First I will write out the abstract classes, and then the base classes before moving onto the next step. These classes are created to ensure that adding new functionality to the framework, would be very straight forward.
The AbstractConsole.py file contains a abstract structure of a Console object that will be used in later steps.
The AbstractCommand.py file contains a abstract structure of a Command object that will be used in later steps.
The BaseConsole.py file contains the base structure which is a child of the AbstractConsole class
The BaseCommand.py file contains the base structure which is a child of the AbstractCommand class, and includes a __init_subclass__ method to allow for the automated storage of Command objects on initial import.
Ok, at this point we are almost done with the basic framework of the custom command interpreter. The next thing in the coding order are the commands.
The ExitCommand.py file contains a Command object that exits the event loop.
The HelpCommand.py file contains a Command object that prints help information on current commands (and later modules).
The OptionsCommand.py file contains a Command object that prints the current options within the options registry.
The SetCommand.py file contains a Command object that allows the user to set values within the options registry.
The __init__.py file contains some logic to include all the files within the command directory when imported.
After the above commands have been coded, there are only 3 more files needed to complete the basic framework, and that would be 1 configuration file (config.json), and both the Console.py file, and the RedCisco.py bootstrap file.
The Console.py file contains the controller of the interactive prompt. Each command is parsed and handled within the Console methods and coroutines.
The config.json file contains a simple configuration to change the prompt, and add/remove a timestamp on the prompt's right hand side.
The RedCisco.py file contains the entry point for this tool, and contains some bootstrap to the console.
Finally! We are done with a basic command interpreter, that was written with speed and size in mind which should help a lot in the next few stages of development. Below is a short video demonstration of the tool in its current state, for those of you not following along with the code.
As you can see in the video above, we now have a fully custom command interpreter with a good base functionality of storing and retrieving values from a registry. This will be required in the next few steps, in order to add some module functionality to the framework.
🠒 SSH Attack
Now with the command interpreter done, it is time to focus on attacking the SSH server using brute-force. For this specific task, I will be using the netmiko package, which uses synchronous or “blocking” communication, so I will need to run the blocking IO in a ThreadPoolExecutor in order for the code to run asynchronously (or close to it). Before I can start coding the SSH brute-force class, I will need to modify the command interpreter framework a bit.
I will be creating the new Module class for the command interpreter framework, by first creating its base and abstract classes as seen below.
The AbstractModule.py file contains a abstract structure of a Module object that will be used in later steps.
The BaseModule.py file contains the base structure which is a child of the AbstractModule class, and includes a __init_subclass__ method to allow for the automated storage of Module objects on initial import.
The Module class also need its own registry, as it tracks modules the same way the Command class tracks commands by using the __init_subclass__ method as seen in the BaseModule.py file.
The ModuleRegistry.py file contains a ModuleRegistry class that when called, will register the passed class instance into a global module registry.
Next step is to create the SSHModule.py attack module, which loads its own values into the registry, and provides its own sub-prompt to allow for separating the one module from the other.
The SSHModule.py file contains coroutines that execute a brute-force attack on the target SSH server.
Lastly, to ensure the modules are loaded into the tool correctly, add the following line into the Console.py file header area:
from src.core.module import *
This will allow the __init__.py file within the modules directory to automatically load all the modules when the tool is started. This is how the commands are loaded at start-up as well. Below you can see a demonstration of the command interpreter with the SSH brute-force module functionality added.
Awesome! It works really good on my test environment, and I ran over 1000 tests and it found the login each time (which is sorta amazing). This takes care of the SSH brute-force attack, now to focus on the secret password brute-forcing.
🠒 Secret Password Attack
As mentioned above in the planning phase, Cisco users have different privilege levels associated with their account which will set limitations. One such limitation is the secret “enable” password, which can allow an unprivileged user access to a privileged account status. This attack also requires credentials to log into the SSH server, which can be obtained in the SSH attack above.
Since I added the ability to use modules within the framework in the previous step, I just need to code the secret brute-force attack logic as seen below.
The SecretModule.py file contains coroutines that execute a brute-force attack on the secret password for a Cisco IOS user.
In the SSH attack above, we discovered some credentials with the username of “bob” and the password “itsbob“, however that specific account is an unprivileged user that requires a secret password to enable access to a privileged shell, or “enabled mode.” In the demonstration video below, I use the credentials discovered in the last attack, and brute-force the secret password to access a privileged mode.
Viola! Now we have moved from an unprivileged account that we recovered from the SSH attack, to a privileged account which we can now take advantage of some legacy tools built-into the Cisco IOS kernel itself. However, the technique I am going to use requires either a local or remote http server that will act as our malicious file server for the next few steps in the process.
🠒 Local File Server
Depending on the target network you might not be able to use a remote server to host the malicious TCL scripts, especially if you are already deep inside a network that does not allow internet access. In cases like this, the local server setup is probably the best to use (without setting up other tools to assist.) Below you can see the LocalFSModule.py file I created, which requires the package aiohttp, so that will need to be added to the project.
pip install aiohttp[speedups]
The LocalFSModule.py file contains coroutines that execute a asynchronous HTTP server to serve malicious TCL scripts.
The above module when executed, will run the local HTTP server in the background, and will terminate when the event loop is killed, either on exiting the application, or by using CTRL+C keyboard combination. Both methods will force a graceful shutdown killing all running and pending tasks in the process. On success you will see that the service is running on a specific address, on a specific port.
🠒 Remote File Server
If your target device allows internet access, than you should be able to utilize a remote file server to serve the malicious TCL files to the Cisco IOS device. This is the most common case in my opinion, because a lot of the routers that get compromised are edge routers, with at least 1 interface on the internet.
For this module, I will be using the cloud provider DigitalOcean to host the remote file server. By using a temporary cloud instance to run as the file server, the operator can quickly create and destroy instances on-demand. This method will also help to keep the red team infrastructure secure, because if the remote file server gets burned, you can quickly spin up another in its place with a new IP and data center region. RemoteFSModule.py requires both terraform and the package python-terraform to be added to the project.
To install the terraform binary on your system, simply download the file for your system here, and unzip and place the terraform file in your OS command path, then to install the python package.
pip install python-terraform
The RemoteFSModule.py file contains coroutines that automate the creation of cloud resources to host a HTTP file server.
The RemoteFSModule.py file requires the following files to handle the creation, tracking and destruction of cloud resources throughout the project.
The manage.py file contains functions to create and destroy terraform cloud instances.
The ServerRegistry.py file contains a simple list to store any running servers for the shutdown process.
With the above files created, I modified the Console.py file to include the ServerRegistry.py, as seen in the updated Console.py file below.
This is the updated version of the Console.py file.
In order to automate the building of a remote server and the configuration of HTTP services, I need to create a Terraform HCL template to handle the creation logic for the DigitalOcean provider. The template itself is actually 4 separate files all using the HCL format, and will each focus on a specific aspect of the build logic.
The provider.tf file contains information on which "provider" (Terraform speak) to utilize when building a resource.
The resources.tf file contains information used to build the resources at a given provider. In this case, it is a DigitalOcean droplet instance.
The variables.tf file contains values to use at run-time.
The output.tf file contains information on what I want to see as output on finished build.
And lastly for the RemoteFSModule.py, a simple bash script to generate a SSH certificate for Terraform to use during the resource provisioning process.
The generate_ssh.sh file contains a bash script to automatically spin up a temporary 4096-bit SSH key for use with the remote filesystem.
Phew! That was a lot of coding for that single module, but it was well worth it. Now we have 4 modules currently within the toolkit, with only 1 or 2 more to go. Next, it’s time to take a look at TCL (Tool Command Language) scripting.
🠒 TCL Scripting
In the last section of our development process, I will take a look at TCL and TCL scripting… but you may be thinking “Why TCL?”, and “WTF is TCL!?”, so this is why:
The Cisco IOS Tcl shell was designed to allow customers to run Tcl commands directly from the Cisco IOS CLI prompt. Cisco IOS software does contain some subsystems such as Embedded Syslog Manager (ESM) and Interactive Voice Response (IVR) that use Tcl interpreters as part of their implementation. These subsystems have their own proprietary commands and keyword options that are not available in the Tcl shell.https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/ios_tcl/configuration/12-4t/ios-tcl-12-4t-book/nm-script-tcl.html
Seeing that the Cisco IOS kernel subsystems require a TCL interpreter to run, means I should be able to write my own TCL to take advantage of some feature set that is specific to Cisco devices. Such features are called Custom Extensions in the Tclshell, which includes a very interesting commands as seen in the image below.
The socket command in the above list caught my attention, especially when the definition is “Opens a TCP network connection *AND* enables you to associated a VRF (Virtual Routing and Forwarding) table name with it.” So the first thing I did was download the TCLPro debugger, and started learning about how to code TCL scripts. After sifting through hundreds of 15 to 20 year old TCL scripts for old, outdated devices, I found some gems for sure.
From the archives of history, I was able to find a TCL script, that with minor modifications, would do exactly what I needed it to. The script below will be used to setup a SOCKS4/SOCKS4a proxy into the internal network, which will allow me to access any networks on the Cisco device.
The proxy.tcl file contains a TCL script for setting up either a SOCKS4 or SOCKS4a proxy into the internal networks of the device.
With the TCL files above ready to be tested, it is time to move onto the SOCKS4/SOCKS4a proxy module!
🠒 SOCKS4/SOCKS4a Proxy
The last module I will be coding for the development phase is the SOCKS4/SOCKS4a proxy, which means I will need to load the proxy.tcl script from within the Cisco IOS device, after achieving privileged access. The ability to get a privileged user was covered with the SSH attack, followed by the Secret attack (if SSH user not privileged). Once I have a valid user that can access privileged mode, I can use the built-in TCL interpreter to execute the proxy.tcl code from a remote location using either the local or remote HTTP file server. The module file can be seen below.
This module requires the netdev package for Python.
pip install netdev
The ProxyModule.py file contains coroutines to spawn a SOCKS4A proxy using the Cisco IOS built-in TCL interpreter to execute malicious TCL scripts from a HTTP file server.
With the final module complete, it is time to move onto the Demonstration phase of the post by attacking the lab infrastructure discussed at the start of the paper. This should allow me to use the Cisco device to tunnel attacks into the internal network using TCL sockets.
For the demonstration, I will be using the RedCisco toolkit to obtain a Cisco IOS user via SSH and secret password brute-force tools. Once I have valid credentials into the device, I will setup a file server to serve malicious TCL scripts that will allow me to set up the SOCKS4a proxy into the target’s internal network(s). Once the pivot has been established, I will attack the internal network which is running a Windows 2016 Active Directory server, with a few Windows workstations joined to its AD domain. I will run a few attacks through the pivot on the Cisco device against the internal network, to demonstrate the capability of this tool within an actual red team engagement.
Note: The following scenario is made up to reflect actual events, this is purely to demonstrate attacks using this toolkit.
🠒 Obtaining SSH user credentials
After doing some research on the target, I found that they only employed maybe 2 to 3 employees that would have the knowledge to setup the Cisco infrastructure, and after reading their social media history, I was left with the following 2 employees: Robert, and Jose. After digging through their lives and social media, blogs, even checking Yelp! reviews one of them had written about a local restaurant, I put together a wordlist of possible usernames, and another of possible passwords. These would include the employees names, pets (if any), kids (if any), favorite color (if any), and damn near anything I could think of without making a 1,000,000 word list.
Now that I have a user and password list to use in the SSH brute-force attack, it was time to find some credentials!
Viola! The employee research paid off on this one, as Robert (who goes by ‘bob’) had a very lazy password, and on further inspection I could see why.
🠒 Obtaining Secret Password
The user ‘bob’ is an unprivileged user, so even though I can log into the Cisco IOS device, the users privileges are very limited restricting my movement. However, users can elevate their privileges by using the ‘enable‘ command, which requires the secret password. Since I don’t know the secret password, I will have to brute-force it using the wordlist rockyou.txt, so this may take a while.
Note: Video has been shortened to demonstrate results on success.
Bingo! The password was “golgo13golgo“, which is actually pretty long, but we got lucky using rockyou.txt. With finding the secret password, I can now access a privileged mode within IOS which allows me to access the TCL interpreter (tclsh) to execute my malicious TCL script(s). Next step is to setup a local HTTP file system to host our malicious scripts, and then call those scripts remotely from within TCL interpreter on the Cisco IOS device.
🠒 Using a local HTTP file server
If your target does not have access to the internet, you will have to setup a local HTTP file server so that the target device can access the files it needs via TCL interpreter. This is easy as setting up which IP and port you want to use, and then start the server. The server will be killed when exiting the RedCisco command prompt.
It is that simple to start a local malicious HTTP file server, as the mapping from the LocalFSModule.py already has the path to the TCL files, so it is set and forget.
🠒 Using a remote HTTP file server
If your target has access to the internet, and you know it can access outside networks, you can setup a remote malicious HTTP file server which will spawn a DigitalOcean VM instance, running a Nginx server containing a custom configuration, and will also contains the malicious TCL scripts already. This option is a fully automated, remote HTTP file server, that happens to be serving malicious scripts to our targets.
Note: Video has been shortened to demonstrate results on success.
Whether you use the local HTTP file server, or the remote version, you are left with an endpoint, which would be the absolute path to the malicious TCL script, proxy.tcl. The endpoint will be used, along with the complete login credentials, to setup the SOCKS proxy for accessing the internal network.
Note: an example of an endpoint would be: http://<ip>:<port>/proxy.tcl
🠒 Setting up the SOCKS proxy
The last step required before I can start the internal host discovery process, is to setup the SOCKS proxy into the target network. If you have been following along, you already know what the ProxyModule.py already does, and how easy this framework makes it to run malicious TCL scripts on the target device(s).
Viola! The SOCKS proxy has successfully been setup on port 65500, and the framework even printed interested networks it parsed out of the running configuration of the device. This speeds up our attack time, by knowing exactly what networks are on the device.
🠒 Abusing proxy for profit!
With a SOCKS4a proxy setup in to our target network, it is time to setup some other tools to use this proxy to attack internal targets on the 192.168.100.0/24 network. The first tool I am going to setup is proxychains, which is a very straight forward tool that will allow you to use dynamic or strict (chain) communication over SOCKS4/SOCKS5 proxies. By adding a single line into the /etc/proxychains.conf file as seen below, you can add the SOCKS proxy we created in the file.
socks4 10.10.10.3 65500
With this line added, I can use proxychains to start poking at their internal network as seen below.
After running a scan with nmap, I found what looks possibly like a Windows workstation, but with OpenSSH and FTP servers running, as well as the RPC endpoint mapper on port 135. On a follow up scan, I took a closer look at the FTP server on port 21, by running the following command as seen below.
Trying to fingerprint a service through a proxy is very hit or miss, as sometimes it works, and sometimes it doesn’t. On this specific attempt, I was able to get a few possible FTP devices it could be, and narrowed it down to the FTP Server 1.0 from Konica Minolta. If this is the case, then I should be able to leverage a SEH overflow in the post-authentication CWD command.
At this point, I am able to access and attack the internal network behind the Cisco IOS router. If you want to see the rest of this attack, then check out the FTP exploitation here, and don’t forget to follow me on twitter at @MCoetus.