Gửi #992814: slackero phpwcms <= 1.12.3 Unrestricted Upload / Remote Code Execution (CWE-434)thông tin

tiêu đềslackero phpwcms <= 1.12.3 Unrestricted Upload / Remote Code Execution (CWE-434)
Mô tả--- title: "phpwcms 1.12.3 public Form upload permits unauthenticated .phtml upload and remote code execution" status: private-draft confidential: true reporter: capivara-research product: phpwcms tested_version: 1.12.3 tested_commit: b8ebd5c8f2e21d187fc006fd8e9f72cfcac9c5cc component: include/inc_front/content/cnt23.article.inc.php weakness: CWE-434 severity: Critical cvss_v3_1: "9.8 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)" date: 2026-08-21 --- # Private Security Report: Unauthenticated `.phtml` Upload to RCE > **Confidential:** This report and its proof of concept are intended only for the phpwcms maintainer until a fix and coordinated disclosure are available. ## Executive Summary The public Form content part accepts an uploaded `.phtml` file when no explicit `accept` list is configured. The default extension denylist blocks `.php`, but it does not block `.phtml`. The uploaded file is written with its dangerous extension to the web-accessible `content/form` directory. The distributed `content/form/.htaccess` denies only `*.php`. A standard Debian Apache PHP configuration maps `.phtml` to the PHP handler. Consequently, an unauthenticated visitor can upload PHP code through a public form, predict the server-generated filename from the HTTP `Date` header, request the uploaded file, and execute arbitrary PHP code. This was reproduced end to end on phpwcms 1.12.3 without disabling form tracking or authentication controls. A normal anti-spam hidden field was obtained by first requesting the public form. ## Suggested Classification - **CWE:** CWE-434, Unrestricted Upload of File with Dangerous Type - **Suggested severity:** Critical - **Suggested CVSS 3.1:** `9.8` - **Vector:** `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H` The score applies when a public Form content part contains an upload field using the default denylist and the web server executes `.phtml`, as standard Debian Apache/mod_php does. On servers that do not execute `.phtml`, direct RCE may not occur, but a dangerous attacker-controlled file is still stored beneath the document root. ## Affected Configuration and Preconditions The tested configuration has: 1. phpwcms 1.12.3. 2. A publicly reachable Form content part (`content type 23`). 3. An upload field in that form. 4. No explicit `accept` list restricting the field to safe extensions. 5. The default upload folder, `content/form`. 6. Apache configured to execute `.phtml` as PHP. No phpwcms account or backend access is required. The administrator only needs to have published an ordinary form containing an upload field. ## Tested Environment | Component | Tested value | |---|---| | phpwcms | 1.12.3 | | Git commit | `b8ebd5c8f2e21d187fc006fd8e9f72cfcac9c5cc` | | Release date | 2026-07-19 | | PHP | 8.4.24 | | Apache | 2.4.68 (Debian) | | MariaDB | 11.8.8 | | Lab target | `http://127.0.0.1:8098/` | | Form tracking | Enabled | | Authentication | None, public frontend form | The repository documents Apache with `.htaccess` as a supported deployment: https://github.com/slackero/phpwcms ## Root Cause ### 1. Incomplete default denylist `include/inc_front/content/cnt23.article.inc.php:1177-1186` selects the web-accessible upload folder and defines a denylist that omits `.phtml` and other server-dependent executable extensions: ```php if(empty($cnt_form['upload_value']['folder'])) { $cnt_form['upload_value']['folder'] = 'content/form'; } if(empty($cnt_form['upload_value']['exclude'])) { $cnt_form['upload_value']['exclude'] = 'php,asp,php3,php4,php5,aspx,cfm,js,exe,com,bat,app,sh,jar,java'; } ``` ### 2. Validation uses only the client filename extension `include/inc_front/content/cnt23.article.inc.php:1206-1231` creates a regular expression from that denylist and then moves the file directly into the selected folder: ```php if(empty($cnt_form['upload_value']['accept'])) { $cnt_form['upload_value']['exclude'] = convertStringToArray($cnt_form['upload_value']['exclude'], ','); $cnt_form['upload_value']['exclude'] = implode('|', $cnt_form['upload_value']['exclude']); $cnt_form['upload_value']['regexp'] = '/(\.'.$cnt_form['upload_value']['exclude'].')$/'; } $cnt_form['upload_value']['filename'] = time().'_'.sanitize_filename($_FILES[$POST_name]['name']); if( (!empty($cnt_form['upload_value']['exclude']) && preg_match($cnt_form['upload_value']['regexp'], strtolower($_FILES[$POST_name]['name']))) || !@move_uploaded_file( $_FILES[$POST_name]['tmp_name'], PHPWCMS_ROOT.'/'.$cnt_form['upload_value']['folder'].'/'.$cnt_form['upload_value']['filename'] ) ) { // Error handling omitted. } ``` `sanitize_filename()` removes path separators and special characters, but intentionally preserves the extension. It therefore does not neutralize `.phtml`. ### 3. The provided directory protection blocks only `.php` The complete distributed `content/form/.htaccess` is: ```apache <Files *.php> Order allow,deny Deny from all </Files> ``` This does not match `.phtml`. ### 4. Standard Apache/PHP maps `.phtml` to PHP The tested Debian PHP module configuration contains: ```apache <FilesMatch "\.ph(?:ar|p|tml)$"> SetHandler application/x-httpd-php </FilesMatch> ``` Therefore, a request for an uploaded `.phtml` file executes it as PHP. ## End-to-End Reproduction ### Benign payload The PoC does not run operating-system commands. It only prints a fixed marker and the PHP version: ```php <?php echo 'PHPWCMS_PHTML_RCE_PROOF_' . PHP_VERSION; ?> ``` SHA-256: ```text 6465009477779a1166fd8fd19e914455028d2ce46281fa740955d0a44ed54edc poc.phtml ``` ### Manual HTTP flow 1. Request the public page containing the upload form. 2. Extract the normal hidden `cpID` field and form-tracking field from the HTML. 3. Submit `positive.phtml` in the upload field using `multipart/form-data`. 4. Read the HTTP `Date` header from the POST response. 5. Try the small timestamp window as `content/form/<unix_timestamp>_positive.phtml`. 6. Request the matching URL and observe the PHP-generated marker. The tested form exposed these fields: ```html <input type="file" name="proof" id="proof" /> <input type="hidden" name="cpID1" value="1" /> <input type="hidden" name="61303361c5eabca6b1326af4ac2c6542" value="1787353672" /> ``` The second hidden field is session/request-specific. An attacker obtains it from the initial GET rather than hard-coding it. ### Automated local reproduction The attached `reproduce-local.sh` is deliberately restricted to `127.0.0.1` or `localhost` targets. Run it against an isolated phpwcms installation: ```bash ./reproduce-local.sh http://127.0.0.1:8098/ /tmp/phpwcms-lab/content/form ``` Actual output: ```text Negative control: .php rejected (no new .php file) Positive control: .phtml saved as 1787353672_positive.phtml Remote discovery: http://127.0.0.1:8098/content/form/1787353672_positive.phtml Execution response: PHPWCMS_PHTML_RCE_PROOF_8.4.24 Evidence: /root/cve-research/international/phpwcms-lab/evidence/reproduction ``` ### Negative control Uploading the same benign structure with a `.php` extension was rejected by the existing denylist. The page returned: ```html <div class="form-error on-send"> <p>Upload failed</p> ... <td class="error">Upload error</td> ``` The file listing was unchanged after the `.php` request. This demonstrates that the upload control is active and that `.phtml` is a specific extension-filter bypass rather than a disabled validation path. ### Positive control The `.phtml` upload added exactly one file: ```text 1787353672_positive.phtml ``` The server returned this timestamp in the upload response: ```http HTTP/1.1 200 OK Date: Fri, 21 Aug 2026 23:07:52 GMT Server: Apache/2.4.68 (Debian) X-phpwcms-Release: 1.12.3 ``` Converting the `Date` value Vendor coordination Vendor contacted via GHSA-2c7r-324f-fxm4. CVE assignment requested from VulDB; no CVE known at submission time.
Nguồn⚠️ https://github.com/slackero/phpwcms/security/advisories/GHSA-2c7r-324f-fxm4
Người dùng
 fernando viana (UID 101484)
Đệ trình21/09/2026 02:15 (cách đây 24 ngày)
Kiểm duyệt13/10/2026 17:12 (23 days later)
Trạng tháiđược chấp nhận
Mục VulDB417752 [slackero phpwcms đến 1.9.49/1.12.3 Extension Check cnt23.article.inc.php POST_name nâng cao đặc quyền]
điểm20

Want to know what is going to be exploited?

We predict KEV entries!