首页 > 其他分享 >如何用Golang写msf插件模块

如何用Golang写msf插件模块

时间:2024-06-06 11:33:46浏览次数:29  
标签:metasploit 插件 string err module Golang msmail go msf

最近有空在看msf,发现msf里面有模块的源码是golang的,去翻了翻wiki,wiki上面的编写日期是2018.12.13,搜了下国内,好像没有这方面的文章,那就自己跟着做做记个笔记

首先第一步自然是安装go,官方wiki上测试是在1.11.2通过,建议使用 version >= 1.11.2 的go,怎么安装go我不再赘述

注意事项

模块限制

不过golang目前还只支持以下几个msf模块的编写

  • remote_exploit
  • remote_exploit_cmd_stager
  • capture_server
  • docs
  • single_scanner
  • single_host_login_scanner
  • multi_scanner

代码限制

目前并不支持第三方库,但是可以在模块目录的 share/src 文件夹下放置你的库,整体上来说还是比较鸡肋

模块之间公有的库的路径在 lib/msf/core/modules/external/go/src/metasploit 目录下,可以自行查看

格式

顶行

首先go源码文件的顶行需要有可执行的标识,需要在文件顶行写上 //usr/bin/env go run "$0" "$@"; exit "$?"

这个原因主要是因为msf是基于ruby的,执行golang代码的话需要知晓执行路径或环境的信息,所以必须插入这一行

比如

//usr/bin/env go run "$0" "$@"; exit "$?"

package main

import (
	"metasploit/module"
	"net/http"
)

元数据信息

下面是需要填入一些元数据的信息来初始化你的模块,这一部分和ruby比较类似,主要是为了让msf能够读取、搜索和使用这些信息

import "metasploit/module"
func main() {
  metadata := &module.Metadata{
    Name: "<module name",
    Description: "<describe>",
    Authors: []string{"<author 1>", "<author 2>"},
    Date: "<date module written",
    Type:"<module type>",
    Privileged:  <true|false>,
    References:  []module.Reference{},
    Options: map[string]module.Option{	
      "<option 1":     {Type: "<type>", Description: "<description>", Required: <true|false>, Default: "<default>"},		
      "<option 2":     {Type: "<type>", Description: "<description>", Required: <true|false>, Default: "<default>"},
  }}

  module.Init(metadata, <the entry method to your module>)
}

可以看到main()方法调用了module.Init,后面的注释我们可以看到是 模块的入口方法

我们刚才说过模块之间公有的库,我们跟到这里面看看这个 module.Init 的定义

/*
 * RunCallback represents the method to call from the module
 */
type RunCallback func(params map[string]interface{})

/*
 * Initializes the module waiting for input from stdin
 */
func Init(metadata *Metadata, callback RunCallback) {
	var req Request

	err := json.NewDecoder(os.Stdin).Decode(&req)
	if err != nil {
		log.Fatalf("could not decode JSON: %v", err)
	}

	switch strings.ToLower(req.Method) {
	case "describe":
		metadata.Capabilities = []string{"run"}
		res := &MetadataResponse{"2.0", req.ID, metadata}
		if err := rpcSend(res); err != nil {
			log.Fatalf("error on running %s: %v", req.Method, err)
		}
	case "run":
		params, e := parseParams(req.Parameters)
		if e != nil {
			log.Fatal(e)
		}
		callback(params)
		res := &RunResponse{"2.0", req.ID, RunResult{"Module complete", ""}}
		if err := rpcSend(res); err != nil {
			log.Fatalf("error on running %s: %v", req.Method, err)
		}
	default:
		log.Fatalf("method %s not implemented yet", req.Method)
	}
}

可以看到,入口方法就是一个参数格式为 map[string]interface{} 的回调函数,下面的 Init 也好理解,首先根据你的传入参数,
查看是否是 describe 还是 run 指令,如果是 run 指令就执行回调,逻辑十分简单。

例子

msf框架的wiki中给了一个完整的示例https://github.com/rapid7/metasploit-framework/blob/master/modules/auxiliary/scanner/msmail/exchange_enum.go

我们直接那这个来分析一下

//usr/bin/env go run "$0" "$@"; exit "$?"

package main

import (
	"crypto/tls"
	"fmt"
	"metasploit/module"
	"msmail"
	"net/http"
	"strconv"
	"strings"
	"sync"
)

func main() {
	metadata := &module.Metadata{
		Name:        "Exchange email enumeration",
		Description: "Error-based user enumeration for Office 365 integrated email addresses",
		Authors:     []string{"poptart", "jlarose", "Vincent Yiu", "grimhacker", "Nate Power", "Nick Powers", "clee-r7"},
		Date:        "2018-11-06",
		Type:        "single_scanner",
		Privileged:  false,
		References:  []module.Reference{},
		Options: map[string]module.Option{
			"RHOSTS":     {Type: "string", Description: "Target endpoint", Required: true, Default: "outlook.office365.com"},
			"EMAIL":      {Type: "string", Description: "Single email address to do identity test against", Required: false, Default: ""},
			"EMAIL_FILE": {Type: "string", Description: "Path to file containing list of email addresses", Required: false, Default: ""},
		}}

	module.Init(metadata, run_exchange_enum)
}

首先开头就是我们刚才所谈到的格式,首先是顶行的固定格式,然后main方法是定义了元数据信息,里面定义这个模块的基本信息,
然后定义了模块的三个参数项 RHOSTS、EMAIL、EMAIL_FILE,紧接着调用了 module.Init

入口方法是 run_exchange_enum

我们看看这个方法的源码

func run_exchange_enum(params map[string]interface{}) {
	email := params["EMAIL"].(string)
	emailFile := params["EMAIL_FILE"].(string)
	threads, e := strconv.Atoi(params["THREADS"].(string))
	ip := params["rhost"].(string)

	if e != nil {
		module.LogError("Unable to parse 'Threads' value using default (5)")
		threads = 5
	}

	if threads > 100 {
		module.LogInfo("Threads value too large, setting max(100)")
		threads = 100
	}

	if email == "" && emailFile == "" {
		module.LogError("Expected 'EMAIL' or 'EMAIL_FILE' field to be populated")
		return
	}

	var validUsers []string
	if email != "" {
		validUsers = o365enum(ip, []string{email}, threads)
	}

	if emailFile != "" {
		validUsers = o365enum(ip, msmail.ImportUserList(emailFile), threads)
	}

	msmail.ReportValidUsers(ip, validUsers)
}

首先方法的开头取了模块的基本配置信息,紧接着是一系列判断,然后我们看到关键的代码

var validUsers []string
if email != "" {
	validUsers = o365enum(ip, []string{email}, threads)
}

if emailFile != "" {
	validUsers = o365enum(ip, msmail.ImportUserList(emailFile), threads)
}

msmail.ReportValidUsers(ip, validUsers)

o365enum 方法就在这个方法的下面,但是这里并不是我们讨论的重点,是什么我们不管,只需要知道,他执行了一些枚举遍历的操作,返回了可用的用户,操作怎么做的不在我们的讨论重点内

然后调用了 msmail.ReportValidUsers(ip, validUsers)

前面我们说过,并不支持第三方库,msmail是他自己在 share/src 下创建的一个库,具体代码可以在https://github.com/rapid7/metasploit-framework/blob/master/modules/auxiliary/scanner/msmail/shared/src/msmail/msmail.go看到

我们直接定位到我们刚才说到的 msmail.ReportValidUsers

package msmail

import (
	...
	"metasploit/module"
	...
)
...
func ReportValidUsers(ip string, validUsers []string) {
	port := "443"
	service := "owa"
	protocol := "tcp"
	for _, user := range validUsers {
		opts := map[string]string{
			"port":         port,
			"service_name": service,
			"address":      ip,
			"protocol":     protocol,
		}
		module.LogInfo("Loging user: " + user)
		module.ReportCredentialLogin(user, "", opts)
	}
}

这里面把传过来的用户名遍历了然后调用了

module.LogInfo("Loging user: " + user)
module.ReportCredentialLogin(user, "", opts)

前一个方法我们能猜到是日志输出
后一个呢?我们都跟过去看看

func rpcSend(res interface{}) error {
	rpcMutex.Lock()
	defer rpcMutex.Unlock()

	resStr, err := json.Marshal(res)
	if err != nil {
		return err
	}
	f := bufio.NewWriter(os.Stdout)
	if _, err := f.Write(resStr); err != nil {
		return err
	}
	if err := f.Flush(); err != nil {
		return err
	}

	return nil
}

...

func LogInfo(message string) {
	msfLog(message, "info")
}

...

func msfLog(message string, level string) {
	req := &logRequest{"2.0", "message", logparams{level, message}}
	if err := rpcSend(req); err != nil {
		log.Fatal(err)
	}
}

可以看到 module.LogInfo 是基础的日志输出

func ReportCredentialLogin(username string, password string, opts map[string]string) {
	base := map[string]string{"username": username, "password": password}
	if err := report("credential_login", base, opts); err != nil {
		log.Fatal(err)
	}
}

func report(kind string, base map[string]string, opts map[string]string) error {
	for k, v := range base {
		opts[k] = v
	}
	req := &reportRequest{"2.0", "report", reportparams{kind, opts}}
	return rpcSend(req)
}

这个方法就是很简单的把数据略微友好型展示了一下

所以整个的流程就是选择好模块,设置好参数,module.Init 进去,然后直接 run,就执行 module.Init 第二个参数传入的回调方法

然后后面就只是你用golang进行其他操作最后再进行调用msf公有模块进行信息展示了

References

 

本文作者:Akkuman

本文链接:https://www.cnblogs.com/Akkuman/p/12380130.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

标签:metasploit,插件,string,err,module,Golang,msmail,go,msf
From: https://www.cnblogs.com/gongzb/p/18234824

相关文章

  • tapPromise 函数 (绑定hooks方法)tapable 库,创建自定义插件的库
    tapPromise函数(绑定hooks方法)tapable库,创建自定义插件的库刚看到了一个插件的use函数//引入组件use(plugin:IPluginClass,options?:IPluginOption){if(this._checkPlugin(plugin)&&this.canvas){this._saveCustomAttr(plugin);constpluginRu......
  • vue3+vueCli实现自动引入 unplugin-auto-import插件版本问题
    vue3项目引入unplugin-auto-import后报错通过引入的方式constAutoImport=require('unplugin-auto-import/webpack');报错如下: 通过直接官网vue-cli方式直接引入 报错如下经测试,是unplugin-auto-import插件版本问题查看unplugin-auto-import插件版本:npmlistu......
  • SemanticKernel:添加插件
    SemanticKernel介绍SemanticKernel是一个SDK,它将OpenAI、AzureOpenAI和HuggingFace等大型语言模型(LLMs)与C#、Python和Java等传统编程语言集成在一起。SemanticKernel通过允许您定义插件来实现这一点,这些插件可以通过几行代码链接在一起。为什么需要添加插件?大语言模型虽然......
  • 【软件插件】SketchUP插件-最新版坯子插件2024 v3.2.2(支持SketchUp2012-2024版本)安装
    下载链接:https://r0vr8xquwul.feishu.cn/docx/MXC5dUMZroLibaxYgZ3cmkyinDe详细图文教程:https://www.yuque.com/zhefengerhuanzaigua/bld6x5/kc2baq1msy6dehb3软件介绍坯子插件库是为SketchUp(草图大师)用户推出的一款插件管理工具,我们知道在使用sketchup进行模型设计的时候是......
  • golang 可变参数用法, handlers ...HandlerFunc
     handlers...HandlerFunc这是什么写法,与group.handle()第三个参数是[]handlerFunc是什么关系呢?下面是gin中的用法:routergroup.go//GETisashortcutforrouter.Handle("GET",path,handle).func(group*RouterGroup)GET(relativePathstring,handlers...Ha......
  • 【Go-多线程】Golang的channel实现消息的批量处理
    【Go-多线程】Golang的channel实现消息的批量处理。当消息量特别大时,使用kafka之类的messagequeue是首选,但这是更加轻量的方案channelx.go//这个方案需要实现以下几点://1.消息聚合后处理(最大条数为BatchSize),核心://(1)带buffer的channel相当于一个FIFO的队列//(2)多个常驻的gorou......
  • Golang初学:一些第三方包
    goversiongo1.22.1-- Web开发gorillahttps://gowebexamples.com中的示例有用到。 Routing(usinggorilla/mux)goget-ugithub.com/gorilla/mux-Sessions"github.com/gorilla/sessions"-Websockets$gogetgithub.com/gorilla/websocket- gingoget......
  • golang使用OpenCC繁简转换
    https://github.com/longbridgeapp/openccmain.gopackagemainimport( "fmt" "log" "github.com/longbridgeapp/opencc")funcmain(){ s2t,err:=opencc.New("s2t") iferr!=nil{ log.Fatal(err) } in:=`......
  • visual studio 插件开发 - 项目介绍
    1.项目结构创建步骤:1.创建名为xxxx的VSIX项目。可以通过搜索“vsix”在“新建项目”对话框中找到VSIX项目模板。2.项目打开时,添加名为FirstCommand的自定义命令项模板。创建好一个vsix项目后最简单的结构:XXXXPackage.cs称为Package类。VisualStudio调用......
  • visual studio 插件开发 - 概述
    VisualStudio插件开发1.插件的概述1.1VisualStudio扩展执行哪些类型的操作?对VisualStudio中不包含的语言的支持,并提供语法着色、IntelliSense和编译器和调试支持。使用更多模板、代码重构、新对话框或工具窗口扩展核心IDE体验的生产力工具。特定于域的设计器,适......