Skip to content

Library

I've been developing this little book library app in Go, but I think something is wrong with the way it's handling book requests. Some of the book titles seem to be giving unexpected results... Maybe you can figure it out?

Source code provided:

Library
├── Dockerfile
├── flag.txt
├── main.go
├── static
│   └── styles.css
└── templates
    ├── book.html
    └── home.html

main.go

package main

import (
    "html/template"
    "log"
    "net/http"
    "os"
    )

    type ReadBook struct{}

    func (rb ReadBook) ReadBook(filePath string) string {
    content, err := os.ReadFile(filePath)
    if err != nil {
        return "Error reading file: " + err.Error()
    }
    return string(content)
}

var books = map[string]string{
    "1984":            "1984 is a dystopian social science fiction novel by George Orwell.",
    "brave_new_world": "Brave New World is a dystopian novel by Aldous Huxley.",
    "f451":            "Fahrenheit 451 is a dystopian novel by Ray Bradbury.",
}

type Book struct {
    Title string
    Param string
    }

    func homeHandler(w http.ResponseWriter, r *http.Request) {
    var bookList []Book
    for key := range books {
        bookList = append(bookList, Book{
            Title: key,
            Param: key,
        })
    }
    tmpl := template.Must(template.ParseFiles("templates/home.html"))
    tmpl.Execute(w, bookList)
}

func bookHandler(w http.ResponseWriter, r *http.Request) {
    userBook := r.URL.Query().Get("book")
    bookContent, validBook := books[userBook]

    if validBook {
        tmpl := template.Must(template.ParseFiles("templates/book.html"))
        tmpl.Execute(w, bookContent)
        return
    }

    if userBook != "" {
        tmpl, err := template.New("book").Parse(userBook)
        if err != nil {
            http.Error(w, "Template parsing error: "+err.Error(), http.StatusInternalServerError)
            return
        }
        tmpl.Execute(w, ReadBook{})
        return
    }

    http.Error(w, "No book specified", http.StatusBadRequest)
}

func main() {
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080" // Default to 8080 if no PORT environment variable is set
    }

    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
    http.HandleFunc("/", homeHandler)
    http.HandleFunc("/books", bookHandler)

    log.Println("Starting server on port " + port)
    log.Fatal(http.ListenAndServe(":"+port, nil))
}

The description and the line template.New("book").Parse(userBook) hints towards SSTI

We need to call the function ReadBook with the argument "flag.txt"

Go templates allow: {{ .Method "arg" }} if the method is exported (starts uppercase) and the method has signature Method(string) string

The endpoint /books?book={{ .ReadBook "flag.txt" }} works!