├── .gitignore ├── README.md ├── ctlofpot └── ofpot.py /.gitignore: -------------------------------------------------------------------------------- 1 | README 2 | *.pyc 3 | *.pyo 4 | ofney* 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | OFPOT 2 | ===== 3 | 4 | OpenFlow HoneyPot (ofpot) is an OpenFlow application running on POX 5 | (http://www.noxrepo.org/pox/about-pox/). 6 | ofpot detects unused IP addresses with arp snooping, 7 | and twists the flow from internet nodes to unused IP addresses to 8 | specified Honeypot using arp spoofing against default routeer and 9 | destination mac address swapping. 10 | 11 | 12 | % git clone git://github.com/noxrepo/pox.git 13 | % git clone git://github.com/upa/ofpot.git 14 | % cp ofpot/ofpot.py pox/ext/ 15 | % cd pox 16 | % ./pox.py ofpot 17 | 18 | % cd ofpot 19 | % ./ctlofpot 20 | OpenFlow Honeypot Control Command 21 | 22 | ctlofpot [Command] ([argument]) 23 | 24 | show-information : print basic information 25 | show-fdb-table : print Forwarding Data Base 26 | show-arp-table : print ARP table 27 | set-honeypot-port [PortNum] : set output port to Honeypot 28 | set-honeypot-mac [MacAddr] : set Mac address of Honeypot node 29 | set-own-prefix [Prefix] : set local IP address prefix 30 | set-virtual-mac [MacAddr] : set Virtual Mac Address 31 | set-router-mac [MacAddr] : set Mac address of Default Router 32 | % 33 | 34 | 35 | For jissenkobo. 36 | -------------------------------------------------------------------------------- /ctlofpot: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | OFPOT_CONTROL_PORT=60003 4 | 5 | if [ -z "$1" ]; then 6 | echo 7 | echo " OpenFlow Honeypot Control Command" 8 | echo 9 | echo " ctlofpot [Command] ([argument])" 10 | echo 11 | echo " show-information : print basic information" 12 | echo " show-fdb-table : print Forwarding Data Base" 13 | echo " show-arp-table : print ARP table" 14 | echo " set-honeypot-port [PortNum] : set output port to Honeypot" 15 | echo " set-honeypot-mac [MacAddr] : set Mac address of Honeypot node" 16 | echo " set-own-prefix [Prefix] : set local IP address prefix" 17 | echo " set-virtual-mac [MacAddr] : set Virtual Mac Address" 18 | echo " set-router-mac [MacAddr] : set Mac address of Default Router" 19 | echo 20 | else 21 | echo $1 $2 | nc 127.0.0.1 ${OFPOT_CONTROL_PORT} 22 | 23 | fi 24 | -------------------------------------------------------------------------------- /ofpot.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | OpenFlow Honey Pot Application 4 | """ 5 | 6 | from pox.core import * 7 | from pox.lib.util import dpidToStr 8 | from pox.lib.recoco import Timer 9 | import pox.lib.packet as pkt 10 | import pox.openflow.libopenflow_01 as of 11 | 12 | import re 13 | import thread 14 | import socket 15 | 16 | log = core.getLogger () 17 | fdb = {} 18 | arptable = {} 19 | 20 | fdb_mutex = thread.allocate_lock () 21 | aca_mutex = thread.allocate_lock () 22 | arp_mutex = thread.allocate_lock () 23 | 24 | FDB_LIFETIME = 60 # sec 25 | OFENTRY_IDLE_TIMEOUT = 10 26 | OFENTRY_HARD_TIMEOUT = 20 27 | 28 | 29 | CONTROL_PORT = 60003 30 | 31 | POT_PORT = 3 32 | POT_MAC = "52:54:00:f8:3c:80" 33 | ROUTERMAC = "00:22:19:c7:a3:35" 34 | VIRTMAC = "00:00:00:ff:ff:ff" 35 | OWN_PREFIX = "153.16.68.128/26" 36 | 37 | def launch () : 38 | 39 | log.info ("OFPOT is Launched") 40 | core.openflow.addListenerByName ("PacketIn", callback_packet_in) 41 | core.openflow.addListenerByName ("ConnectionUp", callback_new_dpid) 42 | core.openflow.addListenerByName ("ConnectionDown", callback_del_dpid) 43 | 44 | # start control thread 45 | thread.start_new_thread (control_thread, ()) 46 | 47 | 48 | def callback_timer () : 49 | 50 | def aging_fdb () : 51 | delmaclist = [] 52 | for mac in fdb : 53 | fdb[mac]["timer"] -= 1 54 | if fdb[mac]["timer"] < 0 : 55 | delmaclist.append (mac) 56 | 57 | for delmac in delmaclist : 58 | log.info ("Delete FDB Entry %s" % mac) 59 | del (fdb[mac]) 60 | 61 | def aging_arptable () : 62 | delmaclist = [] 63 | for mac in arptable : 64 | arptable[mac]["timer"] -= 1 65 | if arptable[mac]["timer"] < 0 : 66 | delmaclist.append (mac) 67 | 68 | for delmac in delmaclist : 69 | log.info ("Delete ARP Table Entry %s" % mac) 70 | del (arptable[mac]) 71 | 72 | 73 | with fdb_mutex : 74 | aging_fdb () 75 | with arp_mutex : 76 | aging_arptable () 77 | 78 | 79 | 80 | Timer (1, callback_timer, recurring = True) 81 | 82 | 83 | 84 | def callback_new_dpid (event) : 85 | log.info ("New OpenFlow Switch is connected [%s]" 86 | % dpidToStr (event.dpid)) 87 | 88 | def callback_del_dpid (event) : 89 | log.info ("OpenFlow Switch [%s] is disconnected" 90 | % dpidToStr (event.dpid)) 91 | 92 | def callback_packet_in (event) : 93 | 94 | packet = event.parse () 95 | log.info ("Packet in : port=%d, src mac=%s, dst mac=%s" % 96 | (event.port, packet.src, packet.dst)) 97 | 98 | 99 | # Arp Spoofing for Default Route 100 | if packet.type == pkt.ethernet.ARP_TYPE : 101 | arp_packet = packet.payload 102 | 103 | if of.EthAddr (ROUTERMAC) == arp_packet.hwsrc and \ 104 | arp_packet.opcode == pkt.arp.REQUEST : 105 | send_instead_arp_reply (event, VIRTMAC, arp_packet) 106 | return 107 | 108 | # Arp snooping 109 | if packet.type == pkt.ethernet.ARP_TYPE : 110 | arp_packet = packet.payload 111 | 112 | # Detect existing IP address 113 | with arp_mutex : 114 | arptable[str (arp_packet.protosrc)] = \ 115 | {"timer" : FDB_LIFETIME, 116 | "mac" : arp_packet.hwsrc} 117 | 118 | 119 | # Aging Arp Table 120 | if packet.type == pkt.ethernet.IP_TYPE : 121 | ip_packet = packet.payload 122 | with arp_mutex : 123 | if packet.src != of.EthAddr (POT_MAC) : 124 | stripsrc = str (ip_packet.srcip) 125 | if netlookup (stripsrc, OWN_PREFIX) : 126 | arptable[stripsrc] = {"timer" : FDB_LIFETIME, 127 | "mac" : packet.src} 128 | 129 | # if dst mac is Virtual Mac, change mac, and output correct port 130 | if packet.dst == of.EthAddr (VIRTMAC) and \ 131 | packet.type == pkt.ethernet.IP_TYPE : 132 | ip_packet = packet.payload 133 | stripdst = str (ip_packet.dstip) 134 | 135 | # Dst IP address exists on arptable 136 | if arptable.has_key (stripdst) : 137 | correct_dstmac = arptable[stripdst]["mac"] 138 | if fdb.has_key (correct_dstmac) : 139 | correct_dstport = fdb[correct_dstmac]["port"] 140 | else : 141 | correct_dstport = of.OFPP_FLOOD 142 | 143 | # Dst IP address does no exist on arptable 144 | else : 145 | correct_dstmac = of.EthAddr (POT_MAC) 146 | correct_dstport = POT_PORT 147 | 148 | flow_mod_change_mac (event, packet, 149 | correct_dstport, correct_dstmac) 150 | 151 | return 152 | 153 | 154 | # Learning Incomming Port 155 | with fdb_mutex : 156 | fdb[packet.src] = {"port" : event.port, "timer" : FDB_LIFETIME} 157 | 158 | with fdb_mutex : 159 | if packet.dst in fdb : 160 | # if dst mac exists on FDB, send port, and install flow entry 161 | port = fdb[packet.dst]["port"] 162 | flow_mod_l2_learning (event, packet, port) 163 | else : 164 | # if dst mac DOES NOT exists on FDB, flooding 165 | packet_out (event, of.OFPP_FLOOD) 166 | 167 | 168 | def send_instead_arp_reply (event, strmacaddr, arp_packet) : 169 | 170 | arp_reply = pkt.arp () 171 | arp_reply.hwsrc = of.EthAddr (strmacaddr) 172 | arp_reply.hwdst = arp_packet.hwsrc 173 | arp_reply.opcode = pkt.arp.REPLY 174 | arp_reply.protosrc = arp_packet.protodst 175 | arp_reply.protodst = arp_packet.protosrc 176 | ether = pkt.ethernet () 177 | ether.type = pkt.ethernet.ARP_TYPE 178 | ether.src = of.EthAddr (strmacaddr) 179 | ether.dst = arp_packet.hwsrc 180 | ether.payload = arp_reply 181 | 182 | msg = of.ofp_packet_out () 183 | msg.actions.append (of.ofp_action_output (port = event.port)) 184 | msg.data = ether 185 | event.connection.send (msg) 186 | 187 | 188 | def flow_mod_l2_learning (event, packet, port) : 189 | 190 | msg = of.ofp_flow_mod () 191 | msg.idel_timeout = OFENTRY_IDLE_TIMEOUT 192 | msg.hard_timeout = OFENTRY_HARD_TIMEOUT 193 | msg.match.dl_dst = packet.dst 194 | msg.actions.append (of.ofp_action_output (port = port)) 195 | msg.buffer_id = event.ofp.buffer_id 196 | event.connection.send (msg) 197 | 198 | log.info ("Install Flow, dstmac=%s, outport=%d" % (packet.dst, port)) 199 | 200 | 201 | def flow_mod_change_mac (event, packet, port, strdstmac) : 202 | 203 | msg = of.ofp_flow_mod () 204 | msg.idel_timeout = OFENTRY_IDLE_TIMEOUT 205 | msg.hard_timeout = OFENTRY_HARD_TIMEOUT 206 | msg.match = of.ofp_match.from_packet (packet) 207 | msg.actions.append (of.ofp_action_dl_addr.set_dst (of.EthAddr (strdstmac))) 208 | msg.actions.append (of.ofp_action_output (port = port)) 209 | msg.buffer_id = event.ofp.buffer_id 210 | event.connection.send (msg) 211 | 212 | ip_packet = packet.payload 213 | 214 | log.info ("Install Mac Change Flow, DstIP=%s, SrcIP=%s outport=%d" 215 | % (ip_packet.dstip, ip_packet.srcip, port)) 216 | 217 | 218 | def packet_out (event, port) : 219 | 220 | log.info ("Command Packet Out, output port=%d" % port) 221 | 222 | msg = of.ofp_packet_out () 223 | msg.actions.append (of.ofp_action_output (port = port)) 224 | msg.buffer_id = event.ofp.buffer_id 225 | msg.in_port = event.port 226 | event.connection.send (msg) 227 | 228 | 229 | def netlookup (address, network_with_mask) : 230 | """ 231 | address is IPv4 Address, network is IPv4 Network Address 232 | """ 233 | def numto8bit (number) : 234 | bitstring = "" 235 | while number : 236 | bit = number % 2 237 | number = (number - bit) / 2 238 | tmp = "%d%s" % (bit, bitstring) 239 | bitstring = tmp 240 | for x in range (8 - len (bitstring)) : 241 | bitstring = "0" + bitstring 242 | 243 | return bitstring 244 | 245 | network, mask = network_with_mask.split ("/") 246 | 247 | addressbitstring = "" 248 | for numstring in address.split (".") : 249 | addressbitstring += numto8bit (int (numstring)) 250 | 251 | networkbitstring = "" 252 | for numstring in network.split (".") : 253 | networkbitstring += numto8bit (int (numstring)) 254 | 255 | if addressbitstring[0:int(mask)] == networkbitstring[0:int(mask)] : 256 | return True 257 | 258 | return False 259 | 260 | 261 | def control_thread () : 262 | 263 | control_functions = { 264 | "show-information" : show_information, 265 | "show-fdb-table" : show_fdb_table, 266 | "show-arp-table" : show_arp_table, 267 | "set-honeypot-port" : set_honeypot_port, 268 | "set-honeypot-mac" : set_honeypot_mac, 269 | "set-own-prefix" : set_own_prefix, 270 | "set-virtual-mac" : set_virtual_mac, 271 | "set-router-mac" : set_router_mac, 272 | } 273 | 274 | sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM) 275 | sock.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 276 | sock.bind (("127.0.0.1", CONTROL_PORT)) 277 | 278 | sock.listen (1) 279 | 280 | while True : 281 | a_sock, client_address = sock.accept () 282 | 283 | recvmsg = a_sock.recv (1024) 284 | recvmsg = recvmsg.strip () 285 | recvmsgtuple = recvmsg.split (" ") 286 | log.info ("Control Command \"%s\"" % recvmsg) 287 | 288 | if recvmsgtuple[0] in control_functions : 289 | control_functions[recvmsgtuple[0]] (a_sock, recvmsg) 290 | else : 291 | a_sock.send ("invalid command\n") 292 | 293 | a_sock.close () 294 | 295 | 296 | def show_fdb_table (sock, recvmsg) : 297 | 298 | with fdb_mutex : 299 | maclist = fdb.keys () 300 | 301 | maclist.sort () 302 | sock.send ("MAC Address\t\tPort\tLifetime\n") 303 | for mac in maclist : 304 | sock.send ("%s\t%d\t%d\n" 305 | % (mac, fdb[mac]["port"], fdb[mac]["timer"])) 306 | 307 | def show_arp_table (sock, recvmsg) : 308 | 309 | with arp_mutex : 310 | addrlist = arptable.keys () 311 | 312 | addrlist.sort () 313 | sock.send ("IP Address\tMAC Address\tLifetime\n") 314 | for addr in addrlist : 315 | sock.send ("%s\t%s\t%d\n" 316 | % (addr, arptable[addr]["mac"], 317 | arptable[addr]["timer"])) 318 | 319 | def show_information (sock, recvmsg) : 320 | 321 | sock.send ("Own Prefix : %s\n" % OWN_PREFIX) 322 | sock.send ("Honeypot Port : %d\n" % POT_PORT) 323 | sock.send ("Honeypot Mac Address : %s\n" % POT_MAC) 324 | sock.send ("Virtual Mac Address : %s\n" % VIRTMAC) 325 | sock.send ("Router Mac Address : %s\n" % ROUTERMAC) 326 | 327 | 328 | def set_honeypot_port (sock, recvmsg) : 329 | 330 | try : 331 | command, port = recvmsg.split (" ") 332 | except : 333 | sock.send ("invalid command \"%s\"\n" % recvmsg) 334 | return 335 | 336 | try : 337 | potport = int (port) 338 | except : 339 | sock.send ("invalid port number \"%s\"\n" % port) 340 | return 341 | 342 | log.info ("Change Honeypot Port %d" % potport) 343 | 344 | global POT_PORT 345 | POT_PORT = potport 346 | 347 | 348 | def set_honeypot_mac (sock, recvmsg) : 349 | 350 | try : 351 | command, mac = recvmsg.split (" ") 352 | except : 353 | sock.send ("invalid command \"%s\"" % recvmsg) 354 | return 355 | 356 | if not re.match (r'([0-9A-Fa-f]{2,2}:){5,5}[0-9A-Fa-f]{2,2}', mac) : 357 | sock.send ("invalid mac address \"%s\". [XX:XX:XX:XX:XX:XX]\n" % mac) 358 | return 359 | 360 | log.info ("Change Honeypot Mac Address %s" % mac) 361 | 362 | global POT_MAC 363 | POT_MAC = mac 364 | 365 | 366 | def set_own_prefix (sock, recvmsg) : 367 | 368 | try : 369 | command, prefix = recvmsg.split (" ") 370 | except : 371 | sock.send ("invalid command \"%s\"" % recvmsg) 372 | return 373 | 374 | if not re.match (r'([0-9]{1,3}\.){3,3}[0-9]{1,3}/[0-9]{1,2}', prefix) : 375 | sock.send ("invalid prefix \"%s\".\n" % prefix) 376 | return 377 | 378 | log.info ("Change Honeypot Own Prefix %s" % prefix) 379 | 380 | global OWN_PREFIX 381 | OWN_PREFIX = prefix 382 | 383 | 384 | def set_virtual_mac (sock, recvmsg) : 385 | 386 | try : 387 | command, mac = recvmsg.split (" ") 388 | except : 389 | sock.send ("invalid command \"%s\"" % recvmsg) 390 | return 391 | 392 | if not re.match (r'([0-9A-Fa-f]{2,2}:){5,5}[0-9A-Fa-f]{2,2}', mac) : 393 | sock.send ("invalid mac address \"%s\". [XX:XX:XX:XX:XX:XX]\n" % mac) 394 | return 395 | 396 | log.info ("Change Virtual Mac Address %s" % mac) 397 | 398 | global VIRTMAC 399 | VIRTMAC = mac 400 | 401 | 402 | def set_router_mac (sock, recvmsg) : 403 | 404 | try : 405 | command, mac = recvmsg.split (" ") 406 | except : 407 | sock.send ("invalid command \"%s\"" % recvmsg) 408 | return 409 | 410 | if not re.match (r'([0-9A-Fa-f]{2,2}:){5,5}[0-9A-Fa-f]{2,2}', mac) : 411 | sock.send ("invalid mac address \"%s\". [XX:XX:XX:XX:XX:XX]\n" % mac) 412 | return 413 | 414 | log.info ("Change Router Mac Address %s" % mac) 415 | 416 | global ROUTERMAC 417 | ROUTERMAC = mac 418 | --------------------------------------------------------------------------------