pythonhttpscapysniffer

HTTP GET packet sniffer in Scapy


I am trying to code a simple sniffer in Scapy, which only prints HTTP packets with GET method only. Here's the code:

#!/usr/bin/python
from scapy.all import *

def http_header(packet):
        http_packet=str(packet)
        if http_packet.find('GET'):
                print GET_print(packet)
        print packet
def GET_print(packet1):
        print "***************************************GET PACKET****************************************************"
        print packet1

        print "*****************************************************************************************************"


sniff(iface='eth0',prn=http_header)

Here is the output:

*****************************************************************************************************
None
T��Г
     )�pEa��@@���h��#/��t
                             �}LGku���U
oTE��I(��Ͻ�9qi���S��?��
                          XuW�F=���-�k=X:�
***************************************GET PACKET****************************************************
T��Г
     )�pE���@@���h��#/��t
                               ʪLGku����
oTE��I�K��AH�*�e��>�v1#D�(mG5T�o�?��8��喷╭���Ի�"�KT^�'�mB���]�����k>
                                                                                �_x�X�����8V?�Ǽw/�Z�=���N�À��\r�����)+}���l�c�9��j;���h��5�T�9Hۖ/O��)��P
         މY�qf爂�%�_`��6x��5D�I3���O�
t��tpI#�����$IC��E��
                     �G�
J��α���=�]��vһ���b5^|P��DK�)uq�2��ț�w�
                    tB������y=���n�i�r�.D6�kI�a���6iC���c'��0dPqED�4����[�[��hGh̃��~|Y/�>`\6yP  Dq١?T��Mѵ���f�;���Җ��Ǵ  gY���di�_x�8|
eo�p�xW9��=���vŅYe�}�T�ۨɑy�^�C
-�_(�<�{����}�������r
$��J�k-�9����}�Ϡf�27��QKԛ�`�GY�8��Sh���Y@8�E9�Rϔ�&a�/vkф��6�DF`�/9�I�d( ��-��[A
                                                                                     ��)pP��y\ռj]���8�_���vf�b����I7�������+�P<_`
*****************************************************************************************************

What I am expecting is:

GET / HTTP/1.1
    Host: google.com
    User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20140722 Firefox/24.0 Iceweasel/24.7.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: en-US,en;q=0.5
    Accept-Encoding: gzip, deflate
    Cookie: PREF=ID=758a20b5fbd4eac9:U=2b2dedf6c84b001f:FF=0:TM=1412150291:LM=1415430021:S=Q-QemmrLqsSsEA9i; NID=67=mRdkPVhtImrOTLi5I1e5JM22J7g26jAcdiDEjj9C5q0H5jj0DWRX27hCM7gLJBeiowW-8omSv-1ycH595SW2InWX2n1JMMNh6b6ZrRsZ9zOCC2a-vstOQnBDSJu6K9LO
    Connection: keep-alive

What can I do to get my expected output?


Solution

  • You need to use the sprintf function of the packet instead of printing the packet itself. You also need to split the string returned from it and join it back together with newline characters, otherwise it spits it out all on one line:

    #!/usr/bin/python
    from scapy.all import *
    
    def http_header(packet):
            http_packet=str(packet)
            if http_packet.find('GET'):
                    return GET_print(packet)
    
    def GET_print(packet1):
        ret = "***************************************GET PACKET****************************************************\n"
        ret += "\n".join(packet1.sprintf("{Raw:%Raw.load%}\n").split(r"\r\n"))
        ret += "*****************************************************************************************************\n"
        return ret
    
    sniff(iface='eth0', prn=http_header, filter="tcp port 80")
    

    I also added a filter for TCP port 80, but this could be removed if you need to.

    Example output:

    ***************************************GET PACKET****************************************************
    'GET /projects/scapy/doc/usage.html HTTP/1.1
    Host: www.secdev.org
    Connection: keep-alive
    Cache-Control: max-age=0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.65 Safari/537.36
    Referer: https://www.google.co.uk/
    Accept-Encoding: gzip, deflate, sdch
    Accept-Language: en-GB,en;q=0.8,en-US;q=0.6
    If-None-Match: "28c84-48498d5654df67640-gzip"
    If-Modified-Since: Mon, 19 Apr 2010 15:44:17 GMT
    
    '
    *****************************************************************************************************
    

    Pierre points out that you can do away with the http_header function entirely by using the lfilter argument to sniff(). I took the liberty of making the code a little more succinct at the same time:

    #!/usr/bin/python
    from scapy.all import *
    
    stars = lambda n: "*" * n
    
    def GET_print(packet):
        return "\n".join((
            stars(40) + "GET PACKET" + stars(40),
            "\n".join(packet.sprintf("{Raw:%Raw.load%}").split(r"\r\n")),
            stars(90)))
    
    sniff(
        iface='eth0',
        prn=GET_print,
        lfilter=lambda p: "GET" in str(p),
        filter="tcp port 80")