TinyShell后门通信模型剖析

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数据(\x58\x90\xAE\x86\xF1\xB9\x1C\xF6\x29\x83\x95\x71\x1D\xDE\x58\x0D),用于校验通信是否建立成功
  • 发送数据时,每16字节进行一次AES运算,前16字节的加密结果将作为第二段数据加密的IV值
  • 通信数据结构
    • 2字节数据:后续实际载荷长度
    • 实际载荷数据

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

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

#************第一个通信数据包   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后门通信数据进行批量解密,解密效果如下:

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

代码实现

代码结构截图如下

 

  • 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
    }
  • 24
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值