HaloUI:给poc添加图形化

命令行有时候不够直观
如何快速给自己的poc添加图形化
GUI的库太麻烦了,索性基于gin写了一个ui库

成品展示

运行程序自动打开浏览器

运行过程截图:

运行完成会有提示(同时会有弹窗)

分为两部分输出,fmt.Print输出命令行调试信息,ui库可以向web页面输出结果

How to use

1.导入库

就两文件,自己复制一下就行了

之后导入gin

go get -u github.com/gin-gonic/gin

或者直接运行

go get "github.com/Aixve-c/HaloUI"

2.设置常规信息

HaloUI.SetTitle("xx漏洞poc")                 //设置标题
HaloUI.SetReadme("ip为目标,端口默认为80")      //设置说明文档

3.设置输入

AddInput入参依次为

  • 参数名

  • ui中显示的名字

  • 是否必填

HaloUI.AddInput("url", "URL", true)            //必填的输入框
HaloUI.AddInput("port", "端口", false)         //非必填的输入框

4.设置入口函数

HaloUI.SetFunc(mypoc)

5.启动

HaloUI.Run()

入口函数

注意入口函数必须接收一个[]string参数
例如:func mypoc(HaloPars []string){xxxxxxxxx}

获取用户输入

可以通过传入的参数获取用户输入的字符串(所有输入都是字符串)
例如:fmt.Println("url:"+HaloPars[0])

追加一行输出

1.1秒同步一次结果

HaloUI.AddOutput("开搞")

完成

入参为弹窗提示词
注意:在报错或者执行成功后都应该加上此函数

HaloUI.Finsh("完成")

代码

HaloUI.go和Config.go
ps:记得单独放进同一个文件夹HaloUI

HaloUI.go

package HaloUI

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"io/ioutil"
	"log"
	"os/exec"
	"runtime"
	"time"
)

var Pars []string
var WebInput []string
var RunFunc func(input []string)

func Run() {
	HaloUI_output = ""                 //还原输出
	gin.DefaultWriter = ioutil.Discard //关闭gin的日志
	ginServer := gin.Default()

	ginServer.POST("/Run", gin_post)
	ginServer.GET("/Run", gin_get)

	//打印主界面UI
	ginServer.GET("/", func(c *gin.Context) {
		c.Header("content-type", "text/html; charset=utf-8")
		c.String(200, HaloUI_index())
	})

	port, err := GetFreePort() //获取空闲端口
	if err != nil {            //错误处理
		log.Print(err)
	}
	WebAddress := fmt.Sprintf("127.0.0.1:%d", port) //定义监听端口
	fmt.Println("UI界面:  ", "http://"+WebAddress)
	go OpenBrowser("http://" + WebAddress)
	err = ginServer.Run(WebAddress) //启动HaloUI的http服务(基于gin)
	if err != nil {                 //错误处理
		log.Print(err)
	}
}

// 一键运行的入口
func gin_post(c *gin.Context) {
	WebInput = []string{}
	fmt.Println("[+]Run...")
	NoFinsh()
	for i := 0; i < len(Pars); i++ {
		WebInput = append(WebInput, c.PostForm(Pars[i]))
		fmt.Println("[+]入参", Pars[i], " : ", WebInput[i])
	}
	go RunFunc(WebInput)
	c.Header("content-type", "text/html; charset=utf-8")
	c.String(200, HaloUI_run())
}

// 更新页面的入口
func gin_get(c *gin.Context) {
	c.Header("content-type", "text/html; charset=utf-8")
	c.String(200, HaloUI_run())
}

// 打开指定url
func OpenBrowser(url string) error {
	var cmd string
	var args []string
	time.Sleep(1 * time.Second) // 睡眠4秒
	switch runtime.GOOS {
	case "windows":
		cmd = "cmd"
		args = []string{"/c", "start"}
	case "darwin":
		cmd = "open"
	default: // "linux", "freebsd", "openbsd", "netbsd"
		cmd = "xdg-open"
	}
	args = append(args, url)
	return exec.Command(cmd, args...).Start()
}

Config.go

package HaloUI

import (
	"html"
	"net"
	"strings"
)

var HaloUI_readme = "│--│┌─┐│·┌─┐│--│·<br>├─┤├─┤│·│·││--││<br>│--││·│└─└─┘└─┘│<br>适用于白帽子的极简的UI库,基于gin开发<br>可以快速为golang编写的poc添加ui界面<br>[+] Halo UI&nbsp;&nbsp;&nbsp;&nbsp;[+] by Aixve<br><br>//设置标题  <br>HaloUI.SetTitle(\"XX漏洞POC\") <br><br>//设置帮助文档<br>HaloUI.SetReadme(\"url为目标,cmd为执行的命令\")<br><br>//增加输入框<br>//入参为:传递的参数(不得重复)、UI中显示的名字、是否必填<br>HaloUI.AddInput(\"url\",\"URL\",true)<br>//可以借此获取用户输入<br><br>//设置要运行的函数<br>HaloUI.SetFunc(mypoc)<br><br>//启动HaloUI<br>HaloUI.Run()<br><br>//结束单次运行并弹窗提示<br>HaloUI.Finsh(\"完成\")"
var HaloUI_output string
var isFinsh bool
var FinshMsg string
var ui_html = `<!DOCTYPE html>  
<body>  
<h2>xxx漏洞poc</h2><!-- HaloUI标题 -->
	<!-- HaloUI输入开始 -->
      <form action="/Run" method="post"> 
		
		<!-- HaloUI用户输入 -->
        <input type="submit" value="一键运行"> 
		
    </form> <!-- HaloUI输入结束 -->
<br>
<div class="dark-code-box">
	此处展示结果<!-- readme -->  
</div>
</body>


<head>  
<meta charset="UTF-8">  
<meta name="viewport" content="width=device-width, initial-scale=1.0">  
<title>HaloUI</title> <!-- WebTitle -->  
<style>  
    .dark-code-box {  
        background-color: #333;                                     /* 深色背景 */  
        color: #fff;                                                            /* 白色文字 */  
        padding: 10px;                                                     /* 内边距 */  
        border-radius: 5px;                                              /* 边框圆角 */  
        overflow-x: auto;                                                 /* 如果代码太长,允许水平滚动 */  
        font-family: monospace;                                      /* 使用等宽字体 */ 
	font-size: 25px;                                                    /* 设置字体大小 */   
    }  
    body {  
        font-family: 'Courier New', Courier, monospace; /* 设置等宽字体 */  
        font-size: 25px;                                                     /* 设置字体大小 */   
        margin: 0 auto;                                                     /* 上下边距为0,左右自动调整以居中(但这里需要配合容器宽度) */  
        padding: 20px;                                                      /* 内边距 */  
        max-width: 800px;                                                /* 设置最大宽度,以便内容在较宽的屏幕上也能居中 */  
        margin-left: auto;  
        margin-right: auto;                                               /* 显式设置左右自动边距以确保居中 */ 
    }  
    form input[type="text"] {  
        height: 25px;                                                       /* 设置输入框的高度 */  
        padding: 0 10px;                                                 /* 设置输入框的内边距 */  
        margin-bottom: 15px;                                         /* 与下一个输入框之间保持间距 */  
    }  
</style>  
</head>    
</html>

`

func AddInput(Params string, ui_name string, not_null bool) { //添加用户输入
	Pars = append(Pars, Params)
	if not_null == true { //设置不能为空的输入框
		ui_html = strings.Replace(ui_html, "<!-- HaloUI用户输入 -->", ui_name+": <input type=\"text\" name=\""+Params+"\" required>  <br>"+"<!-- HaloUI用户输入 -->", -1)
	} else { //设置可以为空的输入框
		ui_html = strings.Replace(ui_html, "<!-- HaloUI用户输入 -->", ui_name+": <input type=\"text\" name=\""+Params+"\" >  <br>"+"<!-- HaloUI用户输入 -->", -1)
	}
}

func GetFreePort() (int, error) { //获取空闲的端口

	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
	if err != nil {
		return 0, err
	}

	l, err := net.ListenTCP("tcp", addr)
	if err != nil {
		return 0, err
	}
	defer l.Close()
	return l.Addr().(*net.TCPAddr).Port, nil
}

func AddOutput(Output string) {
	HaloUI_output += html.EscapeString(Output) + "<br>"
}

func HaloUI_index() string {
	//填充readme
	return strings.Replace(ui_html, "此处展示结果<!-- readme --> ", HaloUI_readme+"<!-- readme --> ", -1)
}

func HaloUI_run() string {
	//填充结果
	a := strings.Replace(ui_html, "此处展示结果<!-- readme --> ", HaloUI_output+"<!-- readme --> ", -1)
	userin := ""
	for i := 0; i < len(Pars); i++ {
		userin += Pars[i] + " = " + WebInput[i] + "<br>"
	}
	//显示输入、持续更新页面
	if !isFinsh {
		a = strings.Replace(a, "HaloUI输入开始 -->", " 注释", 1)
		a = strings.Replace(a, "<!-- HaloUI用户输入 -->", " 注释", 1)
		a = strings.Replace(a, "<!-- HaloUI输入结束 -->", "HaloUI输入结束 -->"+userin, 1)
		a += "<script>window.onload = function() {setTimeout(function() {window.location.href = 'Run';}, 1100);};</script><!-- HaloUI每1.1秒更新1次 -->"
	} else {
		a += "<script>alert('" + html.EscapeString(FinshMsg) + "')</script>"
		a = strings.Replace(a, "<!-- HaloUI输入结束 -->", userin, 1)
	}
	return a
}

func SetFunc(MyFunc func(input []string)) {
	RunFunc = MyFunc
}

func SetTitle(title string) {
	//设置标题
	ui_html = strings.Replace(ui_html, "<title>HaloUI</title> <!-- WebTitle -->  ", "<title>"+title+"</title> <!-- WebTitle -->  ", -1)
	ui_html = strings.Replace(ui_html, "<h2>xxx漏洞poc</h2><!-- HaloUI标题 -->", "<h2>"+title+"</h2><!-- HaloUI标题 -->", -1)
}

func SetReadme(Readme string) {
	//设置readme,在HaloUIHtml中会具体赋值,因为两个get和post的代码框是复用的
	Readme = strings.Replace(Readme, "\n", "<br>", -1)
	HaloUI_readme = html.EscapeString(Readme)
	HaloUI_readme = strings.Replace(Readme, "&lt;br&gt;", "<br>", -1)
}

func Finsh(msg string) {
	isFinsh = true
	FinshMsg = msg
	HaloUI_output = strings.Replace(HaloUI_output, "[*]运行中...<br>", "[+]Yes运行完成!<br>", 1)
}

func NoFinsh() {
	isFinsh = false
	HaloUI_output = "[*]运行中...<br>"
}

posted @ 2024-08-30 11:44  aixve  阅读(67)  评论(0)    收藏  举报