GO Code Snippets Review
Last updated on

Description
GO Code Snippets Review
Arbitry File Read
func uploadFile(w http.ResponseWriter, r *http.Request) {
file, header, err := r.FormFile("file")
if err != nil {
fmt.Println("Error Retrieving the File")
return
}
defer file.Close()
tempFile, err := ioutil.TempFile("/tmp", header.Filename)
if err != nil {
fmt.Println(err)
return
}
fileBytes, err := ioutil.ReadAll(file)
if err != nil {
fmt.Println(err)
return
}
tempFile.Write(fileBytes)
defer tempFile.Close()
fmt.Fprintf(w, "Successfully Uploaded File\n")
}
in line number 8 is vuln to LFI.
No Delimiter
func buildSignatureforPayment(user string, amount int) (string){
mac := hmac.New(sha256.New, []byte(key))
mac.Write([]byte(user+strconv.Itoa(amount)))
return hex.EncodeToString(mac.Sum(nil))
}
func buildUrl(user string, amount int, sign string) (string) {
ret := base_url+"?user="+user
ret+= "&amount="+strconv.Itoa(amount)
ret+="&sign="+sign
return ret
}
func verifyPayment(user string, amount int, sign string) (bool) {
mac := hmac.New(sha256.New, []byte(key))
mac.Write([]byte(user+strconv.Itoa(amount)))
expected := mac.Sum(nil)
provided, err := hex.DecodeString(sign)
if err != nil {
return false
}
return hmac.Equal(expected, provided)
}
in line 16 no delimiter is present so user1 who wants 999 is same as user who wants 1999.
Delimiter injection
func SignforPayment(user string, amount int)(string, string){
data := user+":"+strconv.Itoa(amount)
mac := hmac.New(sha256.New, []byte(key))
mac.Write([]byte(data))
return data, hex.EncodeToString(mac.Sum(nil))
}
func verifyPayment(data string, sign string) (bool) {
parts := strings.Split(data,":")
user,amount := parts[0], parts[1]
mac := hmac.New(sha256.New, []byte(key))
mac.Write([]byte(user+":"+amount))
expected := mac.Sum(nil)
provided, err := hex.DecodeString(sign)
if err != nil {
return false
}
return hmac.Equal( expected, provided)
}
i can inject delimiter and change the data.
Auth issue
func auth(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
user, pass, _ := r.BasicAuth()
if !check(user, pass) {
w.Header().Set("WWW-Authenticate", "Basic realm=\"MY REALM\"")
http.Error(w, "Unauthorized.", 401)
return
}
fn(w, r)
}
}
func check(u, p string) bool {
password:= os.Getenv("MYPASSWORD")
user:= os.Getenv("MYUSER")
if u == user || p== password {
return true
}
return false
}
func handler(w http.ResponseWriter, r *http.Request) {
[...]
}
func main() {
http.HandleFunc("/",auth(handler))
log.Fatal(http.ListenAndServe(":8080", nil))
}
u == user || p== password
is messed up.
Return Issue
func auth(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
user, pass, _ := r.BasicAuth()
if !check(user, pass) {
w.Header().Set("WWW-Authenticate", "Basic realm=\"MY REALM\"")
http.Error(w, "Unauthorized.", 401)
}
fn(w, r)
}
}
func check(u, p string) bool {
password:= os.Getenv("MYPASSWORD")
user:= os.Getenv("MYUSER")
if u == user && p== password {
return true
}
return false
}
func handler(w http.ResponseWriter, r *http.Request) {
[...]
}
func main() {
http.HandleFunc("/",auth(handler))
log.Fatal(http.ListenAndServe(":8080", nil))
}
No return after line 6 so flow will continue.
Sigining oracle
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"net/url"
)
var key string ="[...]"
var base string = "https://pentesterlab.com/"
func sign(user string) (string){
mac := hmac.New(sha256.New, []byte(key))
mac.Write([]byte(user))
return hex.EncodeToString(mac.Sum(nil))
}
func buildReferralLink(user string) (string) {
return base+"referral?user="+url.QueryEscape(user)+
"&sign="+sign(user)
}
func buildPasswordResetLink(user string) (string) {
return base+"password_reset?user="+url.QueryEscape(user)+
"&sign="+sign(user)
}
on line 25 same sign is being used for referral link.
Zip issues
func Unzip(src string, dest string) ([]string, error) {
var filenames []string
r, err := zip.OpenReader(src)
if err != nil { return filenames, err }
defer r.Close()
for _, f := range r.File {
fpath := filepath.Join(dest, f.Name)
filenames = append(filenames, fpath)
if f.FileInfo().IsDir() {
os.MkdirAll(fpath, os.ModePerm)
continue
}
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil { return filenames, err }
rc, err := f.Open()
if err != nil { return filenames, err }
_, err = io.Copy(outFile, rc)
outFile.Close()
rc.Close()
if err != nil {
return filenames, err
}
}
return filenames, nil
}
fpath := filepath.Join(dest, f.Name)
trusting the name from zip.
Filter bypass
func isSignatureValid(value string, signature string) bool {
...
}
func process(value string) {
...
}
func handler(w http.ResponseWriter, r *http.Request) {
value := r.URL.Query()["value"][0]
signature := r.URL.Query()["signature"][0]
if signature !="" && !isSignatureValid(value, signature) {
http.Error(w, "Invalid Signature.", 403)
return
}
process(value)
}
func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
if we give an empty signature we can bypass the filter.
Timing issue
package main
import (
"fmt"
"strconv"
"strings"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
[...]
func verifyPayment(user string, amount int, sign string) (bool) {
mac := hmac.New(sha256.New, []byte(key))
if (strings.Contains(user ,":")) {
return false
}
mac.Write([]byte(user+":"+strconv.Itoa(amount)))
expected := hex.EncodeToString(mac.Sum(nil))
return expected == sign
}
[...]
==
in go isn’t time constant so it will take more time to process the request if the output is correct.
Directory traversal
package main
import (
"fmt"
"path"
"log"
"net/http"
"io"
"os"
)
func handler(w http.ResponseWriter, r *http.Request) {
filename := path.Clean(r.URL.Query()["filename"][0])
fd, err := os.Open(filename)
defer fd.Close()
if err != nil {
http.Error(w, "File not found.", 404)
return
}
io.Copy(w, fd)
}
func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
path.Clean
will remove path traversal stuff.
func filter(r *http.Request) (err error) {
if (strings.Contains(r.URL.Query().Get("path"), "..")) {
return errors.New("Invalid path")
}
return nil
}
func serveFile(w http.ResponseWriter, r *http.Request) {
path:= r.FormValue("path")
fd, err := os.Open("/srv/files/"+path)
defer fd.Close()
if err != nil {
http.Error(w, "File not found.", 404)
return
}
io.Copy(w, fd)
}
func handler(w http.ResponseWriter, r *http.Request) {
err := filter(r)
if err!=nil {
http.Error(w, "Invalid path.", 404)
return
} else {
serveFile(w,r)
}
}
path
isn’t sanitized so it may contain ../
insecure randomness
import (
"fmt"
"math/rand"
"errors"
"time"
)
/* secret is a randomly generated string used when signing a JWT */
var secret string
/* init creates the random secret. */
func init() {
rand.Seed(time.Now().UnixNano())
/* Create a random string of random length for our secret */
randombytes := randomBytes(rand.Intn(64))
if randombytes == nil {
panic(errors.New("Error creating random secret"))
}
secret = string(randombytes)
}
func randomBytes(n int) []byte {
b := make([]byte, n)
_, err := rand.Read(b)
if err != nil {
return nil
}
return b
}
rand.Seed(time.Now().UnixNano())
is insecure coz the time can be bruteforced.