Search
Find a vulnerability
Search criteria
1 vulnerability found for Maian Cart by Maianscriptworld
GCVE-1988-2026-0469
Vulnerability from gna-1988 – Published: 2026-11-02 07:30 – Updated: 2026-11-02 07:30
VLAI
EPSS
VEX
Title
[0day-rubbish] Maian Cart 3.8 addBanners unrestricted banner upload to PHP webshell and administrator command execution (7.2 primary, PR:H)
Summary
0day Rubbish Research Team is publicly disclosing a vulnerability in Maian
Cart 3.8, the self-hosted PHP shopping-cart system from Maian Media (Maian
Script World), verified end to end against a real installation of that
version.
Type: unrestricted upload of a file with a dangerous type (CWE-434), realized
as OS command execution (CWE-78) through an attacker-supplied PHP webshell,
with CWE-269 bearing on the execution privilege context. The Store Banners
page of the administration backend (route /admin/index.php?p=settings&s=9)
dispatches multipart uploads to addBanners() in
admin/control/classes/class.system.php:376-413, which reads the raw client
filename at line 386, derives the stored extension from it at line 392 with
strrchr on the lower-cased name, composes the stored name at line 393 as the
banner prefix plus a counter from the banners table plus that extension, and
writes the file with move_uploaded_file at line 401, a second call site being
recorded at line 410. There is no extension allow-list, no MIME or finfo
validation and no getimagesize() content check anywhere on the path;
mc_safeImport at line 377 is an SQL-escaping pass over $_POST and never
touches $_FILES, and is_uploaded_file only confirms the file arrived over
HTTP. With the shipped defaults RENAME_BANNERS=1 and BANNER_PREFIX='img_',
an upload named shell.php is stored as img_1.php: the rename regenerates the
base name and preserves the attacker's extension. The destination,
content/_theme_default/images/banners/, is inside the document root and
carries no deny rule, in explicit contrast to admin/import/.htaccess,
admin/attachments/.htaccess and content/_theme_default/cache/.htaccess, all
of which ship Deny from all, so a plain GET for the stored img_<id>.php is
handed to the PHP interpreter and a one-line shell_exec webshell yields
command execution in the web server process context. move_uploaded_file runs
before the INSERT INTO banners statement, so the shell survives even a failing
INSERT. The product's own image allow-list ($imgAllow at class.products.php:9,
applied at :1213 and :1907 in addAdditionalProductPictures) is the one-line
control this handler omits.
Scoring. Four readings are published with their vectors, labelled, rather than
one number:
- PRIMARY, and the only classification this advisory claims: 7.2 High,
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H. The sink sits behind the
webmaster gate at admin/control/system-load.php:79
(mc_isWebmasterLoggedIn, whose underlying test at functions.php:324-336
requires a fully populated webmaster session array).
- CONDITIONAL, 9.8 Critical, CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H,
for a deployment that retains an unchanged, known or guessable
administrator credential. This reading is this advisory's own construction,
not a figure produced by the research record, which scores the finding only
under the 7.2 vector and records that the unauthenticated objective was not
reached. It describes deployment state rather than product state: the
product ships no fixed factory credential, the install wizard writes the
operator-chosen USERNAME and PASSWORD into access.php, and no installation
whose credentials were guessable was verified. Its sole basis is the
companion observation that the installer-provisioned credential is never
forced to rotate (a CWE-798-class note about persistent provisioned
credentials without a rotation gate). It does not change the primary
classification.
- CONSIDERED AND NOT ADOPTED: 8.8 High,
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, which differs from the
primary vector only in Privileges Required and was not adopted because no
role below webmaster reaches the settings module in the recorded gate
model; and 9.1 High,
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H, which would require Scope
Changed and does not fit, the vulnerable component and the impacted
component being the same security authority.
Authentication: administrator session. The login handler
(admin/control/modules/system/portal.php:26-47) requires process=1, a cs_rf
CSRF token matching the hidden field served on the login page, a user value
equal to the USERNAME constant in access.php and a password verified through
mc_PassHash; the trigger is isset($_POST['process_banners']) at
settings.php:84-87. The unauthenticated objective was pursued and exhaustively
ruled out: ajax.php exposes no file-write or command primitive an
unauthenticated caller can steer; forging the persistent administrator cookie
is infeasible because mc_encrypt(SECRET_KEY . DB_NAME) requires the
per-installation database name, which is not remotely discoverable; the only
gadget found was PHPMailer's __destruct, not exploitable into a dangerous sink
in this codebase, so no object-population chain exists; the login path is not
injectable; and every move_uploaded_file call site identified sits under the
gated /admin/ backend. No unauthenticated route to the sink is claimed.
Impact: arbitrary OS command execution as the web server process, uid=0(root)
in the laboratory capture because the test web tier ran as root, typically
www-data or apache in production. Full read and write access to store source
code, application configuration including the database credentials the
application itself reads, uploaded order attachments and the surrounding file
system; database access with the application's own privileges, meaning the
store's orders, customer records and settings; persistence, since the stored
img_<id>.php outlives the administrator session and nothing in the product
scans for or removes it; and a pivot into whatever segments the store host can
reach. For an insider or credential-holder the upload is audit-invisible: it
appears in the interface as an ordinary banner addition.
Verification boundary, stated plainly. Verified end to end against a real Maian
Cart 3.8 installation: the product's own install wizard run to completion on a
Linux laboratory host, creating 62 database tables and populating the settings
rows, with MySQL 8.0.46 on a dedicated laboratory schema and account, and the
web tier being the PHP built-in server bound to loopback for laboratory
safety. That process ran as root for the session in question, which is why
uid=0(root) was observed and why the stored 39-byte shell, exactly the length
of the one-line payload, was owned by root. Three independent runs reached
command execution, the third being a from-scratch re-analysis pass by an
independent reviewer who rebuilt the audit and shipped a second, distinctly
named shell through the same handler, stored as img_3.php. An adversarial
refutation review searched for a global upload sanitizer, a deny rule for the
banners directory, FilesMatch restrictions on .php, getimagesize usage on this
path, web-server configuration hardening and install-time hardening, found
none, and left the finding standing. No vendor-hosted or third-party
installation was touched, and no post-exploitation was performed beyond
identity and file-system probes.
What was not exercised: the production Apache and mod_php stack was not run,
so execution under Apache rests on static evidence plus the research record's
own assertion that Apache with mod_php is the default production deployment,
an assertion not substantiated from a vendor document; a non-root process
identity was not demonstrated, so the claim is command execution at whatever
identity the web server holds; the updateBanners edit path, the
TRADE_THEME_FOLDER branch and the dead-code uploadWebLogo variant
(class.system.php:956, same pattern, no call site, excluded from the finding)
were not exercised; only 3.8 was tested. One precondition is unresolved and
stated rather than smoothed over: the banners directory must already exist on
disk. It was absent in the first laboratory iteration, move_uploaded_file
failed with a no-such-file-or-directory error, and the directory was created
manually before a later iteration succeeded; move_uploaded_file does not
create directories and the handler's only filesystem guard tests the
destination file, not the directory. No artifact establishes which product
component creates it, so no first-upload directory creation is claimed.
Related product, scoped out. The research record notes that the sibling Maian
Support product exhibits the same CWE-434-class pattern, a content tree
without .htaccess protection combined with Apache AllowOverride All. That
product is out of scope for this advisory: no exploitation attempt was made
there and no identifier is claimed for it.
Fix direction, in short: enforce an extension allow-list at the sink reusing
the control already in class.products.php; validate uploaded content with
getimagesize() or finfo; discard the client-supplied extension and name stored
files server-side; make the banner storage non-executable by moving it outside
the document root or shipping a deny rule plus documented nginx-equivalent
guidance; route every move_uploaded_file call site through one shared
validator; force first-login rotation of the installer-provisioned
administrator credential; and remove or gate the dead uploadWebLogo code.
Full technical analysis and a reproducible proof-of-concept:
https://0day-rubbish.com/blog/maian-cart-addbanners-upload-extension-webshell-rce
Project archive (ongoing disclosure series):
https://github.com/Exploit-Garbage/0day-Rubbish
The vendor has been notified through the support mailbox it publishes for its
products. No vulnerability identifier has been assigned to this finding yet.
--
0day Rubbish Research Team
disclosure () 0day-rubbish com
https://0day-rubbish.com
_______________________________________________
Sent through the Full Disclosure mailing list
https://nmap.org/mailman/listinfo/fulldisclosure
Web Archives & RSS: https://seclists.org/fulldisclosure/
Severity
No CVSS data available.
Assigner
References
7 references
Impacted products
1 product
| Vendor | Product | Version | |
|---|---|---|---|
| Maianscriptworld | Maian Cart |
Affected:
unknown
|
{
"containers": {
"cna": {
"affected": [
{
"product": "Maian Cart",
"vendor": "Maianscriptworld",
"versions": [
{
"status": "affected",
"version": "unknown"
}
]
}
],
"credits": [
{
"lang": "en",
"type": "finder",
"value": "disclosure via Fulldisclosure"
}
],
"descriptions": [
{
"lang": "en",
"value": "0day Rubbish Research Team is publicly disclosing a vulnerability in Maian\nCart 3.8, the self-hosted PHP shopping-cart system from Maian Media (Maian\nScript World), verified end to end against a real installation of that\nversion.\n\nType: unrestricted upload of a file with a dangerous type (CWE-434), realized\nas OS command execution (CWE-78) through an attacker-supplied PHP webshell,\nwith CWE-269 bearing on the execution privilege context. The Store Banners\npage of the administration backend (route /admin/index.php?p=settings\u0026s=9)\ndispatches multipart uploads to addBanners() in\nadmin/control/classes/class.system.php:376-413, which reads the raw client\nfilename at line 386, derives the stored extension from it at line 392 with\nstrrchr on the lower-cased name, composes the stored name at line 393 as the\nbanner prefix plus a counter from the banners table plus that extension, and\nwrites the file with move_uploaded_file at line 401, a second call site being\nrecorded at line 410. There is no extension allow-list, no MIME or finfo\nvalidation and no getimagesize() content check anywhere on the path;\nmc_safeImport at line 377 is an SQL-escaping pass over $_POST and never\ntouches $_FILES, and is_uploaded_file only confirms the file arrived over\nHTTP. With the shipped defaults RENAME_BANNERS=1 and BANNER_PREFIX=\u0027img_\u0027,\nan upload named shell.php is stored as img_1.php: the rename regenerates the\nbase name and preserves the attacker\u0027s extension. The destination,\ncontent/_theme_default/images/banners/, is inside the document root and\ncarries no deny rule, in explicit contrast to admin/import/.htaccess,\nadmin/attachments/.htaccess and content/_theme_default/cache/.htaccess, all\nof which ship Deny from all, so a plain GET for the stored img_\u003cid\u003e.php is\nhanded to the PHP interpreter and a one-line shell_exec webshell yields\ncommand execution in the web server process context. move_uploaded_file runs\nbefore the INSERT INTO banners statement, so the shell survives even a failing\nINSERT. The product\u0027s own image allow-list ($imgAllow at class.products.php:9,\napplied at :1213 and :1907 in addAdditionalProductPictures) is the one-line\ncontrol this handler omits.\n\nScoring. Four readings are published with their vectors, labelled, rather than\none number:\n- PRIMARY, and the only classification this advisory claims: 7.2 High,\n CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H. The sink sits behind the\n webmaster gate at admin/control/system-load.php:79\n (mc_isWebmasterLoggedIn, whose underlying test at functions.php:324-336\n requires a fully populated webmaster session array).\n- CONDITIONAL, 9.8 Critical, CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H,\n for a deployment that retains an unchanged, known or guessable\n administrator credential. This reading is this advisory\u0027s own construction,\n not a figure produced by the research record, which scores the finding only\n under the 7.2 vector and records that the unauthenticated objective was not\n reached. It describes deployment state rather than product state: the\n product ships no fixed factory credential, the install wizard writes the\n operator-chosen USERNAME and PASSWORD into access.php, and no installation\n whose credentials were guessable was verified. Its sole basis is the\n companion observation that the installer-provisioned credential is never\n forced to rotate (a CWE-798-class note about persistent provisioned\n credentials without a rotation gate). It does not change the primary\n classification.\n- CONSIDERED AND NOT ADOPTED: 8.8 High,\n CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, which differs from the\n primary vector only in Privileges Required and was not adopted because no\n role below webmaster reaches the settings module in the recorded gate\n model; and 9.1 High,\n CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H, which would require Scope\n Changed and does not fit, the vulnerable component and the impacted\n component being the same security authority.\n\nAuthentication: administrator session. The login handler\n(admin/control/modules/system/portal.php:26-47) requires process=1, a cs_rf\nCSRF token matching the hidden field served on the login page, a user value\nequal to the USERNAME constant in access.php and a password verified through\nmc_PassHash; the trigger is isset($_POST[\u0027process_banners\u0027]) at\nsettings.php:84-87. The unauthenticated objective was pursued and exhaustively\nruled out: ajax.php exposes no file-write or command primitive an\nunauthenticated caller can steer; forging the persistent administrator cookie\nis infeasible because mc_encrypt(SECRET_KEY . DB_NAME) requires the\nper-installation database name, which is not remotely discoverable; the only\ngadget found was PHPMailer\u0027s __destruct, not exploitable into a dangerous sink\nin this codebase, so no object-population chain exists; the login path is not\ninjectable; and every move_uploaded_file call site identified sits under the\ngated /admin/ backend. No unauthenticated route to the sink is claimed.\n\nImpact: arbitrary OS command execution as the web server process, uid=0(root)\nin the laboratory capture because the test web tier ran as root, typically\nwww-data or apache in production. Full read and write access to store source\ncode, application configuration including the database credentials the\napplication itself reads, uploaded order attachments and the surrounding file\nsystem; database access with the application\u0027s own privileges, meaning the\nstore\u0027s orders, customer records and settings; persistence, since the stored\nimg_\u003cid\u003e.php outlives the administrator session and nothing in the product\nscans for or removes it; and a pivot into whatever segments the store host can\nreach. For an insider or credential-holder the upload is audit-invisible: it\nappears in the interface as an ordinary banner addition.\n\nVerification boundary, stated plainly. Verified end to end against a real Maian\nCart 3.8 installation: the product\u0027s own install wizard run to completion on a\nLinux laboratory host, creating 62 database tables and populating the settings\nrows, with MySQL 8.0.46 on a dedicated laboratory schema and account, and the\nweb tier being the PHP built-in server bound to loopback for laboratory\nsafety. That process ran as root for the session in question, which is why\nuid=0(root) was observed and why the stored 39-byte shell, exactly the length\nof the one-line payload, was owned by root. Three independent runs reached\ncommand execution, the third being a from-scratch re-analysis pass by an\nindependent reviewer who rebuilt the audit and shipped a second, distinctly\nnamed shell through the same handler, stored as img_3.php. An adversarial\nrefutation review searched for a global upload sanitizer, a deny rule for the\nbanners directory, FilesMatch restrictions on .php, getimagesize usage on this\npath, web-server configuration hardening and install-time hardening, found\nnone, and left the finding standing. No vendor-hosted or third-party\ninstallation was touched, and no post-exploitation was performed beyond\nidentity and file-system probes.\n\nWhat was not exercised: the production Apache and mod_php stack was not run,\nso execution under Apache rests on static evidence plus the research record\u0027s\nown assertion that Apache with mod_php is the default production deployment,\nan assertion not substantiated from a vendor document; a non-root process\nidentity was not demonstrated, so the claim is command execution at whatever\nidentity the web server holds; the updateBanners edit path, the\nTRADE_THEME_FOLDER branch and the dead-code uploadWebLogo variant\n(class.system.php:956, same pattern, no call site, excluded from the finding)\nwere not exercised; only 3.8 was tested. One precondition is unresolved and\nstated rather than smoothed over: the banners directory must already exist on\ndisk. It was absent in the first laboratory iteration, move_uploaded_file\nfailed with a no-such-file-or-directory error, and the directory was created\nmanually before a later iteration succeeded; move_uploaded_file does not\ncreate directories and the handler\u0027s only filesystem guard tests the\ndestination file, not the directory. No artifact establishes which product\ncomponent creates it, so no first-upload directory creation is claimed.\n\nRelated product, scoped out. The research record notes that the sibling Maian\nSupport product exhibits the same CWE-434-class pattern, a content tree\nwithout .htaccess protection combined with Apache AllowOverride All. That\nproduct is out of scope for this advisory: no exploitation attempt was made\nthere and no identifier is claimed for it.\n\nFix direction, in short: enforce an extension allow-list at the sink reusing\nthe control already in class.products.php; validate uploaded content with\ngetimagesize() or finfo; discard the client-supplied extension and name stored\nfiles server-side; make the banner storage non-executable by moving it outside\nthe document root or shipping a deny rule plus documented nginx-equivalent\nguidance; route every move_uploaded_file call site through one shared\nvalidator; force first-login rotation of the installer-provisioned\nadministrator credential; and remove or gate the dead uploadWebLogo code.\n\nFull technical analysis and a reproducible proof-of-concept:\n https://0day-rubbish.com/blog/maian-cart-addbanners-upload-extension-webshell-rce\n\nProject archive (ongoing disclosure series):\n https://github.com/Exploit-Garbage/0day-Rubbish\n\nThe vendor has been notified through the support mailbox it publishes for its\nproducts. No vulnerability identifier has been assigned to this finding yet.\n\n--\n0day Rubbish Research Team\ndisclosure () 0day-rubbish com\nhttps://0day-rubbish.com\n_______________________________________________\nSent through the Full Disclosure mailing list\nhttps://nmap.org/mailman/listinfo/fulldisclosure\nWeb Archives \u0026 RSS: https://seclists.org/fulldisclosure/"
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-269",
"description": "CWE-269",
"lang": "en",
"type": "CWE"
},
{
"cweId": "CWE-434",
"description": "CWE-434",
"lang": "en",
"type": "CWE"
},
{
"cweId": "CWE-78",
"description": "CWE-78",
"lang": "en",
"type": "CWE"
},
{
"cweId": "CWE-798",
"description": "CWE-798",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-11-02T07:30:01Z",
"orgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
"shortName": "VULNARCHIVE"
},
"references": [
{
"tags": [
"technical-description",
"exploit"
],
"url": "https://vuln.freearchive.org/archive/full-disclosure/2026/Oct/6"
},
{
"tags": [
"technical-description"
],
"url": "https://seclists.org/fulldisclosure/2026/Oct/6"
},
{
"url": "https://0day-rubbish.com"
},
{
"url": "https://0day-rubbish.com/blog/maian-cart-addbanners-upload-extension-webshell-rce"
},
{
"url": "https://github.com/Exploit-Garbage/0day-Rubbish"
},
{
"url": "https://nmap.org/mailman/listinfo/fulldisclosure"
},
{
"url": "https://seclists.org/fulldisclosure/"
}
],
"source": {
"defect": [
"https://seclists.org/fulldisclosure/2026/Oct/6"
],
"discovery": "EXTERNAL"
},
"title": "[0day-rubbish] Maian Cart 3.8 addBanners unrestricted banner upload to PHP webshell and administrator command execution (7.2 primary, PR:H)",
"x_gcve": [
{
"recordType": "advisory",
"relationships": [],
"vulnId": "GCVE-1988-2026-0469",
"x_vulnarchive": {
"archiveUrl": "https://vuln.freearchive.org/archive/full-disclosure/2026/Oct/6",
"automated": true,
"contentSha256": "996730e626ea94faa931788cc5994301981a5d0a11716f9aa25f9688cf48c3e0",
"evidenceScore": 11,
"messageId": "",
"originalUrl": "https://seclists.org/fulldisclosure/2026/Oct/6",
"policy": "vulnarchive-1",
"sourceFormat": "text/html",
"sourcePublishedAt": "2026-10-05T17:53:29Z"
}
}
]
}
},
"cveMetadata": {
"assignerOrgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
"assignerShortName": "VULNARCHIVE",
"datePublished": "2026-11-02T07:30:01Z",
"dateUpdated": "2026-11-02T07:30:01Z",
"state": "PUBLISHED",
"vulnId": "GCVE-1988-2026-0469"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}