RADIUS的Message Authenticator计算
freeRADIUS EAP认证验证

由于freeRADIUS 3.x默认不支持AKA的EAP认证方式,最近在更新4.0版本的freeRADIUS后(freeRADIUS此时还未发布release版本,使用的是github代码的编译版),发现客户端发起的EAP认证请求后服务器没有任何返回。

查看freeRADIUS的debug日志,看到下面错误。

Debug : proto_radius_udp - Received Access-Request ID 60 length 149 radius_udp server * port 1812
ERROR : (0)  ERROR: Failed reading packet: invalid Message-Authenticator (shared secret is incorrect)

结果freeRADIUS都没有接收请求,认为secret不对丢掉请求了,起初认为是secret配置错误,一通排查后才发现其实是Message-Authenticator的问题。之前版本的freeRADIUS似乎没有验证Message-Authenticator的正确性,客户端的Message-Authenticator传16个0也可以接收请求进行正常认证,更新新版本就不行了,Message-Authenticator全0就不接收请求。

然后发现client.conf里有个配置require_message_authenticator = no,配置上之后,Message-Authenticator全0还是一样的报错,如果删掉Message-Authenticator的attribute的话,会是另一个报错

proto_radius_udp got a packet which isn't RADIUS: we require Message-Authenticator attribute, but it is not in the packet

既然绕不过,那只能老老实实去计算Message-Authenticator的值了。

按照规范RFC2865的计算方法,借助google和ChatGPT花了亿点点时间写出了python的验证代码。

import hmac
import hashlib

# 核心代码
message_authenticator = hmac.new(shared_secret.encode(), raw_radius_packet_byte, hashlib.md5).hexdigest()
print(f'Message-Authenticator:\n{message_authenticator}\n')

raw_radius_packet_byte是从RADIUS部分的Request Code开始到包结束,raw数据里的原来Message-Authenticator的值必须是全0(0x5012是Message-Authenticator开头的标志),计算出新的Message-Authenticator值后,再替换掉原来Message-Authenticator里的全0值,最后把包发出就可以了。

BTW: Request AuthenticatorMessage-Authenticator不是一个东西,计算方法和作用也不同。

附上完整的python发起EAP请求的完整代码

import hmac
import hashlib
from pyrad.client import Client
from pyrad.dictionary import Dictionary
import pyrad.packet
import socket
from pyrad.client import Client
from pyrad.dictionary import Dictionary
from pyrad.packet import AccessRequest, AccessAccept, AccessReject

def send_radius_request(server, secret, user, password):
    # 创建一个 RADIUS 客户端对象
    client = Client(server=server, secret=secret.encode(), dict=Dictionary("/dictionary.txt"))

    # 创建一个 Access-Request 包
    request = client.CreateAuthPacket()
    request["User-Name"] = user
    #request["User-Password"] = password

    eap_hex_str = "024a0038013034363030313133313533363232303540776c616e2e6d6e633030312e6d63633436302e336770706e6574776f726b2e6f7267"
    eap_byte_str = bytes(int(eap_hex_str[i:i+2], 16) for i in range(0, len(eap_hex_str), 2))
    request["EAP-Message"] = eap_byte_str

	# 设置Message-Authenticator为全0
    request["Message-Authenticator"] = b'\x00' * 16

    raw_packet = request.RequestPacket()
    request.authenticator = hashlib.md5(raw_packet + secret.encode()).digest()

	# 计算Message-Authenticator的值
    raw_packet2 = request.RequestPacket()
    hmac_md5 = hmac.new(secret.encode(), raw_packet2, hashlib.md5).digest()

	# 替换原Message-Authenticator为计算的值
    request["Message-Authenticator"] = hmac_md5

    # 发送请求并接收响应
    try:
        response = client.SendPacket(request)

        if response.code == AccessAccept:
            print("Access Accepted")
        elif response.code == AccessReject:
             print("Access Rejected")
        else:
            print("Received unknown response code", response.code)
    except Exception as e:
        print("An error occurred:", e)

if __name__ == '__main__':

    radius_server = "192.168.0.22"
    radius_secret = "testing123"
    username = "[email protected]"
    password = "password"
    
	send_radius_request(radius_server, radius_secret, username, password)

搞定收工~



Last modified on 2024-06-24