#!/usr/bin/env python2
#
# NomNom Agent v0.1 PreAlpha
#
# Author(s): Pasv (pasvninja [at] gmail[dot]com)
# Licensed under: GPLv3
#
# TODO: import ConfigParser ? or will this project not need
#
from scapy import *
import sys
import os
import string
import socket
import select
import threading
import sqlite3
import re
import time
import getopt
DEBUG=True
DEBUGFILE=False
DEBUGFH=False
SQL_INJECTION_TEST=False
LOGDIR="/tmp/nomnom.log"
SQLDB="/tmp/nomnom.db"
id=0
# Constants:
T_IDCLIENT, T_IDSERVER, T_GET, T_POST, T_TITLE, T_COOKIE, T_SETCOOKIE, T_CSERVER, T_CCLIENT, T_SSERVER, T_SCLIENT, T_COMMAND = range(12)
def debug(msg):
msg = time.ctime() + ":*:" + msg
if DEBUG:
print msg
if DEBUGFILE:
os.write(DEBUGFH.fileno(), msg+"\n")
return
# end of debug()
class loggingThread(threading.Thread):
def __init__(self,readpipe):
self.readpipe=readpipe
threading.Thread.__init__(self)
def run(self):
debug("[+] started logging...")
# this might be CPU expensive, switch to select method later
fileexists=os.path.exists(SQLDB)
self.sqlfh=sqlite3.connect(SQLDB)
self.cur=self.sqlfh # self.sqlfh.cursor()
if(not fileexists):
self.init_sql()
while True: # cant be blocking, thus select
(r,w,x)=select.select([self.readpipe],[],[])
if(r):
try:
sqlquery=os.read(self.readpipe, 8000)
debug("READ THIS FROM PIPE:" + sqlquery)
(id,datatype,tolog) = re.split(":", sqlquery, 2)
#debug("sending: %d; type: %d; data: %s" % (id,datatype,tolog))
if(self.log(id,tolog,datatype)):
debug("[+] SQL query successful: " + sqlquery)
except:
#debug("[-] found an exception in reading from pipe, aborting")
continue
def check_for_injection(self, sqlquery):
# TODO, but for now..
return False
def log(self, id, query, infotype):
debug ("logging query:" + query)
# TODO: check these queries against the sqlite3 cli client
if(check_for_injection(query,id,infotype)):
debug("[*!*] SQL injection detected, aborting: " + query)
# return False
elif(infotype == T_IDCLIENT):
debug("Doing an T_IDCLIENT")
self.cur.execute("INSERT INTO PacketsFromClient VALUES(?,null,null,null,null,null);", (query,id))
elif(infotype == T_IDSERVER): # creates the initial entry
self.cur.execute("INSERT INTO PacketsFromServer VALUES(?,null,null,null,null);", (query,id))
elif(infotype == T_TITLE):
self.cur.execute("UPDATE PacketsFromServer SET Title=? WHERE id=?", (query,id))
elif(infotype == T_COOKIE):
self.cur.execute("UPDATE PacketsFromClient SET Cookie=? WHERE id=?", (query,id))
elif(infotype == T_CSERVER): # CCLIENT stands for Client's CLIENT (hackish: clean!)
self.cur.execute("UPDATE PacketsFromClient SET Server=? WHERE id=?", (query,id))
elif(infotype == T_CCLIENT):
self.cur.execute("UPDATE PacketsFromClient SET Client=? WHERE id=?", (query,id))
# ushiro no do: (strings are not escaped)
elif(infotype == T_COMMAND and SQL_INJECTION_TEST == True):
self.cur.execute("UPDATE PacketsFromClient SET Cmd=%s WHERE id=%s" % (query,id))
else:
debug("[-] unrecognized infotype")
return False
self.sqlfh.commit()
return True
def log2server(self, query):
pass
def init_sql(self):
debug("[+] Initializing database")
self.cur.execute("""CREATE TABLE PacketsFromServer (id INTEGER PRIMARY KEY, Server VARCHAR(50), Client VARCHAR(50), SetCookie VARCHAR(200), Title VARCHAR(100));""")
self.cur.execute("""CREATE TABLE PacketsFromClient (id INTEGER PRIMARY KEY, Server VARCHAR(50), Client VARCHAR(50), Cookie VARCHAR(200), PostQuery VARCHAR(200), GetQuery VARCHAR(200));""")
self.sqlfh.commit()
# End loggingthread() class
# This thread will communicate with the proxy and accept commands
# is this method acceptable????
# TODO: add error/exception checking to the socket stuff
class proxycontrolThread(threading.Thread):
def __init__(self,host,port):
self.host=host
self.port=port
self.soc=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.to_write = False
threading.Thread.__init__(self)
def run(self):
if(not self.host):
pass
else:
self.host=self.host
self.soc.connect((self.host, self.port))
# If cant connect go sleep() and then come back try it again
self.soc.setblocking(0) # is this a good idea? hmmm
while True:
(r,w,x) = select.select([self.soc],[self.soc],[self.soc])
if(r):
self.read_from_proxy(self.soc)
if(w and self.to_write):
self.write_to_proxy(self.soc)
#if(x): ???
# check if socket closed here, do some cleanups
def write_to_proxy(soc):
pass
def read_from_proxy(soc):
# data= read(.... yadda yadda
# parsing it out: (make a better splitter too later..?)
sqlquery=re.split(":*:", data, 3) # maybe not 3 tho....
def sql_query(Want, From, Variable, Value):
return "SELECT %s FROM %s WHERE %s=%s" % (Want, From , Variable, Value)
# End proxycontrolThread() class
# TODO:
# -check and make sure all the ID shit is set well init functions and finish up logging
# - move on to making the connection for the NomNom Proxy (pipe commands to log/and insert auditing
# - maybe even authentication later between Agent-Proxy : PSK? encryption(AES?)?
class sniffer():
def __init__(self,device):
self.device=device
self.id_base = 0
def run(self):
if(not self.device):
sniff(prn=self.handle_packet,filter="tcp port 80",store=0);
else:
sniff(iface=self.device, prn=self.handle_packet,filter="tcp port 80",store=0);
def make_id(self,pkt):
#todo int() these
ip_id=pkt.sprintf("%IP.id%")
tcp_seq=pkt.sprintf("%TCP.seq%")
tcp_ack=pkt.sprintf("%TCP.ack%")
# think of a better one soon...
# return((tcp_seq & tcp_id) - %TCP.ack%)
# or even incremental mode would just work...
self.id_base += 1
return(self.id_base)
def handle_title(self,id,pkt,offset):
debug("[] handling a <title> tag at " + str(offset))
pktcopy=str(pkt)[offset:]
end=pktcopy.find("</title>")
title=pktcopy[7:end]
self.pipe2log(id, T_TITLE, title)
def handle_setcookie(self,id,pkt,offset):
debug("[] handling a Set-Cookie: header at " + str(offset))
pktcopy=str(pkt)[offset:]
cookie=""
while pktcopy.find("Set-Cookie:") >= 0:
end=pktcopy.find("\r\n")
cookie+=pktcopy[12:end]
debug("[] set-cookie len: " + str(len(cookie)))
debug("[] set-cookie:" + cookie)
pktcopy=pktcopy[end:]
self.pipe2log(id,T_SETCOOKIE,cookie)
def handle_get(self,id,pkt,offset):
debug("[] handling a GET request at " + str(offset))
end=str(pkt)[offset:].find("\r\n")
getquery=str(pkt)[offset+4:offset+end]
self.pipe2log(id, T_GET, getquery)
def handle_post(self,id,pkt,offset):
debug ("[] handling a POST request at " + str(offset))
end=str(pkt)[offset:].find("\r\n")
postquery=str(pkt)[offset+5:offset+end]
self.pipe2log(id, T_POST, postquery)
def handle_cookie(self,id,pkt,offset):
debug("[] handling a Cookie: header at " + str(offset))
pktcopy=str(pkt)[offset:]
cookie=""
while pktcopy.find("Cookie:") >= 0:
end=pktcopy.find("\r\n")
cookie+=pktcopy[8:end]
debug("[] cookie len: " + str(len(cookie)))
debug("[] cookie:" + cookie)
pktcopy=pktcopy[end:]
self.pipe2log(id, T_COOKIE, cookie)
# This is from the server
def handle_from_http(self,id,pkt):
#debug("[] handling packet from HTTP server..")
self.pipe2log(id,T_IDSERVER,id)
offset=str(pkt).find("<title>")
if (offset >= 0): # found title
self.handle_title(id,pkt,offset)
offset=str(pkt).find("Set-Cookie:")
if (offset >= 0):
self.handle_setcookie(id,pkt,offset)
# This is from the browser
def handle_to_http(self,id,pkt):
#debug("[] handling packet from HTTP client..")
self.pipe2log(id,T_IDCLIENT,id)
self.pipe2log(id,T_CCLIENT,pkt.sprintf("%IP.src%"))
self.pipe2log(id,T_CSERVER,pkt.sprintf("%IP.dst%"))
offset=-1
offset=str(pkt).find("GET ")
#if(offset != -1):
# self.handle_get(id,pkt,offset)
offset=-1
offset=str(pkt).find("POST ")
if(offset != -1):
self.handle_post(id,pkt,offset)
offset=str(pkt).find("Cookie: ")
if(offset != -1):
self.handle_cookie(id,pkt,offset)
# might be diff for win32
def pipe2log(self,packetid, datatype, query):
(r,w,x)=select.select([],[writepipe],[])
if(w):
try:
os.write(writepipe, "%d:%d:%s" % (packetid,datatype,query))
except:
debug("couldnt write to pipe!")
else: # wtf
pass
def handle_packet(self,pkt):
id=self.make_id(pkt)
if(SQL_INJECTION_TEST == True):
if(str(pkt).find("SQL_INJECTION_TEST") >= 0):
self.pipe2log(id, pkt.payload(), T_SQL) #change
if(pkt.sprintf("%TCP.dport%") == "www"):
self.handle_to_http(id,pkt)
elif(pkt.sprintf("%TCP.sport%") == "www"):
self.handle_from_http(id,pkt)
else:
debug("neither protocol: source: %s dest: %s " % (pkt.sprintf("%TCP.sport%"),pkt.sprintf("%TCP.dport%") ))
# end sniffer class
def usage():
print " NomNomAgent.py Usage:\n"
print "./NomNomAgent.py [-p port] [-t host] [-h] [-d] [-l log]"
print "-p port : NomNom proxy control port"
print "-t host : NomNom proxy host"
print "-l log : debug to file log"
print "-i device: device to sniff on"
print "-d debug : turn on debug output"
print "-h help (this output)"
if __name__ == "__main__":
proxyhost=""
debug("[+] Initiating..")
device=False
try: opts, args = getopt.getopt(sys.argv[1:], "l:t:hdp:", [])
except getopt.GetoptError, e:
usage()
exit(-1)
for opt, value in opts:
if(opt == "-d"):
DEBUG=True
if(opt == "-p"):
port=int(value)
if(opt == "-h"):
usage()
exit(1)
if(opt == "-t"):
proxyhost=str(value)
if(opt == "-l"):
DEBUGFILE=str(value)
if(opt == "-i"):
device=str(value)
if DEBUGFILE:
try:
DEBUGFH = open(DEBUGFILE, "w")
except IOError:
DEBUGFILE=0
if(not proxyhost):
usage()
exit(1)
(readpipe,writepipe) = os.pipe();
#instantiate the main classes:
logthread=loggingThread(readpipe)
logthread.start()
proxythread=proxycontrolThread(proxyhost,port)
proxythread.start()
#debating whether or not to actually thread sniffer, or just leave it as main thread... hrm
snifferthread=sniffer(device)
snifferthread.run()
#end main