以第八届西湖论剑MISC-CSCS为例,学习cobalstrike4.0流量分析
偷张图先
image

首先请求url获取beacon
过滤一下http流量
image
这个/mB9u就是获取beacon的URL会返回beacon文件

characters = 'mB9u'
total_sum = sum(ord(char) for char in characters)
remainder = total_sum % 256
print(f"对 256 取余的结果为: {remainder}")
# 93

93对应为x64 ,92为x86,说明这个beacon是x64的
网上有脚本可以分析这个stagebeacon
https://github.com/minhangxiaohui/CSthing
image
导出文件然后执行脚本
image
server,get-uri就是心跳url,里面的cookie存放了加密过的受害端信息
image
这个就是心跳包

SLHAIOj8/1icVtP6fImtJz6B6wR0t/XwLg1G0Y3AxoxnseBfPONxoyjAWCCOH84IJULnCZZrO7cIRxJPS2PtmDD4MvD8/PIpoW8Gj8536vhwd+tyXjNKyLNyNYcj+JgO4N5FTnKtkONgv7KnsMjJC3E0eI0ctqmZll8SrXLUS9k=

尝试对cookie解密,私钥存放在.cobaltstrike.beacon_keys文件里,如果题目给了这个文件就可以直接拿来解密,但是本题没有给出
于是尝试手解rsa

0x0007 publickey                        0x0003 0x0100 30819e300d06092a864886f70d010101050003818c00308188028180525e1781f2f02d132a7818a6d269baddbf39352c8d20290ec2294fbe4d77e6549ef4766d8b0e1620000adfbd7aff99cd72f05623eb0def202265cf631dd895acd5e981da8424c03a295895c8194a31641f2eecd5a8715ca89cdbf9433c5d437538767666c3bdb0f8629555375b574fe408a94ae82f92960085d416374f1654b302030100010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

去掉末尾0然后转成pem格式并给出n和e

from Cryptodome.PublicKey import RSA
import base64

hex_key = "30819e300d06092a864886f70d010101050003818c00308188028180525e1781f2f02d132a7818a6d269baddbf39352c8d20290ec2294fbe4d77e6549ef4766d8b0e1620000adfbd7aff99cd72f05623eb0def202265cf631dd895acd5e981da8424c03a295895c8194a31641f2eecd5a8715ca89cdbf9433c5d437538767666c3bdb0f8629555375b574fe408a94ae82f92960085d416374f1654b30203010001"
der_data = bytes.fromhex(hex_key)
rsa_key = RSA.import_key(der_data)
n = rsa_key.n
e = rsa_key.e
pem_key = rsa_key.export_key().decode()

print("PEM 格式公钥:\n", pem_key)
print("n =", n)
print("e =", e)

得到

PEM 格式公钥:
 -----BEGIN PUBLIC KEY-----
MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgFJeF4Hy8C0TKngYptJput2/OTUs
jSApDsIpT75Nd+ZUnvR2bYsOFiAACt+9ev+ZzXLwViPrDe8gImXPYx3YlazV6YHa
hCTAOilYlcgZSjFkHy7s1ahxXKic2/lDPF1DdTh2dmbDvbD4YpVVN1tXT+QIqUro
L5KWAIXUFjdPFlSzAgMBAAE=
-----END PUBLIC KEY-----
n = 57840457943390562151183056895981922848981888713417943532946260250633760347281370187050725047626507035739078370095883411759062129893337622945780596526859424702568086671495882125464325172299346781795855931036288858784790023273356190549125683636953077021666004867856782395818167722491980517593426129106599564467
e = 65537

yafu分解n

PS E:\我的工具包\yafu-1.34> ./yafu-x64.exe "factor(57840457943390562151183056895981922848981888713417943532946260250633760347281370187050725047626507035739078370095883411759062129893337622945780596526859424702568086671495882125464325172299346781795855931036288858784790023273356190549125683636953077021666004867856782395818167722491980517593426129106599564467)"


fac: factoring 57840457943390562151183056895981922848981888713417943532946260250633760347281370187050725047626507035739078370095883411759062129893337622945780596526859424702568086671495882125464325172299346781795855931036288858784790023273356190549125683636953077021666004867856782395818167722491980517593426129106599564467
fac: using pretesting plan: normal
fac: no tune info: using qs/gnfs crossover of 95 digits
div: primes less than 10000
fmt: 1000000 iterations
Total factoring time = 0.3899 seconds


***factors found***

P154 = 7605291443685150594150190909345113655196508809219162555499789316232908573154196070425269090153291952292016936024761413150455793038505322748933150548026527
P154 = 7605291443685150594150190909345113655196508809219162555499789316232908573154196070425269090153291952292016936024761413150455793038505322748933150548026221

ans = 1

得到p和q就能求私钥

from Cryptodome.PublicKey import RSA
from Cryptodome.Util.number import inverse

p = 7605291443685150594150190909345113655196508809219162555499789316232908573154196070425269090153291952292016936024761413150455793038505322748933150548026527
q = 7605291443685150594150190909345113655196508809219162555499789316232908573154196070425269090153291952292016936024761413150455793038505322748933150548026221
e = 65537 

n = p * q
phi_n = (p - 1) * (q - 1)

d = inverse(e, phi_n)
key = RSA.construct((n, e, d, p, q))

private_pem = key.export_key().decode()

print("RSA私钥(PEM):\n", private_pem)
-----BEGIN RSA PRIVATE KEY-----
MIICWgIBAAKBgFJeF4Hy8C0TKngYptJput2/OTUsjSApDsIpT75Nd+ZUnvR2bYsO
FiAACt+9ev+ZzXLwViPrDe8gImXPYx3YlazV6YHahCTAOilYlcgZSjFkHy7s1ahx
XKic2/lDPF1DdTh2dmbDvbD4YpVVN1tXT+QIqUroL5KWAIXUFjdPFlSzAgMBAAEC
gYApWVrrvY2c0zZKu/VjQ/ivQUPy0b63GmVyS1Lg8frzAiAaESnE2Pl6bwsGbxTE
I+3jeYuE1IdWOAeMnKPhY80fOSgws6vSri7CcxnMUEEn3AMw4YSwBIaBGkdLnfxf
pbS/kUUb/z7/A1SRtNq1n4hZYinnG2NpUuiO1WqwHqOGoQJBAJE14+VVt8ONGIZ1
qIf4cqAnAmtonPhyDNdYZQC0IlxNzyixo/lnlTc80b3jYUA4w8GGQQZea70op4RS
fIJV5J8CQQCRNePlVbfDjRiGdaiH+HKgJwJraJz4cgzXWGUAtCJcTc8osaP5Z5U3
PNG942FAOMPBhkEGXmu9KKeEUnyCVeNtAkAhlDeuCcNj6hXYyg592tsO49ZwZhGe
dik4Bw3cOsuTUr7r5yBHBUgBLQRHh/QuOLIz50rUITOC24rZU4XNUfV7AkB6vJQu
Ke+zaDVMoXKbyxIH8DEJXFkhXjUgZ+SnXZqVbmclPFEe48Cp+cxGtkRjJhfAIZwg
p/pk3lIJdDctay9ZAkBukZv1vD/LR3Y64R5xkoLIliyCTtHgUCY44xkJvQfCGchn
xSu0tBnGgSI3El1K1eOyT6NKSZGeQUGlLGcsBtcT
-----END RSA PRIVATE KEY-----

https://github.com/minhangxiaohui/CSthing
还是使用这个项目可以进行解密cookie
image

python cs-decrypt-metadata.py -p 私钥 密文

image
有受害机器的主机名,用户名,样本名,其中需要注意的是

Raw key:  28ab951fc96bcb93ec13cf9dd5f21373
aeskey:  9fe14473479a283821241e2af78017e8
hmackey: 1e3d54f1b9f0e106773a59b7c379a89d

有了aeskey就可以加密流量包中的报文
image
这种心跳包post为空的流是cs没有给受害机器发送指令
image
这种就是发送了指令的心跳包
image
对这几个包依次解密,从1382个HTTP流找到上传文件的任务
用cs-parse-http-traffic.py貌似有点问题,原因是响应的报文会比下发任务的报文多4个字节,这里引用官方wp给的脚本
解密
image

import hmac
import binascii
import base64
import hexdump
from Crypto.Cipher import AES

AES_KEY = binascii.unhexlify("9fe14473479a283821241e2af78017e8")
HMAC_KEY = binascii.unhexlify("1e3d54f1b9f0e106773a59b7c379a89d")
encrypt_data = "hex"

def decrypt(encrypted_data, iv_bytes, signature, AES_KEY, hmac_key):
    cipher = AES.new(AES_KEY, AES.MODE_CBC, iv_bytes)
    return cipher.decrypt(encrypted_data)

encrypt_data = bytes.fromhex(encrypt_data)
try:
       encrypt_data_l = encrypt_data[4:]
       data1 = encrypt_data_l[:-16]
       signature = encrypt_data_l[-16:]
       iv_bytes = b"abcdefghijklmnop"
       dec = decrypt(data1, iv_bytes, signature, AES_KEY, HMAC_KEY)
except:
       dec = decrypt(encrypt_data, iv_bytes, signature, AES_KEY, HMAC_KEY)

print("counter: {}".format(int.from_bytes(dec[:4], byteorder='big', signed=False)))
print("任务返回长度: {}".format(int.from_bytes(dec[4:8], byteorder='big', signed=False)))
print("任务输出类型: {}".format(int.from_bytes(dec[8:12], byteorder='big', signed=False)))
print(hexdump.hexdump(dec[:1000]))
open('secret.pcapng','wb').write(dec[64:-76])

导出流量包(注意包头和尾),后面内容与cobalstrike没什么关系了,但还是把他做完
image
基本上全是udp流量,端口27015 27005
流量解密参考
https://www.anquanke.com/post/id/261339
先编译一个解密程序,这是由rehlds项目得来的
https://github.com/rehlds/ReHLDS/blob/e9991716fc908e8b2ebf8ef5004c2a6f098f6eb5/rehlds/HLTV/common/munge.cpp#L147

extern "C" {

    int _LongSwap(int l) {
        unsigned int res = __builtin_bswap32(*(unsigned int *)&l);
        return *(int *)&res;
    }

    const unsigned char mungify_table[] = {
        0x7A, 0x64, 0x05, 0xF1,
        0x1B, 0x9B, 0xA0, 0xB5,
        0xCA, 0xED, 0x61, 0x0D,
        0x4A, 0xDF, 0x8E, 0xC7
    };

    const unsigned char mungify_table2[] = {
        0x05, 0x61, 0x7A, 0xED,
        0x1B, 0xCA, 0x0D, 0x9B,
        0x4A, 0xF1, 0x64, 0xC7,
        0xB5, 0x8E, 0xDF, 0xA0
    };

    unsigned char mungify_table3[] = {
        0x20, 0x07, 0x13, 0x61,
        0x03, 0x45, 0x17, 0x72,
        0x0A, 0x2D, 0x48, 0x0C,
        0x4A, 0x12, 0xA9, 0xB5
    };

    void COM_UnMunge2(unsigned char *data, int len, int seq) {
        int i;
        int mungelen;
        int c;
        int *pc;
        unsigned char *p;
        int j;

        mungelen = len & ~3;
        mungelen /= 4;

        for (i = 0; i < mungelen; i++) {
            pc = (int *)&data[i * 4];
            c = *pc;
            c ^= seq;

            p = (unsigned char *)&c;
            for (j = 0; j < 4; j++) {
                *p++ ^= (0xa5 | (j << j) | j | mungify_table2[(i + j) & 0x0f]);
            }

            c = _LongSwap(c);
            c ^= ~seq;
            *pc = c;
        }
    }
}
root@kali2 [/tmp]vim a.cpp                                                                                                          
root@kali2 [/tmp] ➜  g++ -shared -o dll.so a.cpp  

然后写个python脚本,利用COM_UnMunge2函数解密

from scapy.all import *
from ctypes import *
import struct

lib=CDLL('./dll.so')
COM_UnMunge=lib.COM_UnMunge2

pcaps = rdpcap("secret.pcapng")

f=open('res','wb')
for mpacket in pcaps.filter(lambda x:UDP in x and x[UDP].sport==27015):
    # mpacket.show()
    udp=mpacket[UDP]
    data=bytes(udp.payload)[8:]
    seq=bytes(udp.payload)[:4]
    ack=bytes(udp.payload)[4:8]
    c=create_string_buffer(data)
    COM_UnMunge(c,len(data),seq[0])
    print(mpacket.time,mpacket[IP].src,'->',mpacket[IP].dst)
    decode_bytes=bytes(c)
    if len(decode_bytes)>10:
        if struct.unpack('<L', seq)[0] & (1<<30):
            if len(decode_bytes)>10+struct.unpack('<h', decode_bytes[7:9])[0]+1:
                print('find extra data block:')
                print(decode_bytes[10+struct.unpack('<h', decode_bytes[7:9])[0]:])
            decode_bytes=decode_bytes[10:10+struct.unpack('<h', decode_bytes[7:9])[0]]
        f.write(decode_bytes)
    print(f'finally decode data:{decode_bytes}\nlength:{len(decode_bytes)}')        
f.close
root@kali2 [/tmp] ➜  strings res | grep DAS                                                      
:DASCTF{C0UnT3R_S7R1K3_4nD_C0BaLt_57RIK3_4LL_FUN}