lsassy icon indicating copy to clipboard operation
lsassy copied to clipboard

Help Needed: Automating tests

Open Hackndo opened this issue 2 years ago • 3 comments
trafficstars

lsassy

Description

lsassy is a Python tool designed to remotely extract credentials from a set of hosts, particularly targeting the lsass process on these hosts. This is done in two steps First, code must be executed on the remote target to dump lsass. Then, the dump must be parsed remotely to extract the passwords.

Workflow

Here is how a dump and parsing works in a nutshell:

https://github.com/Hackndo/lsassy/blob/4b1ddf1b3491b014aa27a68f3aa26cb0c962b0a5/lsassy/console.py#L99

From console.py, the ThreadPool is used to execute different lsassy instances in different threads

https://github.com/Hackndo/lsassy/blob/4b1ddf1b3491b014aa27a68f3aa26cb0c962b0a5/lsassy/core.py#L149

Get an SMB session with the target and provided credentials (checks for admin rights)

https://github.com/Hackndo/lsassy/blob/4b1ddf1b3491b014aa27a68f3aa26cb0c962b0a5/lsassy/core.py#L170

Get the dumping method (defined globally in https://github.com/Hackndo/lsassy/blob/master/lsassy/dumpmethod/init.py and every dump method will override get_commands)

https://github.com/Hackndo/lsassy/blob/4b1ddf1b3491b014aa27a68f3aa26cb0c962b0a5/lsassy/core.py#L175 to actually dump lsass remotely.

https://github.com/Hackndo/lsassy/blob/4b1ddf1b3491b014aa27a68f3aa26cb0c962b0a5/lsassy/dumpmethod/init.py#L291

For dumping lsass, a command line is remotely executed on the target using one of the executors (SMB using services by default, code stolen from impacket)

https://github.com/Hackndo/lsassy/blob/4b1ddf1b3491b014aa27a68f3aa26cb0c962b0a5/lsassy/dumpmethod/init.py#L300

Checks if dump was successful

https://github.com/Hackndo/lsassy/blob/4b1ddf1b3491b014aa27a68f3aa26cb0c962b0a5/lsassy/core.py#L193

Then back to core.py, instantiating Parser that will use Pypykatz project to parse the lsass dump remotely.

https://github.com/Hackndo/lsassy/blob/4b1ddf1b3491b014aa27a68f3aa26cb0c962b0a5/lsassy/core.py#L208

To write credentials in console (and file if asked)

Goal

I want to create tests for lsassy to ensure that all features and options work correctly with each new version release. I know how to create tests for everything that happens locally on my machine (and on Github actions), like testing threads number, instantiating classes dynamically, stuff like that.

My issue

The problem I encounter is testing network functionalities. I know about mock from unittest but I don't think it's enough for what I need.

For instance:

  • How to test that command execution works on a remote target:
    • Is there an issue with the command line sent to the target
      • https://github.com/Hackndo/lsassy/blob/master/lsassy/dumpmethod/comsvcs.py#L16
      • ...
    • Does the execution method used work
      • Service creation & execution https://github.com/Hackndo/lsassy/blob/master/lsassy/exec/smb.py
      • Scheduled task creation and launch https://github.com/Hackndo/lsassy/blob/master/lsassy/exec/task.py
      • ...
  • How to test that the filtering parameters provided to the command line are taken into account
    • Tickets harversting
    • DPAPI master keys
    • Dump only users
  • How to test the dumping/parsing options
    • Don't delete remote dump
    • Only parse remote dump
    • Only dump lsass, without parsing
    • ...
  • How to test authentication mechanism
    • Using NTML
    • Using Kerberos

These are behaviors I can test if I execute lsassy in a controlled environment, with a Windows machine whose IP address I know. However, I would like to be able to launch tests from anywhere.

Ideas

One solution I can think of would be to use Docker, with a Linux machine executing the tool, and a Windows machine that would be the target.

But I'm not sure it's a good solution, and I would really appreciate some input

Thanks a lot

Hackndo avatar Oct 09 '23 20:10 Hackndo

It is possible to create a GitHub Workflow (actions), that will execute jobs on 2 separates runners (See https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job ).

I think here, that you can create a workflow that will trigger on commit on the main branch. I'm not an expert in writing workflow but it could be something like this :

name: Lsassy tests

on:
  push:
    branches:
      - main

jobs:
  windows:
    runs-on: windows-latest

    steps:
    - name: Checkout repository
      uses: actions/checkout@v2

    - name: Running lsassy on windows
      run: |
        <YOUR CODE HERE>

  linux:
    runs-on: ubuntu-latest
    needs: windows  #So you make sure the windows tests have been done

    steps:
    - name: Checkout repository
      uses: actions/checkout@v2

    - name: Checking the results and trying remote executions
      run: |
        <YOUR CODE HERE>

It is also possible to use self-hosted runners. ( https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners ) It will allow you to control your environment so you'll be able to do every network tests.

Hope it will help you.

Driikolu avatar Oct 10 '23 23:10 Driikolu

I think I found a workaround. I use a windows runner (provided by Github), I run lsassy on this runner, and I dump lsass on localhost. For this to work, I need to add a local admin and set a registry key, but it seems to work quite well https://github.com/Hackndo/lsassy/commit/92e4ae429404bc68052870e4ddcd6346fd4dd8cf

Hackndo avatar Oct 12 '23 00:10 Hackndo

@Hackndo, great workaround.

It sounds that

  • all data related commands can be tested through unit tests ( like parsing the dump )
  • all "client" (remote handler) can be tested by mocking cases (wrong responses, good ones and special cases)
  • all remote operations (that run on windows hosts) can be either tested directly on the host based on configuration and in that case would require to setup several hosts to test your cases
  • and of course have complete (or integration) tests cases you'll need to pop off the whole stack.

Of course everything I described can be combined. I'm so sorry I didn't had time to look at the code you provided.

But if you can have a quick win by checking the whole is working correctly do so ! Your idea and "workaround" is the way to go. But when you'll need to identify which part is faulty then try to unit test it as the fault arise. So it doesn't happen again. Not sure I would have time but I'll look at the code and try to see how all the above is possible

Cya

NokiDev avatar Oct 16 '23 19:10 NokiDev