Last active
February 19, 2025 15:08
-
-
Save BigNerd95/c572412036fe682afaf53ce3d2544f1f to your computer and use it in GitHub Desktop.
Protocol dumper and cipher for Tecnoalarm Security Systems
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
from Crypto.Cipher import AES | |
class TACipher(): | |
def __init__(self, key, iv): | |
self._key = key # bytes | |
self._iv = iv # bytes | |
self._tail = bytes() | |
self._padding = 0 | |
self.__setup_cipher__(self._key, self._iv) | |
def __calc_padding__(self, size): | |
padding = 0 | |
mod = size % 16 | |
if mod > 0: | |
padding = 16 - mod | |
return padding | |
def __add_tail_padding__(self, data): | |
# add precedent tail | |
buf = self._tail + data | |
# calculate padding (it is needed because the cipher has a segement_size of 16 bytes) | |
self._padding = self.__calc_padding__(len(buf)) | |
# add padding | |
return buf + bytes(self._padding) | |
def __remove_tail_padding__(self, data): | |
# remove padding | |
res = data[:len(data) - self._padding] | |
# remove precedent tail | |
return res[len(self._tail):] | |
def __update_tail_iv__(self, data): | |
# add precedent tail | |
buf = self._tail + data | |
if len(buf) >= 16: | |
mod = len(buf) % 16 | |
# get the new IV from last multiple of 16 bytes | |
if mod > 0: | |
self._tail = buf[-mod:] # buf: ----|----|----|----|XX | |
self._iv = buf[-mod - 16 : -mod] # buf: ----|----|----|XXXX|-- | |
else: | |
self._tail = bytes() # buf: ----|----|----|----| | |
self._iv = buf[-16:] # buf: ----|----|----|XXXX| | |
else: | |
self._tail = buf # buf: XX | |
def __update_cipher__(self, data): | |
self.__update_tail_iv__(data) | |
self.__setup_cipher__(self._key, self._iv) | |
def __setup_cipher__(self, key, iv): | |
self.cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128) | |
def decrypt(self, data): # data (bytes) | |
buf = self.__add_tail_padding__(data) | |
buf = self.cipher.decrypt(buf) | |
buf = self.__remove_tail_padding__(buf) | |
self.__update_cipher__(data) | |
return buf | |
def encrypt(self, data): # data (bytes) | |
buf = self.__add_tail_padding__(data) | |
buf = self.cipher.encrypt(buf) | |
buf = self.__remove_tail_padding__(buf) | |
self.__update_cipher__(buf) | |
return buf | |
def main(): | |
key = b'0123456789abcdef' # 16 bytes | |
iv = b'fedcba9876543210' # 16 bytes (should be random) | |
data = b'abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd' # any length | |
cipher_send = TACipher(key, iv) | |
cipher_recv = TACipher(key, iv) | |
for x in range(1,10): | |
encrypted_data_to_send = cipher_send.encrypt(data) | |
decrypted_data_received = cipher_recv.decrypt(encrypted_data_to_send) | |
print("Original data:", data) | |
print("Encrypted data:", encrypted_data_to_send) | |
print("Decrypted data:", decrypted_data_received) # on receiver side | |
print() | |
if __name__ == "__main__": | |
main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import logging | |
logging.getLogger("scapy.runtime").setLevel(logging.ERROR) | |
from scapy.all import * | |
import sys | |
import Tecnoalarm | |
IP_telefono = "192.168.0.40" | |
IP_centrale = "192.168.0.30" | |
PORT_centrale = 10001 | |
def v_print(*arg): | |
if 'VERBOSE' in globals(): | |
print(*arg) | |
def init(dump_file): | |
v_print("Parsing file:", dump_file) | |
packet_dump = rdpcap(dump_file) | |
telefono_to_centrale = packet_dump.filter(lambda packet: packet.payload.src == IP_telefono and packet.payload.dst == IP_centrale and TCP in packet and packet.payload.dport == PORT_centrale and Raw in packet[TCP]) | |
centrale_to_telefono = packet_dump.filter(lambda packet: packet.payload.src == IP_centrale and packet.payload.dst == IP_telefono and TCP in packet and packet.payload.sport == PORT_centrale and Raw in packet[TCP]) | |
if len(telefono_to_centrale) < 1 or len(centrale_to_telefono) < 1: | |
print("No packet found!") | |
sys.exit(-1) | |
IV = telefono_to_centrale[0][Raw].load # IV is the first message from Phone to Central | |
telefono_to_centrale = telefono_to_centrale[1:] # remove IV from messages from Phone to Central | |
return (IV, telefono_to_centrale, centrale_to_telefono) | |
def hexs(data): | |
s = "" | |
for b in data: | |
s += str(hex(b)) + " " | |
return s | |
def print_packets(key, iv, packets): | |
cipher_recv = Tecnoalarm.TACipher(key, iv) | |
for pkt in packets: | |
plain = cipher_recv.decrypt(pkt[Raw].load) | |
print(hexs(plain)) | |
def main(key, iv, tel2cen, cen2tel): | |
v_print("IV:", len(iv), "bytes") | |
v_print("Phone --> Central:", len(tel2cen), "packets") | |
v_print("Central --> Phone:", len(cen2tel), "packets") | |
print("\nPackets Phone --> Central") | |
print_packets(key, iv, tel2cen) | |
print("\nPackets Central --> Phone") | |
print_packets(key, iv, cen2tel) | |
if __name__ == "__main__": | |
if len(sys.argv) > 2: | |
if len(sys.argv) > 3 and sys.argv[3] == "-v": | |
global VERBOSE | |
VERBOSE = True | |
if len(sys.argv[2]) == 16: | |
iv, tel2cen, cen2tel = init(sys.argv[1]) | |
main(sys.argv[2], iv, tel2cen, cen2tel) | |
else: | |
print("Password must be 16 bytes long") | |
else: | |
print("Usage:", sys.argv[0], "file.pcap PASSWORD [-v]" ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment