SSTI METHOD CONFUSION IN GoLang
This is a write up for a CTF.
My-SKy-Blog
Summary
The application is vulnerable to SSTI method confusion, as described here. This vulnerability allows access to methods within the struct passed to the templates. The /src/templates/
endpoint displays the templates being served, while /src/
reveals the functions in use. By exploiting Golang's template handling, we can access the ChangePassword
method, enabling us to change the admin's password, take over the admin account, and ultimately access /flag
, which only the admin can access.
SSTI Exploitation
The SSTI idea came from knowing the contents of /src/templates/index.html
, where the Author.Username
field was accessible, allowing us to manipulate user data and ultimately access the flag.
The usual payloads to test SSTI do not work here; for example, if you try something like {{7*7}}
, the application will break. Instead, we can register with {{ . }}
as the username, which reveals the data being passed to the template. This reflection in the username allows us to examine the available objects and explore details within the {{ (index .Posts 0).Author }}
object in the template context.
{[0xc000404420 0xc000404180 0xc0002005d0 0xc00009b350] b71b8b6b-bf3f-4dc3-b4be-1363b91cf34e 0xc00009b350 [0xc0000c2680] 1 false
Method Confusion
If we look at /src/
, the most interesting function in users.go
is ChangePassword(newPassword string)
. Calling it like {{ .ChangePassword "123" }}
allows us to change a user's password, which is useful. However, it would be even better if we could target the admin's password instead.
func (u *User) ChangeUsername(username string) bool {
u.Username = username
return true
}
The /index
template file includes the data structure .Posts
, which holds information for various users who have made posts, not just the current user. This is what we can use to access other methods inside users.go
, as the data in that structure fills the parameters in the functions we want to hijack.
<div class="posts">
Posts: {{range .Posts}}
<div class="p-4 rounded-lg bg-gray-100 my-10">
<div class="tint">
<div class="container position-relative px-4 px-lg-5">
<div class="row gx-4 gx-lg-5 justify-content-center">
<div class="col-md-10 col-lg-8 col-xl-7">
<div class="post-heading mt-3">
<h1 class="text-3xl font-bold">{{.Title}}</h1>
<span class="meta">
Posted by {{.Author.Username}} at {{.UpdatedAt.Format "02 Jan 06 15:04"}}
</span>
</div>
</div>
</div>
</div>
</div>
<div class="container px-4 px-lg-5">
<div class="row gx-4 gx-lg-5 justify-content-center">
<div class="col-md-10 col-lg-8 col-xl-7">
<pre class="content">{{.Body}}</pre>
</div>
</div>
</div>
</div>
{{end}}
</div>
Accessing Admin’s Information
We need to find a way to access the admin’s information by inspecting the data structure. To do this, we check if any page reveals that information. In this case, I used the original blog post that was available when we first signed in. By checking the first post, which was authored by the admin, we can use the payload {{ (index .Posts 0).Author }}
to access the admin's data. This lets us reveal the information we need.
The object contains all the information we need for the ChangePassword
function. So, how can we access the method we need? We can do this by calling it directly from the author struct. This is possible because the author struct being used here is our User
struct, which contains the ChangePassword
function. The data passed in the post will fill the parameters with the admin's details.
When you invoke this method, it hashes the new password using sha256
, then updates the user's password with the new hash. This method works because the User
struct, which represents the admin, is passed to the template, and we can directly call this function to change the admin's password.
By accessing the author’s information through {{ (index .Posts 0).Author }}
, we can target the admin's user data and invoke the ChangePassword
method with a new password.
Execution Steps
Now with the information we have, we’ll need to:
- Register using the payload:
{{ (index .Posts 0).Author.ChangePassword "123" }}
- Log in and get redirected to the posts page, which contains the admin’s blog post
- At this point, the password for the admin will be changed to “123”.
- Since only the admin has access to
/flag
, we now have access to/flag
and can retrieve the flag.