├── README.md └── S4x2016 └── SEL-Emulator ├── README.md ├── etc └── init │ └── sel-emulator.conf └── home └── pi ├── sel.py └── serialcode └── serialtest.go /README.md: -------------------------------------------------------------------------------- 1 | # CTF-Challenges 2 | 3 | This repository contains code for old challenges for capture the flag problems. 4 | 5 | Some code snippets may be useful; others may not be. Note that many of these challenges contain security issues: some of the issues are on purpose, others are unintentional. Digital Bond does not recommend using any challenges problems as source material for your own code. 6 | -------------------------------------------------------------------------------- /S4x2016/SEL-Emulator/README.md: -------------------------------------------------------------------------------- 1 | # DO NOT USE THIS AS A PRODUCTION HONEYPOT 2 | # DO NOT USE THIS AS A PRODUCTION HONEYPOT 3 | # DO NOT USE THIS AS A PRODUCTION HONEYPOT 4 | 5 | # SEL Emulator 6 | 7 | ## About 8 | This is a pretty rudimentary 'SEL Emulator'. It was designed for use in the 9 | S4 (SCADA Security Scientific Symposium) conference in 2016. Players were 10 | tasked with identifying a digital protective relay, left available via a 11 | wireless connection. 12 | 13 | This project simulates an SEL-2924 Bluetooth to Serial adapter, as it would 14 | appear if it was accidentally left connected to a SEL-751A Feeder protection 15 | relay. Both the Bluetooth adapter and the SEL relay feature their default 16 | passwords (PIN pairing code 2924 for the Bluetooth, and OTTER/TAIL as the 17 | relay level 1 and level 2 passwords, respectively). 18 | 19 | The sel.py Python script acts as the SEL-751A relay emulator. Note that this 20 | uses the Python cmd2 package for building a command line, and that it contains 21 | vulnerabilities. Specifically, an attacker may gain a command line on your 22 | system with the privileges of the Python process. On a Raspberry Pi system with 23 | default configuration, an attacker could easily get root privileges on the Pi. 24 | 25 | ## Installation 26 | 27 | There are two methods of using the honeypot. The first method is to use the 28 | device as a Bluetooth honeypot. The second method is to use it as a TCP (TELNET) honeypot. 29 | 30 | Again, do not use this in production. There are security vulnerabilities both known and unknown in the implementation. The CMD2 library features many backdoor commands, and an attacker can easily root your Raspberry Pi or any *nix system that you run this code on. 31 | 32 | ### Install as a Bluetooth device 33 | To install this project you will need a bluetooth adapter on your Raspberry Pi. 34 | This code depends upon features of an external Bluetooth Serial adapter. Specifically, you should use the Roving Networks RN-42. 35 | 36 | Connect the RN42 pin PIO2 to GPIO26 *and* GPIO12 on the Raspberry Pi GPIO block. 37 | 38 | Connect the RN42 TX and RX pins to the serial GPIO pins on the Raspberry Pi, and connect the RN42 3.3v and GND pins to the 3.3v and GND pins on the Raspberry Pi. 39 | 40 | Copy the file 'etc/init/sel-emulator.conf' to your Pi's /etc/init/ directory. 41 | 42 | Copy the file 'home/pi/sel.py' to your Pi's /home/pi directory. 43 | 44 | Copy the file 'home/pi/serialcode/serialtest.go' to your Pi's /home/pi/serialcode directory (create this directory if it does not exist). 45 | 46 | Modify the serialtest.go file so that the line 59 'Name' variable is assigned. 47 | to your Raspberry Pi's bluetooth adapter. Change the 'Baud' variable to the baud rate of your Bluetooth adapter. You may wish to verify that you can communicate with your RN42 adapter at this stage, using your favorite terminal software (we use screen). 48 | 49 | Modify the PIN and SSID of the RN42. See the RN42 instruction manual for help on the commands to achieve this. 50 | 51 | Disable the Serial Console on the Raspberry Pi serial port by modifying the /boot/config.txt. Ensure that the boot variable 'console' is set to something other than the Bluetooth's serial port. For example. 52 | 53 | On our test Raspberry Pi 2, we leave the serialtest.go file alone (using /dev/ttyAMA0 as the bluetooth port), and set the boot line to be: 54 | 55 | dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait 56 | 57 | You should now be able to reboot your Pi. You should now see the Bluetooth device as pairable. Pair with your PIN, and you may open a serial terminal on your laptop. Press the 'enter' key to see the classic SEL '=' prompt, indicating that you are logged in with no authentication. 58 | 59 | ### Installation as a TCP Service 60 | 61 | TODO =). You can just wrap the sel.py script in a netcat server call, if you wish, as 'nc -l -k -p 23 -e python /path/to/sel.py'. For slightly better security, it may be better to run this as a service with 'nobody/nogroup' privileges. We will post instructions soon. 62 | -------------------------------------------------------------------------------- /S4x2016/SEL-Emulator/etc/init/sel-emulator.conf: -------------------------------------------------------------------------------- 1 | # sel-emulator - Emulates an SEL relay on serial terminal 2 | # 3 | 4 | description "Schweitzer Emulator" 5 | 6 | start on runlevel [2345] 7 | stop on runlevel [!2345] 8 | 9 | respawn 10 | respawn limit 10 5 11 | 12 | exec /usr/bin/go run /home/pi/serialcode/serialtest.go 13 | -------------------------------------------------------------------------------- /S4x2016/SEL-Emulator/home/pi/sel.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import sys 3 | import getch 4 | import getpass 5 | import os 6 | from cmd2 import Cmd 7 | import time 8 | import serial 9 | 10 | 11 | 12 | def cli_getch(): 13 | pswd = "" 14 | gc = "" 15 | while 1: 16 | gc = getch.getch() 17 | if gc == "\n": 18 | break 19 | pswd += gc 20 | sys.stdout.write("*") 21 | return pswd 22 | 23 | class Sel(Cmd): 24 | prompt = '=' 25 | auth_level = 0 26 | def do_acc(self, arg): 27 | trynumber = 0 28 | mytime = time.time() 29 | myfile = open("Logs/" + mytime.__str__() + ".txt", 'w') 30 | myfile.write(mytime.__str__() + " : ACC detected\n") 31 | while (trynumber < 4): 32 | sys.stdout.write("Password?") 33 | tpass = cli_getch() #raw_input() 34 | myfile.write("User enter password: " + tpass) 35 | myfile.close() 36 | if tpass == 'OTTER': 37 | print """ 38 | 39 | KILLER ROBOTS INC Date: 01/01/1970 Time: 00:00:00.000 40 | ROBOT PLANT 1 Time Source: Unknown 41 | 42 | Level 1 43 | 44 | """ 45 | acc = Acc() 46 | acc.echo = True 47 | acc.cmdloop() 48 | return 49 | else: 50 | trynumber += 1 51 | print("\nInvalid Password") 52 | if trynumber > 3: 53 | print("Alerting the goons!\n\n") 54 | def default(self, arg): 55 | print('Invalid Access Level\n') 56 | def do_help(self, arg): 57 | print('Invalid Access Level\n') 58 | def do_set(self, arg): 59 | self.do_help(arg) 60 | def do_show(self, arg): 61 | self.do_help(arg) 62 | def do_shell(self, arg): 63 | print('I\'m a-gonna keep my eye on you\n') 64 | class Acc(Sel): 65 | prompt = '=>' 66 | auth_level = 1 67 | def do_EOF(self, args): 68 | # nope 69 | # print("caught eof\n") 70 | return 71 | def do_help(self, arg): 72 | print """ 73 | ANALOG - Test an analog output channel. 74 | BREAKER - Display breaker monitor data. 75 | CLOSE - Close Breaker. 76 | COMMUNICATIONS - Display or clear communications channel data. 77 | CONTROL - Control Remote Bits and digital outputs. 78 | COPY - Copy a settings group into another settings group. 79 | COUNTERS - Show current state of device counters. 80 | ETH - Display Ethernet Status Report. 81 | EVENT - Display an event report. 82 | EXIT - Exit to Access Level 0 and terminate the session. 83 | FILE - Work with relay files. 84 | GOOSE - Display GOOSE Communication Information. 85 | GROUP - Change a settings Group. 86 | HISTORY - Display an event history or clear event data. 87 | IRIG - Synchronize the device date and time with the IRIG source. 88 | LDP - Display or clear load profile data. 89 | LOOPBACK - Test a communications channel. 90 | MAC - Display MAC Addresses. 91 | MAP - View DNP or Modbus Map 92 | METER - Display metering data. 93 | OPEN - Open Breaker. 94 | PING - Send Ping messages to a network device. 95 | PULSE - Pulse a digital output. 96 | QUIT - Exit to Access Level 0 97 | RESTORE_RELAY - Restore settings to manufacturing default configuration. 98 | SER - Display Sequential Events Recorder records. 99 | SET/SHOW - Modify or display device settings. 100 | STATUS - Display or clear relay status. 101 | SUMMARY - Display an event summary. 102 | TARGETS - Display internal binary variable values. 103 | TRIGGER - Trigger collection of event data.\n\n 104 | 105 | CAUTION: For security purposes, some commands are not provided in this HELP 106 | response. Elevated privilege, identification, configuration, 107 | etcetera commands are described in the instruction manual.\n\n""" 108 | def default(self, arg): 109 | print('Unknown Command Or Command Not Implemented (Real relays are expensive mkay?)\n\n') 110 | def do_open(self, arg): 111 | print('OPEN relay jumper is installed. Destroying the Killer Robots, maybe.\n\n(PS: This is really dangerous, never do this in real life.)\n\n') 112 | def do_reiddisconnect(self, arg): 113 | do_exit() 114 | def do_reidconnect(self, arg): 115 | do_exit() 116 | def do_2ac(self, arg): 117 | print('\n\nPassword? '), 118 | tpass = cli_getch() 119 | print('\nINVALID PASSWORD, FLY YOU FOOL\n\n') 120 | def do_set(self, arg): 121 | print('Unknown Command\n\n') 122 | def do_show(self, arg): 123 | print """ 124 | 125 | Group 1 126 | Relay Settings 127 | 128 | ID Settings 129 | RID := SET 130 | TID := LAB PROTECCIONES 131 | 132 | Config Settings 133 | CTR := 10 CTRN := 10 PTR := 1.00 134 | DELTA_Y := WYE VNOM := 380.00 SINGLEV := N 135 | 136 | Max Ph Overcurr 137 | 50P1P := 5.00 50P1D := 0.50 138 | 50P1TC := 1 139 | 50P2P := OFF 50P3P := 10.00 50P3D := 0.00 140 | 50P3TC := 1 141 | 50P4P := OFF 142 | 143 | Neutral Overcurr 144 | 50N1P := 0.50 50N1D := 1.00 145 | 50N1TC := 1 146 | 50N2P := OFF 50N3P := OFF 50N4P := OFF 147 | 148 | Residual Overcurr 149 | 50G1P := 1.50 50G1D := 1.00 150 | 50G1TC := 1 151 | 50G2P := OFF 50G3P := OFF 50G4P := OFF 152 | 153 | Neg Seq Overcurr 154 | 50Q1P := OFF 50Q2P := OFF 50Q3P := OFF 155 | 50Q4P := OFF 156 | 157 | Phase TOC 158 | 51AP := OFF 51BP := OFF 51CP := OFF 159 | 160 | Maximum Ph TOC 161 | 51P1P := 3.00 51P1C := U3 51P1TD := 0.50 162 | 51P1RS := Y 51P1CT := 1.00 51P1MR := 0.10 163 | 51P1TC := 1 164 | 51P2P := 6.00 51P2C := U3 51P2TD := 3.00 165 | 51P2RS := N 51P2CT := 0.00 51P2MR := 0.00 166 | 51P2TC := 1 167 | 168 | Negative Seq TOC 169 | 51QP := 6.00 51QC := U3 51QTD := 3.00 170 | 51QRS := N 51QCT := 0.00 51QMR := 0.00 171 | 51QTC := 1 172 | 173 | Neutral TOC 174 | 51N1P := OFF 51N2P := OFF 175 | 176 | Residual TOC 177 | 51G1P := 11.00 51G1C := U3 51G1TD := 1.50 178 | 51G1RS := N 51G1CT := 0.00 51G1MR := 0.00 179 | 51G1TC := 1 180 | 51G2P := 0.50 51G2C := U3 51G2TD := 1.80 181 | 51G2RS := N 51G2CT := 0.00 51G2MR := 0.00 182 | 51G2TC := 1 183 | 184 | Undervoltage Set 185 | 27P1P := OFF 27P2P := OFF 186 | 187 | Overvoltage Set 188 | 59P1P := OFF 59P2P := OFF 59G1P := OFF 189 | 59G2P := OFF 59Q1P := OFF 59Q2P := OFF 190 | 191 | Power Factor Set 192 | 55LGTP := OFF 55LDTP := OFF 55LGAP := OFF 193 | 55LDAP := OFF 194 | 195 | Frequency Set 196 | 81D1TP := OFF 81D2TP := OFF 81D3TP := OFF 197 | 81D4TP := OFF 81D5TP := OFF 81D6TP := OFF 198 | 199 | Rate of Frequency Set 200 | E81R := OFF 201 | 202 | Fast Rate of Frequency Set 203 | E81RF := N 204 | 205 | Demand Mtr Set 206 | EDEM := THM DMTC := 5 PHDEMP := 5.00 207 | GNDEMP := 1.00 3I2DEMP := 1.00 208 | 209 | Power Elements 210 | EPWR := N 211 | 212 | Trip/Close Logic 213 | TDURD := 0.0 CFD := 1.0 214 | TR := ORED50T OR ORED51T OR 81D1T OR 81D2T OR 81D3T OR 81D4T OR 59P1T OR 215 | 59P2T OR 55T OR REMTRIP OR SV01 OR OC OR SV04T OR 50P1P 216 | REMTRIP := 0 217 | ULTRIP := NOT ( 51P1P OR 51G1P OR 51N1P OR 52A ) 218 | 52A := 0 219 | CL := SV03T AND LT02 OR CC 220 | ULCL := 0 221 | 222 | Reclosing Control 223 | E79 := OFF 224 | 225 | """ 226 | def do_status(self, arg): 227 | print """ 228 | KILLER ROBOTS INC Date: 01/01/1970 Time: 00:00:00.000 229 | ROBOT PLANT 1 Time Source: Unknown 230 | 231 | Serial Num = 1380926031 FID = SEL-751A-R419-V0-Z011003-D20131025 232 | CID = 79CC PART NUM = 751A61A1A0X74850330 233 | 234 | SELF TESTS (W=Warn) 235 | FPGA GPSB HMI RAM ROM CR_RAM NON_VOL CLOCK CID_FILE +0.9V +1.2V 236 | OK OK OK OK OK OK OK OK OK 0.91 1.20 237 | 238 | +1.5V +1.8V +2.5V +3.3V +3.75V +5.0V -1.25V -5.0V BATT 239 | 1.50 1.81 2.51 3.32 3.77 4.97 -1.25 -4.92 3.03 240 | 241 | Option Cards 242 | CARD_C CARD_D CARD_E CURRENT 243 | OK OK OK OK 244 | 245 | Offsets 246 | IA IB IC IN VA VB VC 247 | 6 6 5 6 0 0 0 248 | 249 | Relay Enabled 250 | 251 | """ 252 | 253 | #ser = serial.Serial("/dev/ttyAMA0", baudrate=115200) 254 | #sys.stdout = ser.fileno() 255 | #sys.stdin = ser.fileno() 256 | 257 | sel = Sel() 258 | sel.echo = True 259 | sel.cmdloop() 260 | -------------------------------------------------------------------------------- /S4x2016/SEL-Emulator/home/pi/serialcode/serialtest.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os/exec" 7 | "log" 8 | "github.com/tarm/serial" 9 | "github.com/traetox/goGPIO" 10 | "time" 11 | ) 12 | 13 | const ( 14 | unbuffer = "/usr/bin/unbuffer" 15 | dashp = "-p" 16 | selpy = "/home/pi/sel.py" 17 | bluetoothpin = 26 18 | ) 19 | 20 | func pinMonitor(pin *goGPIO.GPIO, in io.WriteCloser, term *serial.Port) { 21 | sleeptimer := 500 * time.Millisecond 22 | exitcmd := []byte("\n\n\n\nexit\nexit\n") 23 | modemkill1 := []byte("$$$") // kill the remote connection if he is monopolizing 24 | modemkill2 := []byte("K,\n") 25 | warningmsg := []byte("***ONE MINUTE WARNING. SOLVE THE FLAG OR LOG OFF THE BLUETOOTH PLZ.***\r\n\r\n") 26 | timeoutmsg := []byte("Sorry pal, time's up. If you didn't solve it, think a while and try again.\r\n\r\n") 27 | starttime := time.Now() 28 | displayedwarning := false 29 | for { 30 | sessiontime := time.Since(starttime) 31 | if sessiontime > (5 * time.Minute) { 32 | term.Write(timeoutmsg) 33 | time.Sleep(1200*time.Millisecond) 34 | term.Write(modemkill1) 35 | time.Sleep(1200*time.Millisecond) 36 | term.Write(modemkill2) 37 | } 38 | if (sessiontime > (4 * time.Minute)) && (displayedwarning == false) { 39 | term.Write(warningmsg) 40 | displayedwarning = true 41 | } 42 | 43 | g, err := goGPIO.New(bluetoothpin) 44 | if err != nil { 45 | panic(err) 46 | } 47 | g.SetInput() 48 | pinState := g.State() 49 | if pinState != true { 50 | fmt.Println("GPIO pin went low, exiting pinMonitor") 51 | in.Write(exitcmd) 52 | break 53 | } 54 | time.Sleep(sleeptimer) 55 | } 56 | } 57 | 58 | func main() { 59 | c := &serial.Config{Name: "/dev/ttyAMA0", Baud: 9600} 60 | s, err := serial.OpenPort(c) 61 | if err != nil { 62 | log.Fatal(err) 63 | } 64 | // confusing =). GPIO26 is WiringPi pin 25. 65 | // GPIO12 is wiringpi 26. So I just hooked up GPIO26 *and* GPIO12 66 | // to the GPIO2 on the bluetooth and it should work 67 | g, err := goGPIO.New(bluetoothpin) 68 | if err != nil { 69 | panic(err) 70 | } 71 | g.SetInput() 72 | sleeptimer := 500 * time.Millisecond 73 | for { 74 | fmt.Println("Starting up!") 75 | cmd := exec.Command(unbuffer, dashp, selpy) 76 | in, err := cmd.StdinPipe() 77 | if err != nil { 78 | panic(err) 79 | } 80 | //defer in.Close() 81 | out, err := cmd.StdoutPipe() 82 | if err != nil { 83 | panic(err) 84 | } 85 | //defer out.Close() 86 | // start the process 87 | if err = cmd.Start(); err != nil { 88 | panic(err) 89 | } 90 | // connect the pipes, otherwise the GPIO doesn't report the right state? 91 | go io.Copy(in, s) 92 | go io.Copy(s, out) 93 | // loop until we get a pin high... 94 | fmt.Println("Waiting for connection") 95 | for { 96 | g, err := goGPIO.New(bluetoothpin) 97 | if err != nil { 98 | panic(err) 99 | } 100 | g.SetInput() 101 | pinState := g.State() 102 | if pinState != false { 103 | fmt.Println("Got connection!") 104 | break 105 | } 106 | time.Sleep(sleeptimer) 107 | } 108 | fmt.Println("Got connection") 109 | pinMonitor(g, in, s) 110 | fmt.Println("Closing out!") 111 | // if we return, close in and out 112 | in.Close() 113 | out.Close() 114 | } 115 | } 116 | --------------------------------------------------------------------------------