Medium severity4.2NVD Advisory· Published Jun 22, 2024· Updated Apr 29, 2026
CVE-2024-21517
CVE-2024-21517
Description
This affects versions of the package opencart/opencart from 4.0.0.0. A reflected XSS issue was identified in the redirect parameter of customer account/login route. An attacker can inject arbitrary HTML and Javascript into the page response. As this vulnerability is present in the account functionality it could be used to target and attack customers of the OpenCart shop. Notes: 1) The fix for this vulnerability is incomplete
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
opencart/opencartPackagist | >= 4.0.0.0 | — |
Affected products
1Patches
10fd1ee4b6c94added login to comment
7 files changed · +42 −36
upload/catalog/controller/account/login.php+9 −3 modified@@ -77,7 +77,7 @@ public function index(): void { unset($this->session->data['redirect']); } elseif (isset($this->request->get['redirect'])) { - $data['redirect'] = urldecode($this->request->get['redirect']); + $data['redirect'] = $this->request->get['redirect']; } else { $data['redirect'] = ''; } @@ -186,9 +186,15 @@ public function login(): void { $this->model_account_customer->deleteLoginAttempts($this->request->post['email']); + if (isset($this->request->post['redirect'])) { + $redirect = urldecode($this->request->post['redirect']); + } else { + $redirect = ''; + } + // Added strpos check to pass McAfee PCI compliance test (http://forum.opencart.com/viewtopic.php?f=10&t=12043&p=151494#p151295) - if (isset($this->request->post['redirect']) && str_starts_with(html_entity_decode($this->request->post['redirect'], ENT_QUOTES, 'UTF-8'), $this->config->get('config_url'))) { - $json['redirect'] = html_entity_decode($this->request->post['redirect'], ENT_QUOTES, 'UTF-8') . '&customer_token=' . $this->session->data['customer_token']; + if ($redirect && str_starts_with($redirect, $this->config->get('config_url'))) { + $json['redirect'] = $redirect . '&customer_token=' . $this->session->data['customer_token']; } else { $json['redirect'] = $this->url->link('account/account', 'language=' . $this->config->get('config_language') . '&customer_token=' . $this->session->data['customer_token'], true); }
upload/catalog/controller/cms/comment.php+13 −7 modified@@ -37,7 +37,7 @@ public function index(): string { } $data['logged'] = $this->customer->isLogged(); - $data['login'] = $this->url->link('account/login', 'language=' . $this->config->get('config_language')); + $data['login'] = $this->url->link('account/login', 'language=' . $this->config->get('config_language') . '&page=' . $page . '&redirect=' . urlencode($this->url->link('cms/blog.info', 'language=' . $this->config->get('config_language') . '&article_id=' . $data['article_id'], true))); $this->session->data['comment_token'] = oc_token(32); @@ -180,7 +180,7 @@ public function getList(): string { $data['refresh'] = $this->url->link('cms/comment.list', 'language=' . $this->config->get('config_language') . '&article_id=' . $article_id . '&page=' . $page, true); $data['logged'] = $this->customer->isLogged(); - + $data['login'] = $this->url->link('account/login', 'language=' . $this->config->get('config_language') . '&redirect=' . urlencode($this->url->link('cms/blog.info', 'language=' . $this->config->get('config_language') . '&article_id=' . $article_id, true))); return $this->load->view('cms/comment_list', $data); } @@ -420,10 +420,20 @@ public function rating(): void { $article_comment_id = 0; } + if (isset($this->request->get['rating'])) { + $rating = (bool)$this->request->get['rating']; + } else { + $rating = 0; + } + if (!isset($this->request->get['comment_token']) || !isset($this->session->data['comment_token']) || $this->request->get['comment_token'] != $this->session->data['comment_token']) { $json['error'] = $this->language->get('error_token'); } + if (!$this->customer->isLogged()) { + $json['error'] = $this->language->get('error_login'); + } + $this->load->model('cms/article'); $article_info = $this->model_cms_article->getArticle($article_id); @@ -439,16 +449,12 @@ public function rating(): void { $json['error'] = $this->language->get('error_article_comment'); } - if (!$this->customer->isLogged() && !$this->config->get('config_comment_guest')) { - $json['error'] = $this->language->get('error_login'); - } - if (!$json) { // Anti-Spam $rating_data = $this->request->post + [ 'article_comment_id' => $article_comment_id, 'customer_id' => $this->customer->getId(), - 'rating' => (bool)$this->request->get['rating'], + 'rating' => $rating, 'ip' => $this->request->server['REMOTE_ADDR'] ];
upload/catalog/controller/startup/seo_url.php+3 −3 modified@@ -81,10 +81,10 @@ public function rewrite(string $link): string { $paths = []; // Parse the query into its separate parts - $parts = explode('&', $url_info['query']); + $queries = explode('&', $url_info['query']); - foreach ($parts as $part) { - $pair = explode('=', $part); + foreach ($queries as $query) { + $pair = explode('=', $query); if (isset($pair[0])) { $key = (string)$pair[0];
upload/catalog/view/template/account/login.twig+3 −3 modified@@ -38,12 +38,12 @@ <input type="password" name="password" value="{{ password }}" placeholder="{{ entry_password }}" id="input-password" class="form-control mb-1"/> <a href="{{ forgotten }}">{{ text_forgotten }}</a> </div> - <div class="text-end"> - <button type="submit" class="btn btn-primary">{{ button_login }}</button> - </div> {% if redirect %} <input type="hidden" name="redirect" value="{{ redirect }}"/> {% endif %} + <div class="text-end"> + <button type="submit" class="btn btn-primary">{{ button_login }}</button> + </div> </form> </div> </div>
upload/catalog/view/template/cms/comment_list.twig+8 −16 modified@@ -2,40 +2,32 @@ {% if comments %} {% for comment in comments %} <div id="comment-{{ comment.article_comment_id }}" class="border mb-3"> - <div class="p-2"> <p>{{ text_by }} {{ comment.author }} - {{ comment.date_added }}</p> <p>{{ comment.comment }}</p> <div class="text-end"> - - <button type="button" value="{{ comment.like }}" data-oc-toggle="rate" class="btn btn-secondary"><i class="fa fa-thumbs-up"></i></button> - <button type="button" value="{{ comment.dislike }}" data-oc-toggle="rate" class="btn btn-secondary"><i class="fa fa-thumbs-down"></i></button> - - {% if comment_guest %} + {% if logged %} + <button type="button" value="{{ comment.like }}" data-oc-toggle="rate" class="btn btn-secondary"><i class="fa fa-thumbs-up"></i></button> + <button type="button" value="{{ comment.dislike }}" data-oc-toggle="rate" class="btn btn-secondary"><i class="fa fa-thumbs-down"></i></button> <button type="button" value="{{ comment.reply_add }}" data-oc-toggle="comment" data-oc-target="#reply-{{ comment.article_comment_id }}" data-oc-trigger="#button-refresh-{{ comment.article_comment_id }}" class="btn btn-secondary">{{ button_reply }}</button> {% else %} - <button type="button" class="btn btn-secondary" disabled="disabled">{{ button_reply }}</button> + <a href="{{ login }}" class="btn btn-secondary"><i class="fa fa-thumbs-up"></i></a> + <a href="{{ login }}" class="btn btn-secondary"><i class="fa fa-thumbs-down"></i></a> + <a href="{{ login }}" class="btn btn-secondary">{{ button_login_reply }}</a> {% endif %} - </div> </div> - <div id="reply-{{ comment.article_comment_id }}"> - <div id="reply-{{ comment.article_comment_id }}-0"></div> - <div class="text-center p-2 border-top"> - <button type="button" value="{{ comment.reply }}" id="button-refresh-{{ comment.article_comment_id }}" data-oc-toggle="refresh" data-oc-target="#reply-{{ comment.article_comment_id }}-0" class="btn btn-secondary"><i class="fa fa-sync"></i></button> - + <button type="button" value="{{ comment.reply }}" id="button-refresh-{{ comment.article_comment_id }}" data-oc-toggle="refresh" data-oc-target="#reply-{{ comment.article_comment_id }}-0" class="btn btn-secondary d-none"><i class="fa fa-sync"></i></button> {% if comment.reply_total %} <button type="button" value="{{ comment.reply }}" data-oc-toggle="next" class="btn btn-secondary">{{ button_replies }} ({{ comment.reply_total }})</button> {% else %} <button type="button" class="btn btn-secondary" disabled>{{ button_replies }}</button> {% endif %} - </div> </div> - </div> {% endfor %} <div class="row"> @@ -47,7 +39,7 @@ {% endif %} </div> <div class="text-center p-2"> - <button type="button" value="{{ refresh }}" id="button-refresh" data-oc-toggle="refresh" data-oc-target="#comment-0" class="btn btn-secondary"><i class="fa fa-sync"></i></button> + <button type="button" value="{{ refresh }}" id="button-refresh" data-oc-toggle="refresh" data-oc-target="#comment-0" class="btn btn-secondary d-none"><i class="fa fa-sync"></i></button> </div>
upload/catalog/view/template/cms/comment_reply.twig+1 −1 modified@@ -9,7 +9,7 @@ </div> {% endif %} <div class="text-center p-2 border-top"> - <button type="button" value="{{ refresh }}" id="button-refresh-{{ parent_id }}" data-oc-toggle="refresh" data-oc-target="#reply-{{ parent_id }}-{{ page }}" class="btn btn-secondary"><i class="fa fa-sync"></i></button> + <button type="button" value="{{ refresh }}" id="button-refresh-{{ parent_id }}" data-oc-toggle="refresh" data-oc-target="#reply-{{ parent_id }}-{{ page }}" class="btn btn-secondary d-none"><i class="fa fa-sync"></i></button> {% if not replies %} <button type="button" class="btn btn-secondary" disabled>{{ button_replies }}</button> {% elseif next %}
upload/catalog/view/template/cms/comment.twig+5 −3 modified@@ -1,10 +1,12 @@ <div class="text-center"> - <button type="button" value="{{ like }}" id="input-rating-like" data-oc-toggle="rate" class="btn btn-secondary"><i class="fa fa-thumbs-up"></i></button> - <button type="button" value="{{ dislike }}" id="input-rating-dislike" data-oc-toggle="rate" class="btn btn-secondary"><i class="fa fa-thumbs-down"></i></button> {% if logged %} + <button type="button" value="{{ like }}" id="input-rating-like" data-oc-toggle="rate" class="btn btn-secondary"><i class="fa fa-thumbs-up"></i></button> + <button type="button" value="{{ dislike }}" id="input-rating-dislike" data-oc-toggle="rate" class="btn btn-secondary"><i class="fa fa-thumbs-down"></i></button> <button type="button" value="{{ comment_add }}" data-oc-toggle="comment" data-oc-target="#comment-0" data-oc-trigger="#button-refresh" class="btn btn-secondary">{{ button_comment }}</button> {% else %} - <a href="" class="btn btn-secondary">{{ button_login }}</a> + <a href="{{ login }}" class="btn btn-secondary"><i class="fa fa-thumbs-up"></i></a> + <a href="{{ login }}" class="btn btn-secondary"><i class="fa fa-thumbs-down"></i></a> + <a href="{{ login }}" class="btn btn-secondary">{{ button_login }}</a> {% endif %} </div> <hr/>
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/opencart/opencart/commit/0fd1ee4b6c94366bf3e5d3831a8336f3275d1860nvdPatchWEB
- security.snyk.io/vuln/SNYK-PHP-OPENCARTOPENCART-7266577nvdExploitPatchThird Party AdvisoryWEB
- github.com/advisories/GHSA-qc3q-8rr8-8p5vghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-21517ghsaADVISORY
News mentions
0No linked articles in our index yet.