Closed Ibonok closed 6 years ago
Hi Ibonok, Thanks for using the project. With the first iptables rule that you show in your message, Polymorph should intercept the network packets that come out of the machine where Polymorph is running. Instead, with the second rule, you should intercept only the packets that enter the machine where Polymorph is running. This can be tested very easily with ICMP packets (using the ping utility) and the next simple precondition.
def precondition(packet):
print(packet['IP']['src'])
return packet
When you use the first rule, it will only show on the screen the address of the machine where Polymorph is installed, if you use the second rule, it will only show the address of the packets that enter the machine.
In order to know what exactly happens in your specific case, would you mind showing me the preconditions, executions and post-conditions that you are using?
Hi shramos,
yes that's right i think there is another problem.
intercept -ipt "iptables -I OUTPUT -j NFQUEUE --queue-num 1"
This is my preconditions script:
def new_prec(packet):
import socket, struct
try:
if packet['IP']['proto'] == 1:
print ('Pre Script')
hex_ip = packet['IP']['src']
addr_long = int(hex_ip,16)
hex(addr_long)
hex_ip = socket.inet_ntoa(struct.pack(">L", addr_long))
print (hex_ip)
return packet
except:
return None
this my execution script:
def new_EXEC(packet):
import binascii
print ('Exec Script')
text = 'Hi there'
bytes_text = bytes(text, 'utf-8')
packet['RAW']['load'] = binascii.hexlify(bytes_text).decode('utf-8')
return packet
and this my postcondition script:
def new_post(packet):
from scapy.all import IP
pkt = IP(packet.raw)
if pkt.haslayer('IP') and pkt.haslayer('ICMP'):
print ('POST Script')
del pkt['IP'].chksum
del pkt['IP'].len
del pkt['ICMP'].chksum
pkt.show2()
print (packet['RAW'])
packet.raw = bytes(pkt)
return packet
if i use:
intercept -ipt "iptables -I OUTPUT -j NFQUEUE --queue-num 1"
i get this output but the response packet is not reach the ping application: tcpdump shows me the right thing.
10:18:28.156540 IP 192.168.124.42 > 85.214.xxx.xxx: ICMP echo request, id 11292, seq 1, length 18
0x0000: 4500 0026 31e6 4000 4001 edc4 c0a8 7c2a E..&1.@.@.....|*
0x0010: 55d6 8883 0800 2bfb 2c1c 0001 4869 204d U.....+.,...Hi.t
0x0020: 6963 6861 656c here
10:18:28.190436 IP 85.214.xxx.xxx > 192.168.124.42: ICMP echo reply, id 11292, seq 1, length 18
0x0000: 4500 0026 14fa 0000 3701 53b1 55d6 8883 E..&....7.S.U...
0x0010: c0a8 7c2a 0000 33fb 2c1c 0001 4869 204d ..|*..3.,...Hi.t
0x0020: 6963 6861 656c 0000 0000 there....
but the ping output shows the following:
ping 85.214.xxx.xxx -c 1
PING 85.214.xxx.xxx 56(84) bytes of data.
^C
--- 85.214.xxx.xxx ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms
Okay, I'll tell you what I've tried and the results, although it seems that it's not something specific to Polymorph. First, I tested your functions by pinging between two virtual machines in the same local network, and everything seems to work correctly, the ping utility sends and receives the packets without problems. Then I tried to do it with an external service such as www.google.es, and, although the modified packet was sent, no response was received from the server. Apparently, this has some relation to the packet length, since if you add a small padding to your message, the external server responds and ping records the response. I put here the small modification that I made to your execution to receive a response from the external server.
def new_EXEC(packet):
import binascii
print ('Exec Script')
text = 'Hi there00000000000000000000000000'
bytes_text = bytes(text, 'utf-8')
packet['RAW']['load'] = binascii.hexlify(bytes_text).decode('utf-8')
return packet
i think this must be an other problem. Ich will test it with a TCP or UDP packet. On my Arch Linux the padding brings no result.
I make a simple test with FTP. In the test case i change the USER from test to anonymous. The result is that wireshark shows a successful login but the ftp application hangs. I notice that polymorph loop the execute condition.
Precondition script:
def ftp_pre(packet):
try:
if packet['TCP']['dport'] == 21:
print ('Pre Script')
#print (packet.raw)
return packet
else:
return None
except:
return None
Execute script:
def exec_ftp(packet):
# your code here
import re
from scapy.all import IP
pkt = IP(packet.raw)
if re.match('^USER',packet['RAW']['load']):
del pkt['IP'].len
del pkt['IP'].chksum
del pkt['TCP'].chksum
load = str(pkt.load, 'utf-8')
print ('Original load:', load)
pkt.load = load.replace('test','anonymous')
print ('Modified load:', pkt.load)
#pkt.show2()
packet.raw = bytes(pkt)
warschon = False
return packet
else:
return None
Postcondition script:
def post_ftp(packet):
from scapy.all import IP
pkt = IP(packet.raw)
if pkt.haslayer('IP') and pkt.haslayer('TCP'):
print ('POST Script')
#del pkt['IP'].chksum
#del pkt['IP'].len
#pkt.show2()
#print (packet['RAW'])
#packet.raw = bytes(pkt)
return packet
Wireshark:
Polymorph log output:
Pre Script
Pre Script
Pre Script
Pre Script
Original load: USER test
Modified load: b'USER anonymous\r\n'
POST Script
Pre Script
Original load: USER test
Modified load: b'USER anonymous\r\n'
POST Script
Pre Script
Original load: USER test
Modified load: b'USER anonymous\r\n'
POST Script
Pre Script
I think that the exec condition (regex) is correct. What do you mean what the problem is?
That seems to have to do with the length of the network packet in relation to the sequence number and acknowledgment of the TCP/IP session, as you are increasing the packet length, the target machine accepts the packet, but when it responds to that packet, the originating machine realizes that it does not match the expected sequence numbers and assumes that a packet loss has occurred, as a result it constantly forwards the previous packet, therefore it is not Polymorph that is forwarding packets, but rather the machine is forwarding packets and Polymorph is modifying them. I leave here a slice of a presentation that I made some time ago that represents this problem.
I also put here some preconditions, postconditions and executions that show the problem, assuming that a user can login to the remote ftp server with user: anonymous and pass: anonymous.
def newprec(packet):
try:
if packet['TCP']['dport'] == 21 and packet['RAW.FTP']['request_command'] == "USER":
return packet
except:
return None
def newexec(packet):
packet['RAW']['load'] = 'USER anonymous\r\n'
return packet
def new_post(packet):
from scapy.all import IP
pkt = IP(packet.raw)
if pkt.haslayer('IP') and pkt.haslayer('TCP'):
del pkt['IP'].len
del pkt['IP'].chksum
del pkt['TCP'].chksum
pkt.show2()
packet.raw = bytes(pkt)
return packet
With the following conditional functions we are going to modify in real time the USER
field of the ftp packets and we are going to introduce the anonymous value.
If when we login we use a user with size less than anonymous, for example testuser, which has a size of 8 characters while anonymous has 9 characters, we will increase the size of the packet and therefore we will leave the sequence numbers of the TCP/IP session inconsistent. The result is that the destination machine accepts the login but when sending the confirmation packet the source machine detects a failure in the sequence number and starts forwarding the previous packet. The ftp application is frozen.
If, on the contrary, we introduce a user with the same size as anonymous, such as testuser1, the resulting packet has the same size, even if it has been modified, and therefore, the sequence numbers of the TCP/IP session remain consistent, and the source ftp application accepts the login.
If you want to increase the size of the original packet, you must perform a recalculation of the TCP/IP session sequence numbers using the following algorithm and in the following way:
Probably this will be one of the conditional functions that in the next release of Polymorph, will come by default with the framework so that the user does not have to deal with it.
I hope this has solved your doubts.
Thank you. Now I understand the problem. I try to fix it but its a little bit complicated :-)
I think it is to fix with:
len(old_payload) - len(modified_payload) = diff_payload
modified_seq = old_seq + len(diff_payload)
I am looking forward to the next version and how you could solve the problem.
Here is the hack for this FTP example :)
Iptables rule:
intercept -ipt "iptables -I OUTPUT -j NFQUEUE --queue-num 1;iptables -I INPUT -j NFQ
UEUE --queue-num 1"
Conditional functions:
def track_tcpip(packet):
from scapy.all import IP
# Global vars for tracking the seq numbers
packet.global_var("sseq", 0)
packet.global_var("sack", 0)
packet.global_var("snextseq", 0)
packet.global_var("dseq", 0)
packet.global_var("dack", 0)
packet.global_var("dnextseq", 0)
packet.global_var("inserted", False)
try:
scapy_pkt = IP(packet.get_payload())
# Saving the seq numbers state for each packet
if scapy_pkt['TCP'].sport == 21:
packet.sseq = scapy_pkt['TCP'].seq
packet.sack = scapy_pkt['TCP'].ack
packet.snextseq = scapy_pkt['TCP'].seq + scapy_pkt['IP'].len - 52
elif scapy_pkt['TCP'].dport == 21:
packet.dseq = scapy_pkt['TCP'].seq
packet.dack = scapy_pkt['TCP'].ack
packet.dnextseq = scapy_pkt['TCP'].seq + scapy_pkt['IP'].len - 52
# TCP/IP session simulation is required
if packet.inserted:
if scapy_pkt['TCP'].sport == 21:
scapy_pkt['TCP'].seq = packet.dack
scapy_pkt['TCP'].ack = packet.dnextseq
elif scapy_pkt['TCP'].dport == 21:
scapy_pkt['TCP'].seq = packet.sack
scapy_pkt['TCP'].ack = packet.snextseq
# Recalculating the control fields of the pkt
del scapy_pkt['IP'].chksum
del scapy_pkt['TCP'].chksum
scapy_pkt.show2()
# Forward the packet
packet.set_payload(bytes(scapy_pkt))
return packet
except:
return None
def newprec(packet):
try:
if packet['TCP']['dport'] == 21 and packet['RAW.FTP']['request_command'] == "USER":
return packet
except:
return None
def newexec(packet):
if len(packet['RAW.FTP']['request_arg']) < len("anonymous"):
print("*************************************")
print("STARTING TCP/IP SESSION RECALCULATION")
print("*************************************")
packet.inserted = True
from scapy.all import IP
pkt = IP(packet.get_payload())
pkt['Raw'].load = 'USER anonymous\r\n'
del pkt['IP'].len
del pkt['IP'].chksum
del pkt['TCP'].chksum
pkt.show2()
packet.set_payload(bytes(pkt))
return packet
Wow this was fast. I will try it at monday. Thanks shramos :-)
hello,according to polymorph's whitepaper_english,modify MQTT publish package ,I have also an question about the intercept function. when i use " intercept -ipt "iptables -I INPUT -j NFQUEUE --queue-num 1"" nothing is intercepted,and when stop,I could not connect Internet and moqsuitto_pub could not publish message. I try many times,get same result. ,I do not know why? can you help me?
Hello, Thank you for using the project. First, check that there are not some iptables rules that prevent communication, you can do it through iptables -S
. If there are any rules set, delete it with iptables -F
.
After this, to make the modification of the MQTT protocol with the clients and the broker in the same machine, you can follow the following video where all the commands that must be executed are shown.
https://www.youtube.com/watch?v=o9EWMBzURos
If there is something from the video that you do not understand, I recommend you take a look at the wiki.
If the problem persists, tell me and we try to solve it!
Thank you very much,this problem is solved,but I have the other problem ,when I use "mosquitto_pub -t test -m hello -h 127.0.0.1" publish a message,after intercept,it shows hello twice,I think it might be because the packet between mosquitto_pub and mosquitto broker and the packet between broker and mosquitto_sub were intercepted.But,the result in the video that you give me is not like this. Sorry ,I am a beginner in this field,I have many problem,hope you don't dislike me.
Hi,
Your project is great. I have an question about the intercept function. I would like to interrupt traffic to a server. But if i use the following command no traffic is reach the internet.
intercept -ipt "iptables -I OUTPUT -j NFQUEUE --queue-num 1"
If i use:
intercept -ipt "iptables -I INPUT -j NFQUEUE --queue-num 1"
the intercept function shows me that the paket is change, but wireshark does not shows me the change.
What i do wrong?
Best regards,
Ibonok