Loki 1.8:有关年轻和有前途的Data Stealer的档案





6月中旬,哈萨克斯坦与冠状病毒的斗争如火如荼。警惕案件数量的增加(甚至前总统努尔苏丹·纳扎尔巴耶夫(Nursultan Nazarbayev)被感染),地方当局决定再次关闭所有购物和娱乐中心,连锁店,市场和集市。那时,网络犯罪分子通过向俄罗斯和国际公司发送恶意邮件来利用这种情况。



威胁检测系统(TDS)组IB拦截了伪装为哈萨克斯坦共和国卫生部长的呼吁的危险信件。附件中包含的文档在启动时安装了Loki PWS(密码窃取者)家族的恶意程序,该程序旨在从受感染的计算机中窃取登录名和密码。将来,攻击者可以使用它们来访问电子邮件帐户,以进行财务欺诈,间谍活动或在黑客论坛上出售。



在本文中,CERT-GIB的分析师Nikita Karpov研究了当今最流行的Data Stealers之一的实例Loki。



今天,我们将考虑机器人的流行版本之一-1.8。它是活跃销售的,管理面板甚至可以在公共领域找到:此处



管理面板示例:







Loki用C ++编写,是最流行的恶意软件之一,用于从受感染的计算机窃取用户信息。就像我们时代的祸害-勒索软件病毒-Data Stealer在被害者的计算机上击中后,以很高的速度执行任务-它不需要立足点并增加其在系统中的特权,因此几乎没有时间来防御攻击。因此,在恶意软件窃取用户数据的事件中,主要事件是调查事件。



打开包装并获得可行的恶意软件转储



在大多数情况下,分发是通过邮件列表中的附件进行的。伪装成合法文件的用户下载并打开附件,启动了恶意软件。



注射标记表明存在装载程序。





在DIE的帮助下,我们可以获得有关源文件是用VB6编写的信息。





熵图表示大量的加密数据。





启动后,第一个进程将创建一个子进程,将其注入并退出。第二个过程负责恶意软件的工作。短时间后,我们停止该过程并保存内存转储。要确认Loki是否在转储文件中,请查看命令中心网址,该网址多数情况下以fre.php结尾





我们转储包含Loki的内存片段,并更正PE标头。



我们将使用TDS Huntbox系统检查转储的性能。





机器人功能



在检查反编译的恶意软件代码的过程中,我们发现包含四个功能的部分,这些功能在初始化操作所需的库后立即生效。在将它们分解后,我们确定了恶意软件的目的和功能。





为了方便起见,已将函数名称重命名为更具描述性的名称。

机器人功能由两个主要功能决定:



  1. Data Stealer是负责从101个应用程序中窃取数据并将其发送到服务器的第一个功能。
  2. 下载器-来自CnC(命令和控制)命令的执行请求。


为方便起见,下表列出了正在检查的Loki实例试图窃取数据的所有应用程序。

功能编号 应用 功能编号 应用 功能编号 应用
1个 火狐浏览器 35 FTP信息 69 经典FTP
2 科莫多冰龙 36 LinasFTP 70 腻子/ KiTTY
3 苹果Safari 37 FileZilla 71 雷鸟
4 K-Meleon 38 员工FTP 72 福克斯邮件
海猴子 39 BlazeFtp 73 Pocomail
6 40 NET文件 74 增量邮件
7 NETGATE黑鹰 41 的FTP 75 Gmail通知程序专业版
8 月神 42 远程FTP 76 检查邮件
谷歌浏览器 43 豪华FTP 77 WinFtp
歌剧 44 总指挥官 78 马丁·普里克里(Martin Prikryl)
十一 QTWeb浏览器 45 FTPGetter 79 32位
12 QupZilla 46 WS_FTP 80 FTP浏览器
十三 IE浏览器 47 邮件客户端配置文件 81 邮件

(softwarenetz)
十四 歌剧2 48 全倾斜扑克 82 歌剧邮件
十五 赛博狐 49 扑克之星 83 邮箱
十六 苍白的月亮 50 ExpanDrive 84 福萨邮件
17 水狐 51 骏马 85 贝基!
十八 皮金 52 FlashFXP 86 POP3
19 超级腻子 53 NovaFTP 87 外表
20 FTPShell 54 网盘 88 Ymail2
21 NppFTP 55 总指挥官2 89 特罗吉塔
22 我的FTP 56 SmartFTP 90 TrulyMail
23 FTP箱 57 FAR经理 91 .spn文件
24 谢罗德FTP 58 比特维斯 92 待办事项清单
25 立即FTP 59 RealVNC

紧VNC
93 胶粘物
26 NexusFile 60 mSecure钱包 94 注意飞
27 XFTP 61 发现 95 注意齐拉
28 易FTP 62 FreshFTP 96 便利贴
29 SftpNetDrive 63 比特币 97 KeePass
三十 AbleFTP 64 超FXP 98 绕过
31 JaSFtp 65 立即FTP 2 99 我的机器人表格
32 自动化 66 Vandyk SecureFX 100 1密码
33 数码鸭 67 Odin安全FTP专家 101 Mikrotik WinBox
34 全同步 68 一扔
在这一阶段,完成了对恶意软件的静态分析,在下一部分中,我们将考虑Loki如何与服务器通信。



联网



记录网络交互需要解决两个问题:



  1. 命令中心仅在发生攻击时可用。
  2. Wireshark不会在回送中记录漫游器通信,因此您需要使用其他方式。


最简单的解决方案是将Loki将与之通信的CnC地址转发到本地主机。对于机器人,服务器现在可以随时使用,尽管它没有响应,但是没有必要记录机器人的通信。为了解决第二个问题,我们将使用RawCap实用程序,该实用程序允许我们编写需要pcap的通信。接下来,我们将在Wireshark中解析记录的pcap。





在每次通信之前,漫游器会检查CnC的可用性,并在可用时打开套接字。所有网络通信均在传输级别使用TCP协议进行,而在应用程序级别使用HTTP。



下表显示了Loki作为标准使用的数据包头。

领域 描述
用户代理 Mozilla / 4.08(Charon; Inferno) Loki的典型用户代理
接受 * / *
内容类型 应用/八位字节流
内容编码 二元
内容密钥 7DE968CC 先前标头的散列结果(散列是通过多项式0xE8677835的自定义CRC算法完成的)
连接
让我们注意包的主体:



  1. 记录数据的结构取决于漫游器的版本,在较早的版本中,没有字段负责加密和压缩选项。
  2. 服务器根据请求的类型确定如何处理接收到的数据。服务器可以读取7种数据:

    • 0x26被盗的钱包数据
    • 0x27被盗的申请数据
    • 来自服务器的0x28命令请求
    • 0x29卸载被盗文件
    • 0x2A POS
    • 0x2B键盘记录器数据
    • 0x2C截图
  3. 在检查的实例中,仅存在0x27、0x28和0x2B。
  4. 每个请求都包含有关漫游器和受感染系统的常规信息,服务器会根据这些信息识别一台计算机的所有报告,然后根据请求的类型提供一些信息。
  5. 在该僵尸程序的最新版本中,仅实现了数据压缩,并且为将来准备了加密字段,并且服务器不对其进行处理。
  6. 开源的APLib库用于压缩数据。


当使用被盗数据形成请求时,漫游器会分配一个大小为0x1388(5000字节)的缓冲区。下表显示了0x27请求的结构:

偏压 规模 描述
0x0 0x2 0x0012 Bot版本
0x2 0x2 0x0027 请求类型(发送被盗数据)
0x4 0xD ckav.ru 二进制ID(也会出现XXXXX11111)
0x11 0x10 -- 用户名
0x21 0x12 -- 电脑名称
0x33 0x12 -- 电脑域名
0x45 0x4 -- 屏幕分辨率(宽度和高度)

0x49 0x4 --
0x4D 0x2 0x0001 用户权限标志(如果为管理员则为1)
0x4F 0x2 0x0001 SID标志(如果设置则为1)
0x51 0x2 0x0001 系统位数标志(如果为x64,则为1)
0x53 0x2 0x0006 Windows版本(主要版本号)
0x55 0x2 0x0001 Windows版本(次要版本号)
0x57 0x2 0x0001 附加系统信息(1 = VER_NT_WORKSTATION)
0x59 0x2 --
0x5B 0x2 0x0000 被盗的数据是否已发送
0x5D 0x2 0x0001 是否使用了数据压缩
0x5F 0x2 0x0000 压缩类型
0x61 0x2 0x0000 是否使用了数据加密
0x63 0x2 0x0000 加密方式
0x65 0x36 -- 来自MachineGuid的MD5寄存器值
0x9B -- -- 压缩的被盗数据
与服务器交互的第二阶段是在系统中固定之后开始的。该漫游器发送类型为0x28的请求,其结构如下所示:



缓冲区大小:0x2BC(700字节)

偏压 规模 描述
0x0 0x2 0x0012 Bot版本
0x2 0x2 0x0028 请求类型(来自命令中心的命令请求)
0x4 0xD ckav.ru 二进制ID(也会出现XXXXX11111)
0x11 0x10 -- 用户名
0x21 0x12 -- 电脑名称
0x33 0x12 -- 电脑域名
0x45 0x4 -- 屏幕分辨率(宽度和高度)
0x49 0x4 --
0x4D 0x2 0x0001 用户权限标志(如果为管理员则为1)
0x4F 0x2 0x0001 SID标志(如果设置则为1)
0x51 0x2 0x0001 系统位数标志(如果为x64,则为1)
0x53 0x2 0x0006 Windows版本(主要版本号)
0x55 0x2 0x0001 Windows版本(次要版本号)
0x57 0x2 0x0001 附加系统信息(1 = VER_NT_WORKSTATION)
0x59 0x2 0xFED0
0x5B 0x36 -- 来自MachineGuid的MD5寄存器值
发出请求后,漫游器希望从服务器收到包含数字和命令本身的响应。使用反编译后的恶意软件代码的静态分析可获得可能的命令变体,下面将进行介绍。



数据包中每个命令的缓冲区大小:0x10(16个字节)+ 0x10(16个字节)。

HTTP标头(数据开始) \ r \ n \ r \ n [0D 0A 0D 0A] 4字节
- - 4
2 [00 00 00 02] 4


4


4


4


4



()

#0

EXE-
[00 00 00 00] [00 00 00 00] [00 00 00 00] [00 00 00 23] www.notsogood.site/malicious.exe
#1

DLL
[00 00 00 00] [00 00 00 01] [00 00 00 00] [00 00 00 23] www.notsogood.site/malicious.dll
#2

EXE-
[00 00 00 00] [00 00 00 02] [00 00 00 00] [00 00 00 23] www.notsogood.site/malicious.exe
#8

(HDB file)
[00 00 00 00] [00 00 00 08] [00 00 00 00] [00 00 00 00] -
#9

[00 00 00 00] [00 00 00 09] [00 00 00 00] [00 00 00 00] -
#10

[00 00 00 00] [00 00 00 0A] [00 00 00 00] [00 00 00 00] -
#14

Loki
[00 00 00 00] [00 00 00 0E] [00 00 00 00] [00 00 00 00] -
#15

Loki
[00 00 00 00] [00 00 00 0F] [00 00 00 00] [00 00 00 23] www.notsogood.site/malicious.exe
#16

更改检查服务器响应的频率
[00 00 00 00] [00 00 00 10] [00 00 00 00] [00 00 00 01]
#17

删除Loki并退出
[00 00 00 00] [00 00 00 11] [00 00 00 00] [00 00 00 00] --


网络流量解析器



通过此分析,我们获得了解析Loki网络交互所需的所有信息。



该解析器是用Python实现的,接收一个pcap文件作为输入,并在其中找到所有属于Loki的通信。



首先,让我们使用dkpt库查找所有TCP数据包。要仅接收http数据包,请在使用的端口上放置一个过滤器。在收到的http数据包中,我们选择那些包含众所周知的Loki头的数据包,并获取需要解析的通信,以便以可读形式从中提取信息。



for ts, buf in pcap:
    eth = dpkt.ethernet.Ethernet(buf)
    if not isinstance(eth.data, dpkt.ip.IP):
        ip = dpkt.ip.IP(buf)
    else:
        ip = eth.data
 
    if isinstance(ip.data, dpkt.tcp.TCP):
        tcp = ip.data
        try:
            if tcp.dport == 80 and len(tcp.data) > 0:  # HTTP REQUEST
                if str(tcp.data).find('POST') != -1:
                    http += 1
                    httpheader = tcp.data
                    continue
                else:
                    if httpheader != "":
                        print('Request information:')
 
                        pkt = httpheader + tcp.data
                        httpheader = ""
                        if debug:
                            print(pkt)
                        req += 1
                        request = dpkt.http.Request(pkt)
                        uri = request.headers['host'] + request.uri
                        parsed_payload['Network']['Source IP'] = socket.inet_ntoa(ip.src)
                        parsed_payload['Network']['Destination IP'] = socket.inet_ntoa(ip.dst)
                        parsed_payload_same['Network']['CnC'] = uri
                        parsed_payload['Network']['HTTP Method'] = request.method
 
                        if uri.find("fre.php"):
                            print("Loki detected!")
                        pt = parseLokicontent(tcp.data, debug)
                        parsed_payload_same['Malware Artifacts/IOCs']['User-Agent String'] = request.headers['user-agent']
 
                        print(json.dumps(parsed_payload, ensure_ascii=False, sort_keys=False, indent=4))
                        parsed_payload['Network'].clear()
                        parsed_payload['Compromised Host/User Data'].clear()
                        parsed_payload['Malware Artifacts/IOCs'].clear()
                        print("----------------------")
            if tcp.sport == 80 and len(tcp.data) > 0:  # HTTP RESPONCE
                resp += 1
                if pt == 40:
                    print('Responce information:')
                    parseC2commands(tcp.data, debug)
                    print("----------------------")
                    pt = 0
        except(dpkt.dpkt.NeedData, dpkt.dpkt.UnpackError):
            continue


在所有Loki请求中,前4个字节负责bot版本和请求类型。使用这两个参数,我们确定如何处理数据。



def parseLokicontent(data, debug):
    index = 0
 
    botV = int.from_bytes(data[0:2], byteorder=sys.byteorder)
    parsed_payload_same['Malware Artifacts/IOCs']['Loki-Bot Version'] =  botV
 
    payloadtype = int.from_bytes(data[2:4], byteorder=sys.byteorder)
    index = 4
    print("Payload type: : %s" % payloadtype)
    if payloadtype == 39:
        parsed_payload['Network']['Traffic Purpose'] =  "Exfiltrate Application/Credential Data"
        parse_type27(data, debug)
    elif payloadtype == 40:
        parsed_payload['Network']['Traffic Purpose'] = "Get C2 Commands"
        parse_type28(data, debug)
    elif payloadtype == 43:
        parsed_payload['Network']['Traffic Purpose'] = "Exfiltrate Keylogger Data"
        parse_type2b(lb_payload)
    elif payloadtype == 38:
        parsed_payload['Network']['Traffic Purpose'] = "Exfiltrate Cryptocurrency Wallet"
    elif payloadtype == 41:
        parsed_payload['Network']['Traffic Purpose'] = "Exfiltrate Files"
    elif payloadtype == 42:
        parsed_payload['Network'].['Traffic Purpose'] = "Exfiltrate POS Data"
    elif payloadtype == 44:
        parsed_payload['Network']['Traffic Purpose'] = "Exfiltrate Screenshots"
 
    return payloadtype


接下来的一行将是解析服务器的响应。要仅读取有用的信息,请查找\ r \ n \ r \ n序列,该序列定义了数据包头的结尾和来自服务器的命令的开头。



def parseC2commands(data, debug):
    word = 2
    dword = 4
    end = data.find(b'\r\n\r\n')
    if end != -1:
        index = end + 4
        if (str(data).find('<html>')) == -1:
            if debug:
                print(data)
            fullsize = getDWord(data, index)
            print("Body size: : %s" % fullsize)
            index += dword
            count = getDWord(data, index)
            print("Commands: : %s" % count)
            if count == 0:
                print('No commands received')
            else:
                index += dword
                for i in range(count):
                    print("Command: %s" % (i + 1))
 
                    id = getDWord(data, index)
                    print("Command ID: %s" % id)
                    index += dword
 
                    type = getDWord(data, index)
                    print("Command type: %s" % type)
                    index += dword
 
                    timelimit = getDWord(data, index)
                    print("Command timelimit: %s" % timelimit)
                    index += dword
 
                    datalen = getDWord(data, index)
                    index += dword
 
                    command_data = getString(data, index, datalen)
                    print("Command data: %s" % command_data)
                    index += datalen
        else:
            print('No commands received')
    return None


到此结束了对解析器算法主要部分的分析,然后继续我们在输出中获得的结果。所有信息均以json格式显示。



下面是解析器工作结果的图像,这些图像是从具有不同CnC并记录在不同环境中的各种漫游器的通信中获得的。



Request information:
Loki detected!
Payload type: 39
Decompressed data: 
{'Module': {'Mozilla Firefox'}, 'Version': {0}, 'Data': {'domain': {'https://accounts.google.com'}, 'username': {'none@gmail.com'}, 'password': {'test'}}}
{'Module': {'NppFTP'}, 'Version': {0}, 'Data': {b'<?xml version="1.0" encoding="UTF-8" ?>\r\n<NppFTP defaultCache="%CONFIGDIR%\\Cache\\%USERNAME%@%HOSTNAME%" outputShown="0" windowRatio="0.5" clearCache="0" clearCachePermanent="0">\r\n    <Profiles />\r\n</NppFTP>\r\n'}}
{
    "Network": {
        "Source IP": "-",
        "Destination IP": "185.141.27.187",
        "HTTP Method": "POST",
        "Traffic Purpose": "Exfiltrate Application/Credential Data",
        "First Transmission": true
    },
    "Compromised Host/User Data": {},
    "Malware Artifacts/IOCs": {}
}


上面是对服务器0x27的请求(上传应用程序数据)的示例。为了进行测试,在三个应用程序中创建了帐户:Mozilla Firefox,NppFTP和FileZilla。Loki具有三个用于记录应用程序数据的选项:



  1. 以SQL数据库的形式(解析器将保存数据库并显示其中的所有可用行)。
  2. 以开放形式显示,例如Firefox中的示例。
  3. 作为NppFTP和FileZilla之类的xml文件。


Request information:
Loki detected!
Payload type: 39
No data stolen
{
    "Network": {
        "Source IP": "-",
        "Destination IP": "185.141.27.187",
        "HTTP Method": "POST",
        "Traffic Purpose": "Exfiltrate Application/Credential Data",
        "First Transmission": false
    },
    "Compromised Host/User Data": {},
    "Malware Artifacts/IOCs": {}
}


第二个请求的类型为0x28,并从服务器请求命令。



Responce information:
Body size: 26
Commands: 1
Command: 1
Command ID: 0
Command type: 9
Command timelimit: 0
Command data: 35


来自CnC的响应示例,该响应发送了一个命令作为响应来启动键盘记录程序。并随后卸载键盘记录器数据。



Request information:
Loki detected!
Payload type: : 43
{
    "Network": {
        "Source IP": "-",
        "Destination IP": "185.141.27.187",
        "HTTP Method": "POST",
        "Traffic Purpose": "Exfiltrate Keylogger Data"
    },
    "Compromised Host/User Data": {},
    "Malware Artifacts/IOCs": {}
}


在工作结束时,解析器显示来自bot的每个请求中所包含的信息(有关bot和系统的信息)以及pcap文件中与Loki相关的请求和响应的数量。



General information:
{
    "Network": {
        "CnC": "nganyin-my.com/chief6/five/fre.php"
    },
    "Compromised Host/User Description": {
        "User Name": "-",
        "Hostname": "-",
        "Domain Hostname": "-",
        "Screen Resolution": "1024x768",
        "Local Admin": true,
        "Built-In Admin": true,
        "64bit OS": false,
        "Operating System": "Windows 7 Workstation"
    },
    "Malware Artifacts/IOCs": {
        "Loki-Bot Version": 18,
        "Binary ID": "ckav.ru",
        "MD5 from GUID": "-",
        "User-Agent String": "Mozilla/4.08 (Charon; Inferno)"
    }
}
Requests: 3
Responces: 3 




完整的解析器代码位于:github.com/Group-IB/LokiParser



结论



在本文中,我们仔细研究了Loki恶意软件,分解了其功能并实现了网络流量解析器,该解析器将大大简化事件分析过程,并帮助我们了解到底是从受感染的计算机中窃取了什么。尽管Loki的开发仍在进行中,但仅合并了1.8(或更早版本)版本,这是安全专业人员每天都会遇到的版本。



在下一篇文章中,我们将分析另一个流行的Data Stealer Pony,并比较这些恶意软件。



危害指标(IOC):



网址:



  • nganyin-my.com/chief6/five/fre.php
  • wardia.com.pe/wp-includes/texts/five/fre.php
  • broken2.cf/Work2/fre.php
  • 185.141.27.187/danielsden/ver.php
  • MD5哈希值:B0C33B1EF30110C424BABD66126017E5
  • User-Agent String: «Mozilla/4.08 (Charon; Inferno)»
  • Binary ID: «ckav.ru»



All Articles