Hikvision 考勤机数据提取(2)
import xml.etree.ElementTree as ET
import requests
from requests.auth import HTTPDigestAuth
import json
import sys
import hashlib
import base64
import time
def get_random():
timestamp = str(int(time.time() * 1e3 ))
md5_hash = hashlib.md5(timestamp.encode()).hexdigest()
e = md5_hash[:8]
e = str(int(e.replace("#", ""), 16))[:8]
return e
ip='192.168.1.100'
port=80
username='admin'
password='admin'
session_url=f'http://{ip}:{port}/ISAPI/Security/sessionLogin/capabilities?username={username}&random={get_random()}'
xml_string=requests.get(session_url).text
# xml_string = '<?xml version="1.0" encoding="UTF-8"?>\n<SessionLoginCap version="2.0" xmlns="http://www.isapi.org/ver20/XMLSchema">\n <sessionID>b81319692d0d270187bd71246c096131eae6f465243b565368e4c6e2f37aa59a</sessionID>\n <challenge>07bf94e08f004c30736eb0835270393e</challenge>\n <iterations>100</iterations>\n <isIrreversible>true</isIrreversible>\n <salt>WTV7Y1NXX57TXW4V0N4LS3OYFRHOPUML4Y8IFVVDGIMDEQOFUS0MV40QBXV0SHLC</salt>\n <sessionIDVersion>2</sessionIDVersion>\n</SessionLoginCap>\n'
root = ET.fromstring(xml_string)
# 定义命名空间
ns = {'ns': 'http://www.isapi.org/ver20/XMLSchema'}
# 使用命名空间查找元素
session_id = root.find('ns:sessionID', ns)
salt=root.find('ns:salt', ns)
challenge=root.find('ns:challenge', ns)
iterations=root.find('ns:iterations', ns)
print(session_id.text, salt.text, challenge.text, iterations.text) # 现在应该能正确输出 sessionID 的值
def get_security_capabilities(ip, username,password, port=80):
"""
Retrieves the solid salt to generate aes key
Args:
ip: IP address of the A10 Plus
username: Username for authentication
password: Password for authentication
port: HTTP port (default 80)
Returns:
Dictionary with device information or an error message
"""
url = f"http://{ip}:{port}/ISAPI/Security/capabilities?username={username}"
auth = HTTPDigestAuth(username, password)
print(f"Querying device salt value at: {url}")
try:
response = requests.get(url, auth=auth, timeout=10, verify=False)
print(f"Response code: {response.status_code}")
if response.status_code == 200:
# Parse the XML response
try:
root = ET.fromstring(response.content)
security_cap = parse_security_capabilities(root)
return {
'status': 'success',
'security_cap': security_cap,
'raw_response': response.text
}
except ET.ParseError as e:
return {
'status': 'error',
'message': f'Error parsing XML: {str(e)}',
'raw_response': response.text
}
else:
return {
'status': 'error',
'message': f"HTTP Error: {response.status_code}",
'details': response.text if hasattr(response, 'text') else 'No details available'
}
except requests.exceptions.RequestException as e:
return {
'status': 'error',
'message': f'Connection Error: {str(e)}'
}
def parse_security_capabilities(root):
"""
Extracts device SecurityCap include salt information from the XML response
"""
fields = [
'supportUserNums','userBondIpNums','userBondMacNums',
'isSupCertificate','issupIllegalLoginLock','isSupportOnlineUser',
'isSupportAnonymous','isSupportStreamEncryption','securityVersion',
'keyIterateNum','isSupportUserCheck','SecurityLimits',
'WebCertificateCap','RSAKeyLength'
]
security_cap = {}
# Handle namespace if it exists
namespace = ''
if '}' in root.tag:
namespace = root.tag.split('}', 1)[0] + '}'
if hasattr(root, 'attrib') and root.attrib:
for attr_name, attr_value in root.attrib.items():
security_cap[attr_name] = attr_value
# Extract all available fields
for field in fields:
# Search with namespace if it exists
element = root.find(f'.//{namespace}{field}') if namespace else root.find(f'.//{field}')
if element is not None:
if len(element) > 0:
security_cap[field] = extract_nested_element(element)
elif element.attrib:
attr_info = []
for attr_name, attr_value in element.attrib.items():
attr_info.append(f"{attr_name}={attr_value}")
security_cap[field] = f"[{', '.join(attr_info)}]"
elif element.text and element.text.strip():
security_cap[field] = element.text.strip()
else:
security_cap[field] = "[Empty]"
for child in root:
tag = child.tag
# Remove namespace if it exists
if '}' in tag:
tag=tag.split('}', 1)[1]
if tag not in security_cap:
if len(child) > 0:
security_cap[tag] = extract_nested_element(child)
elif child.attrib:
attr_info = []
for attr_name, attr_value in child.attrib.items():
attr_info.append(f"{attr_name}={attr_value}")
security_cap[tag] = f"[{', '.join(attr_info)}]"
elif child.text and child.text.strip():
security_cap[tag]=child.text.strip()
root_attrs = extract_element_attributes(root)
security_cap.update(root_attrs)
return security_cap
def extract_nested_element(element):
"""
递归提取嵌套XML元素的信息
"""
result = []
if element.attrib:
attrs = []
for key, value in element.attrib.items():
attrs.append(f"{key}={value}")
if attrs:
result.append(f"{[', '.join(attrs)]}")
if element.text and element.text.strip():
result.append(element.text.strip())
for child in element:
child_info = extract_nested_element(child)
if child_info:
if child.tag !=element.tag:
result.append(f"{child.tag}: {child_info}")
else:
result.append(child_info)
return "; ".join(result) if result else "[No content]"
def extract_element_attributes(element, field_name=""):
"""
提取元素的属性信息,格式化为更易读的形式
"""
if not element.attrib:
return None
result = []
# 提取所有属性
for attr_name, attr_value in element.attrib.items():
result.append(f"{attr_name}={attr_value}")
# 特殊处理某些字段
if field_name == 'securityVersion' and 'opt' in element.attrib:
return f"option={element.attrib['opt']}"
elif field_name == 'RSAKeyLength':
info = []
if 'opt' in element.attrib:
info.append(f"option={element.attrib['opt']}")
if 'def' in element.attrib:
info.append(f"default={element.attrib['def']}")
return "; ".join(info) if info else None
return "; ".join(result) if result else None
def extract_element_attributes(element, prefix=""):
"""
extract element attributes
"""
attributes = {}
if hasattr(element, 'attrib') and element.attrib:
for attr_name, attr_value in element.attrib.items():
key = f"{prefix}{attr_name}" if prefix else attr_name
attributes[key] = attr_value
return attributes
def format_security_cap(security_cap):
"""
Formats the security capacities information for legible presentation
"""
if not security_cap:
return 'No security cap information found'
#
# all_fields = [
# # 基础配置
# "version",
# "supportUserNums",
# "userBondIpNums",
# "userBondMacNums",
# # 用户认证与管理
# "isSupCertificate",
# "isSupportAnonymous",
# "isSupportUserCheck",
# "isSupportONVIFUserManagement",
# # 安全策略
# "issupIllegalLoginLock",
# "isSupportOnlineUser",
# "isSupportStreamEncryption",
# # 密码安全
# "keyIterateNum",
# "SecurityLimits",
# "LoginPasswordLenLimit",
# # 加密算法
# "securityVersion",
# "RSAKeyLength",
# "WebCertificateCap",
# "CertificateType",
# "SecurityAlgorithm",
# "algorithmType"
# ]
priority_fields = [
'version', # 版本号 ✓
'supportUserNums', # 支持用户数量 ✓
'isSupCertificate', # 支持证书认证 ✓
'isSupportAnonymous', # 支持匿名访问 ✓
'isSupportUserCheck', # 支持用户检查 ✓
'isSupportStreamEncryption', # 支持流加密 ✓
'issupIllegalLoginLock', # 支持非法登录锁定 ✓
'isSupportOnlineUser', # 支持在线用户管理 ✓
'keyIterateNum', # 密钥迭代次数 ✓
'securityVersion', # 安全版本 ✓
'RSAKeyLength' # RSA密钥长度 ✓
]
output = ["=== SECURITIES CAPACITIES INFORMATION ==="]
# First display priority fields (only if they exist)
for field in priority_fields:
if field in security_cap:
output.append(f"{field}: {security_cap[field]}")
# Group information
output.append("\n=== Firmware INFORMATION ===")
firmware_fields = [
'securityVersion', # 安全版本 (已在priority_fields中)
'RSAKeyLength' # RSA密钥长度 (已在priority_fields中)
]
for field in firmware_fields:
if field in security_cap and field !='securityVersion':
output.append(f"{field}: {security_cap[field]}")
# Then display the rest of the fields
output.append("\n===ADDITIONAL INFORMATION ===")
for key, value in sorted(security_cap.items()):
if key not in priority_fields and key not in firmware_fields:
output.append(f"{key}: {value}")
return "\n".join(output)
def save_results_to_file(result, filename="security_cap.json"):
"""
Saves the results to a JSON file
"""
with open(filename, "w", encoding="utf-8") as f:
json.dump(result, f, indent=2, ensure_ascii=False)
print(f"Results saved to '{filename}")
result=get_security_capabilities(ip,username,password,port)
if result['status'] == 'success':
print("\n" +format_security_cap(result['security_cap']))
save_results_to_file(result)
else:
print(f"\nError: {result['message']}")
if 'details' in result:
print(f'Details: {result['details']}')
print("\nSuggestions:")
print("1. Verify that the IP and port are correct")
print("2. Check the user credentials (username and password)")
print("3. Ensure the device is accessible on the network")
print("4. Try with different ports (80, 8000)")
print("5. Check if the device requires HTTPS instead of HTTP")
参考:
https://github.com/reiarseni/hikvision-device-info/blob/main/hikvision_device_info.py
浙公网安备 33010602011771号