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 b1180d0d0b5c3cd49366af469c313586e29bd96111bae610609d82c733f8b10767e4b0627570ce5fb1180d0d0b5c3cd49366af469c313586e29bd961 #IV1 11bae610609d82c733f8b10767e4b0627570ce5f #IV2b1180d0d0b5c3cd49366af469c313586 #初始send_aes_iv 11bae610609d82c733f8b10767e4b062 #初始recv_aes_iv#************计算send_aes_key #74696E797368656C6C对应内置的key字符串信息tinyshell 74696E797368656C6C + b1180d0d0b5c3cd49366af469c313586e29bd961a13e91013d7f4a3ef3a149467af680262c45b141 #SHA1运算结果a13e91013d7f4a3ef3a149467af68026 #send_aes_key#************计算recv_aes_key 74696E797368656C6C + 11bae610609d82c733f8b10767e4b0627570ce5f0813d1deba152c1d2c6dc774293c18ff86f3239b #SHA1运算结果0813d1deba152c1d2c6dc774293c18ff #recv_aes_key#************第二个通信数据包 tsh -> tshd 4ff6ac2d77894dbe5355e773c0a6216c8b6fba90ac890c125424c9f3bce4c021cbd0eefd4ea658a45ecfcd49a4efa95177da55e800105890ae86f1b91cf6298395711dde580d #AES解密数据 0010 #载荷长度 5890ae86f1b91cf6298395711dde580d #内置的challenge校验数据#************第三个通信数据包 tshd -> tsh 16516b60fd4b37c540ec7ad3902830c8332393f5a22187147c7cc72e60943c0e36b8a4851cdae3b5058b56af7eed56533743930c00105890ae86f1b91cf6298395711dde580d #AES解密数据 0010 #载荷长度 5890ae86f1b91cf6298395711dde580d #内置的challenge校验数据
模拟构建通信解密程序
为了更便利的对TinyShell后门的通信数据进行解密,笔者尝试编写了一个解密程序,可对TinyShell后门通信数据进行批量解密,解密效果如下:
自动化脚本输入文件格式如下:(备注:直接从wireshark导出”C Arrays“数据即可)
代码实现
代码结构截图如下
- main.go
-
package mainimport ("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 commonimport ("crypto/aes""crypto/cipher""crypto/sha1""encoding/hex""fmt" )func Decrypt_msg(ciphertext []byte, aeskey []byte, aes_iv *[]byte) {total_len := 0blk_len := 0for {//前两字节为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 = tmpplaintext_len := (int(output[0]) << 8) + int(output[1])if (plaintext_len+2)%16 > 0 {blk_len = ((plaintext_len+2)/16+1)*16 + 20total_len = total_len + blk_len} else {blk_len = ((plaintext_len+2)/16)*16 + 20total_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) / 16buffer := []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 = tmpbuffer = 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 }