# misc/electric-bovine @ DamCTF Oct. 2020
* challenge author: **Lyell Read*** solves: **51*** points: **468**
## Description
> Do androids dream of electric bovine? Find out on my new Discord server!
## Analysis
We are sent to a discord server named "Permissions pasture". The onlything we see is the people in the server and a "general" channel, wehave no writing permissions on this channel though.
We also get DM'd by the server's bot: "Secure Permissions Bot".
### Secure Permissions Bot
![](https://i.imgur.com/IHk0lD9.png)
The only useful command at this point is the **about** command:
![](https://i.imgur.com/IWS7xj0.png)
It directs us to the bots source code! And a rick-roll if we try tograb the bot token... ?‍♂️
### Source code
Available [here](https://gist.github.com/dunklastarn/d3e3ca30bb4f476221bf42faebb19a12).
### Getting higher permissions
We are made aware that there is a **botspam** channel, that wecurrently cannot see in the server. This tells us that there areother roles that we can potentially acquire.
By taking a look at the bot profile in the server, we find two newroles:
![](https://i.imgur.com/ZzqyUBT.png)
The next step is to use the **send_msg** command to send a messageto the server through the bot. Since there is no checks on input, wecan chain commands and our intent is to use **role_add** as the botto give us higher permissions.
After a few unsuccessful tries, this attempt had a new result:
![](https://i.imgur.com/2hcWYTl.png)
Note here that what the **role_add** command wants is the user IDand the role ID (which can be found in the source code, or by rightclicking the role in the bot profile and grabbing its ID).
This attempt didn't work perfectly though, because we need to passan authentication check:
```py
def authenticate(author, authorizer, role):
    # Derive a variant to make auth requests resolve uniquely    variant = author.discriminator    variant = int(variant * 4)
    # Add author's name to their credential set    author_credentials = [author.nick]    # Add perms    author_credentials.extend([str(int(y.id) + variant) for y in author.roles])    author_credentials.extend([y.name.lower() for y in author.roles])
    # Add authorizer's name    authorizer_credentials = [authorizer.name]    # Add perms    authorizer_credentials.extend([str(int(y.id) + variant) for y in authorizer.roles])    authorizer_credentials.extend([y.name.lower() for y in authorizer.roles])
    # Add role name    role_information = [role.name, str(int(role.id) + variant)]
    permset = list(        (set(author_credentials) & set(authorizer_credentials)) & set(role_information)    )
    if len(permset) >= 1:        return True    else:        return False```
To simplify that:
First of all, the user's discriminator (the last 4 digits in thediscord username) is used to create a variant.
Three separate sets are created for user, bot, and role.
The user set contains the user's nickname on the server, the user's role ID's + variant, and the user's role names.
The bot set contains the bot's name, the bot's role ID's +variant, and the bot's role names.
The role set contains the role's name and the role's ID + variant.
Finally, to pass the authentication check, the intersection of the 3 sets has to have at least 1 element.
The intended solution here was to calculate the role ID + variantnumber and set your nickname to that number. However, we were toolazy for that, and instead just set our server nickname to "private", since the "private" entry appears in the other 2 sets already.
That being done, we retry our previous command:
![](https://i.imgur.com/YzOuLOz.png)
### Getting the flag
Now that we have higher permissions, we can use the **cowsay**command.
Let's take a look at the important part of the cowsay command:
```py for char in arg:    if char in " `1234567890-=~!@#$%^&*()_+[]\\{}|;':\",./?":        await message.author.send("Invalid character sent. Quitting.")        return
cow = "```\n" + os.popen("cowsay " + arg).read() + "\n```"```
We notice immediately that what the command does is pass our input to thecowsay command on the machine with the flag.
Many characters are disallowed, especially notable are `;` and `.` (dot).This means that we can't really try to pass `cat flag.txt`, or even just`flag.txt`. Armed with this knowledge, and noticing that the redirectioncharacters (`<` & `>`) are not disallowed, we deduce that the flag is in thefile `flag`, and we can direct the `flag` file's content to the cowsay command:
![](https://i.imgur.com/ejGz5Xb.png)