From 7b3ca5c9867f85e3322e97aac2e8256b125b47b0 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Tue, 3 Apr 2018 07:27:04 -0500 Subject: [PATCH 01/37] Readme updates are good, no? --- Readme.md | 73 ++++++++++++++++++++++++++++++++++++++++--------------- decept.py | 7 ++++++ 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/Readme.md b/Readme.md index 0f3b4f3..25d27c9 100644 --- a/Readme.md +++ b/Readme.md @@ -18,45 +18,80 @@ 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. Look at "inbound_handler"/"outbound_handler" + for more information. + + --outhook HOOKFILE | Function Prototype: string outbound_hook(outbound): + --inhook HOOKFILE | Function Prototype: string inbound_hook(inbound): + +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 + ``` # lil_sshniffer.py diff --git a/decept.py b/decept.py index 13d608f..2965ffb 100755 --- a/decept.py +++ b/decept.py @@ -1703,6 +1703,13 @@ class ETH(Structure): --outhook HOOKFILE | Function Prototype: string outbound_hook(outbound): --inhook HOOKFILE | Function Prototype: string inbound_hook(inbound): +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: From d18172ab4d067ee22b89fcc3d6b31cc782f7f546 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Wed, 4 Apr 2018 08:34:28 -0500 Subject: [PATCH 02/37] Fixed proxy_loop params for udp/stdin/stdout --- decept.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/decept.py b/decept.py index 2965ffb..251a8c1 100755 --- a/decept.py +++ b/decept.py @@ -431,6 +431,7 @@ 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: @@ -568,12 +569,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,7 +590,7 @@ 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") @@ -883,12 +884,14 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr,plock): if self.remote_end_type in ConnectionBased: self.buffered_send(schro_remote,buf) + 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) elif self.remote_end_type == "stdout" or self.local_end_type == "stdin": sys.stdout.write(buf) + output("[o.o] Sent %d bytes to remote\n" % (len(buf)),YELLOW) else: self.buffered_sendto(schro_remote,buf,(self.rhost,self.rport)) + output("[o.o] Sent %d bytes to remote (%s:%d)\n" % (len(buf),self.rhost,self.rport),CYAN) - 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) try: @@ -949,12 +952,14 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr,plock): if self.remote_end_type in ConnectionBased: self.buffered_send(schro_remote,buf) + 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) elif self.remote_end_type == "stdout" or self.local_end_type == "stdin": sys.stdout.write(buf) + output("[o.o] Sent %d bytes to remote\n" % (len(buf)),CYAN) else: self.buffered_sendto(schro_remote,buf,(self.rhost,self.rport)) + output("[o.o] Sent %d bytes to remote (%s:%d)\n" % (len(buf),self.rhost,self.rport),CYAN) - 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() @@ -976,11 +981,13 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr,plock): if self.local_end_type in ConnectionBased: self.buffered_send(schro_local,buf) + 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) elif self.remote_end_type == "stdout" or self.local_end_type == "stdin": sys.stdout.write(buf) + output("[o.o] Sent %d bytes to local\n" % (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] Sent %d bytes to local from %s:%d\n" % (len(buf),self.rhost,self.rport),YELLOW) resp_count+=1 try: plock.release() @@ -1002,7 +1009,7 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr,plock): self.pkt_count+=1 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] Sent %d bytes to remote (%s:%d)\n" % (len(buf),self.rhost,self.rport),GREEN) try: plock.release() From f69200e409aca93a574137de1e54b799194e40ed Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Fri, 6 Apr 2018 10:32:44 -0500 Subject: [PATCH 03/37] Lol, removed dumb signature idea for host conf --- decept.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/decept.py b/decept.py index 251a8c1..99536e9 100755 --- a/decept.py +++ b/decept.py @@ -512,11 +512,9 @@ def server_loop(self): 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: @@ -525,9 +523,11 @@ 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 Exception as e: + print e pass From b07b2a6965f8d6bb860d45634d026cae3577071b Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Thu, 31 May 2018 12:05:14 -0500 Subject: [PATCH 04/37] Minor fixes for output sync --- decept.py | 58 ++++++++++++++++++++++--------------------------------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/decept.py b/decept.py index 99536e9..5432acb 100755 --- a/decept.py +++ b/decept.py @@ -406,10 +406,6 @@ def server_socket_init(self): self.server_socket.listen(self.max_conns) except Exception as e: output(e) - - - - def server_loop(self): @@ -507,6 +503,7 @@ def server_loop(self): self.fuzzerData = mutiny.FuzzerData() # attempt to parse config file, if any. Should be located in 'rhost' + # will continue if not found. try: confbuf = "" conflist = [] @@ -526,6 +523,9 @@ def server_loop(self): except Exception as e: print e pass + except IOError as e: + # no such file + pass except Exception as e: print e pass @@ -539,13 +539,9 @@ 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() + output("[>.>] Received Connection from %s" % str(addr),GREEN,self.output_lock) else: - self.output_lock.acquire() - output("[>.>] Received Connection from UnixSocket",GREEN) - self.output_lock.release() + output("[>.>] Received Connection from UnixSocket",GREEN,self.output_lock) if "windows" in system().lower() or "cygwin" in system().lower(): @@ -634,7 +630,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) @@ -644,7 +640,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() @@ -655,7 +651,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) @@ -938,10 +934,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: @@ -949,7 +944,6 @@ 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: self.buffered_send(schro_remote,buf) 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) @@ -959,18 +953,13 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr="",plock=""): else: self.buffered_sendto(schro_remote,buf,(self.rhost,self.rport)) output("[o.o] Sent %d bytes to remote (%s:%d)\n" % (len(buf),self.rhost,self.rport),CYAN) - - - try: - plock.release() - except: - pass + 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: @@ -989,34 +978,31 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr="",plock=""): self.buffered_sendto(schro_local,buf,(self.lhost,self.lport)) output("[o.o] Sent %d bytes to local from %s:%d\n" % (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 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)\n" % (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: @@ -1037,7 +1023,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: @@ -1580,12 +1566,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() From 394a9835214e59d00fd70780860728f03f23c16a Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Tue, 12 Jun 2018 17:22:14 -0500 Subject: [PATCH 05/37] Perhaps fix up tables too --- decept.py | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++- poison.conf | 14 ++++++ 2 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 poison.conf diff --git a/decept.py b/decept.py index 5432acb..514f77a 100755 --- a/decept.py +++ b/decept.py @@ -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 @@ -176,6 +176,8 @@ def __init__(self,lhost,lport,rhost,rport,local_end_type,remote_end_type,receive self.hostconf_dict = {} + self.poison_file = "" + self.poison_int = "eth0" self.local_certfile=local_cert self.local_keyfile=local_key @@ -502,6 +504,16 @@ def server_loop(self): 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: @@ -531,6 +543,9 @@ def server_loop(self): pass + + + #! Todo, no loop for UDP... while True: if self.local_end_type in ConnectionBased: @@ -1260,6 +1275,98 @@ def monitor_loop(self,mon_sock,pcap_fd,kill_flag): 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) self.write_packet_data(inbound,1) @@ -1355,6 +1462,10 @@ def buffered_sendto(self,sock,data,dst_tuple): send_count += sock.sendto(data_chunk,dst_tuple) + + + + ##################################################### ### End class DeceptProxy() ##################################################### @@ -1451,6 +1562,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() @@ -1505,6 +1617,8 @@ 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") @@ -1724,6 +1838,10 @@ 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", @@ -1735,7 +1853,7 @@ class ETH(Structure): "--quiet","--dont_kill","--udppr", "--expect","--really", "--lcert","--lkey","--rcert","--rkey", - "--rverify"] + "--rverify","--poison","--poison_int"] ##################################### ## Global header for pcap file 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 From 7dc505ffccc4a2b6ec7804d23a29288eb5a83df6 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Fri, 15 Jun 2018 14:08:35 -0500 Subject: [PATCH 06/37] Fixed up --dont_kill hopefully, --expresp added --- decept.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/decept.py b/decept.py index 514f77a..2ddd9c8 100755 --- a/decept.py +++ b/decept.py @@ -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 @@ -630,6 +630,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,ploc) except ssl.SSLError as e: output("[x.x] Unable to wrap local SSL socket.",YELLOW, plock) output(str(e),RED, plock) @@ -742,6 +743,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) @@ -858,8 +860,6 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr="",plock=""): output(str(e),RED) remote_socket.close() sys.exit() - - buf = "" @@ -904,7 +904,6 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr="",plock=""): output("[o.o] Sent %d bytes to remote (%s:%d)\n" % (len(buf),self.rhost,self.rport),CYAN) - try: while True: if not len(initial_buff): @@ -1020,10 +1019,10 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr="",plock=""): # 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): @@ -1595,6 +1594,7 @@ def main(): #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") From ec63c03109a3350fc2bf8f0b9abeb00e803d666f Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Fri, 15 Jun 2018 14:09:50 -0500 Subject: [PATCH 07/37] added expresp flag --- decept.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/decept.py b/decept.py index 2ddd9c8..43753e1 100755 --- a/decept.py +++ b/decept.py @@ -1853,7 +1853,8 @@ class ETH(Structure): "--quiet","--dont_kill","--udppr", "--expect","--really", "--lcert","--lkey","--rcert","--rkey", - "--rverify","--poison","--poison_int"] + "--rverify","--poison","--poison_int", + "--expresp"] ##################################### ## Global header for pcap file From d4897f3efdebd8502c275fe0d445c9d59fa3f2f3 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Sat, 16 Jun 2018 15:18:27 -0500 Subject: [PATCH 08/37] Fixed minor output bug with --dont_kill, --outhook and fast timeout together --- decept.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/decept.py b/decept.py index 43753e1..f5f5802 100755 --- a/decept.py +++ b/decept.py @@ -929,7 +929,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: @@ -1408,7 +1407,7 @@ def outbound_handler(self,outbound,src="",dst=""): outbound,self.userdata = tmp #output("[>.>] Post-hook datalen: %d" %len(outbound),CYAN) - output(self.userdata) + #output(self.userdata) return outbound From b3dee1bbad156c81acccf98dda318d64dbb4d756 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Tue, 3 Jul 2018 13:28:37 -0500 Subject: [PATCH 09/37] Minor hexdump fix and update to hook documentation --- decept.py | 12 ++++++++---- hooks/example_external_hook.py | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/decept.py b/decept.py index f5f5802..fc04afe 100755 --- a/decept.py +++ b/decept.py @@ -1498,6 +1498,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): @@ -1805,11 +1808,12 @@ 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. Examine "inbound_handler"/"outbound_handler" + for more information. - --outhook HOOKFILE | Function Prototype: string outbound_hook(outbound): - --inhook HOOKFILE | Function Prototype: string inbound_hook(inbound): + --outhook HOOKFILE | Function Prototype: string outbound_hook(outbound,userdata=""): + --inhook HOOKFILE | Function Prototype: string inbound_hook(inbound,userdata=""): Host Config File: Optionally, instead of specifying a remote host, if you specify a valid diff --git a/hooks/example_external_hook.py b/hooks/example_external_hook.py index 68f4e32..dcb3701 100644 --- a/hooks/example_external_hook.py +++ b/hooks/example_external_hook.py @@ -1,5 +1,5 @@ -def inbound_hook(inbound_data): +def inbound_hook(inbound_data,userdata=""): return inbound_data[0:298] -def outbound_hook(outbound_data): +def outbound_hook(outbound_data,userdata=""): return outbound_data From cce91e86d1138b6bc5c005dcfbd34d9cd8b4536d Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Wed, 4 Jul 2018 10:06:31 -0500 Subject: [PATCH 10/37] Timestamping added, courtesy of Claudio ^_^ --- decept.py | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/decept.py b/decept.py index fc04afe..65a14b4 100755 --- a/decept.py +++ b/decept.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python2 #------------------------------------------------------------------ # Author: Lilith Wyatt <(^,^)> #------------------------------------------------------------------ @@ -543,9 +543,6 @@ def server_loop(self): pass - - - #! Todo, no loop for UDP... while True: if self.local_end_type in ConnectionBased: @@ -554,9 +551,10 @@ def server_loop(self): #print out conn info if addr: - output("[>.>] Received Connection from %s" % str(addr),GREEN,self.output_lock) + ts = datetime.now().strftime("%H:%M:%S.%f") + output("[>.>] %s Received Connection from %s" % (ts,str(addr)),GREEN,self.output_lock) else: - output("[>.>] Received Connection from UnixSocket",GREEN,self.output_lock) + output("[>.>] %s Received Connection from UnixSocket"%(ts),GREEN,self.output_lock) if "windows" in system().lower() or "cygwin" in system().lower(): @@ -790,7 +788,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: @@ -892,16 +891,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] 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->%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] Sent %d bytes to remote\n" % (len(buf)),YELLOW) + 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)\n" % (len(buf),self.rhost,self.rport),CYAN) + output("[o.o] %s Sent %d bytes to remote (%s:%d)\n" % (ts, len(buf),self.rhost,self.rport),CYAN) try: @@ -957,15 +957,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] Sent %d bytes to remote (%s:%d->%s:%d)\n" % (len(buf),cli_addr[0],cli_addr[1],self.rhost,self.rport),CYAN) + 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] Sent %d bytes to remote\n" % (len(buf)),CYAN) + 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)\n" % (len(buf),self.rhost,self.rport),CYAN) + 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: @@ -980,16 +982,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.local_end_type in ConnectionBased: self.buffered_send(schro_local,buf) - 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 (%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] Sent %d bytes to local\n" % (len(buf),YELLOW)) + 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 from %s:%d\n" % (len(buf),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 plock.release() @@ -1007,9 +1010,10 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr="",plock=""): 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)\n" % (len(buf),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) plock.release() From 7bee26b328cc9c6e43d2a9dabd3d6fbb7c85866c Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Thu, 5 Jul 2018 09:47:29 -0500 Subject: [PATCH 11/37] removing redundant code --- decept.py | 48 ++++++++++--------- ...mple_external_hook.py => example_hooks.py} | 1 + 2 files changed, 27 insertions(+), 22 deletions(-) rename hooks/{example_external_hook.py => example_hooks.py} (99%) diff --git a/decept.py b/decept.py index 65a14b4..441ae1b 100755 --- a/decept.py +++ b/decept.py @@ -1631,29 +1631,31 @@ def main(): # 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: @@ -1813,11 +1815,12 @@ class ETH(Structure): Hook Files: Optional function definitions for processing data between inbound and outbound endpoints. Can pass data between the hooks/proxy with - the userdata parameters. Examine "inbound_handler"/"outbound_handler" - for more information. + the userdata parameters. Look at `hooks` folder for some examples/ + prebuilt useful things. - --outhook HOOKFILE | Function Prototype: string outbound_hook(outbound,userdata=""): - --inhook HOOKFILE | Function Prototype: string inbound_hook(inbound,userdata=""): + --hookfile | Functions imported: + string outbound_hook(outbound,userdata=""): + string inbound_hook(outbound,userdata=""): Host Config File: Optionally, instead of specifying a remote host, if you specify a valid @@ -1855,13 +1858,14 @@ class ETH(Structure): "--pcap","--pps","--snaplen", "--fuzzer","--dumpraw","-l","-r", "--l2_filter","--l2_mtu","--L2_forward", - "--L3_raw","--inhook","--outhook", + "--L3_raw", + "--hookfile", "--rbind_addr","--rbind_port", "--quiet","--dont_kill","--udppr", "--expect","--really", "--lcert","--lkey","--rcert","--rkey", "--rverify","--poison","--poison_int", - "--expresp"] + "--expresp",""] ##################################### ## Global header for pcap file diff --git a/hooks/example_external_hook.py b/hooks/example_hooks.py similarity index 99% rename from hooks/example_external_hook.py rename to hooks/example_hooks.py index dcb3701..b1531df 100644 --- a/hooks/example_external_hook.py +++ b/hooks/example_hooks.py @@ -3,3 +3,4 @@ def inbound_hook(inbound_data,userdata=""): def outbound_hook(outbound_data,userdata=""): return outbound_data + From 372497a9798deb5d28362d85201d57bfb4f34663 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Thu, 5 Jul 2018 15:46:43 -0500 Subject: [PATCH 12/37] Added Websocket hooks for decoding. ALso changed self.userdata from a string to a list to make it more useful (since you can actually .append() and .pop() and such across functions) --- decept.py | 27 ++---- hooks/websocket_ops.py | 194 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+), 19 deletions(-) create mode 100644 hooks/websocket_ops.py diff --git a/decept.py b/decept.py index 441ae1b..2eedda9 100755 --- a/decept.py +++ b/decept.py @@ -604,7 +604,7 @@ 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 = "" @@ -1379,15 +1379,10 @@ def inbound_handler(self,inbound,src="",dst=""): f.write(inbound) if self.inbound_hook: - #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 #output("[<.<] Pre-hook datalen: %d" %len(inbound),CYAN) + inbound = self.inbound_hook(inbound,self.userdata) + #output("[<.<] Pre-hook datalen: %d" %len(inbound),CYAN) return inbound @@ -1403,15 +1398,8 @@ 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) return outbound @@ -1818,9 +1806,9 @@ class ETH(Structure): the userdata parameters. Look at `hooks` folder for some examples/ prebuilt useful things. - --hookfile | Functions imported: - string outbound_hook(outbound,userdata=""): - string inbound_hook(outbound,userdata=""): + --hookfile | Functions imported from file: + string outbound_hook(outbound,userdata=[]): + string inbound_hook(outbound,userdata=[]): Host Config File: Optionally, instead of specifying a remote host, if you specify a valid @@ -2125,3 +2113,4 @@ def create_ebpf_filter(ip,port,proto=""): if __name__ == "__main__": main() + 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) + + From 60b1380bec859af259b339457fd9e95de10ed910 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Fri, 6 Jul 2018 14:00:06 -0500 Subject: [PATCH 13/37] Rough draft of api replaying via --dumpraw output. --- scripts/api_generator.py | 215 +++++++++++++++++++++++++ scripts/replayer_template.py | 304 +++++++++++++++++++++++++++++++++++ 2 files changed, 519 insertions(+) create mode 100644 scripts/api_generator.py create mode 100644 scripts/replayer_template.py diff --git a/scripts/api_generator.py b/scripts/api_generator.py new file mode 100644 index 0000000..1ee6cfe --- /dev/null +++ b/scripts/api_generator.py @@ -0,0 +1,215 @@ +#!/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: + with open('replayer_template.py',"rb") as f: + template = f.read() + except: + print "[x.x] Could not find api_replayer.py template..." + 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\""%outdir) + template = template.replace("work_dir = %s","work_dir = \"%s\""%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/replayer_template.py b/scripts/replayer_template.py new file mode 100644 index 0000000..c0ced6f --- /dev/null +++ b/scripts/replayer_template.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python +import socket +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 = False + +request_dict = {} +saved_dict = {} + +IP = "" +PORT = "" +TIMEOUT = .3 + +changes_flag = False + + + +def main(): + cmd_dict = { + "list":list_request, + "send":send_request, + "save":save_request, + "rename":rename_request, + "print":print_request, + "exit":cleanup, + "quit":cleanup, + "chain":chain_request, + "new_workdir":new_workdir, + "load_dir":load_request_dir, + "load":load_request, + "print_mode":set_print_mode, + "sethost":sethost, + "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) + os.mkdir(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() + + print "[^_^] Loaded %d requests"%len(request_dict) + + while True: + try: + inp = filter(None,raw_input("[^.^]> ").split(" ")) + except: + print "" + continue + + if not len(inp): + continue + try: + cmd = inp[0] + args = inp[1:] + cmd_dict[cmd](*args) + except KeyError: + print "[x.x] Invalid command: %s" % inp + except KeyboardInterrupt: + continue + except TypeError: + print "[?.?] Wrong num of params for command %s"%cmd + 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 = sys.argv[1] + if len(IP.split(".")) != 4: + print "[>.>] Invalid IP given" + return + PORT = int(sys.argv[2]) + except: + print "[x.x] Invalid params given to sethost!" + return + + +def set_print_mode(mode): + 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) + +def send_request(request_id): + try: + sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + sock.connect((IP,PORT)) + sock.setttimeout(TIMEOUT) + except: + print "[x.x] Unable to connect to %s:%d"%(IP,PORT) + print "Consider using 'sethost' cmd to fix." + return + + + req = request_dict[request_id] + print "[>.>] Sending %d bytes~"%len(req) + sock.send(req) + + tmp = "" + ret += tmp + while True: + try: + tmp = sock.recv(65535) + if tmp: + ret+=tmp + except: + break + + if len(ret): + print "[<.<] Got %d bytes~" + if len(ret) > 400: + ret = ret[0:400] + print "\\x" + "\\x".join(["%02x"%ord(y) for y in ret]) + print "[...]" + + +def rename_request(old_request_id,new_request_id): + 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) + os.remove(filename) + changes_flag = True + except: + print "Could not remove old request %s"%old_rquest_id + 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 + + try: + request_dict[request_id] = request_value + 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 + + if truncate: + old_len = len(req) + req = req[0:200] + + 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 "-------------------" + print "%s %s" % (request_id,buf) + if truncate: + print "[...] (200/%d bytes)" % old_len + + +def chain_request(): + print "[^_^] Not implimented, lol." + +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 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 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":send_request(request_id) - Sends the api request. Will cause a socket connect + "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. + "print":print_request(request_id) - Prints the given request for + "exit":cleanup() - Obv. + "chain":chain_request(request_id1, + request_id2,...) - ??? Not sure how I want this done yet. + "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") + "sethost":sethost(ip,port) - Change remote endpoint to : + ''' + + print ret + + +if __name__ == "__main__": + + print "<(^_^)> Decept Autogen'ed API replayer thing:" + if len(sys.argv) < 3: + usage() + main() From 4117a9a6a66ff2e2421c2bb8ca65f355288cca51 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Fri, 6 Jul 2018 14:04:21 -0500 Subject: [PATCH 14/37] Maybe the flag should be global. --- scripts/replayer_template.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/replayer_template.py b/scripts/replayer_template.py index c0ced6f..220d873 100644 --- a/scripts/replayer_template.py +++ b/scripts/replayer_template.py @@ -13,7 +13,7 @@ # for printing purposes only ascii_threshold = .60 -ascii_flag = False +ascii_flag = True request_dict = {} saved_dict = {} @@ -24,8 +24,6 @@ changes_flag = False - - def main(): cmd_dict = { "list":list_request, @@ -116,6 +114,8 @@ def sethost(ip,port=""): def set_print_mode(mode): + global ascii_flag + if mode == "ascii": ascii_flag = True elif mode == "binary": From 681700ea18cec5d9f66a297775df4df67e2c7f19 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Fri, 6 Jul 2018 14:48:15 -0500 Subject: [PATCH 15/37] And that's why I said rough draft --- scripts/api_generator.py | 4 ++-- scripts/replayer_template.py | 36 +++++++++++++++++++++++------------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/scripts/api_generator.py b/scripts/api_generator.py index 1ee6cfe..0b2f0b2 100644 --- a/scripts/api_generator.py +++ b/scripts/api_generator.py @@ -126,8 +126,8 @@ def main(dumpraw_dir,delim="",indexes=""): continue with open(fname,"wb") as f: work_dir = outdir + "_workdir" - template = template.replace("inp_dir = %s","inp_dir = \"%s\""%outdir) - template = template.replace("work_dir = %s","work_dir = \"%s\""%work_dir) + 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....?" diff --git a/scripts/replayer_template.py b/scripts/replayer_template.py index 220d873..8ad6ce6 100644 --- a/scripts/replayer_template.py +++ b/scripts/replayer_template.py @@ -57,7 +57,7 @@ def main(): except: try: load_request_dir(inp_dir) - os.mkdir(work_dir) + new_workdir(work_dir) except: print "[x.x] Unable to load %s or %s"%(work_dir,inp_dir) sys.exit() @@ -132,7 +132,7 @@ def send_request(request_id): try: sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) sock.connect((IP,PORT)) - sock.setttimeout(TIMEOUT) + sock.settimeout(TIMEOUT) except: print "[x.x] Unable to connect to %s:%d"%(IP,PORT) print "Consider using 'sethost' cmd to fix." @@ -144,7 +144,7 @@ def send_request(request_id): sock.send(req) tmp = "" - ret += tmp + ret = "" while True: try: tmp = sock.recv(65535) @@ -154,12 +154,12 @@ def send_request(request_id): break if len(ret): - print "[<.<] Got %d bytes~" - if len(ret) > 400: - ret = ret[0:400] + print "[<.<] Got %d bytes~" % len(ret) + if len(ret) > 0x1000: + ret = ret[0:0x1000] print "\\x" + "\\x".join(["%02x"%ord(y) for y in ret]) print "[...]" - + def rename_request(old_request_id,new_request_id): global changes_flag @@ -169,8 +169,8 @@ def rename_request(old_request_id,new_request_id): filename = os.path.join(work_dir,old_request_id) os.remove(filename) changes_flag = True - except: - print "Could not remove old request %s"%old_rquest_id + except Exception as e: + print "Could not remove old request %s (%s)"%(old_request_id,e) return save_request(new_request_id,val) @@ -207,7 +207,7 @@ def print_request(request_id,truncate=False): except KeyError: print "[x.x] Request %s not found in request_dict"%request_id - if truncate: + if truncate and len(req) > 0x1000: old_len = len(req) req = req[0:200] @@ -218,9 +218,9 @@ def print_request(request_id,truncate=False): else: buf+="\\x%02x"%ord(char) print "-------------------" - print "%s %s" % (request_id,buf) - if truncate: - print "[...] (200/%d bytes)" % old_len + print "%s%s %s %s" % (CYAN,request_id,CLEAR,buf) + if len(req) > 0x1000: + print "%s[...] (0x1000/%d bytes)%s" % (YELLOW,old_len,CLEAR) def chain_request(): @@ -295,6 +295,16 @@ def print_help(): 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' if __name__ == "__main__": From 23bb00d73996e82e194ac29feab8b17b39c40354 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Mon, 9 Jul 2018 11:57:05 -0500 Subject: [PATCH 16/37] Minor improvements --- scripts/replayer_template.py | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/scripts/replayer_template.py b/scripts/replayer_template.py index 8ad6ce6..bd57b0e 100644 --- a/scripts/replayer_template.py +++ b/scripts/replayer_template.py @@ -30,6 +30,7 @@ def main(): "send":send_request, "save":save_request, "rename":rename_request, + "reload":reload_request, "print":print_request, "exit":cleanup, "quit":cleanup, @@ -150,6 +151,8 @@ def send_request(request_id): tmp = sock.recv(65535) if tmp: ret+=tmp + else: + break except: break @@ -157,9 +160,20 @@ def send_request(request_id): print "[<.<] Got %d bytes~" % len(ret) if len(ret) > 0x1000: ret = ret[0:0x1000] - print "\\x" + "\\x".join(["%02x"%ord(y) for y in ret]) + + 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 "\\x" + "\\x".join(["%02x"%ord(y) for y in buf]) print "[...]" + print "[!-!] Saving response as %s_resp"%(request_id) + save_request("%s_resp"%request_id,buf) + def rename_request(old_request_id,new_request_id): global changes_flag @@ -182,8 +196,18 @@ 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] = request_value + 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 @@ -206,6 +230,7 @@ def print_request(request_id,truncate=False): 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) > 0x1000: old_len = len(req) @@ -254,6 +279,9 @@ def new_workdir(directory,force=False): 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)) From 8f2c9f534fdc5d7fafb0f0b9891d6455986720cd Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Mon, 9 Jul 2018 12:08:32 -0500 Subject: [PATCH 17/37] Minor fix --- scripts/replayer_template.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/replayer_template.py b/scripts/replayer_template.py index bd57b0e..a5a6672 100644 --- a/scripts/replayer_template.py +++ b/scripts/replayer_template.py @@ -168,8 +168,7 @@ def send_request(request_id): else: buf+="\\x%02x"%ord(char) - print "\\x" + "\\x".join(["%02x"%ord(y) for y in buf]) - print "[...]" + print buf print "[!-!] Saving response as %s_resp"%(request_id) save_request("%s_resp"%request_id,buf) From 706278a3387f853133e155c1b3e8edcc9273bc04 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Tue, 10 Jul 2018 13:50:17 -0500 Subject: [PATCH 18/37] Added commands to api_replayer: pasteraw,cmp(sorta shoddy) --- scripts/replayer_template.py | 136 ++++++++++++++++++++++++++++++++--- 1 file changed, 125 insertions(+), 11 deletions(-) diff --git a/scripts/replayer_template.py b/scripts/replayer_template.py index a5a6672..71604b8 100644 --- a/scripts/replayer_template.py +++ b/scripts/replayer_template.py @@ -1,5 +1,6 @@ #!/usr/bin/env python import socket +import code import sys import os @@ -31,6 +32,7 @@ def main(): "save":save_request, "rename":rename_request, "reload":reload_request, + "del":remove_request, "print":print_request, "exit":cleanup, "quit":cleanup, @@ -40,6 +42,8 @@ def main(): "load":load_request, "print_mode":set_print_mode, "sethost":sethost, + "pasteraw":paste_request, + "cmp":cmp_requests, "help":print_help, "?":print_help, } @@ -71,7 +75,7 @@ def main(): while True: try: - inp = filter(None,raw_input("[^.^]> ").split(" ")) + inp = filter(None,raw_input("%s[^.^]> %s"%(PURPLE,CLEAR)).split(" ")) except: print "" continue @@ -83,11 +87,17 @@ def main(): args = inp[1:] cmd_dict[cmd](*args) except KeyError: - print "[x.x] Invalid command: %s" % inp + try: + os.system(" ".join(inp)) + except Exception as e: + print e + print "[x.x] Invalid command: %s" % inp except KeyboardInterrupt: continue - except TypeError: + except TypeError as e: print "[?.?] Wrong num of params for command %s"%cmd + print e + except Exception as e: print e @@ -170,8 +180,10 @@ def send_request(request_id): print buf - print "[!-!] Saving response as %s_resp"%(request_id) - save_request("%s_resp"%request_id,buf) + print "[!-!] Saving response as %s_resp"%(request_id) + save_request("%s_resp"%request_id,buf) + else: + print "[x.x] No response..." def rename_request(old_request_id,new_request_id): @@ -231,9 +243,9 @@ def print_request(request_id,truncate=False): print "[x.x] Request %s not found in request_dict"%request_id return - if truncate and len(req) > 0x1000: + if truncate and len(req) > 50: old_len = len(req) - req = req[0:200] + req = req[0:50] buf = "" for char in req: @@ -242,9 +254,9 @@ def print_request(request_id,truncate=False): else: buf+="\\x%02x"%ord(char) print "-------------------" - print "%s%s %s %s" % (CYAN,request_id,CLEAR,buf) - if len(req) > 0x1000: - print "%s[...] (0x1000/%d bytes)%s" % (YELLOW,old_len,CLEAR) + if len(req) == 50: + buf+="%s[...] (50/%d bytes)%s" % (YELLOW,old_len,CLEAR) + print "%s%s\n%s%s" % (CYAN,request_id,CLEAR,buf) def chain_request(): @@ -294,6 +306,105 @@ def load_request(request_file): 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]) + 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 + + if i >= len(shorter) or j > len(shorter): + break + + + # no min 4 byte match + buf+=YELLOW + #print "break i(%d),j(%d),y(%d),x(%d)"%(i,j,y,x) + #print "stop match ind(%d) %s\nin\n%s"%(match,repr(shorter[i:]),repr(longer[j:])) + for q in range(0,match+1): + if ord(longer[j+q]) >= 0x30 and ord(longer[j+q]) <= 122: + buf+=longer[j+q] + else: + buf+="\\x%02x"%ord(longer[j+q]) + j+=match + j+=1 + + i+=1 + if j >= len(shorter): + #print "i(%d),j(%d),lenshort(%d)"%(i,j,len(shorter)) + break + + 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 paste_request(request_name): + print "[!.!] Being pasting request" + print "Newlines will be ignored, use \\x0a for newlines in bytestream" + print "Ctrl-C to finish input" + + buf = "" + while True: + try: + buf+=raw_input("") + except KeyboardInterrupt: + break + + save_request(request_name,buf) + def usage(): print "[?.?] Usage: %s " sys.exit() @@ -318,6 +429,9 @@ def print_help(): "print_mode":set_print_mode(mode) - Controls how the print/list commands operate. Available modes: ("binary"||"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 + "cmp":cmp_requests(r1,r2) - Prints out color diff of requests r1,r2 ''' print ret @@ -335,7 +449,7 @@ def print_help(): if __name__ == "__main__": - print "<(^_^)> Decept Autogen'ed API replayer thing:" + print CYAN + "<(^_^)> Decept Autogen'ed API replayer thing:" + CLEAR if len(sys.argv) < 3: usage() main() From 392c4e3d75dbf82a9c4a9eb8f32373b3db4bf0cd Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Tue, 10 Jul 2018 13:56:25 -0500 Subject: [PATCH 19/37] Replayer template new cmd:del,copy,pasteraw,cmp --- scripts/replayer_template.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/scripts/replayer_template.py b/scripts/replayer_template.py index 71604b8..bc8d2db 100644 --- a/scripts/replayer_template.py +++ b/scripts/replayer_template.py @@ -1,6 +1,5 @@ #!/usr/bin/env python import socket -import code import sys import os @@ -186,13 +185,18 @@ def send_request(request_id): print "[x.x] No response..." -def rename_request(old_request_id,new_request_id): +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) - os.remove(filename) + 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) @@ -417,6 +421,7 @@ def print_help(): "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. "chain":chain_request(request_id1, From 47387bb0330147233bc02946eff0ff5c95c10510 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Tue, 10 Jul 2018 14:01:44 -0500 Subject: [PATCH 20/37] Minor fix --- scripts/replayer_template.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/replayer_template.py b/scripts/replayer_template.py index bc8d2db..23dd0f5 100644 --- a/scripts/replayer_template.py +++ b/scripts/replayer_template.py @@ -30,6 +30,7 @@ def main(): "send":send_request, "save":save_request, "rename":rename_request, + "copy":copy_request, "reload":reload_request, "del":remove_request, "print":print_request, @@ -186,7 +187,7 @@ def send_request(request_id): def copy_request(old_request_id,new_request_id): - rename_request(old_request_id,new_request_id,copyOnly=True): + rename_request(old_request_id,new_request_id,copyOnly=True) def rename_request(old_request_id,new_request_id,copyOnly=False): From 9737374eb1fba7cdc50c0b277738a6248cdf08a5 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Thu, 12 Jul 2018 17:49:04 -0500 Subject: [PATCH 21/37] Added server mode to replayer and also persistant socket mode --- scripts/replayer_template.py | 155 ++++++++++++++++++++++++----------- 1 file changed, 105 insertions(+), 50 deletions(-) diff --git a/scripts/replayer_template.py b/scripts/replayer_template.py index 23dd0f5..e199bea 100644 --- a/scripts/replayer_template.py +++ b/scripts/replayer_template.py @@ -7,7 +7,7 @@ inp_dir = %s # after we've loaded > 1 time, everything is saved to here: -work_dir = %s +work_dir = %s # work_dir is overwritten if the --workdir param is given @@ -21,14 +21,19 @@ IP = "" PORT = "" TIMEOUT = .3 +socket_mode = "kill" +sock = None changes_flag = False def main(): cmd_dict = { "list":list_request, - "send":send_request, "save":save_request, + "send_ser":server_send_bytes, + "send":client_send_bytes, + "send_cli":client_send_bytes, + "socket_mode":set_socket_mode, "rename":rename_request, "copy":copy_request, "reload":reload_request, @@ -36,7 +41,6 @@ def main(): "print":print_request, "exit":cleanup, "quit":cleanup, - "chain":chain_request, "new_workdir":new_workdir, "load_dir":load_request_dir, "load":load_request, @@ -86,7 +90,9 @@ def main(): cmd = inp[0] args = inp[1:] cmd_dict[cmd](*args) - except KeyError: + except KeyError as e: + print e + try: os.system(" ".join(inp)) except Exception as e: @@ -97,7 +103,6 @@ def main(): except TypeError as e: print "[?.?] Wrong num of params for command %s"%cmd print e - except Exception as e: print e @@ -114,11 +119,11 @@ def sethost(ip,port=""): print "[;_;] Bad ip/port" try: - IP = sys.argv[1] + IP = ip if len(IP.split(".")) != 4: print "[>.>] Invalid IP given" return - PORT = int(sys.argv[2]) + PORT = int(port) except: print "[x.x] Invalid params given to sethost!" return @@ -139,21 +144,71 @@ def list_request(): for req in req_list: print_request(req,truncate=True) -def send_request(request_id): - 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 + +# 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 + + 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": + 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) + # 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) + resp = get_bytes(sock) + except Exception as e: + if "Broken pipe" in e: + sock = None + else: + print e + + #print "[!-!] Saving response as %s_resp"%(request_id) + #save_request("%s_resp"%request_id,buf) - req = request_dict[request_id] - print "[>.>] Sending %d bytes~"%len(req) - sock.send(req) - +def get_bytes(sock): tmp = "" ret = "" while True: @@ -167,7 +222,7 @@ def send_request(request_id): break if len(ret): - print "[<.<] Got %d bytes~" % len(ret) + print YELLOW + "[<.<] Got %d bytes~" % len(ret) if len(ret) > 0x1000: ret = ret[0:0x1000] @@ -177,11 +232,8 @@ def send_request(request_id): buf+=char else: buf+="\\x%02x"%ord(char) - print buf - print "[!-!] Saving response as %s_resp"%(request_id) - save_request("%s_resp"%request_id,buf) else: print "[x.x] No response..." @@ -189,7 +241,6 @@ def send_request(request_id): 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: @@ -264,9 +315,6 @@ def print_request(request_id,truncate=False): print "%s%s\n%s%s" % (CYAN,request_id,CLEAR,buf) -def chain_request(): - print "[^_^] Not implimented, lol." - def cleanup(): if changes_flag: print "[?.?] Would you like to save request changes to the current workdir? (y/n)" @@ -404,7 +452,12 @@ def paste_request(request_name): buf = "" while True: try: - buf+=raw_input("") + tmp = raw_input("") + tmp = tmp.rstrip().lstrip() + if tmp.startswith("#"): + continue + if len(tmp): + buf+=tmp except KeyboardInterrupt: break @@ -417,27 +470,29 @@ def usage(): def print_help(): ret = '''\ <(^_^)> Decept Autogen'ed API replayer thing: - "list":list_request() - Prints out all available API requests. - "send":send_request(request_id) - Sends the api request. Will cause a socket connect - "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. - "chain":chain_request(request_id1, - request_id2,...) - ??? Not sure how I want this done yet. - "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") - "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 - "cmp":cmp_requests(r1,r2) - Prints out color diff of requests r1,r2 + "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 + "cmp":cmp_requests(r1,r2) - Prints out color diff of requests r1,r2 + ''' print ret From 480184fc6f6c4a624391b2ceb04492799bab1540 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Tue, 17 Jul 2018 09:19:40 -0500 Subject: [PATCH 22/37] readline functionality added to replayer. --- scripts/replayer_template.py | 112 +++++++++++++++++++++++++++++++---- 1 file changed, 99 insertions(+), 13 deletions(-) diff --git a/scripts/replayer_template.py b/scripts/replayer_template.py index e199bea..e73040e 100644 --- a/scripts/replayer_template.py +++ b/scripts/replayer_template.py @@ -1,5 +1,7 @@ #!/usr/bin/env python +import readline import socket +import glob import sys import os @@ -7,7 +9,7 @@ inp_dir = %s # after we've loaded > 1 time, everything is saved to here: -work_dir = %s +work_dir = %s # work_dir is overwritten if the --workdir param is given @@ -20,19 +22,21 @@ IP = "" PORT = "" -TIMEOUT = .3 +TIMEOUT = .5 socket_mode = "kill" sock = None changes_flag = False def main(): + cmd_dict = { "list":list_request, "save":save_request, - "send_ser":server_send_bytes, + "sendser":server_send_bytes, + "send":client_send_bytes, + "edit":edit_request, "send":client_send_bytes, - "send_cli":client_send_bytes, "socket_mode":set_socket_mode, "rename":rename_request, "copy":copy_request, @@ -52,6 +56,9 @@ def main(): "?":print_help, } + + + global work_dir sethost(sys.argv[1],sys.argv[2]) @@ -74,7 +81,9 @@ def main(): 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: @@ -164,8 +173,9 @@ def set_socket_mode(mode): def send_bytes(mode,*request_ids): global sock + serv_sock = None - if socket_mode != "persist" or sock == None : + if socket_mode != "persist" or sock == None: if mode == "client": try: sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) @@ -177,13 +187,16 @@ def send_bytes(mode,*request_ids): return elif mode == "server": - 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) + if serv_sock == None: + print "boop" + 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) @@ -206,6 +219,15 @@ def send_bytes(mode,*request_ids): #print "[!-!] Saving response as %s_resp"%(request_id) #save_request("%s_resp"%request_id,buf) + + if socket_mode != "persist": + sock.close() + if mode == "server": + sock.close() + serv_sock.close() + + + def get_bytes(sock): @@ -238,6 +260,10 @@ def get_bytes(sock): print "[x.x] No response..." +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) @@ -312,7 +338,7 @@ def print_request(request_id,truncate=False): print "-------------------" if len(req) == 50: buf+="%s[...] (50/%d bytes)%s" % (YELLOW,old_len,CLEAR) - print "%s%s\n%s%s" % (CYAN,request_id,CLEAR,buf) + print "%s%s (%d bytes (0x%lx))\n%s%s" % (CYAN,request_id,len(req),len(req),CLEAR,buf) def cleanup(): @@ -461,7 +487,10 @@ def paste_request(request_name): except KeyboardInterrupt: break - save_request(request_name,buf) + if len(buf): + save_request(request_name,buf) + else: + print "[1.1] No-go on saving of %s" % request_name def usage(): print "[?.?] Usage: %s " @@ -508,6 +537,63 @@ def print_help(): 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 From 1d04927068d9d4123f955ede58c179cb28b3c734 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Fri, 20 Jul 2018 10:13:41 -0500 Subject: [PATCH 23/37] pastehex and pastecarray added to replayer_template.py --- scripts/replayer_template.py | 113 ++++++++++++++++++++++++++--------- 1 file changed, 85 insertions(+), 28 deletions(-) diff --git a/scripts/replayer_template.py b/scripts/replayer_template.py index e73040e..14256e7 100644 --- a/scripts/replayer_template.py +++ b/scripts/replayer_template.py @@ -22,9 +22,10 @@ IP = "" PORT = "" -TIMEOUT = .5 +TIMEOUT = 1 socket_mode = "kill" sock = None +serv_sock = None changes_flag = False @@ -51,6 +52,8 @@ def main(): "print_mode":set_print_mode, "sethost":sethost, "pasteraw":paste_request, + "pastehex":paste_hexstream, + "pastecarray":paste_carray, "cmp":cmp_requests, "help":print_help, "?":print_help, @@ -173,7 +176,7 @@ def set_socket_mode(mode): def send_bytes(mode,*request_ids): global sock - serv_sock = None + global serv_sock if socket_mode != "persist" or sock == None: if mode == "client": @@ -187,14 +190,16 @@ def send_bytes(mode,*request_ids): return elif mode == "server": - if serv_sock == None: - print "boop" + 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() + 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 @@ -210,25 +215,43 @@ def send_bytes(mode,*request_ids): print CYAN + "[>.>] Sending %s %d bytes~"%(request_id,len(req)) try: sock.send(req) - resp = get_bytes(sock) + 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": - sock.close() serv_sock.close() + ''' - - def get_bytes(sock): tmp = "" @@ -243,21 +266,7 @@ def get_bytes(sock): except: break - if len(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..." + return ret def edit_request(request_id,start_index,new_val): @@ -468,12 +477,44 @@ def cmp_requests(req1,req2): buf += CLEAR print "[^_^] Request Diff: (Key:%sMATCH,%sDIFF,%sAPPEND)\n%s%s" % (GREEN,YELLOW,CYAN,buf,CLEAR) - -def paste_request(request_name): - print "[!.!] Being pasting request" + +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","") + 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 "Ctrl-C to finish input" + print "Comment lines with '#'. Ctrl-C to finish input (and remember to hit first)" buf = "" while True: @@ -486,12 +527,28 @@ def paste_request(request_name): 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() From 86f0b786f0a9900e98a5e368779c6270584deafd Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Fri, 20 Jul 2018 11:12:49 -0500 Subject: [PATCH 24/37] pastecarray fix for missing bytes --- scripts/replayer_template.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/scripts/replayer_template.py b/scripts/replayer_template.py index 14256e7..1ebe914 100644 --- a/scripts/replayer_template.py +++ b/scripts/replayer_template.py @@ -6,13 +6,13 @@ import os # where we look for our initial first-run requests: -inp_dir = %s +inp_dir = %s # after we've loaded > 1 time, everything is saved to here: -work_dir = %s - +work_dir = %s # work_dir is overwritten if the --workdir param is given + # for printing purposes only ascii_threshold = .60 ascii_flag = True @@ -487,8 +487,11 @@ def process_carray(req_buf): print "[x.x] Bracket missing for paste_carray..." return "" - byte_buf = req_buf[left_bracket:right_bracket].replace("\n","") - for b in byte_buf.split(", "): + 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: From f36a3007ca0b44e83f74b5845d799c161b153d2a Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Sat, 21 Jul 2018 12:05:28 -0500 Subject: [PATCH 25/37] Added --tap option. Will now replicate post-recv traffic over a 127.0.0.2<->127.0.0.3 session, so you can use other tools such as wireshark/tcpdump on the decrypted/post processed traffic --- decept.py | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 114 insertions(+), 5 deletions(-) diff --git a/decept.py b/decept.py index 2eedda9..11f4f41 100755 --- a/decept.py +++ b/decept.py @@ -173,6 +173,8 @@ 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 = {} @@ -212,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 @@ -501,6 +504,82 @@ 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. + + self.inbound_dummy = self.dummy_packet + "\x7f\x00\x00\x02" + "\x7f\x00\x00\x03" + self.outbound_dummy = self.dummy_packet + "\x7f\x00\x00\x03" + "\x7f\x00\x00\x02" + + 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() @@ -511,8 +590,6 @@ def server_loop(self): self.killswitch)) poison_thread.start() - - # attempt to parse config file, if any. Should be located in 'rhost' # will continue if not found. @@ -1276,6 +1353,11 @@ def monitor_loop(self,mon_sock,pcap_fd,kill_flag): pcap_fd.close() + + + + + def arp_poisoner(self,config_file,interface,killswitch): @@ -1384,6 +1466,16 @@ def inbound_handler(self,inbound,src="",dst=""): inbound = self.inbound_hook(inbound,self.userdata) #output("[<.<] Pre-hook datalen: %d" %len(inbound),CYAN) + if self.tapmode: + packet = self.inbound_dummy + inbound + packet = packet.replace("L4TOTESHEADER",struct.pack(">H",len(inbound))) + + # (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.2",0)) + return inbound @@ -1401,6 +1493,18 @@ def outbound_handler(self,outbound,src="",dst=""): outbound = self.outbound_hook(outbound,self.userdata) #output("[>.>] Post-hook datalen: %d" %len(outbound),CYAN) + 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 @@ -1585,6 +1689,7 @@ 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)) @@ -1611,6 +1716,7 @@ 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") @@ -1846,7 +1952,7 @@ class ETH(Structure): "--pcap","--pps","--snaplen", "--fuzzer","--dumpraw","-l","-r", "--l2_filter","--l2_mtu","--L2_forward", - "--L3_raw", + "--L3_raw", "--tap", "--hookfile", "--rbind_addr","--rbind_port", "--quiet","--dont_kill","--udppr", @@ -2111,6 +2217,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() From 9bc8e635328376ba69d2508ab876d1e35ed22416 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Sat, 21 Jul 2018 12:15:53 -0500 Subject: [PATCH 26/37] Changed from spoofed l3 addrs to actual ones, since rawsockets are funny. Also updated readme --- Readme.md | 22 +++++++++++++++++----- decept.py | 16 +++++++++++++--- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/Readme.md b/Readme.md index 25d27c9..475e411 100644 --- a/Readme.md +++ b/Readme.md @@ -60,11 +60,19 @@ SSL Options: 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 @@ -90,7 +98,11 @@ 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 -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 +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) ``` diff --git a/decept.py b/decept.py index 11f4f41..1100acb 100755 --- a/decept.py +++ b/decept.py @@ -529,8 +529,11 @@ def server_loop(self): self.dummy_packet+= "\x00\x00" # header checksum, don't care. - self.inbound_dummy = self.dummy_packet + "\x7f\x00\x00\x02" + "\x7f\x00\x00\x03" - self.outbound_dummy = self.dummy_packet + "\x7f\x00\x00\x03" + "\x7f\x00\x00\x02" + 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) @@ -1474,7 +1477,7 @@ def inbound_handler(self,inbound,src="",dst=""): l3len = (len(packet) - (0x14 + 0xC + (len("L3TOTESHEADER")-2))) packet = packet.replace("L3TOTESHEADER",struct.pack(">H",l3len)) - self.tapsock.sendto( packet, ("127.0.0.2",0)) + self.tapsock.sendto( packet, ("127.0.0.1",0)) return inbound @@ -1916,6 +1919,13 @@ class ETH(Structure): 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. From fed0f8933b73fcd357a304721155e5a8c28122fb Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Thu, 26 Jul 2018 11:15:01 -0500 Subject: [PATCH 27/37] Added inbound/outbound hooks for lil_sshniffer to make things easier. Might not be effective though due to ssh buffering and such :/ --- hooks/example_sshniffer_hook.py | 8 +++++ lil_sshniffer.py | 60 ++++++++++++++++++++++++++------- 2 files changed, 56 insertions(+), 12 deletions(-) create mode 100644 hooks/example_sshniffer_hook.py diff --git a/hooks/example_sshniffer_hook.py b/hooks/example_sshniffer_hook.py new file mode 100644 index 0000000..3b2910b --- /dev/null +++ b/hooks/example_sshniffer_hook.py @@ -0,0 +1,8 @@ +def inbound_hook(inbound_data): + print "Inbound hook working! Message Recieved! len:0x%lx"%len(inbound_data) + return inbound_data + +def outbound_hook(outbound_data): + print "Outbound hook working! Message Recieved! len:0x%lx"%len(outbound_data) + return outbound_data + diff --git a/lil_sshniffer.py b/lil_sshniffer.py index 2c937b9..6057cc7 100644 --- a/lil_sshniffer.py +++ b/lil_sshniffer.py @@ -216,6 +216,8 @@ def main(args): global DST_IP global DST_PORT global host_key + global inhook + global outhook sock = None dst_sock = None @@ -227,7 +229,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 +241,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") @@ -456,6 +481,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 +498,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,) + if len(inb): #print "Post filter inb: %s" % repr(inb) in_chan.send(inb) @@ -496,7 +526,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 +533,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) + if not len(outb): continue @@ -535,13 +568,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 +817,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() From eec3eb07603b5cdc0235025f07bb306ce165d5b2 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Thu, 26 Jul 2018 11:20:31 -0500 Subject: [PATCH 28/37] Added persistant storage parameter userdata={} to the hooks to pass data in between. Trust me, you'll need it. --- hooks/example_sshniffer_hook.py | 9 +++++++-- lil_sshniffer.py | 7 +++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/hooks/example_sshniffer_hook.py b/hooks/example_sshniffer_hook.py index 3b2910b..cbe9c6b 100644 --- a/hooks/example_sshniffer_hook.py +++ b/hooks/example_sshniffer_hook.py @@ -1,8 +1,13 @@ -def inbound_hook(inbound_data): +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): +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/lil_sshniffer.py b/lil_sshniffer.py index 6057cc7..7c71e7c 100644 --- a/lil_sshniffer.py +++ b/lil_sshniffer.py @@ -436,6 +436,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: @@ -501,7 +504,7 @@ def ssh_client_handler(sock,address,out_trans,logfile,kill_switch,hijack_flag): # defined with --hook => def inbound_hook(inbound_msg): if inhook: - inb = inhook(inb,) + inb = inhook(inb,userdata) if len(inb): #print "Post filter inb: %s" % repr(inb) @@ -535,7 +538,7 @@ def ssh_client_handler(sock,address,out_trans,logfile,kill_switch,hijack_flag): # defined with --hook => def inbound_hook(inbound_msg): if outhook: - outb = outhook(outb) + outb = outhook(outb,userdata) if not len(outb): continue From 414a65cc7bf16e8ba52e250946b3e137ee5efe05 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Thu, 2 Aug 2018 09:37:24 -0500 Subject: [PATCH 29/37] Fix for issue #4 --- lil_sshniffer.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/lil_sshniffer.py b/lil_sshniffer.py index 7c71e7c..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' @@ -317,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() @@ -327,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) @@ -349,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): @@ -383,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 = "" @@ -397,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) @@ -425,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) From 3164e087d1ffc6dc0186fcba5b3a4d108f22a89b Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Thu, 16 Aug 2018 09:15:31 -0500 Subject: [PATCH 30/37] Fix for finding replayer_template, update on help output --- scripts/api_generator.py | 5 +++-- scripts/replayer_template.py | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/api_generator.py b/scripts/api_generator.py index 0b2f0b2..521e22e 100644 --- a/scripts/api_generator.py +++ b/scripts/api_generator.py @@ -114,10 +114,11 @@ def main(dumpraw_dir,delim="",indexes=""): template = "" print "[4.4] Reduced down to %d unique requests"%request_count try: - with open('replayer_template.py',"rb") as f: + 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..." + print "[x.x] Could not find api_replayer.py template:%s..." % template_loc sys.exit() for i in range(0,100): diff --git a/scripts/replayer_template.py b/scripts/replayer_template.py index 1ebe914..17da55e 100644 --- a/scripts/replayer_template.py +++ b/scripts/replayer_template.py @@ -580,6 +580,9 @@ def print_help(): "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 ''' From fc178d3274913ebea071f27486a3316792cdcf27 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Thu, 16 Aug 2018 10:07:33 -0500 Subject: [PATCH 31/37] Fixed replayer_template cmp command --- scripts/replayer_template.py | 37 +++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/scripts/replayer_template.py b/scripts/replayer_template.py index 17da55e..c74a087 100644 --- a/scripts/replayer_template.py +++ b/scripts/replayer_template.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +import traceback import readline import socket import glob @@ -433,6 +434,7 @@ def cmp_requests(req1,req2): 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:])) @@ -446,26 +448,28 @@ def cmp_requests(req1,req2): 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 - - # no min 4 byte match - buf+=YELLOW - #print "break i(%d),j(%d),y(%d),x(%d)"%(i,j,y,x) - #print "stop match ind(%d) %s\nin\n%s"%(match,repr(shorter[i:]),repr(longer[j:])) - for q in range(0,match+1): - if ord(longer[j+q]) >= 0x30 and ord(longer[j+q]) <= 122: - buf+=longer[j+q] - else: - buf+="\\x%02x"%ord(longer[j+q]) - j+=match - j+=1 - i+=1 - if j >= len(shorter): - #print "i(%d),j(%d),lenshort(%d)"%(i,j,len(shorter)) - break buf += CYAN s = len(shorter) @@ -580,7 +584,6 @@ def print_help(): "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 From ee9ba3b6c276b04c61a1d7c0a975a989210411db Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Thu, 15 Nov 2018 13:20:23 -0600 Subject: [PATCH 32/37] Fix for Ipv6+UDP recvfrom --- decept.py | 8 +++++++- hooks/gzip_decompression.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 hooks/gzip_decompression.py diff --git a/decept.py b/decept.py index 1100acb..d5f47dd 100755 --- a/decept.py +++ b/decept.py @@ -277,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 = _ 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 + From e976b3c710799c0fabd041ea083a03f9bb2a8645 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Thu, 3 Oct 2019 14:30:03 -0500 Subject: [PATCH 33/37] Fixes for generate_certchain.sh --- scripts/generate_certchain.sh | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) 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!" - From 20ff9af29189cf699aa2b0aef0eb94ffda1ed296 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Thu, 3 Oct 2019 14:51:45 -0500 Subject: [PATCH 34/37] tfw no ones used a local ssl socket in years, lol --- decept.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/decept.py b/decept.py index d5f47dd..3ca9e1d 100755 --- a/decept.py +++ b/decept.py @@ -714,7 +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,ploc) + 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) From 59518b6d6a827ac0f2fdad96021258616d13c032 Mon Sep 17 00:00:00 2001 From: lilith Wyatt Date: Wed, 12 Aug 2020 10:30:58 -0400 Subject: [PATCH 35/37] minor fix for udp to tcp --- decept.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/decept.py b/decept.py index 3ca9e1d..62727b1 100755 --- a/decept.py +++ b/decept.py @@ -1045,7 +1045,7 @@ def proxy_loop(self,local_socket,rhost,rport,cli_addr="",plock=""): self.pkt_count+=1 ts = datetime.now().strftime("%H:%M:%S.%f") - if self.remote_end_type in ConnectionBased: + 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": From f538ce0dbc44c8462057d25e91c1f4686a1930a1 Mon Sep 17 00:00:00 2001 From: Maxwell Schmitt Date: Fri, 30 Sep 2022 14:51:55 -0400 Subject: [PATCH 36/37] python3 support --- decept.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/decept.py b/decept.py index 62727b1..297ba9a 100755 --- a/decept.py +++ b/decept.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 #------------------------------------------------------------------ # Author: Lilith Wyatt <(^,^)> #------------------------------------------------------------------ @@ -442,7 +442,7 @@ def server_loop(self): 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? @@ -619,13 +619,13 @@ def server_loop(self): self.hostconf_dict[name] = ip output("[!.!] Added %s | %s" % (name,ip),CYAN) except Exception as e: - print e + print(e) pass except IOError as e: # no such file pass except Exception as e: - print e + print(e) pass @@ -1282,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 @@ -1357,7 +1357,7 @@ 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() @@ -1394,13 +1394,13 @@ def arp_poisoner(self,config_file,interface,killswitch): 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 + print(e) pass except IOError as e: # no such file pass except Exception as e: - print e + print(e) pass @@ -1754,7 +1754,7 @@ def main(): pass except Exception as e: - print e + print(e) pass From 9c1fd68b804e90d582b0981f65837bc28a1cf171 Mon Sep 17 00:00:00 2001 From: thmorale Date: Fri, 20 Jan 2023 13:23:55 -0600 Subject: [PATCH 37/37] fixed mutiny import issue --- decept.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/decept.py b/decept.py index 297ba9a..2997256 100755 --- a/decept.py +++ b/decept.py @@ -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