当前位置: 首页 > news >正文

Hikvision 考勤机数据提取(3)

同样使用 HTTPDigestAuth

import json
import binascii
import base64
import hashlib
import time
import requests
import argparse
import uuid
import xml.etree.ElementTree as ET
from datetime import datetime, timedelta
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad, pad
from requests.auth import HTTPDigestAuth# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
DEFAULT_IP = "192.168.1.192"
DEFAULT_PORT = "8080"
USERNAME = "admin"
PASSWORD = "password"# 认证对象
DIGEST_AUTH = HTTPDigestAuth(USERNAME, PASSWORD)# ---------------------------------------------------------------------------
# Key Derivation
# ---------------------------------------------------------------------------
def get_aes_key(host_str, username, password, auth, proxies=None):"""Derive AES Key dynamically from device capabilities."""try:url = f"http://{host_str}/ISAPI/Security/capabilities?username={username}"print(f"Fetching capabilities from {url}...")resp = requests.get(url, auth=auth, timeout=10, verify=False, proxies=proxies)resp.raise_for_status()root = ET.fromstring(resp.text)ns = {'ns': 'http://www.isapi.org/ver20/XMLSchema'}salt_node = root.find('ns:salt', ns)if salt_node is None:print("Error: Could not find <salt> in capabilities response.")return Nonesalt = salt_node.textprint(f"Got Salt: {salt}")# 1. Calculate IrreversibleKeycombined_string = f"{username}{salt}{password}"irreversible_key = hashlib.sha256(combined_string.encode('utf-8')).hexdigest()# 2. Calculate Final Key with Challenge# Note: The challenge "AaBbCcDd1234!@#$" appears to be hardcoded or specific to this auth modechallenge = "AaBbCcDd1234!@#$" combined_for_hash = f"{irreversible_key}{challenge}"key = hashlib.sha256(combined_for_hash.encode('utf-8')).hexdigest()# 3. Iterate hashingiterations = 100for _ in range(1, iterations):key = hashlib.sha256(key.encode()).hexdigest()aes_key = key[:32]print(f"Derived AES Key: {aes_key}")return aes_keyexcept Exception as e:print(f"Key derivation failed: {e}")return None# ---------------------------------------------------------------------------
# Encryption / Decryption
# ---------------------------------------------------------------------------
def aes_encrypt(plaintext, key_hex, iv_hex):try:key_bytes = binascii.unhexlify(key_hex)iv_bytes = binascii.unhexlify(iv_hex)b64_str = base64.b64encode(plaintext.encode('utf-8')).decode('utf-8')data_bytes = b64_str.encode('utf-8')padded_data = pad(data_bytes, AES.block_size)cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)ciphertext_bytes = cipher.encrypt(padded_data)return binascii.hexlify(ciphertext_bytes).decode('utf-8')except Exception as e:print(f"Encryption error: {e}")return Nonedef aes_decrypt_base64(ciphertext_hex, key_hex, iv_hex):if not ciphertext_hex:return ""try:key_bytes = binascii.unhexlify(key_hex)iv_bytes = binascii.unhexlify(iv_hex)ct_bytes = binascii.unhexlify(ciphertext_hex)cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)decrypted = cipher.decrypt(ct_bytes)decrypted = unpad(decrypted, AES.block_size)b64_str = decrypted.decode('utf-8')plain_bytes = base64.b64decode(b64_str)return plain_bytes.decode('utf-8')except Exception:return ""def parse_time_arg(time_str):if not time_str:return Noneif 'T' in time_str:if '+' not in time_str and 'Z' not in time_str:return time_str + "+08:00"return time_strtry:dt = Nonetime_str = time_str.strip()if len(time_str) == 10:dt = datetime.strptime(time_str, "%Y-%m-%d")elif len(time_str) == 16:dt = datetime.strptime(time_str, "%Y-%m-%d %H:%M")elif len(time_str) == 19:dt = datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S")if dt:return dt.strftime("%Y-%m-%dT%H:%M:%S+08:00")except ValueError:passreturn time_str# ---------------------------------------------------------------------------
# Data Fetching
# ---------------------------------------------------------------------------
def fetch_attendance_v3(start_time, end_time, aes_key, base_url, employee_no=None, proxies=None, fixed_iv=None):all_records = []# Determine IVif fixed_iv:iv = fixed_ivprint(f"Using Fixed IV: {iv}")else:# Dynamic IV based on timestamplogin_timestamp = int(time.time() * 1000)iv = hashlib.md5(str(login_timestamp).encode()).hexdigest()print(f"Using Dynamic IV: {iv}")url = f"{base_url}/ISAPI/AccessControl/AcsEvent?format=json&security=1&iv={iv}"position = 0batch_size = 24search_id = str(uuid.uuid4())print(f"Fetching data from {start_time} to {end_time}...")target_encrypted_emp = Noneif employee_no:if len(employee_no) > 20 and all(c in '0123456789abcdefABCDEF' for c in employee_no):target_encrypted_emp = employee_noelse:target_encrypted_emp = aes_encrypt(employee_no, aes_key, iv)print(f"  -> Encrypted Employee No: {target_encrypted_emp}")while True:acs_event_cond = {"searchID": search_id,"searchResultPosition": position,"maxResults": batch_size,"major": 0,"minor": 0,"startTime": start_time,"endTime": end_time,}if target_encrypted_emp:acs_event_cond["employeeNoString"] = target_encrypted_emppayload = {"AcsEventCond": acs_event_cond}headers = {"Content-Type": "application/json","X-Requested-With": "XMLHttpRequest",}try:resp = requests.post(url, json=payload, headers=headers, auth=DIGEST_AUTH, proxies=proxies,timeout=30)resp.raise_for_status()data = resp.json()acs = data.get('AcsEvent', {})info = acs.get('InfoList', [])if not info:breakfor item in info:name_enc = item.get('name', '')emp_enc = item.get('employeeNoString', '')name = aes_decrypt_base64(name_enc, aes_key, iv) if len(name_enc) > 20 else name_enccurr_employee_no = aes_decrypt_base64(emp_enc, aes_key, iv) if len(emp_enc) > 20 else emp_encrecord = {'employeeNo': curr_employee_no,'name': name,'time': item.get('time', ''),'cardNo': item.get('cardNo', ''),'raw_employeeNoString': emp_enc }all_records.append(record)position += len(info)total_matches = acs.get('totalMatches', 0)print(f"Fetched {position}/{total_matches} records...")if position >= total_matches:breakexcept Exception as e:print(json.dumps({"error": f"Fetch error: {e}"}, ensure_ascii=False))breakreturn all_records# ---------------------------------------------------------------------------
# Main Function
# ---------------------------------------------------------------------------
def main_v3():global DIGEST_AUTHparser = argparse.ArgumentParser(description="Hikvision Attendance Fetcher V3 (Dynamic Key)")parser.add_argument("--start", help="Start time (e.g. 2025-12-08 08:00)", default=None)parser.add_argument("--end", help="End time (e.g. 2025-12-08 23:59)", default=None)parser.add_argument("--employeeNo", help="Employee Number (e.g. 16128 or encrypted hex)", default=None)parser.add_argument("--out", help="Output JSON file", default="attendance_v3.json")parser.add_argument("--ip", help=f"Target IP (default: {DEFAULT_IP})", default=DEFAULT_IP)parser.add_argument("--port", help=f"Target Port (default: {DEFAULT_PORT})", default=DEFAULT_PORT)parser.add_argument("--proxy", help="Proxy URL (e.g. http://127.0.0.1:8899)", default=None)parser.add_argument("--iv", help="Fixed IV (optional, for encrypted employeeNo)", default=None)args = parser.parse_args()host_str = f"{args.ip}:{args.port}"base_url = f"http://{host_str}"# Configure Proxiesproxies = Noneif args.proxy:proxies = {"http": args.proxy,"https": args.proxy,}print(f"Using Proxy: {args.proxy}")DIGEST_AUTH = HTTPDigestAuth(USERNAME, PASSWORD)# 1. Derive Keyprint("Deriving AES Key...")aes_key = get_aes_key(host_str, USERNAME, PASSWORD, DIGEST_AUTH, proxies)if not aes_key:print("Failed to derive key. Exiting.")return# 2. Parse Timesif args.start:args.start = parse_time_arg(args.start)else:now = datetime.now()start_dt = datetime(now.year, now.month, now.day, 0, 0, 0)args.start = start_dt.strftime("%Y-%m-%dT%H:%M:%S+08:00")if args.end:args.end = parse_time_arg(args.end)else:now = datetime.now()end_dt = datetime(now.year, now.month, now.day, 23, 59, 59)args.end = end_dt.strftime("%Y-%m-%dT%H:%M:%S+08:00")# 3. Fetch Datarecords = fetch_attendance_v3(args.start, args.end, aes_key, base_url, args.employeeNo, proxies, args.iv)# 4. Savewith open(args.out, 'w', encoding='utf-8') as f:json.dump(records, f, ensure_ascii=False, indent=2)print(f"Data successfully saved to {args.out} ({len(records)} records)")if __name__ == "__main__":main_v3()

imageimage

http://www.gsyq.cn/news/78090.html

相关文章:

  • 2025年折弯机上下模实力厂家推荐榜
  • 阅读笔记四
  • 工程模拟分析软件 Abaqus 2024 免费下载安装教程(含中文版设置+ 激活步骤)
  • 2025新手买钓鱼竿指南:高性价比品牌推荐,避坑看这篇
  • 大模型应用开发LangChain框架 - yi
  • 2025年渔具实测:新款鲫鱼竿超轻硬,高性价比钓鱼竿真靠谱
  • 2025年国产鱼竿十大品牌:优选前十的口碑鱼竿盘点
  • omniinfer vllm v0.9.0整体框架图和pangu7b模型图
  • 过碳酸钠源头工厂在哪里?过碳酸钠直销厂家:含氧量高的过碳酸钠厂家推荐
  • 成膜助剂供应商推荐:实力厂家/批发商货源稳定有保障
  • 决策单调性(四边形不等式)学习笔记
  • 应用 SQLAlchemy 操作单表:以 SQLite 用户表为例的完整实战指南
  • MyBatis参数加解密
  • 基于Hadoop+数据可视化+机器学习随机森林预测算法+智能AI大模型+协同过滤推荐算法的青少年饮食习惯数据分析与可视化平台的设计与实现(精品源码+精品论文+上万材料集+答辩PPT)
  • CF1994G
  • 成膜助剂出口厂商有哪些?有出口资质的成膜助剂供应商名单推荐
  • hive ddl dml hivesql命令大全
  • 杭州刑事案件法律咨询找谁?刑事律师推荐
  • 网络编程
  • 2025常州会计师事务所实力榜:汇丰所以审计创新与税务筹划优势领跑,江苏八城专业服务机构深度解析
  • doc-llm-autotest 基于大模型的文档自动化测试平台:worker服务的可靠性增强
  • TB710FU原厂刷机包下载_CN_ZUI_17.0.04.279_ST_250808
  • Mybatis拦截器原理解析
  • TB331FC原厂刷机包下载_CNZUI_17.0.572_ST_250910
  • TB520FU刷机包_CN_17.0.10.158_ST_250817
  • [智能体设计模式] 第 1 章:提示链(Prompt Chaining) - 实践
  • 蓝鲸花呗客服妙招帮你脱困省油大空间低配拆解银河的“水桶车细节值得吵一架
  • 吴恩达深度学习课程四:计算机视觉 第一周:卷积基础知识(一)图像处理基础
  • 索引数组读取修改添加
  • JAVA学习笔记-DAY3