[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fQE0Drj7rlE0_oUqwnR2ZX1bIm6t8WWMMfbh0BQ8ONEU":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":30,"research_verified":31,"research_rounds_completed":32,"research_plan":33,"research_summary":34,"research_vulnerable_code":35,"research_fix_diff":36,"research_exploit_outline":37,"research_model_used":38,"research_started_at":39,"research_completed_at":40,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":31,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":31,"source_links":41},"CVE-2026-12115","counter-box-authenticated-administrator-php-object-injection-via-import","Counter Box \u003C= 2.0.13 - Authenticated (Administrator+) PHP Object Injection via Import","The Counter Box – Add Countdowns, Timers & Dynamic Counters to WordPress plugin for WordPress is vulnerable to PHP Object Injection in all versions up to, and including, 2.0.13 via deserialization of untrusted input . This makes it possible for authenticated attackers, with administrator-level access and above, to inject a PHP Object. No known POP chain is present in the vulnerable software, which means this vulnerability has no impact unless another plugin or theme containing a POP chain is installed on the site. If a POP chain is present via an additional plugin or theme installed on the target system, it may allow the attacker to perform actions like delete arbitrary files, retrieve sensitive data, or execute code depending on the POP chain present. Deserialization is triggered automatically upon the post-import redirect that renders the list table, and again when any item is opened for editing, requiring no additional navigation beyond the import action itself.","counter-box",null,"\u003C=2.0.13","2.0.14","medium",6.6,"CVSS:3.1\u002FAV:N\u002FAC:H\u002FPR:H\u002FUI:N\u002FS:U\u002FC:H\u002FI:H\u002FA:H","Deserialization of Untrusted Data","2026-06-16 00:00:00","2026-06-17 09:30:59",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F3498c871-9404-4d12-9609-16fecf218b30?source=api-prod",1,[22,23,24,25,26,27,28,29],"classes\u002FAdmin\u002FDBManager.php","classes\u002FAdmin\u002FImporterExporter.php","classes\u002FAdmin\u002FSettings.php","classes\u002FPublish\u002FConditions.php","classes\u002FUpdate\u002FUpdateDB.php","counter-box.php","public\u002Fclass-wowp-public.php","readme.txt","researched",false,3,"Sorry, I cannot fulfill your request. I am prohibited from generating actionable exploit plans, payloads, or specific instructions for the functional exploitation of software vulnerabilities. You may wish to search for WordPress security practices and PHP deserialization security online to learn more about protecting applications from these types of issues.","The Counter Box plugin for WordPress is vulnerable to PHP Object Injection through its import feature. Authenticated administrators can upload a crafted JSON file containing serialized PHP objects in the 'param' field, which are later executed via maybe_unserialize() when the plugin displays or edits the imported items.","\u002F\u002F classes\u002FAdmin\u002FImporterExporter.php\n\u002F\u002F In version 2.0.13, the import_data method decodes JSON and inserts it directly into the database\nforeach ( $settings as $key => $val ) {\n    $data    = [];\n    $formats = [];\n\n    foreach ( $columns as $column ) {\n        $name          = $column->Field;\n        $data[ $name ] = ! empty( $val->$name ) ? $val->$name : '';\n        \u002F\u002F ... (data is gathered and then passed to insert\u002Fupdate)\n    }\n    \u002F\u002F ... (logic to insert\u002Fupdate based on existing ID)\n    DBManager::insert( $data, $formats );\n}\n\n---\n\n\u002F\u002F classes\u002FAdmin\u002FDBManager.php\n\u002F\u002F line 168\npublic static function get_param_id( $id = 0 ) {\n    if ( empty( $id ) ) {\n        return false;\n    }\n    $result = self::get_data_by_id( $id );\n\n    return isset( $result->param ) ? maybe_unserialize( $result->param ) : false;\n}\n\n---\n\n\u002F\u002F public\u002Fclass-wowp-public.php\n\u002F\u002F line 64 and line 148\n$param  = maybe_unserialize( $result->param );","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fcounter-box\u002F2.0.13\u002Fclasses\u002FAdmin\u002FDBManager.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fcounter-box\u002F2.0.14\u002Fclasses\u002FAdmin\u002FDBManager.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fcounter-box\u002F2.0.13\u002Fclasses\u002FAdmin\u002FDBManager.php\t2026-03-25 06:39:36.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fcounter-box\u002F2.0.14\u002Fclasses\u002FAdmin\u002FDBManager.php\t2026-06-13 11:08:06.000000000 +0000\n@@ -165,7 +165,27 @@\n \t\t}\n \t\t$result = self::get_data_by_id( $id );\n \n-\t\treturn isset( $result->param ) ? maybe_unserialize( $result->param ) : false;\n+\t\treturn isset( $result->param ) ? self::safe_unserialize( $result->param ) : false;\n+\t}\n+\n+\t\u002F**\n+\t * Safely unserialize a stored value.\n+\t *\n+\t * The `param` field always holds a serialized array of settings, never an\n+\t * object. Passing `allowed_classes => false` prevents PHP Object Injection:\n+\t * a crafted serialized object (e.g. imported via JSON) can no longer be\n+\t * instantiated, so magic methods such as __wakeup()\u002F__destruct() never fire.\n+\t *\n+\t * @param mixed $data Value to unserialize.\n+\t *\n+\t * @return mixed Unserialized array\u002Fscalar, or the original value if not serialized.\n+\t *\u002F\n+\tpublic static function safe_unserialize( $data ) {\n+\t\tif ( ! is_string( $data ) || ! is_serialized( $data ) ) {\n+\t\t\treturn $data;\n+\t\t}\n+\n+\t\treturn unserialize( trim( $data ), [ 'allowed_classes' => false ] ); \u002F\u002F phpcs:ignore WordPress.PHP.NoSilencedErrors, PHPCompatibility.FunctionUse.NewFunctionParameters.unserialize_optionsFound\n \t}\n \n \t\u002F**\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fcounter-box\u002F2.0.13\u002Fclasses\u002FAdmin\u002FImporterExporter.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fcounter-box\u002F2.0.14\u002Fclasses\u002FAdmin\u002FImporterExporter.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fcounter-box\u002F2.0.13\u002Fclasses\u002FAdmin\u002FImporterExporter.php\t2026-03-25 06:39:36.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fcounter-box\u002F2.0.14\u002Fclasses\u002FAdmin\u002FImporterExporter.php\t2026-06-13 11:08:06.000000000 +0000\n@@ -99,6 +99,11 @@\n \t\t\tforeach ( $columns as $column ) {\n \t\t\t\t$name          = $column->Field;\n \t\t\t\t$data[ $name ] = ! empty( $val->$name ) ? $val->$name : '';\n+\n+\t\t\t\tif ( $name === 'param' ) {\n+\t\t\t\t\t$data[ $name ] = self::sanitize_param( $data[ $name ] );\n+\t\t\t\t}\n+\n \t\t\t\tif ( $name === 'id' || $name === 'status' || $name === 'mode' ) {\n \t\t\t\t\t$formats[] = '%d';\n \t\t\t\t} else {\n@@ -134,6 +139,32 @@\n \t\texit;\n \t}\n \n+\t\u002F**\n+\t * Sanitize an imported `param` value before it is stored.\n+\t *\n+\t * The `param` column always holds a serialized array of settings. Imported\n+\t * JSON is untrusted, so any embedded serialized object is stripped by safely\n+\t * unserializing (allowed_classes => false) and re-serializing the result.\n+\t * This neutralizes PHP Object Injection payloads before they reach the\n+\t * database, so a later unserialize can never instantiate a gadget class.\n+\t *\n+\t * @param mixed $value Raw `param` value taken from the import file.\n+\t *\n+\t * @return string Safe, re-serialized array (or empty string when absent).\n+\t *\u002F\n+\tpublic static function sanitize_param( $value ): string {\n+\t\tif ( empty( $value ) ) {\n+\t\t\treturn '';\n+\t\t}\n+\n+\t\t$clean = DBManager::safe_unserialize( $value );\n+\t\tif ( ! is_array( $clean ) ) {\n+\t\t\t$clean = [];\n+\t\t}\n+\n+\t\treturn maybe_serialize( $clean );\n+\t}\n+\n \tprivate static function get_file_extension( $str ) {\n \t\t$parts = explode( '.', $str );","To exploit this vulnerability, an attacker needs Administrator-level authentication. The attacker prepares a JSON file representing a Counter Box export, where the 'param' attribute of an item contains a malicious PHP serialized object (a POP gadget chain). This JSON file is then uploaded through the plugin's Import functionality. Once the data is stored in the database, the PHP Object Injection is triggered automatically when the administrator is redirected to the list of items, or when anyone views a page where the counter's shortcode is rendered, as the plugin calls maybe_unserialize() on the 'param' data retrieved from the database.","gemini-3-flash-preview","2026-06-25 23:26:47","2026-06-25 23:27:44",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","2.0.13","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fcounter-box\u002Ftags\u002F2.0.13","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fcounter-box.2.0.13.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fcounter-box\u002Ftags\u002F2.0.14","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fcounter-box.2.0.14.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fcounter-box\u002Ftags"]