Harald 是一个内存中的微型高级 CPU,能够处理一组指令,以生成用于在给定网络协议(TCP/UDP)上使用的应用层协议。
Harald 将消费一个 OPCODES 流,并对载荷应用不同的转换,使其适用于所定义的应用层协议。
[!TIP] 为什么叫 Harald?你可能知道丹麦国王 Harald Gormsson, 因为他的绰号:Blátǫnn,或者用我们现在的说法,Bluetooth。 这项技术以 Harald 的绰号命名,因为他当时_连接_了丹麦 和维京王国。所以我用他的名字来命名这个项目。
Harald 的行为类似于一个 CPU。它遵循一组定义动作的 OPCODES。 一个 PROTOCOL。Harald 将处理所需的操作,以动态创建所需的 协议。
一旦提供了 PROTOCOL,Harald 将遍历要发送的 PAYLOAD, 对每个片段完整执行一次 PROTOCOL。也就是说:如果载荷 必须被拆分成多个片段,Harald 将对每个片段使用相同的协议, 针对当前载荷片段执行协议中定义的每一个动作。
Harald 持有若干寄存器,供协议用来存储值。 这些寄存器可用于执行多种操作,例如加法、 减法、编码和解码……等等。
Harald 动态分配一个结果数组,协议可以在发送载荷片段时 将每个请求的响应存储在其中。

Harald 支持多种可作为协议提供的操作码。Harald 在接收到给定操作码时 将执行一组高级操作。每个操作码都期望一组预定义长度 的预定义参数。
| OPCODE::Nº | 描述 | ARGS::Size |
|---|---|---|
| H_F_APPEND::0x01 | 将接下来的 N 个字节追加到末尾载荷 | Size::4, NBytes::N |
| H_F_PREPEND::0x02 | 将接下来的 N 个字节前置到末尾载荷 | Size::4, NBytes::N |
| H_F_SPLIT::0x03 | 将载荷拆分为给定大小的片段 | Size::4 |
| H_F_PAYLOAD_INJECT::0x04 | 将载荷片段注入到末尾载荷中 | - |
| H_F_SOCK_INIT::0x05 | 初始化套接字连接 | IsIPAddress:1, SSL_Enabled::1, TCP/UDP::1, [(HostLen::4,HostName:N)/IPAddress::4], Port::2 |
| H_F_SEND::0x06 | 发送构造好的末尾载荷 | - |
| H_F_RECV::0x07 | 接收最多 N 个字节 | Size::4 |
| H_F_CLOSE::0x08 | 关闭套接字并清理 | - |
| H_F_INJECT::0x09 | 将请求的寄存器注入到末尾载荷中 | Reg::1 |
| H_F_ENCODE_STR::0x0A | 将载荷字段编码为其字符串表示形式 | IsReg::1, [(Payload::1, Field::1)/Reg::1], Format::1 |
| H_F_DECODE_STR::0xA0 | 将寄存器的内容解码为其数值表示形式 | Reg::1 |
| H_F_ENCODE_STRB64::0x0B | 将载荷字段编码为其 B64 字符串表示形式 | Payload::1, Field::1 |
| H_F_COMPUTE_FRAG_LENGTH::0x0D | 计算下一个要发送的片段长度 | - |
| H_F_SEARCH::0x0E | 在给定寄存器中搜索给定 N 大小的给定字节模式 | Reg::1, Size::4, NBytes::N |
| H_F_SEEK::0x0F | 在给定载荷中将光标移动给定数量的字节 | Payload::1, Offset::4 |
| H_F_PUSH::0x10 | 将若干字节或某个寄存器的内容推入另一个寄存器 | IsReg::1, [Size::4, NBytes::N]/SrcReg::1, DstReg::2 |
| H_F_ADD::0x12 | 将两个寄存器相加,或将一个数字加到寄存器中 | IsReg::1, [Size::4, NBytes::N]/SrcReg::1, DstReg::2 |
| H_F_SUBSTRACT::0x13 | 将两个寄存器相减,或从寄存器中减去一个数字 | IsReg::1, [Size::4, NBytes::N]/SrcReg::1, DstReg::2 |
| H_F_READ::0x14 | 从给定寄存器读取,最多读取到另一个寄存器中存储的长度 | FromReg::1, SizeReg::1 |
| H_F_STORE::0x15 | 将寄存器值存储到 Harald 的 Storage 中 | Reg::1 |
以下操作码流将生成发送所需的数据,使通信 被理解为 HTTP。它将发起连接、修改载荷使其 看起来像 HTTP 流量、通过 SSL/TLS 连接发送它,并在最后 关闭连接。
H_F_SPLIT, 0x00, 0x00, 0x00, 0xFF, // Splits the Payload
H_F_APPEND, 0x00, 0x00, 0x00, 0x46, // Appends...
'P', 'O', 'S', 'T', ' ', '/', ' ', // POST /
'H', 'T', 'T', 'P', '/', '1', '.', '1', '\r', '\n', // HTTP/1.1
'H', 'o', 's', 't', ':', ' ', // Host:
'd', 'o', 'm', 'a', 'i', 'n', '.', 'c', 'o', 'm', '\r', '\n', // domain.com
'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', ':', ' ', // Connection:
'c', 'l', 'o', 's', 'e', '\r', '\n', // close
'C', 'o', 'n', 't', 'e', 'n', 't', '-', // Content-
'L', 'e', 'n', 'g', 't', 'h', ':', ' ', // Length:
H_F_COMPUTE_FRAG_LENGTH, // Next Fragment's size
H_F_ENCODE_STR, H_PAY_END_PAYLOAD, H_PAY_FRAGMENT_SIZE, 'u', // FragSize encode
H_F_INJECT, H_REG_RET, // Inject the Fragment Size encoded str
H_F_APPEND, 0x00, 0x00, 0x00, 0x04, '\r', '\n', '\r', '\n', // Append Body begin
H_F_PAYLOAD_INJECT, // Inject the Payload
H_F_SOCK_INIT, 0x01, 0x01, 0x7F, 0x00, 0x00, 0x01, 0x04, 0xD2, // Init Socket
H_F_SEND, // Send the Payload over the socket
H_F_CLOSE // Close the socket and cleanup
以下示例遵循与上一个示例相同的流程。然而, 它不是仅仅将载荷作为正文发送,而是让协议指示 Harald 设置一些头部并构造正文,以便它能够与 GitHub API 通信,在私有仓库的 issue 中发布评论。
这种通信方式可能帮助攻击者使用受信任的域名(如 *.github.com) 与 C2 服务器创建通信通道。这个同样的示例可以外推到使用 AWS 资源(例如 sqs..amazonaws.com 下的 SQS 队列)、Azure 资源(例如 <storage_account_name>.blob.core.windows.net 下的 blob)等等。
这个示例对于那些需要 STRUCTURED_FORMAT(JSON)+ APPLICATION-LAYER_PROTOL(HTTPS)+ PROTOCOL(TCP)的通信来说是一个很好的参考。