Source code for sliver.beacon

"""
    Sliver Implant Framework
    Copyright (C) 2022  Bishop Fox

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
"""

import asyncio
import functools
import logging
from typing import Any, Dict, List, Tuple

import grpc

from ._protocols import PbWithRequestProp
from .interactive import BaseInteractiveCommands
from .pb.rpcpb.services_pb2_grpc import SliverRPCStub
from .protobuf import client_pb2, common_pb2, sliver_pb2


[docs]class BaseBeacon: def __init__( self, beacon: client_pb2.Beacon, channel: grpc.aio.Channel, timeout: int = 60, ): """Base class for Beacon classes. :param beacon: Beacon protobuf object. :type beacon: client_pb2.Beacon :param channel: A gRPC channel. :type channel: grpc.aio.Channel :param timeout: Seconds to wait for timeout, defaults to TIMEOUT :type timeout: int, optional """ self._log = logging.getLogger(self.__class__.__name__) self._channel = channel self._beacon = beacon self._stub = SliverRPCStub(channel) self.timeout = timeout self.beacon_tasks: Dict[str, Tuple[asyncio.Future, Any]] = {} asyncio.get_event_loop().create_task(self.taskresult_events()) @property def beacon_id(self) -> str: """Beacon ID""" return self._beacon.ID @property def name(self) -> str: """Beacon name""" return self._beacon.Name @property def hostname(self) -> str: """Beacon hostname""" return self._beacon.Hostname @property def uuid(self) -> str: """Beacon UUID""" return self._beacon.UUID @property def username(self) -> str: """Username""" return self._beacon.Username @property def uid(self) -> str: """User ID""" return self._beacon.UID @property def gid(self) -> str: """Group ID""" return self._beacon.GID @property def os(self) -> str: """Operating system""" return self._beacon.OS @property def arch(self) -> str: """Architecture""" return self._beacon.Arch @property def transport(self) -> str: """Transport Method""" return self._beacon.Transport @property def remote_address(self) -> str: """Remote address""" return self._beacon.RemoteAddress @property def pid(self) -> int: """Process ID""" return self._beacon.PID @property def filename(self) -> str: """Beacon filename""" return self._beacon.Filename @property def last_checkin(self) -> int: """Last check in time""" return self._beacon.LastCheckin @property def active_c2(self) -> str: """Active C2""" return self._beacon.ActiveC2 @property def version(self) -> str: """Version""" return self._beacon.Version @property def reconnect_interval(self) -> int: """Reconnect interval""" return self._beacon.ReconnectInterval def _request(self, pb: PbWithRequestProp): """ Set request attributes based on current beacon, I'd prefer to return a generic Request object, but protobuf for whatever reason doesn't let you assign this type of field directly. `pb` in this case is any protobuf message with a .Request field. :param pb: A protobuf request object. """ pb.Request.BeaconID = self._beacon.ID pb.Request.Timeout = self.timeout - 1 pb.Request.Async = True return pb
[docs] async def taskresult_events(self): """ Monitor task events for results, resolve futures for any results we get back. """ async for event in self._stub.Events(common_pb2.Empty()): if event.EventType != "beacon-taskresult": continue try: beacon_task = client_pb2.BeaconTask() beacon_task.ParseFromString(event.Data) if beacon_task.ID not in self.beacon_tasks: continue task_content = await self._stub.GetBeaconTaskContent( client_pb2.BeaconTask(ID=beacon_task.ID) ) task_future, pb_object = self.beacon_tasks[beacon_task.ID] del self.beacon_tasks[beacon_task.ID] if pb_object is not None: result = pb_object() result.ParseFromString(task_content.Response) else: result = None task_future.set_result(result) except Exception as err: self._log.exception(err)
def beacon_taskresult(pb_object: Any): """ Wraps a class method to return a future that resolves when the beacon task result is available. """ def func(method): @functools.wraps(method) async def wrapper(self, *args, **kwargs): task_response = await method(self, *args, **kwargs) self.beacon_tasks[task_response.Response.TaskID] = ( asyncio.Future(), pb_object, ) return self.beacon_tasks[task_response.Response.TaskID][0] return wrapper return func
[docs]class InteractiveBeacon(BaseBeacon, BaseInteractiveCommands): """Wrap all commands that can be executed against a beacon mode implant"""
[docs] async def interactive_session(self): pass
# ---------------- Wrapped super() commands ----------------
[docs] @beacon_taskresult(sliver_pb2.Ping) async def ping(self, *args, **kwargs) -> sliver_pb2.Ping: return await super().ping(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.Ps) async def ps(self, *args, **kwargs) -> List[common_pb2.Process]: return await super().ps(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.Terminate) async def terminate(self, *args, **kwargs) -> sliver_pb2.Terminate: return await super().terminate(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.Ifconfig) async def ifconfig(self, *args, **kwargs) -> sliver_pb2.Ifconfig: return await super().ifconfig(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.Netstat) async def netstat(self, *args, **kwargs) -> sliver_pb2.Netstat: return await super().netstat(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.Ls) async def ls(self, *args, **kwargs) -> sliver_pb2.Ls: return await super().ls(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.Pwd) async def cd(self, *args, **kwargs) -> sliver_pb2.Pwd: return await super().cd(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.Pwd) async def pwd(self, *args, **kwargs) -> sliver_pb2.Pwd: return await super().pwd(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.Rm) async def rm(self, *args, **kwargs) -> sliver_pb2.Rm: return await super().rm(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.Mkdir) async def mkdir(self, *args, **kwargs) -> sliver_pb2.Mkdir: return await super().mkdir(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.Download) async def download(self, *args, **kwargs) -> sliver_pb2.Download: return await super().download(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.Upload) async def upload(self, *args, **kwargs) -> sliver_pb2.Upload: return await super().upload(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.ProcessDump) async def process_dump(self, *args, **kwargs) -> sliver_pb2.ProcessDump: return await super().process_dump(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.RunAs) async def run_as(self, *args, **kwargs) -> sliver_pb2.RunAs: return await super().run_as(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.Impersonate) async def impersonate(self, *args, **kwargs) -> sliver_pb2.Impersonate: return await super().impersonate(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.RevToSelf) async def revert_to_self(self, *args, **kwargs) -> sliver_pb2.RevToSelf: return await super().revert_to_self(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.GetSystem) async def get_system(self, *args, **kwargs) -> sliver_pb2.GetSystem: return await super().get_system(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.Task) async def execute_shellcode(self, *args, **kwargs) -> sliver_pb2.Task: return await super().execute_shellcode(*args, **kwargs)
[docs] @beacon_taskresult(None) async def msf(self, *args, **kwargs) -> None: return await super().msf(*args, **kwargs)
[docs] @beacon_taskresult(None) async def msf_remote(self, *args, **kwargs) -> None: return await super().msf_remote(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.ExecuteAssembly) async def execute_assembly(self, *args, **kwargs) -> sliver_pb2.ExecuteAssembly: return await super().execute_assembly(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.Migrate) async def migrate(self, *args, **kwargs) -> sliver_pb2.Migrate: return await super().migrate(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.Execute) async def execute(self, *args, **kwargs) -> sliver_pb2.Execute: return await super().execute(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.Sideload) async def sideload(self, *args, **kwargs) -> sliver_pb2.Sideload: return await super().sideload(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.SpawnDll) async def spawn_dll(self, *args, **kwargs) -> sliver_pb2.SpawnDll: return await super().spawn_dll(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.Screenshot) async def screenshot(self, *args, **kwargs) -> sliver_pb2.Screenshot: return await super().screenshot(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.MakeToken) async def make_token(self, *args, **kwargs) -> sliver_pb2.MakeToken: return await super().make_token(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.EnvInfo) async def get_env(self, *args, **kwargs) -> sliver_pb2.EnvInfo: return await super().get_env(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.SetEnv) async def set_env(self, *args, **kwargs) -> sliver_pb2.SetEnv: return await super().set_env(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.RegistryRead) async def registry_read(self, *args, **kwargs) -> sliver_pb2.RegistryRead: return await super().registry_read(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.RegistryWrite) async def registry_write(self, *args, **kwargs) -> sliver_pb2.RegistryWrite: return await super().registry_write(*args, **kwargs)
[docs] @beacon_taskresult(sliver_pb2.RegistryCreateKey) async def registry_create_key( self, *args, **kwargs ) -> sliver_pb2.RegistryCreateKey: return await super().registry_create_key(*args, **kwargs)