Slicing operation causing buffer overrun and leaking user passwords?

400 Views Asked by At

I have a server that has a function to return the registration date of a user. Any user can see any non-hidden user (in this example only user2 is hidden). The server gets a dateRequest from a client and uses id from that to find the corresponding user's date in the user file:

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

As you can see, it works correctly to receive the dates of the users, but if the request has extra bytes on the username, part of the user's password gets returned instead. Not only that, but if you keep adding more bytes, it returns data from user2, which is supposed to be hidden. Why?

When executing main's third line of code, user and publicSection in getData are "admin�2014-01-0140�adminpassword" and "admin�2014-01-0140". But then it returns "dminpasswo". How is slicing publicSection ("admin�2014-01-0140") returning "dminpasswo"? It looks like a buffer overrun problem, but that shouldn't happen because Go is memory-safe. I even tried reading past the buffer of publicSection by printing publicSection[len(publicSection)], but it panics as expected.

I also tried replacing all the []byte with string, and that fixes the issue for some reason.

1

There are 1 best solutions below

6
On

Slice expressions check the bounds of the upper index against the slice capacity, not just the length of the slice. In effect, you can slice past the length of the slice. You cannot however slice outside the bounds of the underlying array to access uninitialized memory.

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

If you really want to prevent slicing past the length of an array, in go1.3 or later you can set the capacity as the third argument when slicing.

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