golang模板渲染可控的条件下可以做什么?
昨天ByteCTF逼我翻了一天的npm手册,一天速成nodejs,,, 今天美团决赛逼我一天速成golang,真的麻了
渲染语法内容学习参考
今天主要看了golang模板渲染的内容(使用的是text/template这个标准库)
对模板渲染的语法学习可以直接看下面这些文章
https://blog.csdn.net/guyan0319/article/details/89083721
https://www.cnblogs.com/7php/p/14981633.html
https://juejin.cn/post/6844903762901860360
基础语法的话看上面的几篇文章就大概足够了, 这里在下面主要讲一下在模板渲染可控的情况下我们可以做什么, 怎么做, 以及有哪些条件限制
学习后的感受总结
先说一下模板渲染能调用哪些函数
只能调用两种函数:
- 内置模板函数
- 自定义的模板函数(定义格式和普通函数时候不一样的)
调用内置模板函数
代码语言:javascript复制var builtins = FuncMap{
"and": and,
"call": call,
"html": HTMLEscaper,
"index": index,
"js": JSEscaper,
"len": length,
"not": not,
"or": or,
"print": fmt.Sprint,
"printf": fmt.Sprintf,
"println": fmt.Sprintln,
"urlquery": URLQueryEscaper,
}
这些函数都是用于编码或者逻辑判断以及输出, 对我们来说并没有太多的用处, 如果说比较有用的的话那就是call
了,但是实际上用不用这个函数对我们的函数调用并没有什么影响
调用自定义的模板函数
需要注意一点, 在模板的空间内, 我们能够拿到的变量只有执行Execute
的时候传入的变量, 其他非传入Execute
函数的变量我们都是获取不到的
至于可以调用的函数, 除了内置的之外也只能调用固定格式定义的模板函数, 其他一般的函数我们也是获取和调用不了的
自定义函数使用下面格式注册。
代码语言:javascript复制func (t TemplateType) Funcname(argv argvType) returnType{
...code...
return...;
}
TemplateType就是我们执行Execute
的时候传入的模板对象的类型
而函数中的t
在执行代码的时候就是我们在Execute
传入的模板类型的变量
之后说一下可以在里面有哪些操作空间:
在函数模板中.
就代表传入的变量
在模板中产生一个新变量的方法有两个:
- {{$temp := true}}
- {{$temp = true}}
第一种方式是用于定义变量, 第二种则是用于变量赋值
个人认为最重要的一点:: 不能产生新的复杂的数据结构和修改单独一个变量的属性
在里面是不能通过{.var = true}
的方式对传入的变量进行修改的, 此外下面这种间接赋值的方式也是不行的
{{temp := .}}{{temp.var = true}} 这个使用方法是错误的
总的来说就是, 你可以在里面直接生成一个temp变量, 但是这个变量只能为true,false,数字这些简单的数据, 如果想要这个temp是一些比较复杂的数据的话, 那么就要从传入的变量中通过.的方式取出子属性然后进行赋值
那么除了使用
.
的方式直接取出数据之外还有什么方式拿到复杂结构的数据? 可以通过调用自定义的模板函数
来对模板变量
(姑且这么称呼吧,也就是执行Execute
函数的时候传入的模板类型变量)进行修改, 或者通过自定义的函数来生成新的复杂数据 需要注意, 上面说明中自定义
很关键, 就是说默认情况下我们是不能自己构造一个新的复杂结构的数据的, 只能从传入的模板变量
通过.
取出然后赋值 另外默认情况下我们使用.
只能直接复制一份数据, 如果想要单独修改里面的某一个属性的话是需要自定义函数
才能做到
想要说的基本说完了, 就是只能使用模板变量
中的数据和模板变量
所属类型定义的相关模板函数, 可以说先是是非常大了,如果模板变量
里面只有一些int
,bool
,string
类型的数据, 并且还没有任何的自定义模板函数
的话即使给我们一个能够任意模板渲染的点, 我们也做不到RCE和文件读取, 想要达到上面的这些目标的话就需要看自定义模板函数
里面有没有漏洞触发点了
那么如果自定义模板函数
有漏洞触发点可以利用, 我们应该怎样传入参数触发调用对应的函数呢?
{{FuncName}}
这个方式会调用FuncName
这个自定义模板函数, 就相当于无参调用执行了FuncName()
{{.FuncName 参数值1 参数值2 ...}} 注意每个参数间都要用空格隔开
这个方式则是调用模板变量
对应的自定义模板函数FuncName,并且后面的作为参数执行FuncName(参数值1,参数值2,…) 执行的返回值会被输出
{{.var|FuncName}}
这个方式则是会先取出模板变量
中的var
属性, 然后将其作为参数执行自定义模板函数FuncName, 相当于执行了FuncName(模板变量.var)
{{call .Method .var1 .var2 ...}}
上面的方式通过call
这个内置模板函数进行函数调用, call
后面解的第一个必须是一个函数方法, 可以有下面两种情况:
- Method函数是
模板变量
对应的自定义模板函数
- Method是
模板变量
中的一个属性, 并且这个属性就是一个函数方法