Have you ever wondered what occurs when you push Ctrl+C or right click some highlighted text/image and click copy on Windows?
We’re going to look into detecting changes to the clipboard, clipboard events, and reading the data copied to the clipboard. Additionally, we’ll read the user’s current active window for additional context on where the user was copying data from. We will achieve this with a Windows form and .NET’s Platform Invoke (P/Invoke) functionality. The ability to P/Invoke provides interoperability and allows us to call unmanaged DLLs within our managed C# code.
Clipboard data can be useful during a penetration test to defeat some password managers or can provide additional context about your target in conjunction with screenshots and keyloggers.
Detecting Clipboard Events
Anytime the clipboard is updated, a WM_CLIPBOARDUPDATE message is sent to all windows registered in the clipboard format list. A window can be added to the clipboard format list using AddClipboardFormatListener(). Additionally, the WndProc() method processes all messages for any given Windows form.
Knowing this, we are able to use AddClipboardFormatListener() to add our Windows form to the clipboard format list. Once it is part of the clipboard format list, our Windows form will receive WM_CLIPBOARDUPDATE messages which we can listen for by overriding the WndProc() method. When a WM_CLIPBOARDUPDATE message is received, we can read the data from the clipboard or perform any other actions when the contents of the clipboard are changed.
Most of the code I will be showing is not original work. I’ve simply moved things around, added some functionality and added additional comments. I’d like to preface this section with the following links:
The gist and the two stack overflow posts are the main source for most of the code I used.
The NativeMethods class holds various constants and native Windows functions that we are calling through P/Invoke.
Originally, I assigned the STAThread attribute to the Main() method. However, when using execute-assembly in Cobalt Strike I ran into the following error.
It seemed that the STAThread attribute was not being assigned to the Main() method when using execute-assembly. The Clipboard class serves to override the Clipboard.GetText() method so that we can assign the STAThread attribute to our thread and retrieve the clipboard contents.
The ClipboardNotification class adds our window to the clipboard format listeners list and overrides the WndProc() method. When our window receives the WM_CLIPBOARDUPDATE message, the user’s current active window is sent to standard output as well as the contents of the clipboard.
Below is an example of the output from testing:
Below is some testing against common password managers such as Keepass and 1Password:
As you can see, we were able to identify when the clipboard is updated in order to read the clipboard contents and the current active windows. Thanks for reading! You can find my code at https://github.com/justinbui/SharpClipboard. I hope you learned a little about the Windows clipboard or C# today. If you spot any errors, please let me know by Twitter (@slyd0g) or by e-mail (firstname.lastname@example.org).
Considerations / Future Work
- If using this with Cobalt Strike, it will keep a post-exploitation process spawned (as set by spawnto) which could lead to detection. You will have to manually kill the job using jobkill.
- I plan to create a small aggressor script to start/stop the clipboard monitoring. Eventually, I want to create a copy event store similar to the credential store already in Cobalt Strike.