切片操作导致缓冲区溢出和泄漏用户密码?

Dog*_*Dog 1 security buffer-overrun go

我有一个服务器,它具有返回用户注册日期的功能.任何用户都可以看到任何非隐藏用户(在此示例中,只隐藏了user2).服务器dateRequest从客户端获取a 并使用id它来在用户文件中查找相应用户的日期:

package main

import (
    "bytes"
    "fmt"
)

const datelen = 10

//format: `name 0xfe date age 0xfd password`; each user is seperated by 0xff
var userfile = []byte("admin\xfe2014-01-0140\xfdadminpassword\xffuser1\xfe2014-03-0423\xfduser1password\xffuser2\xfe2014-09-2736\xfduser2password")

func main() {
    c := clientInfo{0, 0, 0}
    fmt.Println(string(getDate(dateRequest{c, user{0, "admin"}})))
    fmt.Println(string(getDate(dateRequest{c, user{0, "admin______________"}})))
    fmt.Println(string(getDate(dateRequest{c, user{1, "user1"}})))
    //fmt.Println(string(getDate(dateRequest{c,user{2,"user2"}}))) // panic
    fmt.Println(string(getDate(dateRequest{c, user{1, "user1_________________________________________________"}})))
}

func getDate(r dateRequest) []byte {
    if r.id == 2 {
        panic("hidden user")
    }
    user := bytes.Split(userfile, []byte{0xff})[r.id]
    publicSection := bytes.Split(user, []byte{0xfd})[0]
    return publicSection[len(r.username)+1 : len(r.username)+1+datelen]
}

type dateRequest struct {
    clientInfo
    user
}
type clientInfo struct {
    reqTime uint64
    ip      uint32
    ver     uint32
}
type user struct {
    id       int
    username string
}
Run Code Online (Sandbox Code Playgroud)

$ go run a.go 
2014-01-01
dminpasswo
2014-03-04
r2password
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它可以正常接收用户的日期,但如果请求在用户名上有额外的字节,则会返回部分用户密码.不仅如此,如果你继续添加更多字节,它将从user2返回数据,这应该是隐藏的.为什么?

当执行main的代码第三行,user并且publicSection在getData是'admin2014-01-0140adminpassword’和'admin2014-01-0140’.但后来它返回"dminpasswo".切片publicSection("admin 2014-01-0140")如何返回"dminpasswo"?它看起来像是一个缓冲区溢出问题,但这不应该发生,因为Go是内存安全的.我甚至尝试publicSection通过打印读取缓冲区publicSection[len(publicSection)],但它像预期的那样恐慌.

我也试过更换所有的[]byte用string,而修复该问题的某些原因.

Jim*_*imB 6

切片表达式检查上部索引的边界与切片容量,而不仅仅是切片的长度.实际上,您可以切片超过切片的长度.但是,您不能在基础数组的边界之外切片以访问未初始化的内存.

http://play.golang.org/p/oIxXLG-YEV

s := make([]int, 5, 10)
copy(s, []int{1, 2, 3, 4, 5})

fmt.Printf("len:%d cap:%d\n", len(s), cap(s))
// > len:5 cap:10

fmt.Printf("raw slice: %+v\n", s)
// > raw slice: [1 2 3 4 5]


fmt.Printf("sliced past length: %+v\n", s[:10])
// > sliced past length: [1 2 3 4 5 0 0 0 0 0]

// panics
_ = s[:11]
// > panic: runtime error: slice bounds out of range
Run Code Online (Sandbox Code Playgroud)

如果你真的想要防止切片超过数组的长度,在go1.3或更高版本中你可以在切片时将容量设置为第三个参数.

// set the capacity to 5
s := s[:5:5]
// now this will panic
_ = s[:6]
Run Code Online (Sandbox Code Playgroud)

  • 如果你在当前的Go上,你可以使用三值切片操作来减少'cap`,这确实会在结束时停止访问:http://play.golang.org/p/rpiszxkFN- (2认同)