You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When certain services are run within Metasploit (mainly payload handlers, but others as well), they use the "Service Manager", which manages lifetime, including sharing a service instance between multiple jobs.
The bug
When stopping a service run by the service manager, the process should be as follows:
Call the ServiceManager's stop_service static method
It will call the module's stop_service instance method
It will call the service's deref instance method
If this is the last reference to it, it will call the ServiceManager's stop_service static method (again) - I guess because the ServiceManager itself has a reference to it, so we use that special knowledge to remove that one remaining reference. Then it'll return true to say it actually stopped.
This process is violated by the HTTP service handler, which seemingly does not trust the reference counting functionality. Instead, it inspects its own internals and realises "Other resources are still registered on this handler, I'd better not stop the service". This means it chooses not to call stop_service (seemingly presuming that stop_service will actually stop the service... I guess I can understand the confusion). This means that the job terminates, but the service is not dereferenced. The result is that, if you launch the HTTP service twice, with different LURIs, killing one of these jobs will not dereference the service. Killing the second job will dereference it, thus leaving the reference count one higher than it should be, and thus the port is held open indefinitely.
msf6 > use exploit/multi/handler
[*] Using configured payload generic/shell_reverse_tcp
msf6 exploit(multi/handler) > set payload linux/x64/meterpreter_reverse_http
payload => linux/x64/meterpreter_reverse_http
msf6 exploit(multi/handler) > set lport 8080
lport => 8080
msf6 exploit(multi/handler) > set lhost 0.0.0.0
lhost => 0.0.0.0
msf6 exploit(multi/handler) > run -j
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.
[*] Started HTTP reverse handler on http://0.0.0.0:8080
msf6 exploit(multi/handler) > set luri abc
luri => abc
msf6 exploit(multi/handler) > run -j
[*] Exploit running as background job 1.
[*] Exploit completed, but no session was created.
[*] Started HTTP reverse handler on http://0.0.0.0:8080/abc
msf6 exploit(multi/handler) > jobs
Jobs
====
Id Name Payload Payload opts
-- ---- ------- ------------
0 Exploit: multi/handler linux/x64/meterpreter_reverse_http http://0.0.0.0:8080
1 Exploit: multi/handler linux/x64/meterpreter_reverse_http http://0.0.0.0:8080/abc
msf6 exploit(multi/handler) > jobs -k 0
[*] Stopping the following job(s): 0
[*] Stopping job 0
msf6 exploit(multi/handler) > jobs -k 1
[*] Stopping the following job(s): 1
[*] Stopping job 1
msf6 exploit(multi/handler) > jobs
Jobs
====
No active jobs.
msf6 exploit(multi/handler) > run -j
[*] Exploit running as background job 2.
[*] Exploit completed, but no session was created.
[-] Handler failed to bind to 0.0.0.0:8080
[-] Handler failed to bind to 0.0.0.0:8080
[-] Exploit failed [bad-config]: Rex::BindFailed The address is already in use or unavailable: (0.0.0.0:8080).
Sessions are complicit in this circumvention of process, in that they also do a dereference (from the shutdown_passive_dispatcher method), but never add a reference in the first place. It turns out that in this situation, two wrongs kind of make a right, since they won't do a dereference while a resource is attached to the server, and the server won't do a dereference while a session is attached to the handler. Effectively, we have an implicit reference counting implementation on top of our reference counting implementation. There's probably a race condition there if a session ends while a job is being terminated, or if two sessions end at the same time; they would both see each other and choose not to dereference; but it's probably also unlikely. Nonetheless, we have to be aware of this when making a fix.
The text was updated successfully, but these errors were encountered:
Background
When certain services are run within Metasploit (mainly payload handlers, but others as well), they use the "Service Manager", which manages lifetime, including sharing a service instance between multiple jobs.
The bug
When stopping a service run by the service manager, the process should be as follows:
stop_service
static methodstop_service
instance methodderef
instance methodstop_service
static method (again) - I guess because the ServiceManager itself has a reference to it, so we use that special knowledge to remove that one remaining reference. Then it'll return true to say it actually stopped.This process is violated by the HTTP service handler, which seemingly does not trust the reference counting functionality. Instead, it inspects its own internals and realises "Other resources are still registered on this handler, I'd better not stop the service". This means it chooses not to call
stop_service
(seemingly presuming thatstop_service
will actually stop the service... I guess I can understand the confusion). This means that the job terminates, but the service is not dereferenced. The result is that, if you launch the HTTP service twice, with different LURIs, killing one of these jobs will not dereference the service. Killing the second job will dereference it, thus leaving the reference count one higher than it should be, and thus the port is held open indefinitely.Sessions are complicit in this circumvention of process, in that they also do a dereference (from the
shutdown_passive_dispatcher
method), but never add a reference in the first place. It turns out that in this situation, two wrongs kind of make a right, since they won't do a dereference while a resource is attached to the server, and the server won't do a dereference while a session is attached to the handler. Effectively, we have an implicit reference counting implementation on top of our reference counting implementation. There's probably a race condition there if a session ends while a job is being terminated, or if two sessions end at the same time; they would both see each other and choose not to dereference; but it's probably also unlikely. Nonetheless, we have to be aware of this when making a fix.The text was updated successfully, but these errors were encountered: