Relentless Coding

A Developer’s Blog

Updating My Cisco 4321 Router's IOS over HTTP, FTP and TFTP and More

I recently got my hands on a second-hand Cisco 4321 router. First things first, I want to upgrade to the latest IOS version so I have the latest security patches. Having no experience at all with IOS, these are some things I learned along the way.

Conventions Used in This Post

Router> is a command prompt that indicates the user in currently in EXEC mode. Router# indicates that the user is in privileged EXEC mode. You get from EXEC to privileged EXEC mode by:

Router>enable
Router#disable
Router>

Depending on your setup, you might need to type a password after enable.

Every owner of Cisco equipment knows this, but I am one of my own future readers, so I need to keep things simple.

What Version Do I Currently Run?

Router>sh version

That shows:

Cisco IOS XE Software, Version 16.06.04
Cisco IOS Software [Everest], ISR Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 16.6.4, RELEASE SOFTWARE (fc3)
...
System returned to ROM by Reload Command
System image file is "bootflash:isr4300-universalk9.16.06.04.SPA.bin"
Last reload reason: Reload Command
...

I am running IOS XE 16.06.04 and it got loaded from the file isr4300-universalk9.16.06.04.SPA.bin stored on the bootflash.

Finding an Appropriate IOS Image

Turns out Cisco are dicks difficult about updates. You need a service contract before you can get a download link.

You can find your router model and navigate to the downloads tab. If you have a support contract, you can log in and presumably download the latest version. Also, Cisco hints that some older version might be available without a contract. But why would you want an old version with lots of security problems to run on an internet-facing router, right?

I do not have a service contract. I did notice the previous owner had a newer version of the firmware on the router’s flash memory:

Router#dir :flash
...
18  -rw-  763905745  Nov 9 2023 15:45:02 +00:00 isr4300-universalk9.17.09.04.SPA.bin
...

Also, it looks like sometimes an IOS release falls off a truck. Search your favorite search engine for something like intitle:index.of <image-name> to get a sense of where these trucks drive.

Making Sure You Did Not Just Download Malware

How do I know the firmware I got was not tampered with? Get the MD5 and SHA512 checksums from the Cisco Software Download page when hovering over the filename:

Cisco Software Download page shows image checksums when hovering over the filename

At least they did not hide that information.

Now, let’s verify if the checksum on my router equals to what was published. MD5 is old and broken, so let’s go with SHA512:

Router#verify /sha512 flash:isr4300-universalk9.17.09.04.SPA.bin 34f8eff288bb485c8ca902d2e5c03f9d45a4dc881e919a6ae3f0ca3a52161eecc5c9fc7a7ed90423984ee690695293ad011590fa0e75c65ccf003f7f89b0d5c0
........................................................ <snip> ...Done!
Verified (bootflash:isr4300-universalk9.17.09.04.SPA.bin) = 34f8eff288bb485c8ca902d2e5c03f9d45a4dc881e919a6ae3f0ca3a52161eecc5c9fc7a7ed90423984ee690695293ad011590fa0e75c65ccf003f7f89b0d5c0

verify /sha512 flash:<file> calculates the checksum for the specified file. If you provide a checksum yourself with verify /sha512 flash:<file> <checksum>, the checksum is verified against your input. In this case, the checksum matches. Sweet!

Looking at the image name, I can also see that it contains SPA. According to Cisco, that stands for an image Signed with key version A meant for Production use. The file can be verified as follows:

Router#verify flash:isr4300-universalk9.17.09.04.SPA.bin
Verifying file integrity of bootflash:isr4300-universalk9.17.12.04a.SPA.bin...
Embedded Hash   SHA1 : 03A19E90D4E58FEDCAC30959192B8CEEDF6D29CC
Computed Hash   SHA1 : 03A19E90D4E58FEDCAC30959192B8CEEDF6D29CC
Starting image verification
Hash Computation:    100%Done!
Computed Hash   SHA2: 975953771aac683df79da71226460ecb
                      fead063cf39b9b4f4165e8581dee89fd
                      733868cc148e407e76ace6c2f912dac9
                      fb7c95bdbd402b3fffa257c9ba6ebbd4
                      
Embedded Hash   SHA2: 975953771aac683df79da71226460ecb
                      fead063cf39b9b4f4165e8581dee89fd
                      733868cc148e407e76ace6c2f912dac9
                      fb7c95bdbd402b3fffa257c9ba6ebbd4
                      
Digital signature successfully verified in file bootflash:isr4300-universalk9.17.12.04a.SPA.bin

Then there is the following command that:

Displays software authenticity-related information for the current ROMmon and the Cisco IOS image file used for booting.

Router#sh software authenticity running 
SYSTEM IMAGE                                
------------                          
Image type                    : Production  
    Signer Information                  
        Common Name           : CiscoSystems
        Organization Unit     : IOS-XE      
        Organization Name     : CiscoSystems
    Certificate Serial Number : 675E97D5
    Hash Algorithm            : SHA512
    Signature Algorithm       : 2048-bit RSA
    Key Version               : A                                                              

ROMMON
------

... snip ...

You can view more information about the public keys used to verify the signed images:

Router#show software authenticity keys

At this point, I am not sure where my router got these public keys from, so I am happy to just verify the checksum of the firmware image I am downloading on the router. But good to know that there exist more robust ways to verify the firmware actually came from Cisco.

What Transfer Methods Are Available?

Router>enable
Router#copy ?
  /erase          Erase destination file system.
  /error          Allow to copy error file.
  /noverify       Don't verify image signature before reload.
  /verify         Verify image signature before reload.
  bootflash:      Copy from bootflash: file system
  cns:            Copy from cns: file system
  flash:          Copy from flash: file system
  ftp:            Copy from ftp: file system
  http:           Copy from http: file system
  https:          Copy from https: file system
  null:           Copy from null: file system
  nvram:          Copy from nvram: file system
  rcp:            Copy from rcp: file system
  running-config  Copy from current system configuration
  scp:            Copy from scp: file system
  startup-config  Copy from startup configuration
  system:         Copy from system: file system
  tar:            Copy from tar: file system
  tftp:           Copy from tftp: file system
  tmpsys:         Copy from tmpsys: file system
  usb0:           Copy from usb0: file system
  webui:          Copy from webui: file system

Most of these options look familiar, but some I have never heard of, such as cns: and webui:.

Sneakernet (USB)

My router has a USB A port. When I plug in a USB drive (single partition formatted as FAT32), it shows up under usb0. Copy the image to flash memory:

Router#copy usb0:image.bin flash:

But you might not always have a USB drive handy (or a USB port for that matter), so let’s look at some other ways to transfer files.

Using IP

Transfer methods using IP should first have IP configured on the router:

Router>enable
Router#conf t
Router(conf)#int g0/0/0
Router(conf-if)#no shut
Router(conf-if)#ip addr 10.0.0.1 255.255.255.252
Router(conf-if)#end

On the other side, configure 10.0.0.2. I am assuming Linux:

# ip addr add 10.0.0.2/32 peer 10.0.0.1 dev eth1

Over HTTP

I like to use Busybox’ httpd for this:

linux$ busybox httpd -vv -f -p 5000
Router#copy http://10.0.0.2:5000/image.img flash:
10.0.0.1:13911: url:/file.txt
10.0.0.1:13911: response:200
10.0.0.1:26393: url:/file.txt
10.0.0.1:26393: response:200

Notice the duplicate requests coming from the router.

That is why I could not get Busybox’ nc working:

linux$ cat -A res.http
#!/bin/sh

cat <<EOF
HTTP/1.1 200 OK^M$
Content-Type: text/plain^M$
Content-Length: 14^M$
^M$
Hello, world!$
EOF
linux$ chmod u+x res.http
linux$ busybox nc -lk -p 5000 -e ./res.http
connect to 10.0.0.2:5000 from 10.0.0.1:16289 (10.0.0.1:16289)

Wireshark shows nc sending TCP RSTs after the first HTTP exchange with the router, whereas maybe the router only tolerates graceful connection termination, over which we have no control when using nc. (Same goes for using busybox tcpsvd which I will discuss later.)

Then again, maybe these duplicate HTTP requests are just an idiosyncrasy of isr4300-universalk9.16.06.04.SPA.bin, the version I am trying to upgrade from.

TFTP

linux# busybox udpsvd -vE 10.0.0.2 69 busybox tftpd -u ftp /srv/ftp

Busybox’ tftpd is meant to be run by inetd. That daemon would bind to a port, and when a connection is made to that port, it spawns a process which runs an executable. It hooks the network stream directly to stdin and stdout of the process. A big upside is that the process itself does not need to have any network code in it.

Here, instead of starting inetd, we take Busybox’ suggestion and run the helper program called udpsvd that works much like inetd but runs as a foreground process:

Create UDP socket, bind to IP:PORT and wait for incoming packets. Run PROG for each packet, redirecting all further packets with same peer ip:port to it.

We need to run as root to be able to bind to privileged port 69. tftpd also starts as root to be able to chroot into the specified directory, but then drops its privileges to access the files as -u <user>.

On the router we can now transfer files:

Router#copy tftp://10.0.0.2/file.txt flash:
Destination filename [file.txt]?
Accessing tftp://10.0.0.2/file.txt...
Loading file.txt from 10.0.0.2 (via GigabitEthernet0/0/0): !
[OK - 17 bytes]

17 bytes copied in 0.018 secs (944 bytes/sec)

Also, depending on the TFTP software you are using, you might run into this caveat:

Most TFTP applications cannot transfer files larger than 16MB in size. If the Cisco IOS software you install is larger than 16MB, you must use an FTP or RCP server.

I did not have this problem with busybox tftpd, though.

FTP

Transferring files over FTP works much the same as TFTP. Instead of using UDP, it uses TCP, though, so we have to change the Busybox’ helper program:

linux# busybox tcpsvd -vE 10.0.0.2 21 busybox ftpd -vv -a ftp /srv/ftp

On the router:

Router#copy ftp://10.0.0.2/file.txt flash:
Destination filename [file.txt]?
Accessing ftp://10.0.0.2/file.txt...
Loading file.txt
[OK - 17/4096 bytes]

17 bytes copied in 0.048 secs (354 bytes/sec)

Over a Serial Connection With minicom

On the internet, I read that in ROMMON, you can use something called “xmodem” to transfer files. minicom has a “send files” feature where you can choose the xmodem protocol.

It should work like:

rommon 123 >copy xmodem: flash:image.img

However, after entering ROMMON by booting up the router and pressing Ctrl+A and then F to send a BREAK, I see only the following options:

rommon 1 > help
alias               set and display aliases command
boot                boot up an external process
confreg             configuration register utility
dev                 list the device table
dir                 list files in file system
help                monitor builtin command help
history             monitor command history
meminfo             main memory information
repeat              repeat a monitor command
reset               system reset
set                 display the monitor variables
showmon             display currently selected ROM monitor
sync                write monitor environment to NVRAM
token               display board's unique token identifier
unalias             unset an alias
unset               unset a monitor variable

Note there is no copy command. (Nor an tftpdlnd command I saw mentioned elsewhere. That command should, after setting the appropriate environment variables, download files over TFTP.)

I am not sure at this point if this feature is supposed to be supported by every Cisco router or not. Maybe I can unlock a privileged mode (akin to enable in IOS) or update the ROMMON firmware and get extended options? It would suck for sure if all you had was ROMMON mode and no way to boot an image or get a working image in persistent memory.

Speed Considerations

  • TFTP is slow, because it uses UDP segments with a payload of 512 bytes each. It is a “lockstep” protocol: each data packet needs to be acknowledged by the receiver before the sender sends the next packet.
  • FTP and HTTP use TCP. TCP allows for more payload per segment and many segments can be outstanding before the sender stops to wait for an acknowledgement. The receiver can acknowledge many segments at once, reducing overhead. The receive window size (amount of bytes that can be outstanding at any one time before the sender has to wait for acknowledgement) can grow to many megabytes.
  • Sneakernet should be even faster. If the router supports USB 2.0, you should be able to expect around 30+ MiB/s depending on factors such as how fast the router can flush the data to persistent storage.

For example, the transfer of a 700+ MiB image was so tediously long over TFTP, I had to abort. FTP, on the other hand, completed in less that 5 minutes:

776523079 bytes copied in 268.943 secs (2887315 bytes/sec)

Security Considerations

Protocols like HTTP, FTP and TFTP transmit data in plaintext and do not provide any protections against snooping and tampering. While me might not care much about confidentiality when transmitting a router image, we definitely want to make sure we connect to the correct download server and get the correct image loaded in our router. Accidental alterations during transmission, such as bit flips, are caught by transport layer (UDP/TCP) checksums: the faulty UDP/TCP segment is discarded and the download server resends it automatically. Malicious alterations by a MITM can be detected by calculating the digest of the reassembled file on the destination:

On your Linux download server:

linux$ sha512sum isr4300-universalk9.17.09.04.SPA.bin
34f8eff288bb485c8ca902d2e5c03f9d45a4dc881e919a6ae3f0ca3a52161eecc5c9fc7a7ed90423984ee690695293ad011590fa0e75c65ccf003f7f89b0d5c0  isr4300-universalk9.17.09.04.SPA.bin

Compare that output with what you get on the router:

Router#verify /sha512 flash:isr4300-universalk9.17.09.04.SPA.bin 34f8eff288bb485c8ca902d2e5c03f9d45a4dc881e919a6ae3f0ca3a52161eecc5c9fc7a7ed90423984ee690695293ad011590fa0e75c65ccf003f7f89b0d5c0
........................................................ <snip> ...Done!
Verified (bootflash:isr4300-universalk9.17.09.04.SPA.bin) = 34f8eff288bb485c8ca902d2e5c03f9d45a4dc881e919a6ae3f0ca3a52161eecc5c9fc7a7ed90423984ee690695293ad011590fa0e75c65ccf003f7f89b0d5c0

To trust this digest, you need a secure connection to the router, however. This can be a connection via the serial console or over the LAN, for example, or over SSH. If you do not have a secure connection to the destination, you cannot be sure and maybe should not be servicing your router this way. Spend time to set up SSH in this case.

Also, as mentioned at the top, if the image has something like SPA in it, it should be digitally signed. I have not spent much time looking into this, but if you can be certain you have the correct public key from Cisco, this would provide the strongest guarantee that the image has not been tampered with.

Specifying Which Image to Run on Startup

Router#conf t
Router(config)#boot system flash isr4300-universalk9.17.12.04a.SPA.bin
Router(config)#end
Router#copy running-config startup-config
Router#reload

Make sure to persist changes by writing the config to flash memory.