[VNCFT2021]gocalc0复现
[VNCFT2021]gocalc0復(fù)現(xiàn)
這道題當(dāng)時出了非預(yù)期,session兩次base64就能getflag,但是這道題本身的考點是Golang SSTI
正好復(fù)盤學(xué)習(xí)一下
打開題目能看到經(jīng)典計算器
點開flag在這里發(fā)現(xiàn)只有短短一行提示
flag is in your session這里按照非預(yù)期接還是能夠拿到flag
但是我們要看一下具體按照預(yù)期方法怎么能拿到flag
https://www.jianshu.com/p/e0ffb76ba7e9
【查看相關(guān)資料】
1、網(wǎng)絡(luò)安全學(xué)習(xí)路線
2、電子書籍(白帽子)
3、安全大廠內(nèi)部視頻
4、100份src文檔
5、常見安全面試題
6、ctf大賽經(jīng)典題目解析
7、全套工具包
8、應(yīng)急響應(yīng)筆記
這里我們通過{{.}}查看一下作用域
整理一下,就能夠看到整個calc的源碼
package mainimport (_ "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.go var source stringtype 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 stringif 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 stringif session.Get("history") == nil {his = ""} else {his = session.Get("history").(string)}eval := Eval{}if err := c.ShouldBind(&eval); err == nil {his = his + eval.String() + "<br/>"}session.Set("history", his)session.Save()render(c)})r.Run(fmt.Sprintf(":%s", port)) }這里有很多東西,我們可以進行一下刪減整理(對我們getflag沒什么用處),提取出我們想要的東西
package mainimport (_ "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"))這里是創(chuàng)建基于cookie的存儲引擎,woW_you-g0t_sourcE_co6e參數(shù)是用于加密的密鑰
然后是這里
c.String(200, session.Get("FLAG").(string))session.Get(“FLAG”)這里是gin框架中的讀取session,而他指定了FLAG這個key(session是鍵值對格式數(shù)據(jù),因此需要通過key查詢數(shù)據(jù)),所以我們可以嘗試本地起這個gin框架,修改一下代碼,并且把題目中的session傳入cookie,看看他最終會輸出什么
exp:
package main import (_ "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") }結(jié)果如下
成功解密出flag
但是這里有個問題
c.String(200, session.Get("FLAG").(string))筆者在寫exp的時候,對String()產(chǎn)生了疑惑(我的Golang水平停留在Hello world水平),如果exp這樣寫他會直接輸出在界面上
r.GET("/flag", func(c *gin.Context) {session := sessions.Default(c)c.String(200, session.Get("FLAG").(string)) })出于好奇,我跟進了一下
這里給了注釋,我蹩腳的翻譯一下
String()會把給定的字符串寫入響應(yīng)體中跟進Render
Render() 寫入響應(yīng)頭并調(diào)用render。并且渲染出來這里就明了了,shit我根本不用改代碼啊!!!直接讓他渲染出來就好了!!
so,我們驗證一下
package mainimport (_ "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, "\nThis is H3h3QAQ")})r.Run(":8888") }結(jié)果如下
總結(jié)
以上是生活随笔為你收集整理的[VNCFT2021]gocalc0复现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【网络安全】如何搭建MySQL恶意服务器
- 下一篇: 2020-10-23(SMC加密技术)