红队研发:C2的心跳设计

本文涉及到的技术,仅供红队研究学习

C2中的心跳数据包一般可以认为就是携带了主机元数据信息(基础信息)的网络数据包,比如携带了当前IP,机器名,用户名等。
如果你用c#等高级语言开发,可以把这些信息封装到一个类里面,然后序列化加密传输即可。

    public classMetaData
    {
        private Guid MachineID {  get; set; }
        privatestring MachineName { get; set; }
        privatestring User { get; set; }
        privatestring Privilege { get; set; }
        privatebool   IsAdmin { get; set; }
        privatestring InternalIp { get; set; }
        privatestring OsVersion { get; set; }
        privatestring WindowFormTitle {  get; set; }
   }

里面有些信息比如想获取当前窗口,这种经常变动的字段,每次发送心跳数据的时候重新获取一下就可以了。

在UI上动态显示心跳计时

类似于Cobaltstrike,每个agent都对应一个动态的心跳计时,按每秒动态增加,如果在设置的sleep时间到了,收到了心跳包,那么心跳计时归0,重新开始。

得益于UI数据绑定的机制,在Avalonia或者WPF中我们可以很轻松的实现这个效果。

在之前的文章中,有提到过数据绑定的这个概念,在Avalonia中可以将数据控件和数据模型进行绑定,比如在datagrid中,可以和某个类进行绑定,每列通过绑定不同的类字段,从事实现数据元素的UI显示。

        <DataGrid.DataContext>
            <model:AgentDisplayModel></model:AgentDisplayModel>
        </DataGrid.DataContext>
        <DataGrid.Columns>
            <DataGridTextColumn Header="computer" Width="11*" Binding="{Binding HostName}"/>
            <DataGridTextColumn Header="user" Width="11*" Binding="{Binding UserName}" />
            <DataGridTextColumn Header="remote" Width="11*" Binding="{Binding Remote}"/>
            <DataGridTextColumn Header="ip" Width="11*" Binding="{Binding InIpAddress}" />                
            <DataGridTextColumn Header="os" Width="11*" Binding="{Binding OSVersion}"/>
            <DataGridTextColumn Header="privilege" Width="11*" Binding="{Binding Privilege}" />
            <DataGridTextColumn Header="window" Width="14*" Binding="{Binding CurrentWindow}" />
            <DataGridTextColumn Header="last" Width="8*" Binding="{Binding Last}"/>
        </DataGrid.Columns>

这里我们的datagrid绑定了AgentDisplayModel这个数据模型(类):

public class AgentDisplayModel
{
    public Guid Id { get; set; }
    public string HostName { get; set; }
    public string UserName { get; set; }
    public string Remote;
    public string InIpAddress { get; set; }
    public string OSVersion { get; set; }
    public string Privilege { get; set; }
    public string CurrentWindow;
    public string Last;
}

其中Last我们用来显示心跳的部分。

这里还涉及到一个问题,就是我们的心跳数据是一只在变动的,如果像上面这样直接定义一个类然后绑定,后续数据更新的话,UI界面的内容是不会变动的,所以这里又涉及到了一个交叫INotifyPropertyChanged的东西。

在WPF中,INotifyPropertyChanged 是一个接口,用于通知客户端(通常是UI)属性值的变化。它是数据绑定机制的核心组件之一,特别是在MVVM(Model-View-ViewModel)模式中广泛使用

说白了如果你想你的数据动态变化的时候,需要一种机制去通知UI,让UI同时发生变化,这里就用到了这个属性,所以这里我们的类需要继承一下这个接口,并需要声明PropertyChanged事件,实现OnPropertyChanged方法(至于为啥这样,只能说就这流程)

public classAgentDisplayModel:INotifyPropertyChanged
{
    public Guid Id { get; set; }
    publicstring HostName { get; set; }
    publicstring UserName { get; set; }
    publicstring Remote { get; set; }
    publicstring InIpAddress { get; set; }
    publicstring OSVersion { get; set; }
    publicstring Privilege { get; set; }
    publicstring CurrentWindow;

    public DateTime AccessTime;

    privatestring _Last;

    publicstring Last
    {
       get { return _Last; }
       set { _Last = value; OnPropertyChanged(nameof(Last));}
    }
    
    publicevent PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

}

这里注意多了个AccessTime这个字段,这个用来记录接收到agent心跳时的完整时间。

UI动态变化逻辑有了,现在还需要实现一个心跳递增的逻辑,也即没接收到新的心跳数据的时候,心跳计时每秒递增。

计时器

我们这里可以设定一个每秒触发一次的计时器,这个计时器用来对当前时间和上一次收到心跳数据的AccessTime做比较,计算差值,从而实现心跳递增的效果。

计时器:

        TTimer = new System.Timers.Timer(1000);
        TTimer.Elapsed += UpdateHeartBeat;
        TTimer.AutoReset = true;
        TTimer.Enabled = true;
        TTimer.Start();

计时器函数的逻辑就是一个时间的比较,计算出秒数的差值,并更新到Last这个成员上,这个类成员也是我们绑定在UI上的,这样就实现心跳的效果了:

public void UpdateHeartBeat(Object source, ElapsedEventArgs e)
{
     for (int i = 0; i < AgentList.Count; i++)
     {
           var totalSeconds =  (DateTime.UtcNow.ToLocalTime() - AgentList[i].AccessTime).TotalSeconds;
           AgentList[i].Last = $"{totalSeconds}s";
      }
}

AgentList是存储了主机信息的列表,因为我们agent肯定不止一台。