消息认证码
消息认证码
消息认证码的输入包含:任意长度的消息,密钥(发送者和接收者共享).输出:固定长度的密钥。
消息认证码的主要功能:
- 数据完整性:计算MAC时,将消息和密钥进行计算产生MAC值,若数据被第三方攻击者修改则计算的MAC值并不相同,这保证了数据的完整性。
- 数据真实性:计算MAC时,由于密钥只有发送者和接收者共享,则保证了数据的真实性。若密钥被第三方攻击这窃取则就无法保障数据的真实性。
- 密码验证:在某些情况下,MAC还可以用作用户密码的验证机制,当合法的用户数据正确密码时,系统可以提取正确的MAC,使用正确的密码和API key所所生成的MAC与存储在服务器上的MAC进行比较,就可以完成密码验证工作。
消息认证码的几种攻击
-
重放攻击
攻击者截获网络上的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
其中: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
加密过程:
解密过程:
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())