SolarWinds Orion 开发指南

发布于 2024-10-30 07:49:34 字数 9285 浏览 29 评论 0

0x00 前言

SolarWinds Orion 平台是一个统一的网络和系统管理产品套件,可用于监控 IT 基础架构。我们可以通过 SolarWinds Information Service (SWIS) 访问 Orion 平台中的数据。

在程序实现上,我们可以借助 SolarWinds Orion API 进行开发,但是在最近的漏洞利用上,我们无法直接使用 SolarWinds Orion API。

本文将要介绍 SolarWinds Orion API 的用法,分析无法直接使用的原因,提供一种解决方法,开源两个测试代码

0x01 简介

本文将要介绍以下内容:

  • SolarWinds Orion API 的使用
  • 模拟网页操作的实现
  • 开发细节
  • 开源代码

0x02 SolarWinds Orion API 的使用

参考资料:https://github.com/solarwinds/OrionSDK/wiki

Python 语言可使用 orionsdk 库进行开发,地址如下:https://github.com/solarwinds/orionsdk-python

在引入 orionsdk 库后,可以很容易的实现以下功能:

  • query
  • invoke
  • create
  • read
  • update
  • bulkupdate
  • delete
  • bulkdelete

为了研究 SolarWinds Orion API 的实现细节,决定不借助 orionsdk 库实现相同的功能

语法格式的参考资料:https://github.com/solarwinds/OrionSDK/wiki/REST

对于 SolarWinds Orion API,需要注意以下细节:

1.接口地址

默认接口地址为 https://<url>:17778/SolarWinds/InformationService/v3/Json/

2.用户验证

添加 Header: Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

其中, dXNlcm5hbWU6cGFzc3dvcmQusername:password 作 Base64 编码后的结果

3.数据查询

通过 POST 发送查询命令,格式为 application/json 类型

SolarWinds Orion API 使用 SolarWinds Query Language (SWQL),类似于 SQL 语法

数据库的表项可以通过本地搭建测试环境,执行 SolarWinds Orion 下的 DataBase Manager 进行查看

查询数据库的示例代码:

def SWIS_query(api_host, username, password, query, **params):
    authentication = username + ":" + password
    authentication = authentication.encode("utf-8")
    credential = base64.b64encode(authentication).decode("utf8")
    url = "https://" + api_host + ":17778/SolarWinds/InformationService/v3/Json/Query"
    headers = {
        "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0",
        "Authorization": "Basic " + credential,
        "Content-Type": "application/json"
    }

    data =  {
                "query": query,
                "parameters":params   
            }

    r = requests.post(url, headers = headers, data=json.dumps(data), verify = False)
    if r.status_code ==200: 
        print("[+] query success")
        for i in r.json()["results"]:
            print(i)

    else:         
        print("[!]")
        print(r.status_code)
        print(r.text)
        exit(0)

在 Python 代码开发上,需要考虑以下细节:

1.将字典作为命令行参数传递

可以先通过命令行参数传入字符串,再将字符串转义为 json 字符串

固定参数的用法示例:

entity = "Orion.Pollers"
properties = {"PollerType":"hi from curl 2", "NetObject":"N:123"}
SWIS_create(api_host, username, password, entity, **params)

将字典作为命令行参数传递的用法示例:

entity = "Orion.Pollers"
properties = input("input the properties: (eg. {\"PollerType\":\"hi from curl 2\", \"NetObject\":\"N:123\"} )")
SWIS_create(sys.argv[1], sys.argv[2], sys.argv[3], entity, **json.loads(properties))

2.将列表作为命令行参数传递

可以先通过命令行参数传入字符串,再将字符串转为列表

固定参数的用法示例:

uris = ["swis://WIN-KQ48K3S9B92/Orion/Orion.Nodes/NodeID=1/CustomProperties"]
properties = {"City": "Serenity Valley"}
SWIS_bulkupdate(api_host, username, password, uris, **properties)

将列表作为命令行参数传递的用法示例:

uris = input("input the uris: (eg. swis://Server1/Orion/Orion.Nodes/NodeID=1/CustomProperties,swis://Server1/Orion/Orion.Nodes/NodeID=2/CustomProperties )")
properties = {"City": "Serenity Valley"}
SWIS_bulkupdate(sys.argv[1], sys.argv[2], sys.argv[3], uris.split(','), **properties)

完整的实现代码已上传至 Github,地址如下:https://github.com/3gstudent/Homework-of-Python/blob/master/SolarWindsOrionAPI_Manage.py

代码支持以下功能:

  • query
  • invoke
  • create
  • read
  • update
  • bulkupdate
  • delete
  • bulkdelete

为了便于使用,省去输入查询语句的过程,还支持以下功能:

  • GetAccounts
  • GetAlertActive
  • GetAlertHistory
  • GetCredential
  • GetNodes
  • GetOrionServers

猜测是出于安全考虑,SolarWinds Orion API 的功能有限,有些数据库无法进行查询,例如 VirtualMachines 表(存储虚拟机信息)、CredentialProperty 表(存储凭据信息)、Accounts 表的 PasswordHash 项和 PasswordSalt 项

已公开的 SolarWinds 漏洞(CVE-2020-10148、CVE-2020-27870、CVE-2020-27871、CVE-2021-31474) 涉及的均为网络协议接口(默认为 http://<ip>:8787/Orion/ ),同 SolarWinds Orion API 不同,所以无法结合利用

0x03 模拟网页操作的实现

为了结合漏洞利用,我们需要网页登录,通过抓取数据包的方式实现同 SolarWinds Orion 的数据交互,功能同 SolarWinds Orion API 的功能保持一致

1.登录验证

通过抓取数据包发现,SolarWinds Orion 基于 ASP.NET 平台使用了 ViewState 存储数据

但经过实际测试,我们的测试程序可以不带有 ViewState,不影响功能

登录验证的数据包流程如下:

  1. 访问: http://<ip>:8787/Orion/Login.aspx?autologin=no
  2. 返回响应码为 302
  3. 自动跳转至 http://<ip>:8787/Orion/View.aspx
  4. 返回响应码为 302
  5. 自动跳转至 http://<ip>:8787/Orion/SummaryView.aspx
  6. 返回响应码为 200,获得最终结果

在程序实现上,我们可以对返回的最终结果进行判断,如果 Cookie 中带有 __AntiXsrfToken 项,那么代表用户验证成功

示例代码:

def Check(api_host, username, password):
    url = api_host + "/Orion/Login.aspx?autologin=no"

    body = {
            "__EVENTTARGET": "ctl00$BodyContent$LoginButton",
            "ctl00$BodyContent$Username": username,
            "ctl00$BodyContent$Password": password
            }

    postData = urllib.parse.urlencode(body).encode("utf-8")

    headers = {
        "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0",
        "Content-Type": "application/x-www-form-urlencoded",    
    }

    r = requests.post(url, headers = headers, data=postData, verify = False)
    if r.status_code ==200 and "__AntiXsrfToken" in r.headers['set-cookie']:
        print("[+] Valid:%s  %s"%(username, password))
        r.close()
    else:         
        print("[!]")
        print(r.status_code)
        print(r.text)
        r.close()
        exit(0)

2.查询操作

Header 中需要额外添加属性 X-XSRF-TOKEN

属性 X-XSRF-TOKEN 的值在初次登录时访问 http://<ip>:8787/Orion/Login.aspx?autologin=no 返回的 302 结果中获得

在程序实现上,可以通过添加参数 allow_redirects=Faslse 来禁用跳转,在返回的 Cookie 中取出 X-XSRF-TOKEN

查询接口地址: http://<ip>:8787/api2/swis/query

示例代码:

def QueryData(api_host, username, password, query, **params):
    session = requests.session()
    url = api_host + "/Orion/Login.aspx?autologin=no"

    body = {
            "__EVENTTARGET": "ctl00$BodyContent$LoginButton",
            "ctl00$BodyContent$Username": username,
            "ctl00$BodyContent$Password": password
            }

    postData = urllib.parse.urlencode(body).encode("utf-8")

    headers = {
        "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0",
        "Content-Type": "application/x-www-form-urlencoded",    
    }

    r = session.post(url, headers = headers, data=postData, verify = False)
    if r.status_code !=200 or "__AntiXsrfToken" not in r.headers['set-cookie']:
        print("[!]")
        print(r.status_code)
        print(r.text)
        r.close()
        exit(0)

    print("[+] Valid:%s  %s"%(username, password))

    r = session.post(url, headers = headers, data=postData, verify = False, allow_redirects=False)
    index = r.headers['Set-Cookie'].index('XSRF-TOKEN')
    xsrfToken = r.headers["Set-Cookie"][index+11:index+55]
    print("[+] XSRF-TOKEN: " + xsrfToken)

    url1 = api_host + "/api2/swis/query?lang=en-us&swAlertOnError=false"
    headers = {
        "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0",
        "Content-Type": "application/json",
        "X-XSRF-TOKEN":xsrfToken
    }

    data =  {
                "query": query,
                "parameters":params   
            }

    r = session.post(url1, headers = headers, data=json.dumps(data), verify = False)
    if "Result" in r.json():
        print("[+] Result: ")
        dic = r.json()['Result']
        for i in dic:       
            print(i)
    else:
        print("[!]")
        print(r.json())

数据格式同 SolarWinds Orion API 的 query 命令保持一致,所以我们可以直接对照 SolarWinds Orion AP 实现相同的功能

完整的实现代码已上传至 Github,地址如下:https://github.com/3gstudent/Homework-of-Python/blob/master/SolarWindsOrionAPI_Manage.py

代码支持用户口令验证和数据库查询的功能

为了便于使用,省去输入查询语句的过程,支持以下功能:

  • GetAccounts
  • GetAlertActive
  • GetAlertHistory
  • GetCredential
  • GetNodes
  • GetOrionServers

0x04 小结

本文分别介绍了使用 SolarWinds Orion API 和模拟网页操作实现数据查询的方法,开源测试代码,便于同其他漏洞利用相结合。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

文章
评论
26 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文