Change TCP Payload with nfqueue/scapy - python

Hello I am using nfqueue and scapy and I my goal is to recieve packets at my NFQUEUE, change the payload and resend them.
I can change fields like the TTL without any kind of problem, but when it comes to change the payload, I am encoutering problems.
When I change the payload, I sniff the packet with wireshark and apparently I send the packet with the payload modified, but the server doesn't answer.
This is my code:
#!/usr/bin/env python
import nfqueue
from scapy.all import *
def callback(payload):
data = payload.get_data()
pkt = IP(data)
pkt[TCP].payload = str(pkt[TCP].payload).replace("ABC","GET")
pkt[IP].ttl = 40
print 'Data: '+ str(pkt[TCP].payload)
print 'TTL: ' + str(pkt[IP].ttl)
del pkt[IP].chksum
payload.set_verdict_modified(nfqueue.NF_ACCEPT, str(pkt), len(pkt))
def main():
q = nfqueue.queue()
q.open()
q.bind(socket.AF_INET)
q.set_callback(callback)
q.create_queue(0)
try:
q.try_run() # Main loop
except KeyboardInterrupt:
q.unbind(socket.AF_INET)
q.close()
main()
I have set this rule for outgoing traffic to port 80: iptables -I OUTPUT -s 192.168.1.10 -p tcp --dport 80 -j NFQUEUE
And, to test it, for example I open telnet to google port 80, do a GET / HTTP/1.1 and this is what I see:
TTL: 40
DATA: GET / HTTP/1.1
Now, if I do ABC / HTTP/1.1 I receive no answer! My telnet just get stuck.
I have also tried on HTTP websites browers to browse something, check on wireshark how my TTL is really changing to 40, then, browse the string "ABC" and my browser again get stuck.
I sniff the request changed to GET but I receive no answer.
Thank is kind of giving me a headache and I would really appreciate if someone with more experience could lead me to the right way. Thank you in advance.

I added the line for recalculate the TCP checksum, that was usefull.
That only works if I change payload I don't alter the lenght of it, otherwise, I would need to change the field length of the IP Header, and answering myself, and maybe other people that is looking for this answer, I achieve that just by doing:
payload_before = len(pkt[TCP].payload)
pkt[TCP].payload = str(pkt[TCP].payload).replace("Heading","Other string")
payload_after = len(pkt[TCP].payload)
payload_dif = payload_after - payload_before
pkt[IP].len = pkt[IP].len + payload_dif
I know that I have to change more fields, because sometimes, if you add enough payload for needing to fragment into a new packet, you have to change more fields.
Currently I don't know how to achieve this efficiently but little by little. Hope someone find my solution for altering the payload useful.

In the second case, you are tampering the TCP layer as well as the IP layer.
You're letting Scapy fix the IP checksum, but not the TCP one. Change del pkt[IP].chksum to del pkt[IP].chksum pkt[TCP].chksum in your code.

Related

Scapy proxy HTTP packet

I want to send an HTTP packet to port 31112, but I want to change the IP identification header to 0xabcd.
What I am doing is using iptables for, whatever packet with destination port 31112, redirect it to a queue:
iptables -A OUTPUT -p tcp --dport 31112-j NFQUEUE --queue-num 1
I have also enabled forwarding:
sysctl -w net.ipv4.ip_forward=1
My program is this one:
#!/usr/bin/env python
from netfilterqueue import NetfilterQueue
from scapy.all import *
def print_and_accept(pkt):
data = pkt.get_payload()
ip_h = IP(data)
print ('source: ' + ip_h[IP].src)
print ('destination: ' + ip_h[IP].dst)
print ('IP TTL: ' + str(ip_h[IP].ttl))
print (str (ip_h[TCP].payload))
ip_h[IP].ttl = 40
ip_h[IP].id = 0xabcd
#print (ip_h[IP].id)
del ip_h[IP].chksum
send(ip_h,verbose=0)
nfqueue = NetfilterQueue()
nfqueue.bind(1, print_and_accept)
try:
nfqueue.run()
except KeyboardInterrupt:
print ('\nProgram Ended')
And, when I send a curl to my destination:
curl http://serverexample.com:31112/
I get this in my program's output:
source: 192.168.206.128
destination: 35.182.181.240
IP TTL: 64
It is weird that I don't capture this:
print (str (ip_h[TCP].payload))
which I think it must be something like "GET / HTTP/1.1" and whatever headers might follow.
I want to know if someone can spot the issue.
Regards
You change the source of the TCP SYN, which means that the SYN+ACK from the server gets send to the IP address you gave at source - and thus does not arrive at your system. This means the TCP handshake will not be completed. But transfer of application data (i.e. the HTTP messages) will only be done after a completed TCP handshake.

when i made a 3 handshake with ubuntu in VMware return package R

#!/usr/bin/python
from scapy.all import *
def findWeb():
a = sr1(IP(dst="8.8.8.8")/UDP()/DNS(qd=DNSQR(qname="www.google.com")),verbose=0)
return a[DNSRR].rdata
def sendPacket(dst,src):
ip = IP(dst = dst)
SYN = TCP(sport=1500, dport=80, flags='S')
SYNACK = sr1(ip/SYN)
my_ack = SYNACK.seq + 1
ACK = TCP(sport=1050, dport=80, flags='A', ack=my_ack)
send(ip/ACK)
payload = "stuff"
PUSH = TCP(sport=1050, dport=80, flags='PA', seq=11, ack=my_ack)
send(ip/PUSH/payload)
http = sr1(ip/TCP()/'GET /index.html HTTP/1.0 \n\n',verbose=0)
print http.show()
src = '10.0.0.24'
dst = findWeb()
sendPacket(dst,src)
I'm trying to do HTTP packets with SCAPY
I am using UBUNTU on VMwaer
The problem is that every time I send messages I have RESET
How do we fix it?
Thanks
sniff package image
Few things I notice wrong.
1. You have your sequence number set statically (seq=11) which is wrong. Sequence numbers are always randomly generated and they must be used as per RFC793. So the sequence should be = SYNACK[TCP].ack
You set your source port as 1500 during SYN packet, but then you use it as 1050 (typo?)
You don't need extra payload/PUSH.
Also, have a look at these threads:
How to create HTTP GET request Scapy?
Python-Scapy or the like-How can I create an HTTP GET request at the packet level

How to create HTTP GET request Scapy?

I need to create HTTP GET request and save the data response.
I tried to use this:
syn = IP(dst=URL) / TCP(dport=80, flags='S')
syn_ack = sr1(syn)
getStr = 'GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n'
request = IP(dst='www.google.com') / TCP(dport=80, sport=syn_ack[TCP].dport,
seq=syn_ack[TCP].ack, ack=syn_ack[TCP].seq + 1, flags='A') / getStr
reply = sr1(request)
print reply.show()
But when I print reply I don't see any data response.
In addition, when I checked in 'Wireshark' I got SYN, SYN/ACK but I didn't get an ACK.
Image:
Edit:
I try to do that now:
# Import scapy
from scapy.all import *
# Print info header
print "[*] ACK-GET example -- Thijs 'Thice' Bosschert, 06-06-2011"
# Prepare GET statement
get='GET / HTTP/1.0\n\n'
# Set up target IP
ip=IP(dst="www.google.com")
# Generate random source port number
port=RandNum(1024,65535)
# Create SYN packet
SYN=ip/TCP(sport=port, dport=80, flags="S", seq=42)
# Send SYN and receive SYN,ACK
print "\n[*] Sending SYN packet"
SYNACK=sr1(SYN)
# Create ACK with GET request
ACK=ip/TCP(sport=SYNACK.dport, dport=80, flags="A", seq=SYNACK.ack, ack=SYNACK.seq + 1) / get
# SEND our ACK-GET request
print "\n[*] Sending ACK-GET packet"
reply,error=sr(ACK)
# print reply from server
print "\n[*] Reply from server:"
print reply.show()
print '\n[*] Done!'
but its print me in reply from server;
0000 IP / TCP 192.168.44.130:23181 > 216.58.208.164:http A / Raw ==>
IP / TCP 216.58.208.164:http > 192.168.44.130:23181 A / Padding None
And I need Line-based text data: text/html.
You are sending a RST segment in response to the SYN-ACK because your kernel has no knowledge of the SYN you sent via Scapy (see here). This could be solved with an iptable rule:
iptables -A OUTPUT -p tcp --tcp-flags RST RST -s <your ip> -j DROP
Because you are ending the connection with that RST segment, when you send your HTTP request, the endpoint answers with a RST too because connection is not established and so you are using show() on a RST segment with no data, that is why you do not see anything.
You are sending a SYN and correctly receiving a SYN_ACK. At this point, you should generate and send an ACK based on the SYN_ACK that you've received, and THEN finally transmit the HTTP GET request. It seems that you are somewhat confused about the TCP 3-way handshake mechanism. In short, you are not supposed to 'get' an ACK, you are supposed to generate and send this yourself.
After setting the rule in your iptables as has been suggested above, you could do the following :
from scapy.all import *
seq = 12345
sport = 1040
dport = 80
ip_packet = IP(dst='192.168.56.107')
syn_packet = TCP(sport=sport, dport=dport, flags='S', seq=seq)
packet = ip_packet/syn_packet
synack_response = sr1(packet)
next_seq = seq + 1
my_ack = synack_response.seq + 1
ack_packet = TCP(sport=sport, dport=dport, flags='A', seq=next_seq, ack=my_ack)
send(ip_packet/ack_packet)
payload_packet = TCP(sport=sport, dport=dport, flags='A', seq=next_seq, ack=my_ack)
payload = "GET / HTTP/1.0\r\nHOST: 192.168.56.107\r\n\r\n"
reply, error = sr(ip_packet/payload_packet/payload, multi=1, timeout=1)
for r in reply:
r[0].show2()
r[1].show2()
Hope this helps. Basically, the first response you get back does not really hold the HTTP response data. I tested the script against an INETSIM simulated HTTP server and in that case (at least) the first packet (after the 3-way TCP handshake) that the server responded with was a series of NULL (0x00) bytes. Hence using multi somehow did the stuff in my case.

PYTHON - UDP listening without knowing the server

using python2.X (for Linux (++) and Windows (+))
I'm trying to create a python client, to listen to an adress and a port.
The messages are sent by an app on a specific adress and a specific port, but I don't know how it is written. I'm just sure it is functionnal and it uses UDP protocole.
So, I've written this code to receive the messages :
import socket
#---socket creation
connexion = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#---Bind
try:
connexion.bind(('', 10015))
except socket.error:
print "connexion failed"
connexion.close()
sys.exit()
#---testing
while 1:
data, addr = connexion.recvfrom(1024)
print "messages : ", data
At this point, there is no error running it, but the code stops at this line :
data, addr = connexion.recvfrom(1024)
and nothing happens... I think I'm not connected and can't receive messages but I don't know why.
I tried to change the adresse by : '192.168.X.X', but it's the same.
If anyone could help me, that would be great. I'm not very comfortable why python...
PS : sorry for my english.
I found my problem. I changed the IP adress (in the documentation, it was 192.168. 00 8. 0 15 ; I tried whithout the zeros like a normal adress (192.168.8.15) and I received my messages
I changed the structure of my code too : (don't know if it has impact) I put my "try: [...] except:[...]" on the line
data, addr = connexion.recvfrom(1024)
instead of the line :
connexion.bind(('', 10015))
Thanks for helpping ;)
Your code is all well and fine. You can verify that it works using the following Python snippet:
import socket
connexion = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
connexion.sendto("sending data via udp", ("localhost", 10015))
If a message send with this snippet is not displayed, check your firewall. To investigate whether (or what) your other application is actually producing try using tcpdump or wireshark.

Socket.receive in python

I made a simple TCP fuzzer in Python. I need it to be able to receive some response and if I didn't get the response, break the loop. My code is this:
import socket
from time import sleep
import sys
ip = raw_input ("please insert host ip: ")
port = input ("please insert port to fuzz: ")
packet = raw_input ("what string would you like to fuzz with? : ")
multi = input ("in what jumps would you liike to multiply the string ? (10 = A*10) : ")
host = ip, port
s = socket.socket()
char = packet * multi
a = 1
try:
while a > 0:
s.connect((host))
s.send(packet)
sleep(1)
print 'fuzzing param %s' % (packet)
packet = char + packet
s.close()
except (Exception):
print "Connection lost for some reason"'
But when I run the program I get this error:
please insert host ip: 10.0.0.138
please insert port to fuzz: 80
what string would you like to fuzz with? : A
in what jumps would you liike to multiply the string ? (10 = A*10) : 2
fuzzing param A
Connection lost
which is weird because it just suppose to reconnect in an endless loop , (i know the server didn't crush)
The remote endpoint simply hung up, probably because the data you send doesn't match the format it expects.
You can either create a new connection every time the remote end hangs up, or send a data in the format that the remote end expects. For example, if the remote end is an HTTP server, you may want to send the request line first, and then the fuzzed part, like this:
GET / HTTP/1.0
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
When you fuzz testing (and in general) it is very important to handle errors. You should expect that something will get wrong when you are sending Gibberish to your server. So I suggest that you wrap the calls with try ... except ... finally: s.close() clause. And print debug messages to see when you are fail to send and start see why - You don't know how the server react to what you send, and you might just have killed the server after the first call...

Categories