decrypt ssl encrypted data in python - python

I'm analyzing a packet capture with python using dpkt. The application layer is encrypted with ssl. I'd like to decrypt the ssl data (i.e., the tcp payload). I have the private key, so I should be able to use the key to decrypt the data. Here's my script:
#!/bin/python
import sys
import dpkt
def main():
if not len(sys.argv) == 2:
print "need a pcap file"
return 1
filename = sys.argv[1]
f = open(filename)
pcap = dpkt.pcap.Reader(f)
framenum = 1
for ts, buf in pcap:
if framenum == 123:
eth = dpkt.ethernet.Ethernet(buf)
ip = eth.data
tcp = ip.data
ssl = tcp.data
# decrypt ssl
framenum += 1
if __name__ == '__main__':
sys.exit( main() )
What can I put in place of that "decrypt ssl" comment to get the decrypted ssl bytes? I'm guessing there should be some library that can do this for me, but all my searches for ssl and python give information about writing socket programs that can receive ssl connections. I'm not interested in that. Rather, I need to decrypt data that is encrypted with ssl.
Thanks!

You're not likely going to find a ready-made library to do this. Decrypting from a packet dump is rather involved, and I believe the best featured tool right now is still Wireshark.
Note that you will also need to have the entire TLS session captured, from the handshake onward. Also, if the connection used an ephemeral mode which offers forward secrecy (anything with DHE or ECDHE), the data cannot be decrypted.

Related

Unable to transfer file using Sockets on different computers

I recently wrote a code for a file transfer in Python. Sockets connect fine when I connect them from different terminals on the same system. But the same doesn't seem to work when I connect them from different computers which are connected over the same Wifi network.
Here's the server code:
import os
import socket
# Creating a socket
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.bind(("192.164.X.X",2222))
sock.listen(5)
print("Host Name: " , sock.getsockname())
# Accepting the connection
client , addr = sock.accept()
# Getting file details
file_name = input("File Name:")
file_size = os.path.getsize(file_name)
# Sending file name and details
client.send(file_name.encode())
client.send(str(file_size).encode())
# Opening file and sending data
with open(file_name,"rb") as file:
c = 0
while c <= file_size:
data = file.read(1024)
if not (data):
break
client.sendall(data)
c += len(data)
# closing the socket
sock.close()
Here's my client code:
import os
import socket
host = input("Host Name: " )
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Trying to connect to socket
sock.connect((host,2222))
print("Connected Successfully")
# send file details
file_name = sock.recv(100).decode()
file_size = sock.recv(100).decode()
with open("./rec/" + file_name , "wb") as file:
c = 0
while c <= int(file_size):
data = sock.recv(1024)
if not (data):
break
file.write(data)
c += len(data)
sock.close()
When I try to connect The client From a different computer I get this error :
while c <= int(file_size):
ValueError: invalid literal for int() with base 10: '3hi\n'
The file I am trying to transfer has a single word 'hi'.
File transfer works correctly from different terminals on same machine. But the same doesn't work on different computers which are connected over the same wifi network.
I understand the error (trying to convert string to int) but I don't WHY it's happening and how to fix it.
Your server code is sending a single TCP packet containing the content of multiple client.send() calls. This is commonly known as "corking", and can usually be disabled (depending on your OS) using the socket.TCP_NODELAY socket option after accepting the connection.
client, addr = sock.accept()
client.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
This is however not guaranteed to work, and depends on your OS and OS settings.
The real solution would be to create a more robust protocol and avoid relying on data being sent in different packets. In fact, this is the only sane way of implementing any protocol based on TCP. Never rely on data being split in packets in a specific way.
Decide a fixed size for encoding and sending lengths, then do the following on the server:
Send a length (of fixed size, for example 8 characters or 8 bytes, or whatever you would like) for the file name.
Send the filename.
Send the file size (again of fixed size).
Send the file contents.
While on the client:
Receive exactly 8 bytes and decode the length.
Receive exactly length bytes for the filename.
Receive exactly 8 bytes and decode the file size.
Receive exactly size bytes for the file contents.
Most importantly, note that the .recv() method of sockets can return less than the requested amount (you seem to already know that), so whatever kind of receiving operation you need to do, you will need to accumulate data in a loop until you have received the expected amount, for example:
expected = 100
data = b''
while len(data) < expected:
data += sock.recv(expected - len(data))

Peer not responding to Handshake Message in BitTorrent Protocol

I am sending a handshake to a peer. This is what the handshake looks like:
b'\x13BitTorrent Protocol\x00\x00\x00\x00\x00\x00\x00\x00\x08O\xae=J2\xc5g\x98Y\xafK\x9e\x8d\xbb\x7f`qcG\x08O\xff=J2\xc5g\x98Y\xafK\x9e\x8d\xbb\x7f`qcG'
However, I get an empty b'' in response. I have set timeout to 10.
Here's my code:
clientsocket=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientsocket.settimeout(5)
print("trying")
try:
clientsocket.connect((ip,port))
except:
continue
print('connected')
#print(req)
clientsocket.send(req)
clientsocket.settimeout(10)
try:
buffer = clientsocket.recv(1048)
except:
continue
Any idea what my mistake is?
There are a few issues with your sample code. The core issue is the header in your handshake mistakenly capitalizes "Protocol", most BitTorrent implementations will drop the TCP connection if this header isn't byte-for-byte correct.
The following is a slightly cleaned up version of the code that works:
# IP and Port, obviously change these to match where the server is
ip, port = "127.0.0.1", 6881
import socket
# Broken up the BitTorrent header to multiple lines just to make it easier to read
# The main header, note the lower "p" in protocol, that's important
req = b'\x13'
req += b'BitTorrent protocol'
# The optional bits, note that normally some of these are set by most clients
req += b'\x00\x00\x00\x00\x00\x00\x00\x00'
# The Infohash we're interested in. Let python convert the human readable
# version to a byte array just to make it easier to read
req += bytearray.fromhex("5fff0e1c8ac414860310bcc1cb76ac28e960efbe")
# Our client ID. Just a random blob of bytes, note that most clients
# use the first bytes of this to mark which client they are
req += bytearray.fromhex("5b76c604def8aa17e0b0304cf9ac9caab516c692")
# Open the socket
clientsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientsocket.settimeout(5)
print("Trying")
clientsocket.connect((ip,port))
print('Connected')
# Note: Use sendall, in case the handshake doesn't make it one packet for
# whatever reason
clientsocket.sendall(req)
# And see what the server sends back. Note that really you should keep reading
# till one of two things happens:
# - Nothing is returned, likely meaning the server "hung up" on us, probably
# because it doesn't care about the infohash we're talking about
# - We get 68 bytes in the handshake response, so we have a full handshake
buffer = clientsocket.recv(1048)
print(buffer)

Sending png files over python sockets

I've set up a python client and server with socket in Python, that allows the server to send text to the client and I've been trying to extend it so that images can be sent to the client.
Server code:
import socket
#setup and bind server socket
s_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#setup socket
s_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)#reuses same port (allows reconnection)
s_socket.bind(('192.168.178.52', 9001))
s_socket.listen(1)
#connects and prints clients data and send message
clientsocket, address = s_socket.accept()
print('Connection from {}'.format(address))
clientsocket.send(bytes('Welcome to the server', 'utf-8'))
#Loops for server to sent text data to client
while True:
m = input('Enter')
try:
file = open(m, 'rb')
b = file.read(2048)
clientsocket.send(b)
except:
clientsocket.send(bytes(m, 'utf-8'))
Client code:
import socket
import webbrowser
import os
import pyautogui
#setup and bind client socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.connect(('----------', 9001))#ip swapped for post
while True:
message = s.recv(2048)#recieves all messages sent with buffer size
if message:
txt = str(message)
with open('plswork.png', 'wb') as file:
file.write(message)
file.close()
The problem I'm having is that it will send the file over and create it perfectly fine, but only part of the image will load in when i open it (see image) I am pretty sure this is something to do with the buffer size however when I increase it, it wont recognise the file at all and I'll get an error trying to open the photo (preferably you would be able to send most photos). New to python sockets so any help would be appreciated!
(at the moment trying to send a pic of tux...)
https://i.stack.imgur.com/lBblq.png
I don't know the size of the file, but shouldn't you read the file until it is read completely and send data in chunks?
while True:
m = input('Enter')
try:
file = open(m, 'rb')
while True:
b = file.read(2048)
if not b:
break
clientsocket.send(b)
except:
clientsocket.send(bytes(m, 'utf-8'))
Client side had to be adapted as well.
Most network protocols add more information to simplify reception.
It could for example be a good idea, if you first send the number of bytes, that the welcome message contains, then the welcome message, then some indicator, that you will send a file, then some information, how many bytes you will send for the image and only then the bytes of the image
You will also find out, that it is complicated for the client to know what is the text message and what is part of the png file.
In fact if you would remove the input() command from the server and hard code a file name you might probably notice. that the welcome message and the png file could arrive combined at the client side. and it would have difficulties separating the two.
So to make your code robust, there's a lot of work to do.

Python raw socket receive issue

I am using the following python script for raw socket packet transfer. Packet transfer is fine, but I am not able to print the incoming packet from the other end.
from socket import socket, AF_PACKET, SOCK_RAW
s = socket(AF_PACKET, SOCK_RAW)
s.bind(("eth0", 0))
src_addr = "\x54\xbe\xf7\x40\xf5\x82"
dst_addr = "\xff\xff\xff\xff\xff\xff"
payload = ("[("*30)+"Hello"+("]"*30)
checksum = "\x1a\x2b\x3c\x4d"
data = payload+checksum
s.send(dst_addr+src_addr+data)
#for receive function
response=s.recv(4096)
print response
s.close()
There is a third argument to the socket function: protocol. If not given, it's defaulting to 0. For AF_PACKET / SOCK_RAW, the protocol argument specifies what kind of packets you're interested in receiving. The values are documented in the packet(7) man page: http://man7.org/linux/man-pages/man7/packet.7.html
I don't think the values are actually defined anywhere in the core python2 modules. Some of them can be found in scapy (http://www.secdev.org/projects/scapy/), or you can just hunt up the linux header file where they are defined (/usr/include/linux/if_ether.h).
So, to fix this, change your code to:
from socket import socket, AF_PACKET, SOCK_RAW, htons
ETH_P_ALL = 3
ETH_P_IP = 0x800 # Alternatively using this will receive the next IP packet
s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))
...
Other comments on your code:
As written, the packet you're sending is unlikely to be comprehensible by anyone. You've got a dst and src MAC address, but then you're not providing an EtherType. Instead the first "[(" will be seen as the EtherType. That probably won't make sense to any receiver of the packet so it will just be discarded.
Also, you should understand that with a raw socket, you're going to receive the next packet of the type you've specified in the protocol. That isn't necessarily (and in fact probably won't be) a response to the packet you just sent.
Thanks everyone now I am able receive the packet with the following the script. But still I am facing issue with printing multiple response packets(While doing in loop).
from socket import socket, AF_PACKET, SOCK_RAW, htons
from struct import *
import select
import time
ETH_P_ALL = 3
ETH_P_IP = 0x800
s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))
s.bind(("eth0", 0))
src_addr = "\x54\xbe\xf7\x40\xf7\x82"
dst_addr = "\xff\xff\xff\xff\xff\xff"
l = "\x00\x21"
ethertype = "\x08\x01"
a ="\x00\x10\x00\x10\x00\x10"
b = "\x00\x11\x00\x11\x00\x11"
payload = ethertype + l + a + b
for i in range(5):
time.sleep(5)
s.send(dst_addr+src_addr+ payload)
message=s.recv(4096)
print message

Public key not recognized

I am trying to export a public key from openssl using python. I have the actual key information transferred to the client from the server but the PEM encoding is not transferred so the key on the client is useless. I basically send the public key using send all in python but unfortunately this does not send the PEM encoding. Does anyone know how to transfer the encoding?
I didn't know that the encoding would not transfer along with the key.
THe code where the string is read in
import socket
import M2Crypto as m2c
import os
max_transfer_block = 1024
server_addr = "10.1.1.2"
dest_port = 3333
listen_port = 8888
client_addr = "10.1.1.3"
mysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mysocket.connect((server_addr, dest_port))
#receive the public key from the server
keysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
keysocket.bind((client_addr, listen_port))
keysocket.listen(1)
conn, client_addr = keysocket.accept()
print 'connected by', client_addr
data = conn.recv(max_transfer_block)
#FILE = m2c.RSA.save_pub_key(data, "serverPubKey.pem")
FILE = open("sPub.pem", "w")
FILE.write(data)
keysocket.close()
#transfer encrypted file
key = m2c.RSA.load_pub_key('serverPubKey.pem')
FILE = open("test.txt", "r")
data = FILE.read()
encrypted = key.public_encrypt(data, m2c.RSA.pkcs1_padding)
mysocket.sendall(encrypted)
mysocket.close()
When I use the line key = m2c.RSA.load_pub_key('serverPubKey.pem') I get an error telling me that there is no starting point.
raise RSAError, m2.err_reason_error_string(m2.err_get_error()) M2Crypto.RSA.RSAError: no start line
I have figured out that this is because there is not in PEM format. Unfortunately, I don't know how to put it in that format.
The mistake was that the public/private key pair needs to be created from the same wrapper. What I mean by this is that not all key pairs are the same. My specific problem was that Openssl and the M2Crypto instances of key pairs were not in the same underlying format. Thus creating keys with Openssl and then trying to use M2Crypto to use the keys was wrong. The lesson from all of this is to not import keys from other wrappers. If you do, make sure that they are in the same underlying format like ASCII or Unicode before trying to use them.

Categories