消息认证码

消息认证码的输入包含:任意长度的消息,密钥(发送者和接收者共享).输出:固定长度的密钥。

消息认证码的主要功能:

  • 数据完整性:计算MAC时,将消息和密钥进行计算产生MAC值,若数据被第三方攻击者修改则计算的MAC值并不相同,这保证了数据的完整性。
  • 数据真实性:计算MAC时,由于密钥只有发送者和接收者共享,则保证了数据的真实性。若密钥被第三方攻击这窃取则就无法保障数据的真实性。
  • 密码验证:在某些情况下,MAC还可以用作用户密码的验证机制,当合法的用户数据正确密码时,系统可以提取正确的MAC,使用正确的密码和API key所所生成的MAC与存储在服务器上的MAC进行比较,就可以完成密码验证工作。

消息认证码_IP

消息认证码的几种攻击

  • 重放攻击
    攻击者截获网络上的MAC数据包,并将其重新发送到目标用户,欺骗目标用户相信这是合法的通信。比如:
假设银行使用一个MAC来验证客户端的请求。每个请求都包含时间戳(防止重放攻击)。攻击者成功的截获了一个合法请求和对应的MAC值。在一段时间以后,攻击者重新发送原始请求和相同的MAC值。由于请求的时间戳被攻击者处理成和之前截获的请求时间戳相同。由于攻击者使用之前的MAC,使得银行无法区分新的请求是否已经被更改或者重放。

实验:

from scapy.all import *

# Function for sending ARP reply packets
def send_arp_reply(pkt):
    if pkt[ARP].op == 1:
        ether = Ether(dst=pkt[Ether].src, src=get_if_hwaddr(conf.iface))
        arp = ARP(op=2, hwsrc=get_if_hwaddr(conf.iface), psrc=pkt[ARP].pdst,
                  hwdst=pkt[ARP].hwsrc, pdst=pkt[ARP].psrc)
        pkt = ether / arp
        print("ARP Replying to: " + pkt[ARP].psrc)
        #sendp(pkt, inter=0.5, iface=conf.iface)

# Function for sending ICMP packets
def send_icmp(pkt):
    if pkt[IP].dst == get_if_addr(conf.iface):
        ip = IP(src=pkt[IP].dst, dst=pkt[IP].src)
        icmp = ICMP(type="echo-reply", id=pkt[ICMP].id,
                    seq=pkt[ICMP].seq, load=pkt[ICMP].load)
        pkt = ip / icmp
        print("ICMP Replying to: " + pkt[IP].dst)
        #send(pkt, iface=conf.iface)

# Function for sending TCP packets
def send_tcp(pkt):
    if pkt[TCP].flags == "S":
        ip = IP(src=pkt[IP].dst, dst=pkt[IP].src)
        tcp = TCP(sport=pkt[TCP].dport, dport=pkt[TCP].sport,
                  flags="SA", seq=pkt[TCP].ack, ack=pkt[TCP].seq+1)
        pkt = ip / tcp
        print("TCP Replying to: " + pkt[IP].dst + ":" + str(pkt[TCP].dport))
        #send(pkt, iface=conf.iface)

# Capture and replay packets
pkts = sniff(iface=conf.iface, filter="arp or icmp or tcp", count=10)

for pkt in pkts:
    if pkt.haslayer(ARP):
        send_arp_reply(pkt)
    elif pkt.haslayer(ICMP):
        send_icmp(pkt)
    elif pkt.haslayer(TCP):
        send_tcp(pkt)

在上面的代码中,使用了ARP,ICMP和TCP三种不同类型的数据包的处理函数。每种函数根据接收到的数据包构建新的数据包,并使用send或者sendp函数将其发送回网络中。在主程序中执行sniff函数捕获10个数据包,然后对每个数据包进行重放攻击,既实现的三个函数。

  • 中间人攻击
    攻击者伪装为合法的通信方,并试图截获或篡改数据包。
    下面这个实验使用scapy实现消息认证码的中间人攻击的一个简单案例,该脚本将获取网络上的数据包,篡改身份验证并重放。
#!/usr/bin/env python

from scapy.all import *
import hashlib

def modify_packet(packet):
    # 查找TCP层并检查它是否有有效的负载
    if packet.haslayer(TCP) and packet.haslayer(Raw):
        # 获取原始数据负载,即消息认证码(MAC)
        mac = str(packet[Raw])

        # 对mac进行修改或者破坏,以达到欺骗的目的
        # 我们在这里简单地将所有小写字母转换为大写字母
        modified_mac = mac.upper()

        # 通过计算SHA256哈希值更新修改后的MAC
        hash_object = hashlib.sha256(bytes(modified_mac, 'utf-8'))
        modified_mac_sha256 = hash_object.hexdigest().encode('utf-8')

        # 更新TCP和数据包中的MAC字段
        packet[TCP].seq += len(mac) - len(modified_mac)
        packet[Raw].load = modified_mac_sha256

        # 删除IP和TCP校验和以及长度字段,以便Scapy重新计算它们
        del(packet[IP].chksum)
        del(packet[TCP].chksum)
        del(packet[IP].len)

        print("Modified MAC: ", modified_mac)
        print("Modified MAC SHA256: ", modified_mac_sha256)

    return packet

# 定义回调函数来嗅探和修改数据包
def sniff_packets(pkt):
    # 对基于TCP的HTTP流量进行过滤
    if pkt.haslayer(TCP) and pkt.haslayer(Raw) and \
            (pkt[TCP].dport == 80 or pkt[TCP].sport == 80):
        modified_pkt = modify_packet(pkt)
        # 这里先注释掉
        #sendp(modified_pkt, iface="eth0")

# 启动捕获器
sniff(filter="tcp port 80", prn=sniff_packets)
  • 内部攻击
    当攻击者拿到系统内部权限时,攻击者获取到密钥,从而达到攻击的目的。
  • 差分攻击
    攻击者拥有2个或者多个有效的MAC输入,并试图推测出正确结果。这种攻击需要针对具体实现的算法进行分析。
    差分攻击需要对加密算法进行分析,在python中有HMAC库支持差分攻击,下面是一个简单的案例:
import hashlib
import hmac

def diff_attack(key, data, output):
    for a in range(256):
        for b in range(256):
            key1 = bytes([key[0] ^ a]) + key[1:]
            key2 = bytes([key[1] ^ b]) + key[2:]
            if hmac.new(key1, data, hashlib.sha256).digest() == output:
                return (a, b, key2)
    return None

# example usage
key = b'\x01\x23\x45\x67\x89\xab\xcd\xef\xfe\xdc\xba\x98\x76\x54\x32\x10'
data = b'Test Data'
output = hmac.new(key, data, hashlib.sha256).digest()

result = diff_attack(key, data, output)
if result:
    print('Key byte 1: {:02X}'.format(result[0]))
    print('Key byte 2: {:02X}'.format(result[1]))
    print('Recovered key: {}'.format(result[2].hex().upper()))
else:
    print('Key not found')

上面的代码使用hmac库来计算SHA-256 HMAC,并通过枚举查找与给定输出匹配的密钥字节。

消息认证码的实现原理

  • 密钥生成算法(Gen):
    输入:安全参数n, 输出: 满足特定分布的密钥k
  • 消息验证码子算法(Mac)
    输入:密钥k, 消息m; 输出:MAC标签t, \(Mac(k, m) \to k\)
  • 验证子算法(Vrfy)
    输入: 密钥k, 消息m, 标签t; 输出:\(b = Vrfy(k, m, t)\)
    其中b=1则认证成功,否则失败

消息认证码的消息验证码子算法\(Mac(k, m)\)有两个方法:

  • 单向散列函数(\(HMAC\))的实现
  • 分组密码

单向散列函数HMAC

\[HMAC(k, m)=H((k\ XOR\ opad)+H(k\ XOR\ ipad)+m) \]

其中:k是密钥,m是认证消息,XOR是异或运算,opad是外部的填充常数,ipad是内部的填充常数,“+”是连接, H()是哈希函数,比如MD5,SHA1,SHA256等。

md5:		128位二进制(被破解)
sha1:		160位二进制(被破解)
sha256:	256位二进制(未破解)
  • MD5-MAC
import hmac
from hashlib import md5

# 此处的 "message" 是要进行消息认证的内容
message = b'This is a secret message'

# 此处的 "key" 用于HMAC的计算 (应该保密)
key = b'secret_key'

# 创建 HMAC 对象
hmac_md5 = hmac.new(key, message, md5)

# 执行消息认证并输出结果
print("HMAC-MD5 digest: {}".format(hmac_md5.digest()))
  • SHA1-MAC
import hmac
import hashlib

KEY = b"secret_key"
MESSAGE = b"my_message"

# 使用SHA1算法创建HMAC
digest_maker = hmac.new(KEY, MESSAGE, hashlib.sha1)

# 输出HMAC十六进制表示形式的字符串
print(digest_maker.hexdigest())
  • SHA256-MAC

分组密码

  • CBC-MAC
    加密过程:

解密过程:

消息认证码_IP_02

CBC-MAC的消息认证公式如下:

\[H_1 \gets E_k(x_1)\\ H_i \gets E_k(H_{i-1}+x_i),\ 2 \le i \le t \]

实验:

from scapy.all import *
msg = b"Hello, this is a test message"

# 创建一个加密密钥并将其设置为变量
key = [0x24, 0x75, 0xa2, 0xb3, 0xc9, 0xf1, 0xed, 0xce]

我们需将此消息进行cbc-mac计算。在发送之前,需要对其进行填充以确保其长度是AES块大小的倍数(在本例中为16字节)。代码如下:

pad_len = (-len(msg)) % 16               # 计算填充长度
cmac_msg = msg + bytes([pad_len])*pad_len # 添加一个填充以得到到达16字节长度的数据块
iv = bytes([0] * 16)

使用pyaes库来计算经过CBC-MAC处理后的数据块,利用循环使整个信息经过处理:

import pyaes
aes = pyaes.AES(bytes(key))

# 对处理后的信号作出循环
for i in range(0,len(cmac_msg),16):
    block = cmac_msg[i:i+16]
    iv = aes.encrypt(block_xor(iv, block))

cmac_result = iv
print(cmac_result.hex())

参考博客