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

时间:2014-09-02 17:18:51

标签: security go buffer-overrun

我有一台服务器,它具有返回用户注册日期的功能。任何用户都可以看到任何非隐藏用户(在此示例中,只隐藏了user2)。服务器从客户端获取dateRequest并使用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
}

$ go run a.go 
2014-01-01
dminpasswo
2014-03-04
r2password

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

执行main的第三行代码时,user中的publicSectiongetData为"admin�2014-01-0140� ADMINPASSWORD"和"admin�2014-01-0140"。但随后又返回了#d;; dminpasswo"。如何切换publicSection("admin�2014-01-0140")返回" dminpasswo"?它看起来像是一个缓冲区溢出问题,但这不应该发生,因为Go是内存安全的。我甚至尝试通过打印publicSection读取publicSection[len(publicSection)]的缓冲区,但它会按预期发生恐慌。

我还尝试将所有[]byte替换为string,并且由于某种原因解决了这个问题。

1 个答案:

答案 0 :(得分: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

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

// set the capacity to 5
s := s[:5:5]
// now this will panic
_ = s[:6]