Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
0 contributors

Users who have contributed to this file

161 lines (130 sloc) 7 KB
#!/usr/bin/python
import binascii
import socket
import argparse
import threading
import ssl
# Packets
ssl_negotiation_request = binascii.unhexlify("030000130ee000000000000100080001000000")
non_ssl_negotiation_request = binascii.unhexlify("030000130ee000000000000100080000000000")
non_ssl_client_data = binascii.unhexlify("030001ac02f0807f658201a00401010401010101ff30190201220201020201000201010201000201010202ffff020102301902010102010102010102010102010002010102020420020102301c0202ffff0202fc170202ffff0201010201000201010202ffff0201020482013f000500147c00018136000800100001c00044756361812801c0d800040008000005000401ca03aa09080000b01d0000000000000000000000000000000000000000000000000000000000000000000007000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ca01000000000018000f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000004c00c00110000000000000002c00c001b0000000000000003c0380004000000726470647200000000008080726470736e640000000000c0647264796e766300000080c0636c6970726472000000a0c0")
ssl_client_data = binascii.unhexlify("030001ac02f0807f658201a00401010401010101ff30190201220201020201000201010201000201010202ffff020102301902010102010102010102010102010002010102020420020102301c0202ffff0202fc170202ffff0201010201000201010202ffff0201020482013f000500147c00018136000800100001c00044756361812801c0d800040008000005000401ca03aa09080000b01d0000000000000000000000000000000000000000000000000000000000000000000007000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ca01000000000018000f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000100000004c00c00110000000000000002c00c001b0000000000000003c0380004000000726470647200000000008080726470736e640000000000c0647264796e766300000080c0636c6970726472000000a0c0")
ping_packet = binascii.unhexlify("0300000e02f0803c443728190200")
# Arguments
parser = argparse.ArgumentParser(description="Detect present of DOUBLEPULSAR RDP implant\n\nAuthor: Luke Jennings\nWebsite: https://countercept.com\nTwitter: @countercept", formatter_class=argparse.RawTextHelpFormatter)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--ip', help='Single IP address to check')
group.add_argument('--file', help='File containing a list of IP addresses to check')
group.add_argument('--net', help='Network CIDR to check (requires python netaddr library)')
parser.add_argument('--timeout', help="Timeout on connection for socket in seconds", default=None)
parser.add_argument('--verbose', help="Verbose output for checking of commands", action='store_true')
parser.add_argument('--threads', help="Number of connection threads when checking file of IPs (default 10)", default="10")
args = parser.parse_args()
ip = args.ip
filename = args.file
net = args.net
timeout = args.timeout
verbose = args.verbose
num_threads = int(args.threads)
semaphore = threading.BoundedSemaphore(value=num_threads)
print_lock = threading.Lock()
def print_status(ip, message):
global print_lock
with print_lock:
print "[*] [%s] %s" % (ip, message)
def check_ip(ip):
global ssl_negotiation_request, non_ssl_negotiation_request, non_ssl_client_data, ssl_client_data, ping_packet, timeout, verbose
# Connect to socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(float(timeout) if timeout else None)
host = ip
port = 3389
s.connect((host, port))
# Send/receive negotiation request
if verbose:
print_status(ip, "Sending negotiation request")
s.send(ssl_negotiation_request)
negotiation_response = s.recv(1024)
# Determine if server has chosen SSL
if len(negotiation_response) >= 19 and negotiation_response[11] == "\x02" and negotiation_response[15] == "\x01":
if verbose:
print_status(ip, "Server chose to use SSL - negotiating SSL connection")
sock = ssl.wrap_socket(s)
s = sock
# Send/receive ssl client data
if verbose:
print_status(ip, "Sending SSL client data")
s.send(ssl_client_data)
s.recv(1024)
# Server explicitly refused SSL
elif len(negotiation_response) >= 19 and negotiation_response[11] == "\x03" and negotiation_response[15] == "\x02":
if verbose:
print_status(ip, "Server explicitly refused SSL, reconnecting")
# Re-connect
s.close()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(float(timeout) if timeout else None)
s.connect((host, port))
# Send/receive non-ssl negotiation request
if verbose:
print_status(ip, "Sending non-ssl negotiation request")
s.send(non_ssl_negotiation_request)
s.recv(1024)
# Server requires NLA which implant does not support
elif len(negotiation_response) >= 19 and negotiation_response[11] == "\x03" and negotiation_response[15] == "\x05":
with print_lock:
print "[-] [%s] Server requires NLA, which DOUBLEPULSAR does not support" % ip
s.close()
return
# Carry on non-ssl
else:
# Send/receive non-ssl client data
if verbose:
print_status(ip, "Sending client data")
s.send(non_ssl_client_data)
s.recv(1024)
# Send/receive ping
if verbose:
print_status(ip, "Sending ping packet")
s.send(ping_packet)
# Non-infected machines terminate connection, infected send a response
try:
ping_response = s.recv(1024)
with print_lock:
if len(ping_response) == 288:
print "[+] [%s] DOUBLEPULSAR RDP IMPLANT DETECTED!!!" % ip
else:
print "[-] [%s] Status Unknown - Response received but length was %d not 288" % (ip, len(ping_response))
s.close()
except socket.error as e:
with print_lock:
print "[-] [%s] No presence of DOUBLEPULSAR RDP implant" % ip
def threaded_check(ip_address):
global semaphore
try:
check_ip(ip_address)
except Exception as e:
with print_lock:
print "[ERROR] [%s] - %s" % (ip_address, e)
finally:
semaphore.release()
if ip:
check_ip(ip)
elif filename:
with open(filename, "r") as fp:
for line in fp:
semaphore.acquire()
ip_address = line.strip()
t = threading.Thread(target=threaded_check, args=(ip_address,))
t.start()
elif net:
from netaddr import IPNetwork
network = IPNetwork(net)
for addr in network:
# Skip the network and broadcast addresses
if ((network.size != 1) and ((addr == network.network) or (addr == network.broadcast))):
continue
semaphore.acquire()
ip_address = str(addr)
t = threading.Thread(target=threaded_check, args=(ip_address,))
t.start()
You can’t perform that action at this time.