Metasploit Reverse Handler Detector#21551
Open
h00die wants to merge 1 commit into
Open
Conversation
Contributor
Author
|
Just a note, this is still a WIP. while the code seems to work fine, I'm still adding comments, cleaning it up, and trying to figure out a decent method to capture all the info. I expect this to be done end of the week as long as other things don't distract me. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Ever see persistence on your box and get the penetration tester's IP address (or port even) and wonder what they're up to? This module will help with that! This module scans open ports on a host to determine if they're a Metasploit Reverse Handler or not. This module can also help determine what kind of shell they were going to land.
Claude assisted in the development of this module.
An example run (since colors help):

We're able to do this because Metasploit reverse handlers act in unique ways. Reverse shells on connection send "echo " for instance w/o being probed. The Apache server used in http payloads (and fetch) responds to
/with the default200 OKandIt works!. However it also responds to EVERY URI (non-payload bearing) with the same response, which normal (although i'm sure its possible to reconfigure Apache to do it) Apache would respond with 404. There are more, but thats just a quick review.Mermaid decision tree
flowchart TD A["check_port: TCP connect"] -->|refused| R0["Closed / not a listener"] A -->|connected| B["drain_stage: read unsolicited bytes<br/>(up to FIRST_BYTE_WAIT)"] B --> C{"server talked<br/>first?"} C -->|"yes (bytes)"| FP["fingerprint(buf)"] C -->|"no (silent)"| SIL["silent-port fallbacks"] %% ---- fingerprint() ordered checks ---- FP --> F1{"starts with<br/>'echo TOKEN'?"} F1 -->|yes| RShell["Command shell handler<br/>(echo probe) — high"] F1 -->|no| F2{"base64 + 4-byte<br/>big-endian length?"} F2 -->|yes| RPy["python meterpreter<br/>(base64/zlib staged)"] F2 -->|no| F3{"4-byte little-endian<br/>length delivered?"} F3 -->|yes| RWin["Windows native staged<br/>(metsrv/shell) — high"] F3 -->|no| F4{"4-byte big-endian<br/>length delivered?"} F4 -->|yes| RBE["php / java / android<br/>staged — medium"] F4 -->|no| F5{"small buf<br/>with /bin/sh?"} F5 -->|yes| RExec["unix execve staged shell"] F5 -->|no| F6{"starts with 0xFC?"} F6 -->|yes| RStager["Windows raw stager shellcode<br/>(reverse_nonx/ord) — medium"] F6 -->|no| F7{">=128 bytes,<br/>mostly non-text?"} F7 -->|yes| RNative["linux/osx native stage<br/>or RC4/encrypted — low"] F7 -->|no| RBanner["Talks first, not a stage<br/>(unrelated service)"] RShell --> EB{"ECHO_BACK?"} EB -->|yes| CAP["echo token back →<br/>capture AutoRunScript → loot"] %% ---- silent-port fallbacks ---- SIL --> H1["HTTP probe: GET random URI"] H1 --> H1c{"200 + 'It works!'?"} H1c -->|yes| RHttp["reverse_http handler"] H1c -->|no| H2{"Rex 404 page?"} H2 -->|yes| RRex["web_delivery / fetch server"] H2 -->|no| HS["HTTPS probe: TLS + GET"] HS --> HSc{"reply?"} HSc -->|"'It works!'"| RHttps["reverse_https"] HSc -->|"echo / stage"| RSsl["ssl shell / reverse_tcp_ssl"] HSc -->|nothing| DP{"DEEP_PROBE on?"} DP -->|no| SOpen["Open, no data<br/>(stageless / powershell / not MSF)"] DP -->|yes| D1["2nd TCP connection"] D1 --> D1c{"echo on the pair?"} D1c -->|yes| RDouble["ReverseTcpDouble<br/>(cmd/unix/reverse)"] D1c -->|no| D2["2 TLS connections"] D2 --> D2c{"echo on the pair?"} D2c -->|yes| RDoubleSsl["double-SSL handler"] D2c -->|no| D3["send 16-byte UUID"] D3 --> D3c{"fast reply-less<br/>close?"} D3c -->|yes| RPing["pingback — low"] D3c -->|no| SOpen %% ---- UDP (independent, if SCAN_UDP) ---- UDP["check_port_udp (if SCAN_UDP):<br/>send datagram → fingerprint()"] --> RUdp["reverse_udp"]Testing
Testing this PR can be difficult. In theory we'd want to launch every payload and run this module against them all, however that is A LOT, it takes about 4hrs on my system to launch all those, and then since ruby/msf seems to be single threaded it pegs the CPU to 100% for that core. Then the sessions are unreliable because the CPU is trying to do too much. So the best way is to remove sessions that use a duplicate setup handshake or method. If we can find one, we can find them all. So while I have tested this in 10 batches to test every payload, it took a half day to do. Don't do that unless you hate your life. Instead use the following
Also, on
shell_reverse_*type sessions, we may be able to steal commands sent (although it may just beecho ....in reality from modules doing checks).To test
First, create this
msfconsole.rcwith this content, this will start up the multi-handler with a bunch of payload types (tcp, udp, meterpreter, shell, pingbackmsfconsole.rcautorun_demo.rbIf you want to test sending fake commands to verify they get capture, drop this in
~/.msf4/modules/post/test/autorun_demo.rbFinal test
Now run this module against the ports
Output
Verify you get similar results: