Python3 Scapy custom payload from string

Viewed 3112

Intro

I am using python3 and scapy to modify and resend a captured packet.

The issue

When I send the modified payload in the Raw section the \r and \n parts of the message are not interpreted as special characters. Instead they are included as strings as shown in the cmd and wireshark screenshots below.

Current Payload

Cmd wrong payload screenshot Wireshark wrong payload screenshot

Expected Payload

This is a correct packet format captured on the network. Wireshark correct payload description

The packet displayed in wireshark is the following:

SIP/2.0 486 Busy Here Via: SIP/2.0/UDP 192.168.1.5:5060;branch=z9hG4bK226016822;received=192.168.1.5;rport=5060 From: sip:301@192.168.1.2;tag=2032604445 To: sip:300@192.168.1.2;tag=as1b0290be Call-ID: 338695025 CSeq: 21 INVITE Server: Asterisk PBX 16.10.0 Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH, MESSAGE Supported: replaces, timer X-Asterisk-HangupCause: Call Rejected X-Asterisk-HangupCauseCode: 21 Content-Length: 0

The code

This is the code so far:

from scapy.all import sniff, Ether, IP, UDP, sendp, ICMP, rdpcap,Raw
import scapy.fields
import re
import codecs
import argparse

def traffic_parser(packet):
    BUSY_1 = 'SIP/2.0 486 Busy Here'
    BUSY_2 = 'X-Asterisk-HangupCause: Call Rejected\\\\r\\\\nX-Asterisk-HangupCauseCode: 21'

    payload = packet[3].command()
    print(bytes(payload))

    header=re.findall("Ringing", payload)
    if header:
        
        eth_attributes={}
        eth_attributes['dst']=packet[0].dst
        eth_attributes['src']=packet[0].src
        eth_attributes['type']=packet[0].type
        
        eth = Ether_layer(eth_attributes)


        udp_attributes={}
        udp_attributes['sport']=packet[2].sport
        udp_attributes['dport']=packet[2].dport
        udp_attributes['len']=444
    
        udp = UDP_layer(udp_attributes)

        # Implement packet modification
        payload = payload.replace("SIP/2.0 180 Ringing", BUSY_1, 1)
        payload = re.sub("Contact\:.*>", BUSY_2, payload,1)
        payload = payload.replace("Raw(load=b\'", '', 1)
        payload = re.sub("\'\)$", '', payload, 1)

        for incr in range(1,5):
            ip_attributes={}
            ip_attributes['version']=packet[1].version
            ip_attributes['tos']=packet[1].tos
            ip_attributes['len']=464 
            ip_attributes['id']=0 #Zero is handled by scapy on send by default
            ip_attributes['flags']=packet[1].flags
            ip_attributes['frag']=packet[1].frag
            ip_attributes['ttl']=packet[1].ttl
            ip_attributes['proto']=packet[1].proto
            ip_attributes['src']=packet[1].src
            ip_attributes['dst']=packet[1].dst

            ip = IP_layer(ip_attributes)

            sendp(eth/ip/udp/Raw(load=payload))

            print(payload)
            print(Raw(load=payload))
            print("\n")

def Ether_layer(attributes):
    layer2=Ether()
    layer2.dst=attributes['dst']
    layer2.src=attributes['src']
    layer2.type=attributes['type']

    return layer2


def IP_layer(attributes):
    layer3=IP()
    layer3.version=attributes['version']
    layer3.tos=attributes['tos']
    layer3.len=attributes['len']
    layer3.id=attributes['id']
    layer3.flags=attributes['flags']
    layer3.frag=attributes['frag']
    layer3.ttl=attributes['ttl']
    layer3.proto=attributes['proto']
    layer3.src=attributes['src']
    layer3.dst=attributes['dst']

    return layer3


def UDP_layer(attributes):
    layer4=UDP()
    layer4.sport=attributes['sport']
    layer4.dport=attributes['dport']
    layer4.len=attributes['len']

    return layer4


parser = argparse.ArgumentParser(description="rtp replay script. Arguments: -i <interface> -f <sniff filter> -o <sniff outputfile> Interface defaults to 'eth0' and filter defaults to 'udp and port 5060'")
parser.add_argument('-i', "--interface", default="eth0", help="interface to use for sniffing")
parser.add_argument('-f', '--filter', default="udp and port 5060", help="filter to be used in scapy")
parser.add_argument('-o', "--outfile", help="output file (optional)")
parser.add_argument('-t', "--testfile", help="parse test file (optional)")
args=parser.parse_args()

if __name__ == '__main__':

    if args.testfile:
        packets = rdpcap(args.testfile)
        for packet in packets:
            traffic_parser(packet)
    else:
        sniff(iface=args.interface, prn=traffic_parser, filter="udp and port 5060", store=0)

Q

How can I attach the payload in the desired form?

Edit:

This pcap file can be used for testing. pcap_file

Ιndicative script execution: sudo python byespam.py -t <filename.pcapng>

2 Answers

To be able to use the payload as a string you need to convert it to one and also ensure that you have the correct encoding.

In your case you should:

  1. You should only get only the load part of the Raw from scapy.packet.Packet.
  2. Encode the load part as UTF-8 so that Python interprets correctly the special characters.

Here is some working code for your case:

payload = packet[UDP].payload.load *
payload = payload.decode("utf-8")

then you can print the payload and it will get interpreted "correctly".

* I used UDP because in your script and the pcapng file you have only UDP packets. If there were TCP packets you should use payload = packet[TCP].payload.load

\r and \n are interpreted as literal strings because you've escaped them

Here's your string:

>>> BUSY_2 = 'X-Asterisk-HangupCause: Call Rejected\\\\r\\\\nX-Asterisk-HangupCauseCode: 21'
>>> print(BUSY_2)
X-Asterisk-HangupCause: Call Rejected\\r\\nX-Asterisk-HangupCauseCode: 21

Here's the same string, but without the escape:

>>> BUSY_2 = 'X-Asterisk-HangupCause: Call Rejected\r\nX-Asterisk-HangupCauseCode: 21'
>>> print(BUSY_2)
X-Asterisk-HangupCause: Call Rejected
X-Asterisk-HangupCauseCode: 21
Related