服务热线
15527777548/18696195380
发布时间:2022-05-09
简要描述:
这道题当时出了非预期,session两次base64就能getflag,但是这道题本身的考点是Golang SSTI,正好复盘学习一下。打开题目能看到经典计算器。点开flag在这里发现只有短短一行提示f...
这道题当时出了非预期,session两次base64就能getflag,但是这道题本身的考点是Golang SSTI,正好复盘学习一下。
打开题目能看到经典计算器。
点开flag在这里发现只有短短一行提示
flag is in your session
这里按照非预期接还是能够拿到flag。
但是我们要看一下具体按照预期方法怎么能拿到flag。
https://www.jianshu.com/p/e0ffb76ba7e9
这里我们通过{{.}}查看一下作用域。
整理一下,就能够看到整个calc的源码。
package main import ( _ "embed" "fmt" "os" "reflect" "strings" "text/template" "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" "github.com/maja42/goval") var tpl string //go:embed main.govar source string type Eval struct { E string `json:"e" form:"e" binding:"required"`} func (e Eval) Result() (string, error) { eval := goval.NewEvaluator() result, err := eval.Evaluate(e.E, nil, nil) if err != nil { return "", err } t := reflect.ValueOf(result).Type().Kind() if t == reflect.Int { return fmt.Sprintf("%d", result.(int)), nil } else if t == reflect.String { return result.(string), nil } else { return "", fmt.Errorf("not valid type") }} func (e Eval) String() string { res, err := e.Result() if err != nil { fmt.Println(err) res = "invalid" } return fmt.Sprintf("%s = %s", e.E, res)} func render(c *gin.Context) { session := sessions.Default(c) var his string if session.Get("history") == nil { his = "" } else { his = session.Get("history").(string) } fmt.Println(strings.ReplaceAll(tpl, "{{result}}", his)) t, err := template.New("index").Parse(strings.ReplaceAll(tpl, "{{result}}", his)) if err != nil { fmt.Println(err) c.String(500, "internal error") return } if err := t.Execute(c.Writer, map[string]string{ "s0uR3e": source, }); err != nil { fmt.Println(err) }}func main() { port := os.Getenv("PORT") if port == "" { port = "8080" } r := gin.Default() store := cookie.NewStore([]byte("woW_you-g0t_sourcE_co6e")) r.Use(sessions.Sessions("session", store)) r.GET("/", func(c *gin.Context) { render(c) }) r.GET("/flag", func(c *gin.Context) { session := sessions.Default(c) session.Set("FLAG", os.Getenv("FLAG")) session.Save() c.String(200, "flag is in your session") }) r.POST("/", func(c *gin.Context) { session := sessions.Default(c) var his string if session.Get("history") == nil { his = "" } else { his = session.Get("history").(string) } eval := Eval{} if err := c.ShouldBind(&eval); err == nil { his = his + eval.String() + " " } session.Set("history", his) session.Save() render(c) }) r.Run(fmt.Sprintf(":%s", port))}
这里有很多东西,我们可以进行一下删减整理(对我们getflag没什么用处),提取出我们想要的东西。
package main import ( _ "embed" "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" "os") func main() { port := os.Getenv("PORT") if port == "" { port = "8888" //这个port可以自己指定奥 } r := gin.Default() store := cookie.NewStore([]byte("woW_you-g0t_sourcE_co6e")) r.Use(sessions.Sessions("session", store)) r.GET("/flag", func(c *gin.Context) { session := sessions.Default(c) c.String(200, session.Get("FLAG").(string)) }) r.Run(":8888")}
注意下这里:
store := cookie.NewStore([]byte("woW_you-g0t_sourcE_co6e"))
这里是创建基于cookie的存储引擎,woW_you-g0t_sourcE_co6e参数是用于加密的密钥。
然后是这里:
c.String(200, session.Get("FLAG").(string))
session.Get("FLAG")这里是gin框架中的读取session,而他指定了FLAG这个key(session是键值对格式数据,因此需要通过key查询数据),所以我们可以尝试本地起这个gin框架,修改一下代码,并且把题目中的session传入cookie,看看他最终会输出什么。
exp:
package mainimport ( _ "embed" "fmt" "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" "os")func main() { port := os.Getenv("PORT") if port == "" { port = "8888" //这个port可以自己指定奥 } r := gin.Default() store := cookie.NewStore([]byte("woW_you-g0t_sourcE_co6e")) r.Use(sessions.Sessions("session", store)) r.GET("/flag", func(c *gin.Context) { session := sessions.Default(c) b := session.Get("FLAG") fmt.Println(b) }) r.Run(":8888")}
结果如下:
成功解密出flag。
但是这里有个问题。
c.String(200, session.Get("FLAG").(string))
笔者在写exp的时候,对String()产生了疑惑(我的Golang水平停留在Hello world水平),如果exp这样写他会直接输出在界面上。
r.GET("/flag", func(c *gin.Context) { session := sessions.Default(c) c.String(200, session.Get("FLAG").(string))})
出于好奇,我跟进了一下。
这里给了注释,我蹩脚的翻译一下
String()会把给定的字符串写入响应体中
跟进Render。
Render() 写入响应头并调用render。并且渲染出来
这里就明了了,shit我根本不用改代码啊!!!直接让他渲染出来就好了!!
so,我们验证一下:
package main import ( _ "embed" "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" "os") func main() { port := os.Getenv("PORT") if port == "" { port = "8888" //这个port可以自己指定奥 } r := gin.Default() store := cookie.NewStore([]byte("woW_you-g0t_sourcE_co6e")) r.Use(sessions.Sessions("session", store)) r.GET("/flag", func(c *gin.Context) { session := sessions.Default(c) c.String(200, session.Get("FLAG").(string)) c.String(200, "This is H3h3QAQ") }) r.Run(":8888")}
结果如下:
又是学到了新知识的一天!
如果您有任何问题,请跟我们联系!
联系我们