checkin_go

开头是一个提交用户名、密码和MD5的登录框,这里的md5属于老生常谈的爆破不提,用户名需要admin登录,否则无法获得admin的cookie来进行一个金钱的修改。正解是在本地删掉用户认证和302跳转来获得一个admin的cookie进行一个32位的整型溢出。

当然由于go本身不支持session,这里的session是源自于gin,于是我查了一下资料,得知在gin框架中有多种session的存储方式,例如cookie-based或者redis-based等,其中基于Redis肯定是较为安全的一种存储,基于cookie虽然减轻了服务器的存储负担,但是显而易见的增加了不安全性,从这里可以大概猜测这里使用的存储方式是基于cookie类型的一个存储。这题在main.go中进行了设置:

	storage := cookie.NewStore(randomChar(16)) // 使用cookie-based存储类型
	r.Use(sessions.Sessions("o", storage)) // 设置o作为存储session的cookie

这里初始化之后直接进入sessions.go中的func Sessions,会基于name和store进行一个初始化。

func Sessions(name string, store Store) gin.HandlerFunc {
	return func(c *gin.Context) {
		s := &session{name, c.Request, store, nil, false, c.Writer}
		c.Set(DefaultKey, s)
		defer context.Clear(c.Request)
		c.Next()
	}
}

初始化后这里的session本身是没有存储意义的(没有存储uname),于是尝试提交uname=admin,删除其他waf,观察调用情况。

首先在gin.go的445行断点,观察这里的c.Keys是nil

	engine.handleHTTPRequest(c)

进入之后会对c进行一个处理,也就是一种set操作,到最后到c.Next()

context.go在go中自带,这里的context.go是gin框架下的文件,进入Next函数,这里对c的index进行了一个自增操作,然后判断c.handlers的长度并一一执行,也就是会一一执行handlers中的所有函数,观察到第三个函数是session的处理,也就是框架刚开始的处理函数。

这里的name和store没有传入,于是获得了r最开始设置的name和store,也就是cookie-based的方式,最后进入set,设置了Keys

func (c *Context) Set(key string, value interface{}) {
	c.mu.Lock()
	if c.Keys == nil {
		c.Keys = make(map[string]interface{})
	}

	c.Keys[key] = value
	c.mu.Unlock()
}

到这里设置好了store后,到了也就是表单提交数据之后的处理,进入s.Set()后会对uname=admin进行处理,也就是session.Values['uname']='admin'

func (s *session) Set(key interface{}, val interface{}) {
	s.Session().Values[key] = val
	s.written = true
}

最后在s.Save()中调用http.SetCookie,返回set-cookie,实现对cookie的设置。

题目本身不难,主要还是对框架的分析花了比较久的时间,毕竟比较少做到Goweb,总体来说还是学到了不少东西的。

Last updated