My issue is as follows: I want to implement a listen service using scapy to stimulate a honeypot (because honeypot uses a fake ip, so I can't use OS sockets) and I chose scapy.
I implemented a very simple TCP hand-shake procedure, however one thing frustrated me: one byte of the packet I use PSH to send is eaten.
For example I send "abc" out to a client, but the client's socket, for example netcat or wget, only receive "bc". Another example is "HTTP/1.1 200 OK" becomes "TTP/1.1 200 OK". I captured packet and wireshark can correctly recognize my hand-made packet as HTTP, but the client socket just lack 1 byte. I don't know why.
The code is as follows:
192.168.1.100 stands for server(my) ip addr,9999 is the port. For example, I run this python script on 192.168.1.100, then I use "nc 192.168.1.100 9999". I expect to get "abc", but I can only get "bc", but the packet seems no problem in Wireshark. it's so strange.
'''
Created on Jun 2, 2012
#author: root
'''
from scapy import all
from scapy.layers.inet import IP, ICMP, TCP
from scapy.packet import ls, Raw
from scapy.sendrecv import sniff, send
from scipy.signal.signaltools import lfilter
import scapy.all
HOSTADDR = "192.168.1.100"
TCPPORT = 9999 'port to listen for'
SEQ_NUM = 100
ADD_TEST = "abc"
def tcp_monitor_callback(pkt):
global SEQ_NUM
global TCPPORT
if(pkt.payload.payload.flags == 2):
'A syn situation, 2 for SYN'
print("tcp incoming connection")
ACK=TCP(sport=TCPPORT, dport=pkt.payload.payload.sport, flags="SA",ack=pkt.payload.payload.seq + 1,seq=0)
send(IP(src=pkt.payload.dst,dst=pkt.payload.src)/ACK)
if(pkt.payload.payload.flags & 8 !=0):
'accept push from client, 8 for PSH flag'
print("tcp push connection")
pushLen = len(pkt.payload.payload.load)
httpPart=TCP(sport=TCPPORT, dport=pkt.payload.payload.sport, flags="PA", ack=pkt.payload.payload.seq + pushLen)/Raw(load=ADD_TEST)
'PROBLEM HERE!!!! If I send out abc, the client socket only receive bc, one byte disappers!!!But the packet received by client is CORRECT'
send(IP(src=pkt.payload.dst,dst=pkt.payload.src)/httpPart)
if(pkt.payload.payload.flags & 1 !=0):
'accept fin from cilent'
print ("tcp fin connection")
FIN=TCP(sport=TCPPORT, dport=pkt.payload.payload.sport, flags="FA", ack=pkt.payload.payload.seq +1, seq = pkt.payload.payload.ack)
send(IP(src=pkt.payload.dst,dst=pkt.payload.src)/FIN)
def dispatcher_callback(pkt):
print "packet incoming"
global HOSTADDR
global TCPPORT
if(pkt.haslayer(TCP) and (pkt.payload.dst == HOSTADDR) and (pkt.payload.dport == TCPPORT)):
tcp_monitor_callback(pkt)
else:
return
if __name__ == '__main__':
print "HoneyPot listen Module Test"
scapy.all.conf.iface = "eth0"
sniff(filter=("(tcp dst port %s) and dst host %s") % (TCPPORT,HOSTADDR), prn=dispatcher_callback)
Some suggestions:
Sniff may append some payload to the end of the packet, so len(pkt.payload.payload.load) may not be the real payload length. You can use pkt[IP].len-40 (40 is the common header length of IP+TCP). You may also use -len(pkt[IP].options)-len(pkt[TCP].options) for more accurate results.
Usually the application layer above TCP uses line breaks ("\r\n") to separate commands, so you'd better change ADD_TEST to "abc\r\n"
If none of above methods work, you may upgrade to the latest netcat and try again.
I tested your code, you are missing sending proper tcp sequence
httpPart=TCP(sport=TCPPORT, dport=pkt.payload.payload.sport, flags="PA", ack=pkt.payload.payload.seq + pushLen, seq=pkt.payload.payload.ack)/Raw(load=ADD_TEST)
should fix the issue, you may have other packet length issue, but the eaten 1 byte is caused by missing proper tcp sequence
Related
I have 2 elixir applications.
In one of them I create a TCP server which listens on the port 8080 for packets. I know that it is defined correctly, because I connected to it with telnet and everything was working fine.
The problem appears when I try to connect to this TCP server from my other Elixir application.
That's how I connect to it
host = 'localhost'
{:ok, socket} = :gen_tcp.connect(host, 8080, [])
Not sure about the options that I should specify tho.
When trying to connect to it I get in the logs of the application with TCP server:
00:21:11.235 [error] Task #PID<0.226.0> started from MessageBroker.Controller terminating
** (MatchError) no match of right hand side value: {:error, :closed}
(message_broker 0.1.0) lib/message_broker/network/controller.ex:110: MessageBroker.Controller.read_line/1
(message_broker 0.1.0) lib/message_broker/network/controller.ex:101: MessageBroker.Controller.serve/1
(elixir 1.12.3) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
(stdlib 3.12) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Function: #Function<0.126110026/0 in MessageBroker.Controller.loop_acceptor/1>
Args: []
At the line with
{:ok, data} = :gen_tcp.recv(socket, 0)
Any ideas, suggestions?
(MatchError) no match of right hand side value: {:error, :closed}
At the line with
{:ok, data} = :gen_tcp.recv(socket, 0)
The error message is saying that the function call :gen_tcp.recv(socket, 0) returned {:error, :closed} and that {:ok, data} does not match {:error, :closed}. If you call recv() on a socket that was closed by the other side, then recv() returns {:error, :closed}, and the other side will not be sending anymore data to that socket.
Not sure about the options that I should specify tho.
Yeah, those are pretty important. The basic tenet is that when you send data to a socket, you have no idea how many chunks the data will be split into. But, the client and server have to be able to know when to stop trying to read data because the end of the data has been reached. To solve the "indeterminate number of chunks problem", the client and server have to agree on some signal to mark the end of the data:
A newline?
The sender closes the socket?
Use an agreed upon number of bytes at the start of the data to specify the length of the data?
For #3, send() and recv() will automatically handle packaging and unpackaging the data for you if you simply tell gen_tcp how many bytes are necessary to specify the length of a message, for instance you can specify the option {:packet, 2}. If you do that, send() will automatically calculate the length of the data, then add 2 bytes to the front of the data containing the length of the data. Likewise, recv() will automatically read the first 2 bytes from the socket, then read the integer contained in those 2 bytes, say 64,999, then recv() will wait until it has read an additional 64,999 bytes from the socket, then it will return the whole 64,999 bytes of data.
How to send a message through TCP protocol to a TCP server using
gen_tcp in Elixir?
Here is an example of #1, where a newline is used to mark the end of the data:
TCP Server:
defmodule App1 do
def start_tcp_server do
port = 8080
{:ok, listen_socket} = :gen_tcp.listen(
port,
[
{:mode, :binary}, #Received data is delivered as a string (v. a list)
{:active, :false}, #Data sent to the socket will not be
#placed in the process mailbox, so you can't
#use a receive block to read it. Instead
#you must call recv() to read data directly
#from the socket.
{:packet, :line}, #recv() will read from the socket until
#a newline is encountered, then return.
#If a newline is not read from the socket,
#recv() will hang until it reads a newline
#from the socket.
{:reuseaddr, true} #Allows you to immediately restart the server
#with the same port, rather than waiting
#for the system to clean up and free up
#the port.
]
)
IO.puts "Listening on port #{port}...."
listen_loop(listen_socket)
end
defp listen_loop(listen_socket) do
{:ok, client_socket} = :gen_tcp.accept(listen_socket) #client_socket is created with the same options as listen_socket
handle_client(client_socket)
listen_loop(listen_socket)
end
defp handle_client(client_socket) do
case :gen_tcp.recv(client_socket, 0) do #Do not specify the number
#of bytes to read, instead write 0
#to indicate that the :packet option
#will take care of how many bytes to read.
{:ok, line} ->
#Echo back what was received:
IO.write("Server echoing back: #{line}")
:gen_tcp.send(client_socket, "Server received: #{line}") #line has a "\n" on the end
{:error, :closed} ->
{:ok, :client_disconnected}
end
end
end
TCP client:
defmodule App2 do
def send_data do
host = :localhost
port = 8080
{:ok, socket} = :gen_tcp.connect(host, port,
[
{:active, :false}, #Data sent to the socket will not be put in the process mailbox.
{:mode, :binary},
{:packet, :line} #Must be same as server.
]
)
:ok = :gen_tcp.send(socket, "Hi server!\n")
case :gen_tcp.recv(socket, 0) do
{:ok, line} ->
IO.puts ~s(Client got: "#{String.trim line}")
:ok = :gen_tcp.close(socket)
{:error, :closed} -> IO.puts("Server closed socket.")
end
end
end
Output in server window:
iex(1)> App1.start_tcp_server
Listening on port 8080....
Server echoing back: Hi server!
Output in client window:
ex(1)> App2.send_data
Client got: "Server received: Hi server!"
:ok
If you want to use {:error, :closed} to mark the end of the data, then you need to:
Specify the option {:packet, :raw} for both the client and the server socket.
After sending the data, close the socket by calling :gen_tcp.shutdown/2, which will ensure that all the data has been sent before closing the socket.
Loop over the recv(), saving each chunk returned by recv(), until the recv() returns {:error, :closed}, marking the end of the data, then your loop can return all the chunks it read. For instance:
defp get_data(socket, chunks) do
case :gen_tcp.recv(socket, 0) do #reads a chunk of indeterminate length from the socket
{:ok, chunk} ->
get_data(socket, [chunk | chunks])
{:error, :closed} ->
{:ok, Enum.reverse(chunks) }
end
end
You would call that function like this:
{:ok, data} = get_data(client_socket, [])
For more details on the gen_tcp options, see the following answer
Erlang client-server example using gen_tcp is not receiving anything
I just recently learned the Scapy part and currently want to use Scapy for the TCP handshack test.
I currently use mininet to create two and execute the following code:
from scapy.all import *
ip=IP(src="10.0.0.1", dst="10.0.0.41")
TCP_SYN=TCP(sport=433, dport=443, flags="S", seq=100)
TCP_SYNACK=sr1(ip/TCP_SYN)
TCP_ACK=TCP(sport=433, dport=443, flags="A", seq=101, ack=TCP_SYNACK.seq + 1)
send(ip/TCP_ACK)
host 2 replied:
enter image description here
But host 2 did not reply to the SYN-ACK packet, even if host 2 was allowed to execute the following code
python3 -m http.server 80
Even if dport is changed to 80, host 1 will receive SYN-ACK from host 2, but host 1 will not reply with ACK
I have a problem with a very basic usage of Scapy on Windows 7 (Python 3.6, Scapy 2.4.0). I'm also running Npcap 0.99r7 and Wireshark 2.6.2 on this sytem. The system does only have one wireless network interface plus the Npcap loopback interface.
I set up this very classic TCP server... :
import socket
host = '127.0.0.1'
port = 8089
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, port))
s.listen(1)
connection, address = s.accept()
while 1:
try :
data = connection.recv(1024)
except ConnectionAbortedError:
break
if data:
print('Received: %s' % (data.decode ('utf-8')))
connection.sendall('Data received'.encode())
connection.close()
s.close()
...and I set up this very classic TCP client:
import socket
host = '127.0.0.1'
port = 8089
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.send('Hello, world!'.encode())
data = s.recv(1024)
print('Received: %s' % (data.decode('utf-8')))
s.close()
Both works fine. Wireshark does report the whole TCP traffic on the loopback interface.
Now, I'm running the server, and I try to run that piece of code that would just send a SYN to the server with Scapy :
from scapy.layers.inet import IP
from scapy.layers.inet import TCP
from scapy.sendrecv import *
dstHost='127.0.0.1'
dstPort = 8089
packet = IP(src='127.0.0.1', dst=dstHost)/TCP(dport=dstPort, flags='S')
response=sr1(packet, timeout=10)
response.display()
Python reports :
Begin emission:
..Finished sending 1 packets.
......Traceback (most recent call last):
File "R:/Documents/Projets/python/hacking/scan.py", line 46, in <module>
response.display()
AttributeError: 'NoneType' object has no attribute 'display'
Received 8 packets, got 0 answers, remaining 1 packets
Moreover, Wireshark does not see anything on the loopback interface. May somebody give an hint ?
Update 1
As suggested, I tried a more explicit code using sendp() and not send(), since we are talking layer 2 here:
route_add_loopback()
packet = Loopback()/IP(src='127.0.0.1', dst='127.0.0.1')/TCP(dport=8089, flags='S')
sendp(packet,iface='Npcap Loopback Adapter')
Unfortunately, Wireshark does not sniff the packet on either interfaces (the 'Intel(R) Centrino(R) Advanced-N 6235' and the 'Npcap Loopback Adapter').
Note that the call to route_add_loopback() is required, or show_interfaces() won't report the 'Npcap Loopback Adapter', which means that sendp() will fail. It is possible to restore the Scapy routing table by calling conf.route.resync () after route_add_loopback(), but the result is the same : Wireshark does not sniff the packet on either interface.
Should somebody find some Python piece of code running on Windows 7 that succesfully sends a simple TCP packet on the 'Npcap Loopback Adapter', he would be welcome...
The loopback interface is not a "regular" interface; this is particularly true for Windows.
You can check the route used by Scapy to send the packet by running: packet.route().
If the route displayed does not use the loopback interface, you can try to run (that's windows specific) route_add_loopback() and try again.
Another option would be to use srp1() instead of sr1(), and specify the loopback interface as iface= parameter.
I am sending syslog on UDP to remotehost its working fine but while i am sending log on tcp then logs are not routing to remote host.
I am using syslog server on FreeBSD8.3
can anybody tell me how to send logs to remote host on TCP using syslog server.
Thanks in advance.
One issue I just ran into while trying to send log messages to an rsyslog server listening on tcp port 10514 was that it requires a last byte of 0A or line feed. I discovered this tcpdumping net cat as that was working but a socket connection was not. For ex the following will not work (the 155 is for local3 + err level) :
import socket
syslogSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
syslogSocket.connect(('127.0.0.1', 10514))
syslogSocket.send('<155>TEST FROM PYTOHON\000')
syslogSocket.close()
But this will work:
import socket
syslogSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
syslogSocket.connect(('127.0.0.1', 10514))
syslogSocket.send('<155>TEST FROM PYTOHON\n')
syslogSocket.close()
To get the 155 check out a page like:
http://linux.byexamples.com/archives/412/syslog-sending-log-from-remote-servers-to-syslog-daemon/
Since PRI = (facility << 3) + severity
And local3 facility == 19
And severity err = 3
PRI = (19<<3) + 3
152 + 3
155
Does anyone know how to use python to ping a local host to see if it is active or not? We (my team and I) have already tried using
os.system("ping 192.168.1.*")
But the response for destination unreachable is the same as the response for the host is up.
Thanks for your help.
Use this ...
import os
hostname = "localhost" #example
response = os.system("ping -n 1 " + hostname)
#and then check the response...
if response == 0:
print(hostname, 'is up!')
else:
print(hostname, 'is down!')
If using this script on unix/Linux replace -n switch with -c !
Thats all :)
I've found that using os.system(...) leads to false positives (as the OP said, 'destination host unreachable' == 0).
As stated before, using subprocess.Popen works. For simplicity I recommend doing that followed by parsing the results. You can easily do this like:
if ('unreachable' in output):
print("Offline")
Just check the various outputs you want to check from ping results. Make a 'this' in 'that' check for it.
Example:
import subprocess
hostname = "10.20.16.30"
output = subprocess.Popen(["ping.exe",hostname],stdout = subprocess.PIPE).communicate()[0]
print(output)
if ('unreachable' in output):
print("Offline")
The best way I could find to do this on Windows, if you don't want to be parsing the output is to use Popen like this:
num = 1
host = "192.168.0.2"
wait = 1000
ping = Popen("ping -n {} -w {} {}".format(num, wait, host),
stdout=PIPE, stderr=PIPE) ## if you don't want it to print it out
exit_code = ping.wait()
if exit_code != 0:
print("Host offline.")
else:
print("Host online.")
This works as expected. The exit code gives no false positives. I've tested it in Python 2.7 and 3.4 on Windows 7 and Windows 10.
I've coded a little program a while back. It might not be the exact thing you are looking for, but you can always run a program on the host OS that opens up a socket on startup. Here is the ping program itself:
# Run this on the PC that want to check if other PC is online.
from socket import *
def pingit(): # defining function for later use
s = socket(AF_INET, SOCK_STREAM) # Creates socket
host = 'localhost' # Enter the IP of the workstation here
port = 80 # Select port which should be pinged
try:
s.connect((host, port)) # tries to connect to the host
except ConnectionRefusedError: # if failed to connect
print("Server offline") # it prints that server is offline
s.close() #closes socket, so it can be re-used
pingit() # restarts whole process
while True: #If connected to host
print("Connected!") # prints message
s.close() # closes socket just in case
exit() # exits program
pingit() #Starts off whole process
And here you have the program that can recieve the ping request:
# this runs on remote pc that is going to be checked
from socket import *
HOST = 'localhost'
PORT = 80
BUFSIZ = 1024
ADDR = (HOST, PORT)
serversock = socket(AF_INET, SOCK_STREAM)
serversock.bind(ADDR)
serversock.listen(2)
while 1:
clientsock, addr = serversock.accept()
serversock.close()
exit()
To run a program without actually showing it, just save the file as .pyw instead of .py.
It makes it invisible until user checks running processes.
Hope it helped you
For simplicity, I use self-made functions based on socket.
def checkHostPort(HOSTNAME, PORT):
"""
check if host is reachable
"""
result = False
try:
destIp = socket.gethostbyname(HOSTNAME)
except:
return result
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(15)
try:
conn = s.connect((destIp, PORT))
result = True
conn.close()
except:
pass
return result
if Ip:Port is reachable, return True
If you wanna to simulate Ping, may refer to ping.py
Try this:
ret = os.system("ping -o -c 3 -W 3000 192.168.1.10")
if ret != 0:
print "Host is not up"
-o waits for only one packet
-W 3000 gives it only 3000 ms to reply to the packet.
-c 3 lets it try a few times so that your ping doesnt run forever
Use this and parse the string output
import subprocess
output = subprocess.Popen(["ping.exe","192.168.1.1"],stdout = subprocess.PIPE).communicate()[0]
How about the request module?
import requests
def ping_server(address):
try:
requests.get(address, timeout=1)
except requests.exceptions.ConnectTimeout:
return False
return True
No need to split urls to remove ports, or test ports, and no localhost false-positive.
Timeout amount doesn't really matter since it only hits the timeout when there is no server, which in my case meant performance no longer mattered. Otherwise, this returns at the speed of a request, which is plenty fast for me.
Timeout waits for the first bit, not total time, in case that matters.