avoid race condition in the extension callback on windows beacons #1159
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Card
Beacons on Windows can crash if you run too many extensions at the same time.
I believe this is because of a race condition in WindowsExtenions.Call, which stores the function
onFinish
referred to in the callback on the struct.The change is to use a closure and don't reference the struct.
Details
I've noticed the following behaviour: Write yourself an extension or install one from the armory. For example
armory install sa-whoami
. Get a Windows HTTP beacon and callsa-whoami
slowly, only once per check in. Everything will be fine (well, the first time there is no output, but I don't yet know why). Now runsa-whoami
lots of times so that they all execute on the next check in. There is a good chance your implant will die with exit code 2, probably a panic.In the code I saw the following:
beaconMain
launches a Goroutine for each task, with an anonymous function for results processing: https://github.com/BishopFox/sliver/blob/master/implant/sliver/sliver.go#L412Run
on the extension, again with an anonymous callback function wrapping the one from above: https://github.com/BishopFox/sliver/blob/master/implant/sliver/handlers/handlers_windows.go#L718Run
toCall
in the extension and it stores the callback on the extension struct: https://github.com/BishopFox/sliver/blob/master/implant/sliver/extension/extension_windows.go#L104extensionCallback
passed to it which is related to the original callback only by referring to the callback on the struct.Now my thinking is that because all calls to extensions are created in their own Goroutine they may overwrite the
onFinish
callback stored on the struct, which is stored before a lock is acquired. Moreover, in beacon mode it seems that each call to the extension also loads the extension. Not sure about it but I wonder if the entire extension struct may be garbage-collected by the time the DLL calls back.The change is to decouple the DLL callback from the extension struct. This should ensure the reference to the callback survives until it is called.
In my experiments the beacon did not crash a single time anymore after this change no matter the number of extension calls. Still, you probably want to give it a try yourself before merging this!!! I only tried with
sa-whoami
and an extension I wrote myself, with HTTP baecon.