diff --git a/Readme.md b/Readme.md index 0f3b4f3..475e411 100644 --- a/Readme.md +++ b/Readme.md @@ -18,45 +18,92 @@ format that is suitable for fuzzing with the Mutiny Fuzzing Framework. * Based off of the tcp proxy.py from Black Hat Python by Justin Seitz ``` +[<_<] Decept proxy/sniffer [>_>] + + usage: decept.py [OPTIONS] optional arguments: -h, --help show this help message and exit + --quiet Don't show hexdumps --recv_first Receive stuff first? --timeout TIMEOUT Timeout for outbound socket --loglast LOGLAST Log the last packet (unimplimented) - --pcapdir PCAPDIR Directory to store pcaps (extensions required) - --pps Create a new pcap for each session - --snaplen SNAPLEN Length of packet truncation --fuzzer FUZZFILE *.fuzzer output for mutiny (extensions required) --dumpraw DUMPDIR Directory to dump raw packet files into (fmt = %d-%s % (pkt_num,[inbound|outbound])) - --l_abstract Treat local socket as abstract namespace socket - --r_abstract Treat remote socket as abstract namespace socket - - -L4 options: - -l, --localEnd {ssl,udp,unix,tcp,unix_udp} - Local endpoint type - -r, --remoteEnd {ssl,udp,unix,tcp,unix_udp} - Remote endpoint type - -L3 options: - --L3_proto PROTO L3 proxy, PROTO=>raw to access >= L3 (IPHDR_INCL=1) - otherwise, set Proto to OSPF/EIGRP/etc... and kernel - will craft the headers up till the protocol itself + --max-packet-len LEN Max amount of data per packet when sending data + --dont_kill For when you don't want the connection to die if + neither side sends packets for TIMEOUT seconds. + Use with --expect if you still need the session + to end though. + --expect RESPCOUNT Useful with --dont_kill. Wait for RESPCOUNT + responses from the remote server, and then kill + the connection. Good for fuzzing campaigns. + + -l, {ssl,udp,tcp}|[L3 Proto] Local endpoint type + -r, {ssl,udp,tcp}|[L3 Proto] Remote endpoint type + + --rbind_addr IPADDR IP address to use for remote side. Make sure that + you have the IP somewhere on an interface though. + --rbind_port PORT PORT to bind to for remote side. + +SSL Options: + --lcert SSL_PEM_CERT Cert to use for accepting local SSL + (Optionally cert and key in one file) + --lkey SSL_PEM_KEY Private key for local cert + --rcert SSL_PEM_CERT Cert to use for connecting to remote SSL + (Optionally cert and key in one file) + --rkey SSL_PEM_KEY Private key for remote cert + --rverify HOSTNAME Verify remote side as host HOSTNAME before + connecting. + +Hook Files: + Optional function definitions for processing data between inbound + and outbound endpoints. Can pass data between the hooks/proxy with + the userdata parameters. Look at `hooks` folder for some examples/ + prebuilt useful things. + + --hookfile | Functions imported from file: + string outbound_hook(outbound,userdata=[]): + string inbound_hook(outbound,userdata=[]): + +Tap Mode (--tap): + Decept will replicate any inbound/outbound traffic over localhost now + also, such that you can view traffic that has been decrypted or processed + by the inbound/outbound hooks in something more legit than the hexdump + function. (e.g. tcpdump/wireshark/tshark/etc) + +Host Config File: + Optionally, instead of specifying a remote host, if you specify a valid + filename, you can multiplex HTTP/HTTPS connections to different URLs. + Please examine the example "hosts.conf" for more information. + +------------------------------------------------------------------------ L2 usage: decept.py L2 options: - --l2_filter MACADDR Ignore inbound traffic except from MACADDR --l2_MTU MTU Set Maximum Transmision Unit for socket --l2_forward Bridge the local interface and remote interface + --pcap PCAPDIR Directory to store pcaps + --pps Create a new pcap for each session + --snaplen SNAPLEN Length of packet truncation + --pcap_interface IFACE Specify which interface the packets will be + coming in on. "eth0" by default. + L4 Usage: decept.py 127.0.0.1 9999 10.0.0.1 8080 -L3 Usage: decept.py 127.0.0.1 0 10.0.0.1 0 --L3_proto OSPF +L3 Usage: decept.py 127.0.0.1 0 10.0.0.1 0 -l icmp -r icmp L2 Usage: decept.py lo 00:00:00:00:00:00 eth0 ff:aa:cc:ee:dd:00 +Unix: decept.py localsocketname 0 remotesocketname 0 +Abstract: decept.py \\x00localsocketname 0 \\x00remotesocketname 0 + +Arp Poisoning options: + --poison Contains "mac1|mac2|ip1|ip2" to poison. + --poison_int Interface on which to poison (eth0 default) + ``` # lil_sshniffer.py diff --git a/decept.py b/decept.py index 13d608f..2997256 100755 --- a/decept.py +++ b/decept.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 #------------------------------------------------------------------ # Author: Lilith Wyatt <(^,^)> #------------------------------------------------------------------ @@ -66,7 +66,7 @@ from ctypes import * from re import match -from time import time +from time import time,sleep from datetime import datetime from os.path import join,isfile,abspath @@ -75,11 +75,11 @@ try: sys.path.append(abspath(join(__file__,"../../mutiny_fuzzing_framework"))) - import backend.fuzzerdata as mutiny + import backend.fuzzer_data as mutiny except ImportError: try: sys.path.append(abspath(join(__file__, "../../mutiny-fuzzer"))) - import backend.fuzzerdata as mutiny + import backend.fuzzer_data as mutiny except ImportError: pass @@ -124,7 +124,7 @@ def __init__(self,lhost,lport,rhost,rport,local_end_type,remote_end_type,receive # don't exit if no data (streaming) self.dont_kill = False # except if we get past expected_resp_count ^_^ - self.expected_resp_count = 1 + self.expected_resp_count = -1 self.udp_port_range = None @@ -173,9 +173,13 @@ def __init__(self,lhost,lport,rhost,rport,local_end_type,remote_end_type,receive self.snaplen = 65535 self.pcap_interface = "eth0" self.mon_flag = None + + self.tapmode = False self.hostconf_dict = {} + self.poison_file = "" + self.poison_int = "eth0" self.local_certfile=local_cert self.local_keyfile=local_key @@ -210,8 +214,9 @@ def __init__(self,lhost,lport,rhost,rport,local_end_type,remote_end_type,receive sys.exit(0) elif self.local_end_type == "dtls": - # DTLS made possible by smarter people than I, thank you Mr. Concolato ^_^; - #https://stackoverflow.com/questions/27383054/python-importerror-usr-local-lib-python2-7-lib-dynload-io-so-undefined-symb + # DTLS made sorta possible by smarter people than I, thank you Mr. Concolato ^_^; + # https://stackoverflow.com/questions/27383054/python-importerror-usr-local-lib-python2-7-lib-dynload-io-so-undefined-symb + # it's a hack and I can only get one side to work. Definiately a 'todo'. try: DTLSv1_server_method = 7 SSL.Context._methods[DTLSv1_server_method] = _util.lib.DTLSv1_server_method @@ -272,7 +277,13 @@ def get_bytes(self,sock): # necessary for connectionless if self.local_end_type == "udp": - tmp,(_,tmpport) = sock.recvfrom(65535) + ret_struct = sock.recvfrom(65535) + try: + tmp,(_,tmpport) = ret_struct + except ValueError: + # ipv6 recvfrom gives more data + tmp,(_,tmpport,_,_) = ret_struct + if tmpport != self.lport and tmpport != self.rport and not self.srcport: self.lport = tmpport self.lhost = _ @@ -406,10 +417,6 @@ def server_socket_init(self): self.server_socket.listen(self.max_conns) except Exception as e: output(e) - - - - def server_loop(self): @@ -431,10 +438,11 @@ def server_loop(self): try: sub_folder = str(datetime.now()).replace(" ","_") + sub_folder = sub_folder.replace(":","_") self.dumpraw = join(self.dumpraw,sub_folder) mkdir(self.dumpraw) except Exception as e: - print e + print(e) pass # If we're attempting to write to a pcap, shouldn't it be required to be L2? @@ -502,20 +510,106 @@ def server_loop(self): # socket Family/type/protocol self.server_socket_init() + if self.tapmode: + + ''' + # since we can't do AF_PACKET with windows, we only go down to L3. + dummy_frame = "\x00"*0xC #dst/src mac + dummy_frame += "\x80\x00" #proto IP + ''' + + self.dummy_packet = "\x45" #ver,headerlen + self.dummy_packet+= "\x00" # DSC + self.dummy_packet+= "L3TOTESHEADER" # Total len, will fixup + self.dummy_packet+= "??" # ID field, doesnt' matter. + self.dummy_packet+= "\x00\x00" # flags. + self.dummy_packet+= "\x80" #ttl. + + if self.remote_end_type == "ssl" or self.remote_end_type == "tcp": + self.dummy_packet+= "\x06" #proto => tcp + elif self.remote_end_type == "udp": + self.dummy_packet+= "\x17" #proto => udp + else: + # if you need something else, add it yourself. + self.dummy_packet+= "\x01" # proto => ICMP + + self.dummy_packet+= "\x00\x00" # header checksum, don't care. + + l_ip = ''.join(chr(int(octet)) for octet in self.lhost.split(".")) + r_ip = ''.join(chr(int(octet)) for octet in self.rhost.split(".")) + + self.inbound_dummy = self.dummy_packet + l_ip + r_ip + self.outbound_dummy = self.dummy_packet + r_ip + l_ip + + self.inbound_dummy += struct.pack(">H",self.rport) + self.inbound_dummy += struct.pack(">H",self.lport) + self.outbound_dummy += struct.pack(">H",self.lport) + self.outbound_dummy += struct.pack(">H",self.rport) + + self.l4_dummy = "" + if self.remote_end_type == "ssl" or self.remote_end_type == "tcp": + self.l4_dummy+="\x00\x00\x00\x00" # seq num + self.l4_dummy+="\x00\x00\x00\x00" # ack num + self.l4_dummy+="\x50" # tcp header len + self.l4_dummy+="\x18" # we only really care about [psh,ack] + self.l4_dummy+="\x01\x00" # window size => 256 + self.l4_dummy+="\x00\x00" # checksum, w/e. + self.l4_dummy+="\x00\x00" # urgent pointer + + elif self.remote_end_type == "udp": + l4_dummy+="L4TOTESHEADER" + l4_dummy+="\x00\x00"# checksum + + else: + pass + + self.inbound_dummy+=self.l4_dummy + self.outbound_dummy+=self.l4_dummy + + + ''' + try: + ''' + # No AF_PACKET support in windows. + #self.tapsock = socket.socket(socket.AF_INET,socket.SOCK_RAW,socket.IPPROTO_RAW) + self.tapsock = socket.socket(socket.AF_INET,socket.SOCK_RAW,socket.IPPROTO_RAW) + self.tapsock.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) + + + + + ''' + except Exception: + # Perms yo. + output("[x.x] Could create a raw interface. Are you running as root?",RED) + ''' + + + + + + if self.fuzz_file: self.fuzzerData = mutiny.FuzzerData() + if self.poison_file: + poison_thread = multiprocessing.Process(target = self.arp_poisoner, + args = (self.poison_file, + self.poison_int, + self.killswitch)) + poison_thread.start() + + # attempt to parse config file, if any. Should be located in 'rhost' + # will continue if not found. try: confbuf = "" conflist = [] with open(self.rhost,"r") as f: confbuf = f.read() - # check for config signature - if confbuf[0:9] == "# <(^_^)>": - for line in filter(None,confbuf.split("\n")): - if line[0] != "#": - conflist.append(line) + for line in filter(None,confbuf.split("\n")): + if line[0] != "#": + conflist.append(line) if len(conflist) > 0: for entry in conflist: @@ -524,9 +618,14 @@ def server_loop(self): if len(name) and len(ip): self.hostconf_dict[name] = ip output("[!.!] Added %s | %s" % (name,ip),CYAN) - except: + except Exception as e: + print(e) pass - except: + except IOError as e: + # no such file + pass + except Exception as e: + print(e) pass @@ -538,13 +637,10 @@ def server_loop(self): #print out conn info if addr: - self.output_lock.acquire() - output("[>.>] Received Connection from %s" % str(addr),GREEN) - self.output_lock.release() + ts = datetime.now().strftime("%H:%M:%S.%f") + output("[>.>] %s Received Connection from %s" % (ts,str(addr)),GREEN,self.output_lock) else: - self.output_lock.acquire() - output("[>.>] Received Connection from UnixSocket",GREEN) - self.output_lock.release() + output("[>.>] %s Received Connection from UnixSocket"%(ts),GREEN,self.output_lock) if "windows" in system().lower() or "cygwin" in system().lower(): @@ -568,12 +664,12 @@ def server_loop(self): addr = None elif self.local_end_type == "udp" or self.local_end_type == "dtls": - self.proxy_loop(self.server_socket,self.rhost,self.rport) + self.proxy_loop(self.server_socket,self.rhost,self.rport,"",self.output_lock) self.exit_triggers() return elif self.server_socket == sys.stdin: - self.proxy_loop(sys.stdin,self.rhost,self.rport,("stdin",0)) + self.proxy_loop(sys.stdin,self.rhost,self.rport,("stdin",0),"",self.output_lock) elif self.server_socket.family == socket.AF_PACKET: output("[>.>] L2 ready: %s:%s <=> %s:%s" % (str(self.lhost),str(self.lport),str(self.rhost),str(self.rport)),YELLOW) @@ -589,12 +685,12 @@ def server_loop(self): pass - def proxy_loop(self,local_socket,rhost,rport,cli_addr,plock): + def proxy_loop(self,local_socket,rhost,rport,cli_addr="",plock=""): self.thread_id = multiprocessing.current_process().name.replace("Process","session") # use for passing data between inbound and outbound handlers. - self.userdata = "" + self.userdata = [] # used with hostconf_dict initial_buff = "" @@ -618,6 +714,7 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr,plock): if self.local_end_type == "ssl": try: schro_local = self.server_context.wrap_socket(local_socket, server_side=True) + output("[^.^] Local ssl wrap successful",GREEN,plock) except ssl.SSLError as e: output("[x.x] Unable to wrap local SSL socket.",YELLOW, plock) output(str(e),RED, plock) @@ -633,7 +730,7 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr,plock): except Exception as e: if len(initial_buff) > 0: output("[x.x] No 'Host:' header from local socket with hostconf!",RED, plock) - output(initial_buff), plock + output(initial_buff, plock) else: output("[x.x] Empty Message",RED, plock) output(str(e),RED, plock) @@ -643,7 +740,7 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr,plock): if len(hostname) == 0: output("[x.x] Empty Message",RED, plock) - output(initial_buff), plock + output(initial_buff, plock) schro_local.close() sys.exit() @@ -654,7 +751,7 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr,plock): output(initial_buff, plock) self.rhost = hostname - output("[-.0] Connecting to %s (%s:%d)"%(rhost,hostname,rport),ORANGE), plock + output("[-.0] Connecting to %s (%s:%d)"%(rhost,hostname,rport),ORANGE,plock) remote_socket = self.socket_plinko(rhost,self.remote_end_type) @@ -730,6 +827,7 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr,plock): if self.remote_verify: try: schro_remote = self.remote_context.wrap_socket(remote_socket,server_hostname=self.remote_verify) + output("[^_^] Wrapped remote!") except Exception as e: output("[x.x] Unable to verify remote host as '%s' "%self.remote_verify,YELLOW, plock) output(str(e),RED, plock) @@ -776,7 +874,8 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr,plock): #if data to send to local, do so if len(remote_buffer): - output("[<.<] Sending %d bytes inbound (%s:%d)." % (len(remote_buffer),self.lhost,self.lport),ORANGE, plock) + ts = datetime.now().strftime("%H:%M:%S.%f") + output("[<.<] %s Sending %d bytes inbound (%s:%d)." % (ts,len(remote_buffer),self.lhost,self.lport),ORANGE, plock) if self.local_end_type in ConnectionBased: self.buffered_send(schro_local,remote_buffer) else: @@ -846,8 +945,6 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr,plock): output(str(e),RED) remote_socket.close() sys.exit() - - buf = "" @@ -880,15 +977,17 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr,plock): if len(buf): self.pkt_count+=1 + ts = datetime.now().strftime("%H:%M:%S.%f") if self.remote_end_type in ConnectionBased: self.buffered_send(schro_remote,buf) + output("[o.o] %s Sent %d bytes to remote (%s:%d->%s:%d)\n" % (ts, len(buf),cli_addr[0],cli_addr[1],self.rhost,self.rport),CYAN, plock) elif self.remote_end_type == "stdout" or self.local_end_type == "stdin": sys.stdout.write(buf) + output("[o.o] %s Sent %d bytes to remote\n" % (ts, len(buf)),YELLOW) else: self.buffered_sendto(schro_remote,buf,(self.rhost,self.rport)) - - output("[o.o] Sent %d bytes to remote (%s:%d->%s:%d)\n" % (len(buf),cli_addr[0],cli_addr[1],self.rhost,self.rport),CYAN, plock) + output("[o.o] %s Sent %d bytes to remote (%s:%d)\n" % (ts, len(buf),self.rhost,self.rport),CYAN) try: @@ -916,7 +1015,6 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr,plock): if self.local_end_type == "dtls" and s == schro_local: if not handshake_flag: s.set_accept_state() - print dir(s) s.do_handshake() handshake_flag = True else: @@ -935,10 +1033,9 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr,plock): # Need to see which direction first though if s == schro_local: # Case LOCAL] => [REMOTE - + plock.acquire() buf = self.outbound_handler(buf,self.lhost,self.rhost) if byte_count and self.verbose: - plock.acquire() if byte_count < 0x2000: hexdump(buf,CYAN) else: @@ -946,26 +1043,24 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr,plock): if len(buf): self.pkt_count+=1 - - if self.remote_end_type in ConnectionBased: + + ts = datetime.now().strftime("%H:%M:%S.%f") + if self.remote_end_type in ConnectionBased and cli_addr: self.buffered_send(schro_remote,buf) + output("[o.o] %s Sent %d bytes to remote (%s:%d->%s:%d)\n" % (ts, len(buf),cli_addr[0],cli_addr[1],self.rhost,self.rport),CYAN) elif self.remote_end_type == "stdout" or self.local_end_type == "stdin": sys.stdout.write(buf) + output("[o.o] %s Sent %d bytes to remote\n" % (ts, len(buf)),CYAN) else: self.buffered_sendto(schro_remote,buf,(self.rhost,self.rport)) - - output("[o.o] Sent %d bytes to remote (%s:%d->%s:%d)\n" % (len(buf),cli_addr[0],cli_addr[1],self.rhost,self.rport),CYAN) - - try: - plock.release() - except: - pass + output("[o.o] %s Sent %d bytes to remote (%s:%d)\n" % (ts, len(buf),self.rhost,self.rport),CYAN) + plock.release() if s == schro_remote: # Case LOCAL] <= [REMOTE + plock.acquire() buf = self.inbound_handler(buf,rhost,rport) if byte_count and self.verbose: - plock.acquire() if byte_count < 0x2000: hexdump(buf,YELLOW) else: @@ -973,49 +1068,50 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr,plock): if len(buf): self.pkt_count+=1 + ts = datetime.now().strftime("%H:%M:%S.%f") if self.local_end_type in ConnectionBased: self.buffered_send(schro_local,buf) + output("[o.o] %s Sent %d bytes to local (%s:%d<-%s:%d)\n" % (ts, len(buf),cli_addr[0],cli_addr[1],self.rhost,self.rport),YELLOW) elif self.remote_end_type == "stdout" or self.local_end_type == "stdin": sys.stdout.write(buf) + output("[o.o] %s Sent %d bytes to local\n" % (ts, len(buf),YELLOW)) else: self.buffered_sendto(schro_local,buf,(self.lhost,self.lport)) - output("[o.o] Sent %d bytes to local (%s:%d<-%s:%d)\n" % (len(buf),cli_addr[0],cli_addr[1],self.rhost,self.rport),YELLOW) + output("[o.o] %s Sent %d bytes to local from %s:%d\n" % (ts, len(buf),self.rhost,self.rport),YELLOW) resp_count+=1 - try: - plock.release() - except: - pass + plock.release() try: #udp port range case if s in schro_local: + plock.acquire() + buf = self.outbound_handler(buf,self.lhost,self.rhost) if byte_count and self.verbose: - plock.acquire() if byte_count < 0x2000: hexdump(buf) else: output("[!_!] Truncated Message len %d!"%byte_count) - if len(buf): + plock.acquire() self.pkt_count+=1 + ts = datetime.now().strftime("%H:%M:%S.%f") active_udp = s # so we know where to throw packets back to self.buffered_sendto(schro_remote,buf,(self.rhost,self.rport)) - output("[o.o] Sent %d bytes to remote (%s:%d->%s:%d)\n" % (len(buf),cli_addr[0],cli_addr[1],self.rhost,self.rport),GREEN) + output("[o.o] %s Sent %d bytes to remote (%s:%d)\n" % (ts,len(buf),self.rhost,self.rport),GREEN) - try: - plock.release() - except: - pass - + plock.release() + + except Exception as e: + # will error unless schro_local is a list of sockets (i.e. port range). pass - if resp_count >= self.expected_resp_count: + if self.expected_resp_count > 0 and resp_count >= self.expected_resp_count: break - if self.dont_kill and resp_count < self.expected_resp_count: + if self.dont_kill and (resp_count < self.expected_resp_count or self.expected_resp_count == -1): continue if not byte_count or len(exceptional): @@ -1030,7 +1126,7 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr,plock): schro_local.close() if self.remote_end_type in ConnectionBased: schro_remote.close() - output("[-.-] No more data, closing connection (%s:%d<->%s:%d)\n" % (cli_addr[0],cli_addr[1],self.rhost,self.rport),ORANGE) + output("[-.-] No more data, closing connection (%s:%d<->%s:%d)\n" % (cli_addr[0],cli_addr[1],self.rhost,self.rport),ORANGE,plock) break except KeyboardInterrupt: @@ -1186,7 +1282,7 @@ def raw_proxy_loop(self,r_mac,r_int): else: if DEBUGGING: - print "DST:" + macdump(buff[0:6]) + " SRC:" + macdump(buff[6:12]) + print("DST:" + macdump(buff[0:6]) + " SRC:" + macdump(buff[6:12])) break self.pkt_count+=1 @@ -1261,11 +1357,108 @@ def monitor_loop(self,mon_sock,pcap_fd,kill_flag): #print "writing! %s " % repr(packet[0:self.snaplen]) except Exception as e: - #print e + #print(e) pass pcap_fd.close() + + + + + + + def arp_poisoner(self,config_file,interface,killswitch): + + arp_poison_list = [] + + try: + confbuf = "" + conflist = [] + with open(config_file,"r") as f: + confbuf = f.read() + for line in filter(None,confbuf.split("\n")): + if line[0] != "#": + conflist.append(line) + + if len(conflist) > 0: + for entry in conflist: + try: + mac1,mac2,ip1,ip2 = entry.split("|") + if len(mac1) and len(mac2) and len(ip1) and len(ip2): + output("[!.!] Poisoning %s | %s | %s | %s" % (mac1,mac2,ip1,ip2),PURPLE) + + m1 = ''.join(chr(int(c,16)) for c in mac1.split(":")) + m2 = ''.join(chr(int(c,16)) for c in mac2.split(":")) + ip1 = ''.join(chr(int(c,10)) for c in ip1.split(".")) + ip2 = ''.join(chr(int(c,10)) for c in ip2.split(".")) + arp_poison_list.append((m1,m2,ip1,ip2)) + except Exception as e: + print(e) + pass + except IOError as e: + # no such file + pass + except Exception as e: + print(e) + pass + + + poison_sock = socket.socket(socket.AF_PACKET,socket.SOCK_RAW,0x300) + LOCAL_MAC_IOCTL = fcntl.ioctl(poison_sock.fileno(),0x8927,struct.pack('256s',interface[0:15])) + LOCAL_MAC = LOCAL_MAC_IOCTL[18:24] + + #buf = dst_addr + src_addr + buf = "??????" + "!!!!!!" + buf += "\x08\x06" # arp + buf += "\x00\x01" #ethernet + buf += "\x08\x00" #ipv4 + buf += "\x06" # hardware size + buf += "\x04" # protocol size + buf += "\x00\x02" # arp opcode (reply) + #buf += our_mac # + #buf += our_IP # + #buf += target_mac # + #buf += target_IP # + + while not killswitch.is_set(): + try: + for mac1,mac2,ip1,ip2 in arp_poison_list: + frame1 = buf.replace("??????",mac1) #dst + frame1 = frame1.replace("!!!!!!",LOCAL_MAC) #src + frame1 += LOCAL_MAC + frame1 += ip2 + frame1 += mac1 + frame1 += ip1 + ret = poison_sock.sendto(frame1,(interface,0x0)) + + frame2 = buf.replace("??????",mac2) #dst + frame2 = frame2.replace("!!!!!!",LOCAL_MAC) #src + frame2 += LOCAL_MAC + frame2 += ip1 + frame2 += mac2 + frame2 += ip2 + ret = poison_sock.sendto(frame2,(interface,0x0)) + sleep(2) + + except KeyboardInterrupt: + # do some fixup (hopefully) + frame1 = buf.replace("??????",mac1) #dst + frame1 = frame1.replace("!!!!!!",mac2) #src + frame1 += mac2 + frame1 += ip2 + frame1 += mac1 + frame1 += ip1 + ret = poison_sock.sendto(frame1,(interface,0x0)) + + frame2 = buf.replace("??????",mac2) #dst + frame2 = frame1.replace("!!!!!!",mac1) #src + frame2 += mac1 + frame2 += ip1 + frame2 += mac2 + frame2 += ip2 + ret = poison_sock.sendto(frame2,(interface,0x0)) + return def inbound_handler(self,inbound,src="",dst=""): #write_packet_header(inbound,src,dst) @@ -1277,15 +1470,20 @@ def inbound_handler(self,inbound,src="",dst=""): f.write(inbound) if self.inbound_hook: + + #output("[<.<] Pre-hook datalen: %d" %len(inbound),CYAN) + inbound = self.inbound_hook(inbound,self.userdata) #output("[<.<] Pre-hook datalen: %d" %len(inbound),CYAN) - tmp = self.inbound_hook(inbound,self.userdata) - if isinstance(tmp,basestring): - inbound = tmp - else: - inbound,self.userdata = tmp + if self.tapmode: + packet = self.inbound_dummy + inbound + packet = packet.replace("L4TOTESHEADER",struct.pack(">H",len(inbound))) - #output("[<.<] Pre-hook datalen: %d" %len(inbound),CYAN) + # (l3stuff+l2 stuff+ string=>field bytes) + l3len = (len(packet) - (0x14 + 0xC + (len("L3TOTESHEADER")-2))) + packet = packet.replace("L3TOTESHEADER",struct.pack(">H",l3len)) + + self.tapsock.sendto( packet, ("127.0.0.1",0)) return inbound @@ -1301,15 +1499,20 @@ def outbound_handler(self,outbound,src="",dst=""): if self.outbound_hook: #output("[>.>] Pre-hook datalen: %d" %len(outbound),CYAN) - tmp = self.outbound_hook(outbound,self.userdata) - - if isinstance(tmp,basestring): - outbound = tmp - else: - outbound,self.userdata = tmp - + outbound = self.outbound_hook(outbound,self.userdata) #output("[>.>] Post-hook datalen: %d" %len(outbound),CYAN) - output(self.userdata) + + if self.tapmode: + packet = self.outbound_dummy + outbound + packet = packet.replace("L4TOTESHEADER",struct.pack(">H",len(outbound))) + + # (l3stuff+l2 stuff+ string=>field bytes) + l3len = (len(packet) - (0x14 + 0xC + (len("L3TOTESHEADER")-2))) + packet = packet.replace("L3TOTESHEADER",struct.pack(">H",l3len)) + self.tapsock.sendto( packet, ("127.0.0.1",0)) + + + pass return outbound @@ -1362,6 +1565,10 @@ def buffered_sendto(self,sock,data,dst_tuple): send_count += sock.sendto(data_chunk,dst_tuple) + + + + ##################################################### ### End class DeceptProxy() ##################################################### @@ -1396,6 +1603,9 @@ def hexdump(src,color,length=16): # Licensed with PSF # http://code.activestate.com/recipes/142812-hex-dumper # with minor edits + if not src: + return + result=[] digits = 4 if isinstance(src,unicode) else 2 for i in xrange(0,len(src),length): @@ -1458,6 +1668,7 @@ def main(): if (rport == lport) and (rhost == lhost) and "--really" not in sys.argv: output("[>_>] Really? If you really want to see this, use with --really flag",YELLOW) + output("[<_<] Protip: it's not pretty",PURPLE) sys.exit() @@ -1487,9 +1698,11 @@ def main(): proxy.verbose = False if "--quiet" in sys.argv else True proxy.dont_kill = True if "--dont_kill" in sys.argv else False + proxy.tapmode = True if "--tap" in sys.argv else False #next, ints and strings that don't require processing proxy.timeout = float(dumb_arg_helper("--timeout",2)) + proxy.expected_resp_count = int(dumb_arg_helper("--expresp",-1)) # pcap options proxy.pcap = dumb_arg_helper("--pcap") @@ -1512,35 +1725,40 @@ def main(): proxy.remote_keyfile = tmp_key proxy.remote_certfile = tmp_cert + + proxy.poison_file = dumb_arg_helper("--poison") + proxy.poison_int = dumb_arg_helper("--poison_int") proxy.remote_verify = dumb_arg_helper("--rverify") # look for and parse the files first... - inbound_hook = dumb_arg_helper("--inhook") - outbound_hook = dumb_arg_helper("--outhook") - - - if inbound_hook or outbound_hook: + hookfile = dumb_arg_helper("--hookfile") + + if hookfile: import imp - # if inbound_hook == outbound_hook file, no biggie - if inbound_hook: + try: + imp.load_source("hooks",hookfile) try: - imp.load_source("in_hook",inbound_hook) - proxy.inbound_hook = sys.modules["in_hook"].inbound_hook + proxy.inbound_hook = sys.modules["hooks"].inbound_hook + output("Loaded inbound_hook from %s" % hookfile,YELLOW) except: - output("Could not import inbound hook: %s" % inbound_hook,YELLOW) - + pass - if outbound_hook: try: - imp.load_source("out_hook",outbound_hook) - proxy.outbound_hook = sys.modules["out_hook"].outbound_hook + proxy.outbound_hook = sys.modules["hooks"].outbound_hook + output("Loaded outbound_hook from %s" % hookfile,YELLOW) except: - output("Could not import outbound hook: %s" % outbound_hook,YELLOW) - + pass + + except Exception as e: + print(e) + pass + + + for arg in sys.argv[4:]: if "-" in arg and arg not in ValidCmdlineOptions: @@ -1573,12 +1791,14 @@ def main(): def output(inp,color=None,lock=None): if lock: lock.acquire() + if color: sys.__stdout__.write("%s%s%s\n" % (color,str(inp),CLEAR)) sys.__stdout__.flush() else: sys.__stdout__.write(str(inp)+"\n") sys.__stdout__.flush() + if lock: lock.release() @@ -1697,11 +1917,27 @@ class ETH(Structure): Hook Files: Optional function definitions for processing data between inbound - and outbound endpoints. Look at "inbound_handler"/"outbound_handler" - for more information. + and outbound endpoints. Can pass data between the hooks/proxy with + the userdata parameters. Look at `hooks` folder for some examples/ + prebuilt useful things. - --outhook HOOKFILE | Function Prototype: string outbound_hook(outbound): - --inhook HOOKFILE | Function Prototype: string inbound_hook(inbound): + --hookfile | Functions imported from file: + string outbound_hook(outbound,userdata=[]): + string inbound_hook(outbound,userdata=[]): + +Tap Mode (--tap): + Decept will replicate any inbound/outbound traffic over localhost now + also, such that you can view traffic that has been decrypted or processed + by the inbound/outbound hooks in something more legit than the hexdump + function. (e.g. tcpdump/wireshark/tshark/etc) + + +Host Config File: + Optionally, instead of specifying a remote host, if you specify a valid + filename, you can multiplex HTTP/HTTPS connections to different URLs. + Please examine the example "hosts.conf" for more information. + +------------------------------------------------------------------------ L2 usage: decept.py @@ -1722,18 +1958,24 @@ class ETH(Structure): Unix: decept.py localsocketname 0 remotesocketname 0 Abstract: decept.py \\x00localsocketname 0 \\x00remotesocketname 0 +Arp Poisoning options: + --poison Contains "mac1|mac2|ip1|ip2" to poison. + --poison_int Interface on which to poison (eth0 default) + ''' ValidCmdlineOptions = ["--recv_first","--timeout","--loglast", "--pcap","--pps","--snaplen", "--fuzzer","--dumpraw","-l","-r", "--l2_filter","--l2_mtu","--L2_forward", - "--L3_raw","--inhook","--outhook", + "--L3_raw", "--tap", + "--hookfile", "--rbind_addr","--rbind_port", "--quiet","--dont_kill","--udppr", "--expect","--really", "--lcert","--lkey","--rcert","--rkey", - "--rverify"] + "--rverify","--poison","--poison_int", + "--expresp",""] ##################################### ## Global header for pcap file @@ -1991,5 +2233,9 @@ def create_ebpf_filter(ip,port,proto=""): # need to return both or else epbf prog is deref'ed return fprog,b + + + if __name__ == "__main__": main() + diff --git a/hooks/example_external_hook.py b/hooks/example_external_hook.py deleted file mode 100644 index 68f4e32..0000000 --- a/hooks/example_external_hook.py +++ /dev/null @@ -1,5 +0,0 @@ -def inbound_hook(inbound_data): - return inbound_data[0:298] - -def outbound_hook(outbound_data): - return outbound_data diff --git a/hooks/example_hooks.py b/hooks/example_hooks.py new file mode 100644 index 0000000..b1531df --- /dev/null +++ b/hooks/example_hooks.py @@ -0,0 +1,6 @@ +def inbound_hook(inbound_data,userdata=""): + return inbound_data[0:298] + +def outbound_hook(outbound_data,userdata=""): + return outbound_data + diff --git a/hooks/example_sshniffer_hook.py b/hooks/example_sshniffer_hook.py new file mode 100644 index 0000000..cbe9c6b --- /dev/null +++ b/hooks/example_sshniffer_hook.py @@ -0,0 +1,13 @@ +def inbound_hook(inbound_data,userdata): + print "Inbound hook working! Message Recieved! len:0x%lx"%len(inbound_data) + try: + userdata[inbound_data] = inbound_data + except: + pass + return inbound_data + +def outbound_hook(outbound_data,userdata): + print "Outbound hook working! Message Recieved! len:0x%lx"%len(outbound_data) + print userdata + return outbound_data + diff --git a/hooks/gzip_decompression.py b/hooks/gzip_decompression.py new file mode 100644 index 0000000..a113004 --- /dev/null +++ b/hooks/gzip_decompression.py @@ -0,0 +1,29 @@ +def inbound_hook(inbound_data,userdata=""): + import gzip + import tempfile + if "Content-Encoding: gzip" in inbound_data or "content-encoding: gzip" in inbound_data: + data_loc = inbound_data.find("\r\n\r\n") + if data_loc > -1: + data = inbound_data[data_loc+4:] + try: + f,fname = tempfile.mkstemp() + f.write(data) + f.close() + with gzip.open(fname,"rb") as gz: + decoded = gz.read() + os.remove(fname) + + inbound_data = inbound_data[:data_loc+4] + decoded + + inbound_data = inbound_data.replace("Content-Encoding: gzip","Content-Encoding: text") + inbound_data = inbound_data.replace("content-encoding: gzip","content-encoding: text") + except: + pass + + + return inbound_data + + +def outbound_hook(outbound_data,userdata=""): + return outbound_data + diff --git a/hooks/websocket_ops.py b/hooks/websocket_ops.py new file mode 100644 index 0000000..336d2ec --- /dev/null +++ b/hooks/websocket_ops.py @@ -0,0 +1,194 @@ +# http://lucumr.pocoo.org/2012/9/24/websockets-101/ +# https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers +''' +Frame format: + bytes + 0 1 2 3 4 + +-+-+-+-+-------+-+-------------+-------------------------------+ + |F|R|R|R| opcode|M| Payload len | Extended payload length | + |I|S|S|S| (4) |A| (7) | (16/64) | + |N|V|V|V| |S| | (if payload len==126/127) | + | |1|2|3| |K| | | + +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + | Extended payload length continued, if payload len == 127 | + + - - - - - - - - - - - - - - - +-------------------------------+ + | |Masking-key, if MASK set to 1 | + +-------------------------------+-------------------------------+ + | Masking-key (continued) | Payload Data | + +-------------------------------- - - - - - - - - - - - - - - - + + : Payload Data continued ... : + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + | Payload Data continued ... | + +---------------------------------------------------------------+ +''' +ops = { + 0x0:"continuation", + 0x1:"text", + 0x2:"binary", + # control opcodes + 0x8:"close", + 0x9:"ping", + 0xA:"pong" +} + +def outbound_hook(outbound_data,userdata=[]): + if not outbound_data: + return "" + + try: + fin,opcode,mask,unpacked = unpack_ws_packet(outbound_data) + #print "unpack_ws_packet ret: (%d, %d, %s)" % (fin,opcode,repr(unpacked)) + if fin == "" or mask == "" or opcode == "" or unpacked == "": + return outbound_data + + # do ops on unpacked. + if fin == 0: + if ops[opcode] == "continuation"\ + or ops[opcode] == "binary"\ + or ops[opcode] == "text": + # buffer that shit, yo. + userdata.append(unpacked) + #print "Buffered %d websocket bytes" % len(unpacked) + return "" + + elif fin: + if len(userdata): + print "Reassembled fragment message:" + unpacked = ''.join(userdata) + unpacked + print repr(unpacked) + for i in range(0,len(userdata)): + userdata.pop() + + if ops[opcode] == "text": + print unpacked + elif ops[opcode] == "binary": + print "Websocket binary dump: " + print "\\x" + "\\x".join([c for c in unpacked]) + elif ops[opcode] == "close": + print "Websocket Close Msg" + return outbound_data + elif ops[opcode] == "ping": + print "Websocket Ping Msg" + return outbound_data + elif ops[opcode] == "pong": + print "Websocket Pong Msg" + return outbound_data + elif ops[opcode] == "continuation": #conintuation? + unpacked = ''.join(userdata) + unpacked + #print "Continuation/fin" + + + ## make edits here. + + + + # repack + repacked = repack_ws_packet(opcode,mask,unpacked) + + #print "returning: %s" % repacked + return repacked + + except Exception as e: + print e + return outbound_data + +# According to the rfc, the mask should not be set here (i.e. no encoding). +# If acting as a websocket server just switch the function names of the hooks. +def inbound_hook(inbound_data,userdata=[]): + try: + opcode,fin,key,unpacked = unpack_ws_packet(inbound_data,server=True) + # do ops on unpacked. + # + repacked = repack_ws_packet(opcode,mask,unpacked) + return repacked + except: + return inbound_data + +#### Ws code +def xor_ops(key,data,length): + import struct + buf = "" + #print "xor: (0x%x,%d)" % (key,length) + for i in range(0,length,4): + try: + newval = struct.unpack(">I",data[i:i+4])[0] ^ key + buf += struct.pack(">I",newval) + except: + break + + for j in range(0,length%4): + keybyte = (key&(0xFF000000>>(i*8))) + databyte = ord(data[i+j]) + buf+= chr(keybyte^databyte) + return buf + + +def unpack_ws_packet(data,server=False): + import struct + options = ord(data[0]) + fin = options >> 7 + opcode = options & 0x0F + + paylen_mask = ord(data[1]) + mask = paylen_mask >> 7 + payload_len = paylen_mask & 0x7F + + if mask != 1 and not server: + print "[?.?] Non-masked message? (mask=>%d)"%mask + + if payload_len == 126: + payload_len = struct.unpack(">H",data[2:4])[0] + bytes_read = 4 + elif payload_len == 127: + payload_len = struct.unpack(">Q",data[2:10])[0] + bytes_read = 10 + + if payload_len > (len(data)): + print "Malformed length field: %d (actual: %d). Truncating"%(payload_len,len(data)) + payload_len = (len(data)) + + if mask and not server: + mask = struct.unpack(">I",data[bytes_read:bytes_read+4])[0] + print "fin: %d, opcode: %s, mask: 0x%x, payload_len: %d"%(fin,ops[opcode],mask,payload_len) + bytes_read+=4 + payload_data = data[bytes_read:] + decoded = xor_ops(mask,payload_data,payload_len) + return fin,opcode,mask,decoded + elif mask and server: + #print "Unexpeced masked message from server side..." + return fin,opcode,mask,payload_data + else: + return "","","","" + + +def repack_ws_packet(opcode,key,data): + import struct + #print "entered repack: %s,0x%x"%(ops[opcode],key) + encoded_buf = chr(opcode+0x80) + # mask should always be set if we get to this point + mask_len = 0x80 + + out_len = len(data) + if out_len <= 125: + mask_len += len(data) + encoded_buf += chr(mask_len) + elif out_len <= 0xFFFF: + mask_len += 126 + encoded_buf += chr(mask_len) + encoded_buf+=struct.pack(">H",out_len) + elif out_len > 0xFFFF: + mask_len += 127 + encoded_buf += chr(mask_len) + encoded_buf+=struct.pack(">Q",out_len) + + encoded_buf+=struct.pack(">I",key) + encoded_buf += xor_ops(key,data,len(data)) + return encoded_buf + + +if __name__ == "__main__": + buf=[] + boop = outbound_hook("\x01\xfe\x00\x10\x16\x4c\xc4\xec\x45\x09\x8a\xa8\x1c\x2f\xab\x82",buf) + doop = outbound_hook("\x80\xfe\x00\x04AAAAabcd",buf) + + diff --git a/lil_sshniffer.py b/lil_sshniffer.py index 2c937b9..bc57e04 100644 --- a/lil_sshniffer.py +++ b/lil_sshniffer.py @@ -86,6 +86,10 @@ single_command = "" subsystem = "" +# Hooks +inhook = None +outhook = None + #COLORS!!! ATTN = '\033[96m' PURP = '\033[0;35m' @@ -216,6 +220,8 @@ def main(args): global DST_IP global DST_PORT global host_key + global inhook + global outhook sock = None dst_sock = None @@ -227,7 +233,7 @@ def main(args): except Exception as e: print e print_bad("[x.x] Unable to open keyfile %s" % args.spoof_key) - print_bad("[-_-] Might need to generate with: ssh-keygen -t rsa -N "" -f my.key") + print_bad("[-_-] Might need to generate with: ssh-keygen -t rsa -N \"\" -f id_rsa") sys.exit() if args.lhost: @@ -239,6 +245,29 @@ def main(args): if args.rport: DST_PORT = args.rport + + if args.hookfile: + import imp + # if inbound_hook == outbound_hook file, no biggie + try: + imp.load_source("hooks",args.hookfile) + try: + inhook = sys.modules["hooks"].inbound_hook + print_purp("Loaded inbound_hook from %s" % args.hookfile) + except: + pass + + try: + outhook = sys.modules["hooks"].outbound_hook + print_purp("Loaded outbound_hook from %s" % args.hookfile) + except: + pass + + except Exception as e: + print e + pass + + # Only care about these, since we might just be piping plain text through an ssh tunnel instead of sniffing if not DST_IP: print_bad("[x.x] Invalid lhost|lport|rhost|rport") @@ -292,7 +321,14 @@ def main(args): client.close() sys.exit() - client_thread = threading.Thread(target=client_handler_helper,args=(client,addr,dst_sock,kill_switch,hijack_flag)) + client_thread = threading.Thread(target=client_handler_helper, + args=(client, + addr, + dst_sock, + kill_switch, + hijack_flag, + inhook, + outhook)) client_thread.start() sock.close() @@ -302,7 +338,7 @@ def main(args): -def client_handler_helper(sock,address,dst_sock,kill_switch,hijack_flag): +def client_handler_helper(sock,address,dst_sock,kill_switch,hijack_flag,inhook,outhook): dt = datetime.datetime.today() logfile_name = dt.__str__() + ".log" print_purp("[c.c] Logging to %s" % logfile_name) @@ -324,9 +360,9 @@ def client_handler_helper(sock,address,dst_sock,kill_switch,hijack_flag): print_attn("[0.<] Started Transport session....") if sniff == True: - ssh_client_handler(sock,address,out_trans,logfile,kill_switch,hijack_flag) + ssh_client_handler(sock,address,out_trans,logfile,kill_switch,hijack_flag,inhook,outhook) else: - tcp_client_handler(sock,address,out_trans,logfile,kill_switch) + tcp_client_handler(sock,address,out_trans,logfile,kill_switch,inhook,outhook) def create_ssh_channel(out_trans): @@ -358,7 +394,7 @@ def create_ssh_channel(out_trans): return out_chan -def tcp_client_handler(sock,address,out_trans,logfile,kill_switch): +def tcp_client_handler(sock,address,out_trans,logfile,kill_switch,inhook,outhook): inb = "" outb = "" @@ -372,11 +408,19 @@ def tcp_client_handler(sock,address,out_trans,logfile,kill_switch): break inb = get_bytes(out_chan) + + if inhook: + inb = inhook(inb,userdata) + if len(inb): print_warn(inb) sock.send(inb) + outb = get_bytes(sock) + if outhook: + outb = outhook(outb,userdata) + if len(outb): print_attn(outb) out_chan.send(outb) @@ -400,7 +444,7 @@ def tcp_client_handler(sock,address,out_trans,logfile,kill_switch): sys.exit() -def ssh_client_handler(sock,address,out_trans,logfile,kill_switch,hijack_flag): +def ssh_client_handler(sock,address,out_trans,logfile,kill_switch,hijack_flag,inhook,outhook): # If we're sniffing ssh, we also need to create # an SSH server that's listening for inbound conns in_trans = paramiko.Transport(sock) @@ -411,6 +455,9 @@ def ssh_client_handler(sock,address,out_trans,logfile,kill_switch,hijack_flag): enable=False + # this is the dict for keeping track of things from the inbound/outbound hooks. + userdata = {} + try: in_trans.start_server(server=ssh_sniff) if args.debug: @@ -456,6 +503,7 @@ def ssh_client_handler(sock,address,out_trans,logfile,kill_switch,hijack_flag): if filtering: ssh_sniff.netkit.init_client_buffer(in_chan,out_chan) + while True and not kill_switch.is_set(): # since we're not using select() @@ -472,7 +520,11 @@ def ssh_client_handler(sock,address,out_trans,logfile,kill_switch,hijack_flag): if filtering: inb = ssh_sniff.netkit.inbound_filter(inb) - + + # defined with --hook => def inbound_hook(inbound_msg): + if inhook: + inb = inhook(inb,userdata) + if len(inb): #print "Post filter inb: %s" % repr(inb) in_chan.send(inb) @@ -496,7 +548,6 @@ def ssh_client_handler(sock,address,out_trans,logfile,kill_switch,hijack_flag): if in_chan.recv_ready(): outb = get_bytes(in_chan) - if len(outb): log_buffer+=outb @@ -504,6 +555,10 @@ def ssh_client_handler(sock,address,out_trans,logfile,kill_switch,hijack_flag): if filtering: outb = ssh_sniff.netkit.outbound_filter(outb) + # defined with --hook => def inbound_hook(inbound_msg): + if outhook: + outb = outhook(outb,userdata) + if not len(outb): continue @@ -535,13 +590,14 @@ def ssh_client_handler(sock,address,out_trans,logfile,kill_switch,hijack_flag): resp_expected = True inb = "" - try: - if ssh_sniff.netkit.client_buffer.hijack_flag == True: - print "Setting Hijack" - hijack_flag.set() - except Exception as e: - print e - pass + if filtering: + try: + if ssh_sniff.netkit.client_buffer.hijack_flag == True: + print "Setting Hijack" + hijack_flag.set() + except Exception as e: + print e + pass ###### ##/end while True and not kill_switch.is_set(): ###### @@ -783,11 +839,13 @@ def get_bytes(chan,timeout=0): ssh_type = parser.add_mutually_exclusive_group() ssh_type.add_argument("--subsystem","-S",help="Execute the given subsystem (scp/sftp/ssh/netconf/etc)") ssh_type.add_argument("--execute","-e",help="Execute a single command") - ssh_type.add_argument("--interactive","-i",action="store_true",help="Requests a shell w/pty (default)"+CLEAR) + ssh_type.add_argument("--interactive","-i",action="store_true",help="Requests a shell w/pty (default)") + + parser.add_argument("--hookfile",help="Will import inbound_hook and/or outbound_hook functions/utilize after netfilter, if any.") parser.add_argument("-f","--filtering",help="Filter input and output w/lil_netkit",action="store_true") parser.add_argument("-?","--cisco",help="For when you're filtering on a connection with a Cisco CLI device",action="store_true") - parser.add_argument("-j","--hijack",help="Hijack ssh session after target quits",action="store_true") + parser.add_argument("-j","--hijack",help="Hijack ssh session after target quits"+CLEAR,action="store_true") args = parser.parse_args() diff --git a/poison.conf b/poison.conf new file mode 100644 index 0000000..21d3e75 --- /dev/null +++ b/poison.conf @@ -0,0 +1,14 @@ +# <(^_^)> poison.conf +# Arp poisoning configuration file. +# In order to use, specify this file as the `--poison` param value +# e.g python decept.py 10.10.10.1 1111 172.16.10.5 5555 --poison poison.conf +# +# To specify which interface to arp poison on, use --poison-int +# +# Example Configuration Format: +# mac1|mac2|ip1|ip2 +# 11:22:33:aa:bb:cc|11:22:33:99:88:77|127.9.24.9|127.10.9.1 +# [...] +# +# +11:22:33:aa:bb:cc|11:22:33:99:88:77|127.0.0.2|127.0.0.3 diff --git a/scripts/api_generator.py b/scripts/api_generator.py new file mode 100644 index 0000000..521e22e --- /dev/null +++ b/scripts/api_generator.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python +import re +import os +import sys +import md5 +import struct +# +# +def main(dumpraw_dir,delim="",indexes=""): + dir_list = [] + file_list = [] + hash_dict = {} + request_dict = {} + + if delim: + delim_buf = "" + if "\\x" in delim: + delim_buf = ''.join([chr(int(x,16)) for x in filter(None,delim.split("\\x"))]) + else: + try: + delim_buf = ''.join([chr(int(x,16)) for x in filter(None,delim.split("x"))]) + except: + delim_buf = delim + + # directory structure of a --dumpraw: + # basedir + #### + #########session-X-Y-[inbound|outbound] + # for x => session num, y => message num in session + outdir = os.path.join(dumpraw_dir,"minimized") + attempt_counter = 0 + + while True: + try: + os.mkdir(outdir) + print "[0.0] Minimized dir: %s"%outdir + break + except: + outdir = os.path.join(dumpraw_dir,"minimized_%d"%attempt_counter) + attempt_counter += 1 + if attempt_counter >=20: + print "Comon, get rid of some of those old minimized_* dirs. Exiting" + sys.exit() + + for f in os.listdir(dumpraw_dir): + dirpath = os.path.join(dumpraw_dir,f) + if not os.path.isdir(dirpath): + continue + if "minimized" in f: + continue + dir_list.append(dirpath) + + total_file_counter = 0 + valid_file_regex = re.compile(r'^session-(\d+)-(\d+)-(in|out)bound$') + for d in dir_list: + listing = os.listdir(d) + for f in listing: + result = re.match(valid_file_regex,f) + + if not result: + continue + + inp_file = os.path.join(d,f) + if os.path.isdir(inp_file): + continue + + try: + with open(inp_file,'rb') as f: + inp_buf = f.read() + md5hash = md5.new(inp_buf).digest() + hash_dict[inp_file] = md5hash + total_file_counter+=1 + except Exception as e: + print e + continue + + print "[1.1] %d entries found in dump_directory" % total_file_counter + print "[2.2] Added %d unique entries to hash_dict" % len(hash_dict) + + if delim: + print "[3.3] Sorting the entries based on delimeter: %s"%repr(delim_buf) + elif indexes: + print "[3.3] Sorting the entries bytes in file: %s"%str(indexes) + + request_count = 0 + for filename in hash_dict: + with open(filename,"rb") as f: + inp_buf = f.read() + + if not len(inp_buf): + continue + + #if delim: + # while + + if indexes: + key = "" + for num in indexes: + try: + key+=inp_buf[num] + except: + print "Small file? %s" %(filename) + try: + _ = request_dict[key] + except: + request_dict[key] = inp_buf + request_count+=1 + # copy over to min dir. + file_id = "id_0x" + ''.join(["%02x"%ord(y) for y in key]) + min_file = os.path.join(outdir,file_id) + with open(min_file,"wb") as f: + f.write(inp_buf) + + template = "" + print "[4.4] Reduced down to %d unique requests"%request_count + try: + template_loc = os.path.join(os.path.dirname(os.path.abspath(__file__)),"replayer_template.py") + with open(template_loc,"rb") as f: + template = f.read() + except: + print "[x.x] Could not find api_replayer.py template:%s..." % template_loc + sys.exit() + + for i in range(0,100): + fname = 'api_replayer_%d.py'%i + if os.path.isfile(fname): + continue + with open(fname,"wb") as f: + work_dir = outdir + "_workdir" + template = template.replace("inp_dir = %s","inp_dir = \"%s\""%os.path.abspath(outdir)) + template = template.replace("work_dir = %s","work_dir = \"%s\""%os.path.abspath(work_dir)) + f.write(template) + break + print "[>.>]; Why are there 100 api_replayers here....?" + sys.exit() + print "[^_^] There should hopfully be a %s script now, cheers." %fname + + +def usage(): + print "<(^_^)> Decept's API script generator.\ + \n**********************************\ + \nPass it a decept --dumpraw directory and the delimeter used for the api reqs.\ + \nHopefully you'll get a cool script to play with the api in return.\ + \n(For nonprintable delims, use \\x0a\\x0d... format)\ + \n\n%s ''\ + \n\nAlternatively, use --index to sort by the set of bytes inside the index\ + \nfiles located at the i'th indexes (for i in numberRange ) \ + \n\n%s --index (e.g. 1-4 or 1,3,9-15)\n"%(sys.argv[0],sys.argv[0]) + sys.exit() + + +# Takes a string of numbers, seperated via commas +# or by hyphens, and generates an appropriate list of +# numbers from it. +# e.g. str("1,2,3-6") => list([1,2,xrange(3,7)]) +# +# If flattenList=True, will return a list of distinct elements +# +# If given an invalid number string, returns None +def validateNumberRange(inputStr, flattenList=False): + retList = [] + tmpList = filter(None,inputStr.split(',')) + + # Print msg if invalid chars/typo detected + for num in tmpList: + try: + retList.append(int(num)) + except ValueError: + if '-' in num: + intRange = num.split('-') + # Invalid x-y-z + if len(intRange) > 2: + print "Invalid range given" + return None + try: + if not flattenList: + # Append iterator with bounds = intRange + retList.append(xrange(int(intRange[0]),int(intRange[1])+1)) + else: + # Append individual elements + retList.extend(range(int(intRange[0]),int(intRange[1])+1)) + except TypeError: + print "Invalid range given" + return None + else: + try: + retList.append(float(num)) + except: + print "Invalid number given" + return None + # All elements in the range are valid integers or integer ranges + if flattenList: + # If list is flattened, every element is an integer + retList = sorted(list(set(retList))) + return retList + + + +if __name__ == "__main__": + if len(sys.argv) < 3: + usage() + + dumpdir = sys.argv[1] + delim = "" + index_list = "" + + try: + tmp = sys.argv.index("--index") + index_list = validateNumberRange(sys.argv[tmp+1],flattenList=True) + except ValueError: + delim = sys.argv[2] + except IndexError: + print "No index range for --index was found [;_;]" + sys.exit() + + main(dumpdir,delim,index_list) diff --git a/scripts/generate_certchain.sh b/scripts/generate_certchain.sh index ad292b9..ea99b0d 100755 --- a/scripts/generate_certchain.sh +++ b/scripts/generate_certchain.sh @@ -16,32 +16,31 @@ check_return() { #check_return echo -e "\e[30;48;5;82m[1.1] Generating RootCA key and cert\e[0m" -openssl genrsa -out mitmRoot.key 4096 -aes256 +openssl genrsa -out mitmRoot.key 4096 check_return + openssl req -x509 -new -nodes -key mitmRoot.key -sha256 -days 3650 -out mitmRoot.crt check_return echo -e "\e[30;48;5;82m[2.2] Generating intermediate RSA aes256 client key\e[0m" -openssl genrsa -out mitm_inter.key 4096 -aes256 +openssl genrsa -out mitm_inter.key 4096 check_return echo -e "\e[30;48;5;82m[3.3] Generating intermediate client CSR\e[0m" +echo -e "\e[30;48;5;79m[4.4] Note: you must create with different attributes than root, or else verification will fail.\e[0m" openssl req -new -key mitm_inter.key -out mitm_inter.csr check_return - -echo -e "\e[30;48;5;82m[4.4] Signing Intermediate CSR with RootCA\e[0m" +echo -e "\e[30;48;5;82m[5.5] Signing Intermediate CSR with RootCA\e[0m" openssl x509 -req -days 3650 -in mitm_inter.csr -CA mitmRoot.crt -CAkey mitmRoot.key\ -CAcreateserial -out mitm_inter.crt -sha256 check_return -echo -e "\e[30;48;5;82m[5.5] Verifying Intermediate with RootCA\e[0m" +echo -e "\e[30;48;5;82m[6.6] Verifying Intermediate with RootCA\e[0m" openssl verify -verbose -x509_strict -CAfile mitmRoot.crt mitm_inter.crt check_return - -dst=`date | tr " " "-"` -mkdir $date -mv mitm* $date +dst=certchain-`date | tr " " "-"` +mkdir $dst +mv mitm* $dst echo "[^_^] All done!" - diff --git a/scripts/replayer_template.py b/scripts/replayer_template.py new file mode 100644 index 0000000..c74a087 --- /dev/null +++ b/scripts/replayer_template.py @@ -0,0 +1,668 @@ +#!/usr/bin/env python +import traceback +import readline +import socket +import glob +import sys +import os + +# where we look for our initial first-run requests: +inp_dir = %s + +# after we've loaded > 1 time, everything is saved to here: +work_dir = %s +# work_dir is overwritten if the --workdir param is given + + +# for printing purposes only +ascii_threshold = .60 +ascii_flag = True + +request_dict = {} +saved_dict = {} + +IP = "" +PORT = "" +TIMEOUT = 1 +socket_mode = "kill" +sock = None +serv_sock = None + +changes_flag = False + +def main(): + + cmd_dict = { + "list":list_request, + "save":save_request, + "sendser":server_send_bytes, + "send":client_send_bytes, + "edit":edit_request, + "send":client_send_bytes, + "socket_mode":set_socket_mode, + "rename":rename_request, + "copy":copy_request, + "reload":reload_request, + "del":remove_request, + "print":print_request, + "exit":cleanup, + "quit":cleanup, + "new_workdir":new_workdir, + "load_dir":load_request_dir, + "load":load_request, + "print_mode":set_print_mode, + "sethost":sethost, + "pasteraw":paste_request, + "pastehex":paste_hexstream, + "pastecarray":paste_carray, + "cmp":cmp_requests, + "help":print_help, + "?":print_help, + } + + + + + global work_dir + sethost(sys.argv[1],sys.argv[2]) + + try: + ind = sys.argv.index("--workdir") + work_dir = sys.argv[ind+1] + except: + pass + + try: + load_request_dir(work_dir) + except: + try: + load_request_dir(inp_dir) + new_workdir(work_dir) + except: + print "[x.x] Unable to load %s or %s"%(work_dir,inp_dir) + sys.exit() + + if len(request_dict) == 0: + print "[x.x] Unable to read in any requests from %s" % inp_dir + sys.exit() + + + tab_complete = TabCompleter(cmd_dict,request_dict) + print "[^_^] Loaded %d requests"%len(request_dict) + + while True: + try: + inp = filter(None,raw_input("%s[^.^]> %s"%(PURPLE,CLEAR)).split(" ")) + except: + print "" + continue + + if not len(inp): + continue + try: + cmd = inp[0] + args = inp[1:] + cmd_dict[cmd](*args) + except KeyError as e: + print e + + try: + os.system(" ".join(inp)) + except Exception as e: + print e + print "[x.x] Invalid command: %s" % inp + except KeyboardInterrupt: + continue + except TypeError as e: + print "[?.?] Wrong num of params for command %s"%cmd + print e + except Exception as e: + print e + + +def sethost(ip,port=""): + global IP + global PORT + + # if someone does "ip:port", w/e. + if not port: + try: + ip,port = ip.split(":") + except: + print "[;_;] Bad ip/port" + + try: + IP = ip + if len(IP.split(".")) != 4: + print "[>.>] Invalid IP given" + return + PORT = int(port) + except: + print "[x.x] Invalid params given to sethost!" + return + + +def set_print_mode(mode): + global ascii_flag + + if mode == "ascii": + ascii_flag = True + elif mode == "binary": + ascii_flag = False + +def list_request(): + print "[!.!] Current Request Listing~" + req_list = request_dict.keys() + req_list.sort() + for req in req_list: + print_request(req,truncate=True) + + +# connect to server and then send commands. +def client_send_bytes(*request_ids): + send_bytes("client",*request_ids) + +# wait for client to connect before sending bytes. +def server_send_bytes(*request_ids): + send_bytes("server",*request_ids) + +def set_socket_mode(mode): + global socket_mode + if mode == "persist": + socket_mode = "persist" + elif mode == "kill": + socket_mode = "kill" + else: + print "invalid set_socket_mode args: %s" % (mode,) + +def send_bytes(mode,*request_ids): + global sock + global serv_sock + + if socket_mode != "persist" or sock == None: + if mode == "client": + try: + sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + sock.connect((IP,PORT)) + sock.settimeout(TIMEOUT) + except: + print "[x.x] Unable to connect to %s:%d"%(IP,PORT) + print "Consider using 'sethost' cmd to fix." + return + + elif mode == "server": + try: + sock,cli_addr = serv_sock.accept() + except: + serv_sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + serv_sock.bind((IP,PORT)) + print "Bound to %s:%d"%(IP,PORT) + serv_sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) + serv_sock.listen(1) + sock,cli_addr = serv_sock.accept() + + print "[1.1] Connection bekommt: (%s:%d)"%(cli_addr) + sock.settimeout(TIMEOUT) + # we recv first if server sock. Might need cmdline flag later + # if/when we run into a server socket that needs to send first. + get_bytes(sock) + else: + print "[?.?] Invalid send_bytes mode: %s" % mode + return + + + for request_id in request_ids: + req = request_dict[request_id] + print CYAN + "[>.>] Sending %s %d bytes~"%(request_id,len(req)) + try: + sock.send(req) + ret = get_bytes(sock) + + if ret: + print YELLOW + "[<.<] Got %d bytes~" % len(ret) + if len(ret) > 0x1000: + ret = ret[0:0x1000] + + buf = "" + for char in ret: + if ord(char) >= 0x30 and ord(char) <= 122 and ascii_flag: + buf+=char + else: + buf+="\\x%02x"%ord(char) + print buf + else: + print "[x.x] No response..." + + except Exception as e: + if "Broken pipe" in e: + sock = None + break + else: + print e + + #print "[!-!] Saving response as %s_resp"%(request_id) + #save_request("%s_resp"%request_id,buf) + + sock = None + + ''' + if socket_mode != "persist": + sock.close() + if mode == "server": + serv_sock.close() + ''' + + + +def get_bytes(sock): + tmp = "" + ret = "" + while True: + try: + tmp = sock.recv(65535) + if tmp: + ret+=tmp + else: + break + except: + break + + return ret + + +def edit_request(request_id,start_index,new_val): + pass + + +def copy_request(old_request_id,new_request_id): + rename_request(old_request_id,new_request_id,copyOnly=True) + +def rename_request(old_request_id,new_request_id,copyOnly=False): + global changes_flag + try: + val = request_dict[old_request_id] + del request_dict[old_request_id] + filename = os.path.join(work_dir,old_request_id) + if not copyOnly: + os.remove(filename) + changes_flag = True + except Exception as e: + print "Could not remove old request %s (%s)"%(old_request_id,e) + return + + save_request(new_request_id,val) + + +# Will generate a new request file with name +# in the work_dir that contains the request in +def save_request(request_id,request_value): + global request_dict + global changes_flag + + filtered_request = "" + escape_loc = request_value.find("\\x") + # all chars should be escaped if not ascii, so no slashes shuold be in buf. + while escape_loc > -1: + filtered_request += request_value[:escape_loc] + filtered_request += chr(int(request_value[escape_loc+2:escape_loc+4],16)) + request_value = request_value[escape_loc+4:] + escape_loc = request_value.find("\\x") + filtered_request+=request_value + + try: + request_dict[request_id] = filtered_request + except Exception as e: + print "[x.x] Could not add request %s to request_dict, returning."%request_id + return + + req_path = os.path.join(work_dir,request_id) + + try: + with open(req_path,"wb") as f: + f.write(request_value) + changes_flag = True + except Exception as e: + print "[x.x] Could not create %s in work_dir"%request_id + print e + return + + +# Print out the given +def print_request(request_id,truncate=False): + try: + req = request_dict[request_id] + except KeyError: + print "[x.x] Request %s not found in request_dict"%request_id + return + + if truncate and len(req) > 50: + old_len = len(req) + req = req[0:50] + + buf = "" + for char in req: + if ord(char) >= 0x30 and ord(char) <= 122 and ascii_flag: + buf+=char + else: + buf+="\\x%02x"%ord(char) + print "-------------------" + if len(req) == 50: + buf+="%s[...] (50/%d bytes)%s" % (YELLOW,old_len,CLEAR) + print "%s%s (%d bytes (0x%lx))\n%s%s" % (CYAN,request_id,len(req),len(req),CLEAR,buf) + + +def cleanup(): + if changes_flag: + print "[?.?] Would you like to save request changes to the current workdir? (y/n)" + if raw_input(":").lower() == "y": + new_workdir(work_dir,force=True) + sys.exit() + + +def new_workdir(directory,force=False): + try: + os.mkdir(directory) + except: + if not force: + print "[?.?] Dst dir already exists, would you like to overwrite? (y/n)" + if raw_input(":").lower() != "y": + print "[-.-] Declining to write then, returning" + return + + for req in request_dict: + try: + req_name = os.path.join(directory,req) + with open(req_name,"wb") as f: + f.write(request_dict[req]) + except Exception as e: + print "[;_;] Unable to save request %s to %s (%e)"%(req,directory,e) + if not force: + return + +def reload_request(): + load_request_dir(work_dir) + +def load_request_dir(directory): + for f in os.listdir(directory): + load_request(os.path.join(directory,f)) + +def load_request(request_file): + global request_dict + try: + request_name = os.path.basename(request_file) + with open(request_file,"rb") as f: + request_dict[request_name] = f.read() + except Exception as e: + print "[x.x] Unable to load request %s (%s)" % (request_name,e) + +def remove_request(request_name): + try: + del request_dict[request_name] + print "[!.!] %s removed from request_dict" % request_name + except Exception as e: + print "[?.?] Unable to remove %s: %s"%(request_name,e) + pass + + try: + os.remove(os.path.join(work_dir,request_name)) + except: + pass + +def cmp_requests(req1,req2): + try: + r1 = request_dict[req1] + r2 = request_dict[req2] + except Exception as e: + print "[?.?] Could not find one or more reqs: %s, %s"%(req1,req2) + print e + return + + if len(r1) > len(r2): + longer = r1 + shorter = r2 + else: + longer = r2 + shorter = r1 + print "%sLen(%s): %d, Len(%s): %d%s"%(GREEN,req1,len(r1),req2,len(r2),CLEAR) + + buf = "" + i = 0 + j = 0 + y = 0 + orig_diff = 0 + while i < len(shorter): + for x in range(len(shorter),i+4,-1): + #print "start i(%d),j(%d),y(%d),x(%d)"%(i,j,y,x) + match = longer[j:].find(shorter[i:x]) + # exact match at beginning of both + if match == 0: + #print YELLOW + "Found match " + CLEAR + "ind(%d) %s\nin\n%s"%(match,repr(shorter[i:x]),repr(longer[j:])) + + buf += GREEN + for y in range(i,x): + if ord(shorter[y]) >= 0x30 and ord(shorter[y]) <= 122: + buf+=shorter[y] + else: + buf+="\\x%02x"%ord(shorter[y]) + j+=(x-i) + i+=(x-i) + break + + # extra bytes in longer before match, add extra then continue + elif match > 0: + buf += YELLOW + for long_ind in range(j,j+match+1): + if ord(longer[long_ind]) >= 0x30 and ord(longer[long_ind]) <= 122: + buf+=longer[long_ind] + else: + buf+="\\x%02x"%ord(longer[long_ind]) + + j += match + continue + + # No match for the given byteset... + #if x == i+3: + # pass + + + # We hit the end of shorter req, just append longer now. + if i >= len(shorter) or j > len(shorter): + break + + i+=1 + + buf += CYAN + s = len(shorter) + for i in range(s,len(longer)): + if ord(longer[i]) >= 0x30 and ord(longer[i]) <= 122: + buf+=longer[i] + else: + buf+="\\x%02x"%ord(longer[i]) + buf += CLEAR + + print "[^_^] Request Diff: (Key:%sMATCH,%sDIFF,%sAPPEND)\n%s%s" % (GREEN,YELLOW,CYAN,buf,CLEAR) + + +def process_carray(req_buf): + buf = "" + left_bracket = req_buf.find("*/")+2 + right_bracket = req_buf.rfind("}") + if left_bracket == -1 or right_bracket == -1: + print "[x.x] Bracket missing for paste_carray..." + return "" + + byte_buf = req_buf[left_bracket:right_bracket].replace("\n","").replace(" ","") + + print repr(byte_buf) + print byte_buf.split(",") + for b in byte_buf.split(","): + try: + i = int(b,16) + if i > 0x30 and i < 122: + buf+=chr(i) + else: + buf+= "\\x%02x"%i + except: + pass + + return buf + + +def process_hexstream(buf): + req_buf = "" + for i in range(0,len(buf),2): + tmp = chr(int(buf[i:i+2],16)) + if ord(tmp) > 0x30 and ord(tmp) < 122: + req_buf += tmp + else: + req_buf += "\\x%02x"%ord(tmp) + + return req_buf + +def paste_process(request_name,format_function): + print "Newlines will be ignored, use \\x0a for newlines in bytestream" + print "Comment lines with '#'. Ctrl-C to finish input (and remember to hit first)" + + buf = "" + while True: + try: + tmp = raw_input("") + tmp = tmp.rstrip().lstrip() + if tmp.startswith("#"): + continue + if len(tmp): + buf+=tmp + except KeyboardInterrupt: + break + + if format_function != None: + buf = format_function(buf) + + if len(buf): + save_request(request_name,buf) + else: + print "[1.1] No-go on saving of %s" % request_name + +def paste_carray(request_name): + print "[!.!] Being pasting carray request" + paste_process(request_name,process_carray) + +def paste_hexstream(request_name): + print "[!.!] Being pasting hexstream request" + paste_process(request_name,process_hexstream) + +def paste_request(request_name): + print "[!.!] Being pasting request" + paste_process(request_name,None) + + +def usage(): + print "[?.?] Usage: %s " + sys.exit() + +def print_help(): + ret = '''\ + <(^_^)> Decept Autogen'ed API replayer thing: + "list":list_request() - Prints out all available API requests. + "send_server":server_send_bytes(*request_ids) - Sends the requests to a server (connecting as client) + "send_client":client_send_bytes(*request_ids) - Sends a requests to a client (after waiting for the connect/first packet.) + "socket_mode":set_socket_mode(mode) - Toggle killing of sockets after sending all reqs. + Available: {"persist","kill"} (default => kill) + "save":save_request(request_id,request) - Adds request to the request_dict + and also writes a file to the workdir. + "rename":rename_request(old,new) - Moves request in request_dict and filesystem. + "del":remove_request(request) - Removes request. + "print":print_request(request_id) - Prints the given request for + "exit":cleanup() - Obv. + "new_workdir":new_workdir(directory) - Writes all request entries to and + switches work_dir to + "load_dir":load_request_dir(directory) - Loads all requests from into the + current request_dict. + "load":load_request(file) - Loads a single request into the request_dict + "print_mode":set_print_mode(mode) - Controls how the print/list commands operate. + Available modes: {"binary","ascii"} (default => ascii) + "sethost":sethost(ip,port) - Change remote endpoint to : + "pasteraw":paste_request(request_name) - Enter mode to input raw bytes till CTRL+C + and save as + "pastehex":paste_hexstream(request_name) - Enter mode to input a hexstream till CTRL+C. + (e.g. "3abc12ef10") + "cmp":cmp_requests(r1,r2) - Prints out color diff of requests r1,r2 + + ''' + + print ret + +#colors +RED='\033[31m' +ORANGE='\033[91m' +GREEN='\033[92m' +LIME='\033[99m' +YELLOW='\033[93m' +BLUE='\033[94m' +PURPLE='\033[95m' +CYAN='\033[96m' +CLEAR='\033[00m' + +class TabCompleter(object): + + def __init__(self,options,requests): + self.cmdoptions = sorted(options) + self.requests = sorted(requests) + self.matches = [] + self.text = "" + + readline.parse_and_bind('tab: complete') + readline.parse_and_bind('set editing-mode vi') + readline.set_completer(self.tabcomplete) + + def tabcomplete(self,text,index): + answer = "" + if text != self.text or not text: + self.matches = [] + self.text = text + + if not readline.get_begidx(): + for o in self.cmdoptions: + if o.startswith(text): + self.matches.append(o) + else: + cmd_text = filter(None,readline.get_line_buffer().split(" ")) + if len(cmd_text) == 1: + self.matches = self.requests[:] + else: + self.matches = [s for s in self.requests if s.startswith(cmd_text[-1])] + + + if len(self.matches) == 1: + ret = self.matches[0] + elif len(self.matches) > 1: + self.text = str(self.matches) + elif len(self.matches) == 0: + self.text = str(self.matches) + + else: + try: + ret = self.matches[state] + except IndexError: + pass + + try: + ret = self.matches[index] + except: + pass + + ''' + if len(ret): + sys.__stdout__.write("\n") + sys.__stdout__.write("%s[^.^]> %s%s" % (PURPLE,CLEAR,ret)) + sys.__stdout__.flush() + ''' + return ret + + +if __name__ == "__main__": + + print CYAN + "<(^_^)> Decept Autogen'ed API replayer thing:" + CLEAR + if len(sys.argv) < 3: + usage() + main()