RenderQuest
Você encontrou uma aplicação web que permite que você insira templates remotamente para serem renderizados. Sua tarefa é se aproveitar das vulnerabilidades desse sistema para obter a flag escondida. Boa sorte!
Como descrito, esse desafio consiste de uma aplicação web que carrega templates .tpl
de fontes externas e internas. Como o código-fonte é disponibilizado, é possível analisar o comportamento da aplicação e explorar uma vulnerabilidade de Server-Side Template Injection.
Reconhecimento
Na página inicial, é possível ver alguns dados que são utilizados para carregar os templates e um formulário para informar uma URL para o template.

Vale notar também que a URL possui um parâmetro "page", o qual indica a fonte da qual o template será carregado. Analisando o código-fonte da página, é possível encontrar a função responsável por processar e carregar o conteúdo da URL informada, além de outras funções interessantes.
import (
...
"html/template"
"os/exec"
)
...
func (p RequestData) FetchServerInfo(command string) string {
out, err := exec.Command("sh", "-c", command).Output()
if err != nil {
return ""
}
return string(out)
}
...
// executado caso o recurso informado seja local
func readFile(filepath string, basePath string) (string, error) {
if !isSubdirectory(basePath, filepath) {
return "", fmt.Errorf("Invalid filepath")
}
data, err := os.ReadFile(filepath)
...
return string(data), nil
}
// executado caso o recurso informado seja remoto
func readRemoteFile(url string) (string, error) {
response, err := http.Get(url)
content, err := io.ReadAll(response.Body)
...
return string(content), nil
}
// responsável por processar o template
func getTpl(w http.ResponseWriter, r *http.Request) {
var page string = r.URL.Query().Get("page")
var remote string = r.URL.Query().Get("use_remote")
reqData := &RequestData{}
reqData.ServerInfo.Hostname = reqData.FetchServerInfo("hostname")
reqData.ServerInfo.OS = reqData.FetchServerInfo("cat /etc/os-release | grep PRETTY_NAME | cut -d '\"' -f 2")
reqData.ServerInfo.KernelVersion = reqData.FetchServerInfo("uname -r")
reqData.ServerInfo.Memory = reqData.FetchServerInfo("free -h | awk '/^Mem/{print $2}'")
...
err = tmpl.Execute(w, reqData)
}
...
Essas funções são essenciais para identificarmos como esse sistema pode ser explorado. Na ordem em que os processos ocorrem, temos a função getTpl
, que é responsável por processar o template e enviar os objetos que podem ser acessados pelo template. Para isso, ele chama as funções readFile
e readRemoteFile
, de acordo com o valor do parâmetro "use_remote" passado na URL.
A princípio, pode-se imaginar que seja possível incluir o arquivo da flag através do parâmetro page
e resolver o desafio. Entretanto, isso não é possível principalmente pois o arquivo da flag acompanha um sufixo imprevisível, além de a função readFile
impedir a leitura de arquivos que não estejam no diretório templates
ou app
.
Porém, se observarmos, após realizar a leitura do arquivo de template, a aplicação passa o objeto reqData
para a instrução Execute
. Isso permite que os parâmetros do objeto reqData
sejam acessados pelo template. Entretanto, pesquisando sobre a biblioteca html/template, utilizada para renderização dos templates da aplicação, encontrei este artigo que descreve uma vulnerabilidade de SSTI que pode ser escalada para RCE. Quando um objeto é transmitido para ser processado pelo template, todas as suas propriedades se tornam acessíveis no template. Isso se torna um problema principalmente por conta da função FetchServerInfo
, que permite a execução de código do sistema. Quando o objeto reqData
é passado para processar o template informado, torna-se possível acessar e executar a função FetchServerInfo
passando um comando como parâmetro. Dessa forma, tem-se uma RCE (execução de código remoto).
Exploração
Podemos explorar essa vulnerabilidade criando um arquivo de template em nossa máquina local e expondo-o através de um servidor HTTP e o ngrok. Podemos iniciar um servidor HTTP em Python através do comando python -m http.server
e expor nossa máquina através do ngrok com o seguinte comando: ngrok http 8000
. Dessa forma, podemos criar um arquivo de template com um payload malicioso. Como exemplo, um arquivo template.tpl contendo {{.}}
vai imprimir as propriedades do objeto acessível pelo template. Alterando esse payload para {{.FetchServerInfo "id"}}
e enviando novamente para ser carregado pela aplicação, a resposta será o resultado do comando id
executado pela máquina alvo. Dessa forma, temos um RCE que permite navegar pelo sistema e encontrar a flag, e basta apenas manipular o argumento do comando FetchServerInfo`.
Prova de Conceito
{{.FetchServerInfo "cat $(find / -name flag*.txt -type f)"}}
Atualizado
Isto foi útil?