python SIP log file processing - python

i have a sniff/log file for VoIP/SIP generated by python scapy in format
time | src | srcport | dst | dstport | payload
the sniff python script looks like this:
## Import Scapy module
from scapy.all import *
import sys
sys.stdout = open('data.txt', 'w')
pkts = sniff(filter="udp and port 5060 and not port 22", count=0,prn=lambda x:x.sprintf("%sent.time% | %IP.src% | %IP.sport% | %IP.dst% | %IP.dport% | Payload {Raw:%Raw.load%\n}"))
each packet in one line and each line can have different size depends on SIP message type (Register, 200 OK, Invite, Notify and so on...)
What i would like to get from the file are fields
time, src, srcport, dst, dstport and from Payload type (just right after Payload) of SIP message, From, To, Call-iD, Contact
and the whole payload and then prepare these to insert into MySQL database.
1st msg:
07:57:01.894990 | 192.168.1.10 | 5060 | 192.168.1.1 | 5060 | Payload 'INVITE sip:210#test-lab.org SIP/2.0\r\nVia:
SIP/2.0/UDP 192.168.1.10:5060;rport;branch=z9hG4bK-9cbb0ba8\r\nRoute: <sip:192.168.1.1:5060;lr>\r\nFrom: "test-311" <sip:311#test-lab.org>;tag=3d13bd6f\r\n
To: <sip:210#test-lab.org>\r\nCall-ID: 21b0e2c755973976d6d06702ca33b32f#10.193.40.249\r\nCSeq: 1 INVITE\r\n
Contact: "test-311" <sip:311#192.168.1.10:5060;transport=UDP>\r\nMax-Forwards: 70\r\n
Supported: 100rel,replaces\r\nAllow: ACK, BYE, CANCEL, INFO, INVITE, OPTIONS, NOTIFY, PRACK, REFER, UPDATE, MESSAGE\r\nContent-Type: application/sdp\r\nContent-Length: 276\r\n\r\nv=0\r\no=- 3506863524 285638052 IN IP4 192.168.1.10\r\ns=-\r\nc=IN IP4 192.168.1.10\r\nt=0 0\r\nm=audio 8000 RTP/AVP 8 0 18 101\r\nc=IN IP4 192.168.1.10\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:18 G729/8000\r\na=rtpmap:101 telephone-event/8000\r\na=fmtp:101 0-15\r\na=ptime:20\r\n'
2nd msg:
07:57:01.902618 | 192.168.1.1 | 5060 | 192.168.1.10 | 5060 | Payload 'SIP/2.0 100 Trying\r\nVia: SIP/2.0/UDP 192.168.1.10:5060;received=192.168.1.10;branch=z9hG4bK-9cbb0ba8;rport=5060\r\nFrom: "test-311" <sip:+38551311#test-lab.org>;tag=3d13bd6f\r\nTo: <sip:210#test-lab.org>\r\nCall-ID: 21b0e2c755973976d6d06702ca33b32f#192.168.1.10\r\nCSeq: 1 INVITE\r\n\r\n'
I have tried to read line by line and split but I do not know how to split and take data from payload part.
Any help is more then welcome.

Well, you can enter the data into mysql straight from this program too; it might very well be the easiest approach.
from scapy.all import *
import sys
# connect to mysql
connection = ...
def insert_into_mysql(packet):
# now you can use packet.src, packet.sport, packet.dst, packet.dport, and
# I believe packet['Raw'].load
connection.execute(...)
# to not print the packet
return None
# to print the packet
return x.sprintf("%sent.time% | %IP.src% | %IP.sport% | %IP.dst% | %IP.dport% | Payload {Raw:%Raw.load%\n}"
pkts = sniff(filter="udp and port 5060", count=0, store=0, prn=insert_into_mysql)
But if you need to use the existing log, I think you need to use:
for line in open('log.txt'):
sent_time, src, sport, dst, dport, payload = line.split(' | ', 6)
payload = payload.replace('Payload ', '')
# to get the unquoted payload, I'd guess (can't test SIP though)
from ast import literal_eval
payload = literal_eval(payload)

from scapy.all import *
import MySQLdb, string, sys
def insert_into_mysql(packet):
db = MySQLdb.connect("localhost","test","testpwd","my_db" )
cursor = db.cursor()
# now you can use packet.src, packet.sport, packet.dst, packet.dport,
# and packet['Raw'].load
add_sip = ("INSERT INTO py_sniff "
"(time, src_ip, src_port, dst_ip, dst_port, message) "
"VALUES (%s, %s, %s, %s, %s, %s)")
# data from sniff
add_sip = {
'time': packet.sprintf("%sent.time%"),
'src_ip': packet.sprintf("%IP.src%"),
'src_port': packet.sprintf("%IP.sport%"),
'dst_ip': packet.sprintf("%IP.dst%"),
'dst_port': packet.sprintf("%IP.dport%"),
'message': packet.sprintf("{Raw:%Raw.load%}"),
}
# to print the packet
# return packet.sprintf("%sent.time% | %IP.src% | %IP.sport% | %IP.dst% | %IP.dport% | Payload {Raw:%Raw.load%\n}"
cursor.execute(add_sip)
db.commit()
pkts = sniff(iface="eth0", filter="udp and port 5060", count=0, store=0, prn=insert_into_mysql)

Related

Scapy NTP request

I would like to get the ntp_monlist response of my NTP server.
Actually the packet is send but I don't receive anything.
Someone can tell me why ?
Code :
#!/usr/bin/env python
from scapy.all import *
import threading
import os
import sys
import socket
#Data to send
ntpip = "xxx.xx.xxx.xx"
packet = IP(dst=ntpip)/UDP(dport=123)/Raw(load=str("\x17\x00\x03\x2a")+ str("\x00")*4)
packet.show()
rep,non_rep = srp(packet)
rep.show()
Reponse :
###[ IP ]###
version = 4
ihl = None
tos = 0x0
len = None
id = 1
flags =
frag = 0
ttl = 64
proto = udp
chksum = None
src = xxx.xxx.xxx.xxx
dst = xxx.xxx.xxx.xxx
\options \
###[ UDP ]###
sport = domain
dport = ntp
len = None
chksum = None
###[ Raw ]###
load = '\x17\x00\x03*\x00\x00\x00\x00'
Begin emission:
Finished to send 1 packets.
......................................................................................................................
Received XXX packets, got 0 answers, remaining 1 packets
As you can see, I never receive any response.
You have multiple issues:
you're sending a layer 3 packet, so you need to use sr, not srp;
you need to specify a UDP source port;
your NTP payload is malformed.
There are two issues with your NTP payload. First of all, the first word of the NTP packet is defined as follows (RFC 5905):
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|LI | VN |Mode | Stratum | Poll | Precision |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
You want LI to be 0, VN to be 3, and Mode to be 3, so this gives a first octet of 0 + (3 * 8) + 3, or 0x1b.
Second, the minimum NTP packet is 12*4 octets.
So you need to say:
packet = IP(dst=ntpip)/UDP(dport=123,sport=50000)/("\x1b\x00\x00\x00"+"\x00"*11*4)
rep,non_rep = sr(packet)

Storing xbee source addr into database in Python

I have raspberry pi connected with xbees and a motion sensor, receiving data from the motion sensor connected to one of the xbee. It would then send the data to my raspberry pi. Is there any way I could manipulate or split the output such as the status being only True/False and the address =\x00\x13\xa2\x00#\xbbJ as I wanted to store the address into database if status=="True".
So if I do
if status[0]['dio-0'] == True :
print "Yes"
cur = con.cursor()
cur.execute("INSERT ignore into sensor(sensor_id, status) VALUES(%s,True)",(add[0]))
con.commit()
But the address stored into the database is weird characters instead of \x00\x13\xa2\x00#\xbbJ. or should I do any other ways?
This is the codes.
from xbee import XBee
import serial
PORT = '/dev/ttyUSBXBEE'
BAUD_RATE = 9600
# Open serial port
ser = serial.Serial(PORT, BAUD_RATE)
# Create API object
xbee = XBee(ser)
def decodeReceivedFrame(response):
add = str(response['source_addr_long'])
status = response['samples']
return [add, status]
# Continuously read and print packets
while True:
try:
response = xbee.wait_read_frame()
decodedData = decodeReceivedFrame(response)
status = decodeReceivedFrame(response)[1]
print status
print decodedData
add= decodedData[0]
except KeyboardInterrupt:
break
ser.close()
And this is the output.
[{'dio-0': True}]
['\x00\x13\xa2\x00#\xbbJ}', [{'dio-0': True}]]
[{'dio-0': False}]
['\x00\x13\xa2\x00#\xbbJ}', [{'dio-0': False}]]
In the database
+------------+--------+
| sensor_id | status |
+------------+--------+
| ¢ #»J} | 1 |
+------------+--------+
The variable sensor_id is an array of bytes, and it sounds like you want to store it in a human-readable format.
One way is to convert it to a formatted string before storing it in the database.
sensor_id = ':'.join("%02X" % ord(b) for b in add)
That statement loops through the bytes in the address (for b in add), formats each as a two-character hex string ("%02X" % ord(b)), and then joins each of those strings together with a colon in between (':'.join()).

How can I create a relay server using tulip/asyncio in Python?

I have the example echo server
import asyncio
class EchoServer(asyncio.Protocol):
def connection_made(self, transport):
peername = transport.get_extra_info('peername')
print('connection from {}'.format(peername))
self.transport = transport
def data_received(self, data):
self.transport.write(data)
# Client piece goes here
loop = asyncio.get_event_loop()
coro = loop.create_server(EchoServer, '127.0.0.1', 8888)
server = loop.run_until_complete(coro)
print('serving on {}'.format(server.sockets[0].getsockname()))
try:
loop.run_forever()
except KeyboardInterrupt:
print("exit")
finally:
server.close()
loop.close()
What I'm trying to do is add a client piece where I've commented that will connect to a new server and send the data off that-a-way. There's the echo client, but I need a process that looks like this:
+-----------+ +-----------+ +--------------+
| My Server | | My Client | | Other Server |
+-----------+ +-----------+ +--------------+
| | |
===>Get some data | |
| | |
Send data ---------->| |
| | |
| Send data ----------->|
| | |
| | Do Stuff
| | |
| | <-----------Send Data
| | |
| <--------- Send data |
| | |
<=== Send data | |
| | |
| | |
| | |
| | |
Obviously I can do this synchronously, but I'm trying to make the client -> other server bit async, and I'm not really figuring out how to use the asyncio methods to communicate between my server piece and a client piece.
What do I need to do here?
Here is a simple proxy which allow you to wget 127.0.0.1:8888 and get a html response from google:
import asyncio
class Client(asyncio.Protocol):
def connection_made(self, transport):
self.connected = True
# save the transport
self.transport = transport
def data_received(self, data):
# forward data to the server
self.server_transport.write(data)
def connection_lost(self, *args):
self.connected = False
class Server(asyncio.Protocol):
clients = {}
def connection_made(self, transport):
# save the transport
self.transport = transport
#asyncio.coroutine
def send_data(self, data):
# get a client by its peername
peername = self.transport.get_extra_info('peername')
client = self.clients.get(peername)
# create a client if peername is not known or the client disconnect
if client is None or not client.connected:
protocol, client = yield from loop.create_connection(
Client, 'google.com', 80)
client.server_transport = self.transport
self.clients[peername] = client
# forward data to the client
client.transport.write(data)
def data_received(self, data):
# use a task so this is executed async
asyncio.Task(self.send_data(data))
#asyncio.coroutine
def initialize(loop):
# use a coroutine to use yield from and get the async result of
# create_server
server = yield from loop.create_server(Server, '127.0.0.1', 8888)
loop = asyncio.get_event_loop()
# main task to initialize everything
asyncio.Task(initialize(loop))
# run
loop.run_forever()

Can't seem to get pexpect to print data from command

I'm steadily working on how to ssh and parse data on a device by running a command. I have had a few questions along the way of this endeavor and much help with the questions I have asked. I'm now working with pexpect and I'm not seeing much in documentation with what I am doing. Basically I need to ssh in, as I said, and then run a command that will print out data, then get that data to print to my console.
Here is my code:
import pexpect
import pxssh
import getpass
child = pexpect.spawn('ssh www.example.com')
password = getpass.getpass('password: ')
child.sendline ('foo bar')
data = (child.read_nonblocking(size=1000, timeout=100))
print data
OUTPUT:
password:
foo bar
In the foo bar command the first line of the print out is foo bar so I am wondering if this is trying to print this data but only printing the first line. I add the read_nonblocking(size=1000, timeout=100) trying to set the size to be greater and a timeout to let the data print.
UPDATE with PXSSH
I have also tried to use the pxssh samples to do this and get only the list of commands that foo can run. I need to get the print out of foo bar which is the list of configs. My guess is that you can't have commands with spaces? Here is the code I have tried:
import pxssh
import getpass
try:
s = pxssh.pxssh()
s.force_password = True
hostname = raw_input('hostname: ')
username = raw_input('username: ')
password = getpass.getpass('password: ')
s.login (hostname, username, password)
s.sendline ('foo bar') # run a command
s.prompt() # match the prompt
print s.before # print everything before the prompt.
s.logout()
except pxssh.ExceptionPxssh, e:
print "pxssh failed on login."
print str(e)
Which gives me this back in console:
pxssh failed on login.
could not set shell prompt
:
Session idle time out is disabled
SSH> unset PROMPT_COMMAND
Error - Command [unset PROMPT_COMMAND] not found.
foo [ bar | bart | ran | up
| cmd | bee | hvac | monkey
| selective | list | help ]
check[v,nv,beep] [ list | help ]
delete [ all | bee | neewb | stuff
| up | cmd | fooconfig | root
| app | list | hvac | monkey
| selective | <filename> | confirmed | list | help ]
exit [ help ]
get [ vcf | nvcf | snmpcf | help ] [<filename>]
verbose [ help ]
help [ <command> | help ]
up arrow - brings up old command lines
down arrow - brings up newer command lines
right arrow - moves cursor to the right
left arrow - moves cursor to the left
insert - inserts a space at the cursor
delete - deletes character at the cursor
SSH> PS1='[PEXPECT]\$ '
Error - Command [PS1='[PEXPECT]\$ '] not found.
foo [ bar | bart | ran | up
| cmd | bee | hvac | monkey
| selective | list | help ]
check[v,nv,beep] [ list | help ]
delete [ all | bee | neewb | stuff
| up | cmd | fooconfig | root
| app | list | hvac | monkey
| selective | <filename> | confirmed | list | help ]
exit [ help ]
get [ vcf | nvcf | snmpcf | help ] [<filename>]
verbose [ help ]
help [ <command> | help ]
up arrow - brings up old command lines
down arrow - brings up newer command lines
right arrow - moves cursor to the right
left arrow - moves cursor to the left
insert - inserts a space at the cursor
delete - deletes character at the cursor
And as I mentioned I'm just trying to get the console to print out the foo bar command configs. This is the code I had working with python-exscript before finding out I needed to work in older Python 2.4.
CODE THAT I HAD WORKING IN EXSCRIPT THAT I NEED PEXPECT TO DO
account = read_login()
conn = SSH2()
conn.connect('example.com')
conn.login(account)
conn.execute('foo bar')
data = conn.response
conn.send('exit\r')
conn.close()
print data
Any help on how to get this code to work is greatly appreciated! Thanks!
Figured out the issue. I was missing a s.prompt()
try:
s = pxssh.pxssh(timeout=60, maxread=2000000)
s.force_password = True
hostname = raw_imput('hostname: ')
username = raw_input('password: ')
password = getpass.getpass('password: ')
s.PROMPT= 'SSH> '
s.login (hostname, username, password, auto_prompt_reset=False)
s.prompt()
s.sendline('foo bar')
s.prompt()
data = s.before
print data
s.logout()
except pxssh.ExceptionPxssh, e:
print "pxssh failed on login."
print str(e)

writing an ethernet bridge in python with scapy

I'd like to make something like this:
10.1.1.0/24 10.1.2.0/24
+------------+ +------------+ +------------+
| | | | | |
| | | | | |
| A d +-------+ e B f +-------+ g C |
| | | | | |
| | | | | |
+------------+ +------------+ +------------+
d e f g
10.1.1.1 10.1.1.2 10.1.2.1 10.1.2.2
So that Acan send packets to C through B.
I attempted to build this thing by running a scapy program on B that would sniff ports e and f, and in each case modify the destination IP and MAC address in the packet and then send it along through the other interface. Something like:
my_macs = [get_if_hwaddr(i) for i in get_if_list()]
pktcnt = 0
dest_mac_address = discover_mac_for_ip(dest_ip) #
output_mac = get_if_hwaddr(output_interface)
def process_packet(pkt):
# ignore packets that were sent from one of our own interfaces
if pkt[Ether].src in my_macs:
return
pktcnt += 1
p = pkt.copy()
# if this packet has an IP layer, change the dst field
# to our final destination
if IP in p:
p[IP].dst = dest_ip
# if this packet has an ethernet layer, change the dst field
# to our final destination. We have to worry about this since
# we're using sendp (rather than send) to send the packet. We
# also don't fiddle with it if it's a broadcast address.
if Ether in p \
and p[Ether].dst != 'ff:ff:ff:ff:ff:ff':
p[Ether].dst = dest_mac_address
p[Ether].src = output_mac
# use sendp to avoid ARP'ing and stuff
sendp(p, iface=output_interface)
sniff(iface=input_interface, prn=process_packet)
However, when I run this thing (full source here) all sorts of crazy things start to happen... Some of the packets get through, and I even get some responses (testing with ping) but there's some type of feedback loop that's causing a bunch of duplicate packets to get sent...
Any ideas what's going on here? Is it crazy to try to do this?
I'm kind of suspicious that the feedback loops are being caused by the fact that B is doing some processing of its own on the packets... Is there any way to prevent the OS from processing a packet after I've sniffed it?
IP packets bridging using scapy:
first make sure you have ip forwarding disabled otherwise duplicate packets will be noticed:
echo "0" > /proc/sys/net/ipv4/ip_forward <br>
second run the following python/scapy script:
!/usr/bin/python2
from optparse import OptionParser
from scapy.all import *
from threading import Thread
from struct import pack, unpack
from time import sleep
def sp_byte(val):
return pack("<B", val)
def su_nint(str):
return unpack(">I", str)[0]
def ipn2num(ipn):
"""ipn(etwork) is BE dotted string ip address
"""
if ipn.count(".") != 3:
print("ipn2num warning: string < %s > is not proper dotted IP address" % ipn)
return su_nint( "".join([sp_byte(int(p)) for p in ipn.strip().split(".")]))
def get_route_if(iface):
try:
return [route for route in conf.route.routes if route[3] == iface and route[2] == "0.0.0.0"][0]
except IndexError:
print("Interface '%s' has no ip address configured or link is down?" % (iface));
return None;
class PacketCapture(Thread):
def __init__(self, net, nm, recv_iface, send_iface):
Thread.__init__(self)
self.net = net
self.netmask = nm
self.recv_iface = recv_iface
self.send_iface = send_iface
self.recv_mac = get_if_hwaddr(recv_iface)
self.send_mac = get_if_hwaddr(send_iface)
self.filter = "ether dst %s and ip" % self.recv_mac
self.arp_cache = []
self.name = "PacketCapture(%s on %s)" % (self.name, self.recv_iface)
self.fw_count = 0
def run(self):
print("%s: waiting packets (%s) on interface %s" % (self.name, self.filter, self.recv_iface))
sniff(count = 0, prn = self.process, store = 0, filter = self.filter, iface = self.recv_iface)
def process(self, pkt):
# only bridge IP packets
if pkt.haslayer(Ether) and pkt.haslayer(IP):
dst_n = ipn2num(pkt[IP].dst)
if dst_n & self.netmask != self.net:
# don't forward if the destination ip address
# doesn't match the destination network address
return
# update layer 2 addresses
rmac = self.get_remote_mac(pkt[IP].dst)
if rmac == None:
print("%s: packet not forwarded %s %s -) %s %s" % (self.name, pkt[Ether].src, pkt[IP].src, pkt[Ether].dst, pkt[IP].dst))
return
pkt[Ether].src = self.send_mac
pkt[Ether].dst = rmac
#print("%s: forwarding %s %s -> %s %s" % (self.name, pkt[Ether].src, pkt[IP].src, pkt[Ether].dst, pkt[IP].dst))
sendp(pkt, iface = self.send_iface)
self.fw_count += 1
def get_remote_mac(self, ip):
mac = ""
for m in self.arp_cache:
if m["ip"] == ip and m["mac"]:
return m["mac"]
mac = getmacbyip(ip)
if mac == None:
print("%s: Could not resolve mac address for destination ip address %s" % (self.name, ip))
else:
self.arp_cache.append({"ip": ip, "mac": mac})
return mac
def stop(self):
Thread._Thread__stop(self)
print("%s stopped" % self.name)
if __name__ == "__main__":
parser = OptionParser(description = "Bridge packets", prog = "brscapy", usage = "Usage: brscapy -l <intf> (--left= <intf>) -r <inft> (--right=<intf>)")
parser.add_option("-l", "--left", action = "store", dest = "left", default = None, choices = get_if_list(), help = "Left side network interface of the bridge")
parser.add_option("-r", "--right", action = "store", dest = "right", default = None, choices = get_if_list(), help = "Right side network interface of the bridge")
args, opts = parser.parse_args()
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
lif = args.left
rif = args.right
lroute = get_route_if(lif)
rroute = get_route_if(rif)
if (lroute == None or rroute == None):
print("Invalid ip addressing on given interfaces");
exit(1)
if (len(lroute) != 5 or len(rroute) != 5):
print("Invalid scapy routes")
exit(1)
conf.verb = 0
lthread = PacketCapture(rroute[0], rroute[1], lif, rif)
rthread = PacketCapture(lroute[0], lroute[1], rif, lif)
lthread.start()
rthread.start()
try:
while True:
sys.stdout.write("FORWARD count: [%s -> %s %d] [%s <- %s %d]\r" % (lif, rif, lthread.fw_count, lif, rif, rthread.fw_count))
sys.stdout.flush()
sleep(0.1)
except KeyboardInterrupt:
pass
lthread.stop()
rthread.stop()
lthread.join()
rthread.join()
On my pc:
# ./brscapy.py --help
Usage: brscapy -l <intf> (--left= <intf>) -r <inft> (--right=<intf>)
Bridge packets
Options:
-h, --help show this help message and exit
-l LEFT, --left=LEFT Left side network interface of the bridge
-r RIGHT, --right=RIGHT
Right side network interface of the bridge
# ./brscapy.py -l e0 -r e2
PacketCapture(Thread-1 on e0): waiting packets (ether dst 00:16:41:ea:ff:dc and ip) on interface e0
PacketCapture(Thread-2 on e2): waiting packets (ether dst 00:0d:88:cc:ed:15 and ip) on interface e2
FORWARD count: [e0 -> e2 5] [e0 <- e2 5]
It is kinda crazy to do this, but it's not a bad way to spend your time. You'll learn a bunch of interesting stuff. However you might want to think about hooking the packets a little lower - I don't think scapy is capable of actually intercepting packets - all libpcap does is set you promisc and let you see everything, so you and the kernel are both getting the same stuff. If you're turning around and resending it, thats likely the cause of your packet storm.
However, you could set up some creative firewall rules that partition each interface off from each-other and hand the packets around that way, or use something like divert sockets to actually thieve the packets away from the kernel so you can have your way with them.

Categories