login-securite / lsassy

Extract credentials from lsass remotely
https://en.hackndo.com/remote-lsass-dump-passwords/
MIT License
2.05k stars 247 forks source link

Exec methods do not clean up as intended resulting in hanging indefinitely #96

Closed NeffIsBack closed 4 months ago

NeffIsBack commented 4 months ago

Version(s)

Describe the bug

While testing our test suite at NetExec i encountered the problem, that the lsassy module keeps hanging indefinitely when it fails. Taking some time to debug it appears that exec_methods, once executed, aren't "cleaned up" properly (the clean() function of the exec method is never called. This leads to the dcom connection staying open and therefore the weird dcom timer stopping the main thread from executing (see screenshots below).

This can be solved by simply adding exec_method.clean() after execution in line 291: https://github.com/login-securite/lsassy/blob/0b59a5bc340dbaeda4676ae2809167b6c39b7b70/lsassy/dumpmethod/__init__.py#L291 This will call the clean up and therefore termination of the dcom connection and its timer.

Expected behavior

The connection being terminated for each exec method.

Additional Info

As i am not that deep into lsassy i can't say that adding that line won't break any exec_method, as clean() is not implemented in the exec protocol interface IExec. This should probably get added there.

Screenshots

Without calling clean(): image

With the added exec_method.clean() line: image

Debugging: image Added print statement for debugging: image image

NeffIsBack commented 4 months ago

Can be reproduced by executing nxc smb <ip> -u <user> -p <password> -M lsassy. The lsassy debug logs can be (re-)enabled in the logger.py file of netexec

Hackndo commented 4 months ago

Hey there @NeffIsBack The clean() method should be called inside the exec modules. So it shouldn't be needed to call it again, in the __init__.py file.

wmiexec.py

For wmiexec.py, it's either closed in the try block

https://github.com/login-securite/lsassy/blob/0b59a5bc340dbaeda4676ae2809167b6c39b7b70/lsassy/exec/wmi.py#L68-L69

Or self.clean() is called in the except blocks

https://github.com/login-securite/lsassy/blob/0b59a5bc340dbaeda4676ae2809167b6c39b7b70/lsassy/exec/wmi.py#L72

https://github.com/login-securite/lsassy/blob/0b59a5bc340dbaeda4676ae2809167b6c39b7b70/lsassy/exec/wmi.py#L76

So it should always execute

self.iWbemServices.disconnect()
self.dcom.disconnect()

at some point.

So this method shouldn't be blocking.

mmc.py

Regarding mmc.py, there's a missing cleaning call, you're right, and a missing raise call.

A raise call should be called after the Exception: https://github.com/login-securite/lsassy/blob/0b59a5bc340dbaeda4676ae2809167b6c39b7b70/lsassy/exec/mmc.py#L140-L142

As such:

        except Exception as e:
            lsassy_logger.debug("Error : {}".format(e), exc_info=True)
            self.clean()
            raise Exception(e)

Then, a clean() should be called at the end of the function, right before the return True statement

https://github.com/login-securite/lsassy/blob/0b59a5bc340dbaeda4676ae2809167b6c39b7b70/lsassy/exec/mmc.py#L170-L176

As such:

        self.__executeShellCommand[0].Invoke(self.__executeShellCommand[1], 0x409, DISPATCH_METHOD, dispParams, 0, [], [])
        self.clean()
        return True

Conclusion

If you add the raise statement and a self.clean() in mmc.py as explained, does it solve your issue?

Hackndo commented 4 months ago

Hey there, I just tried my fix, and it seems to be working fine. If you have any other issue, please let me know. Thank you for your detailed issue, it helped a lot to figure out what was hapenning.

NeffIsBack commented 4 months ago

Sorry, didn't have the time to get back to you. This indeed fixes the issue, thanks!

Thank you for your detailed issue, it helped a lot to figure out what was hapenning.

Glad i could help :)