Introduction
GitHub is the world’s largest platform for hosting open-source projects. Many companies, including ours, use it to host code. A colleague of mine pointed out one overlooked feature: GitHub’s user attachments file hosting. This could be an adversary method for distributing malware and malicious scripts under the guise of a trusted domain—who would block GitHub?
Here is a statement from GitHub Docs: “We do not allow anyone to use our platform in direct support of unlawful attacks that cause technical harms, such as using GitHub as a means to deliver malicious executables or as attack infrastructure.” However, GitHub allows dual-use content. This means malware and/or exploits can be uploaded and distributed IF they have educational value and provide a net benefit to the security community. In this POC, we are not focusing on a repository per se, which links to a user account or organization. We want to focus on something more anonymous (and innocuous at first sight).
What Are GitHub User Attachments?
Users can upload files as attachments when they create an issue, a pull request, or a comment on GitHub. These files are hosted at a URL like:
https://github.com/user-attachments/files/{unique_id}/{filename}
Unlike repositories, which have access controls and visibility settings, user-attachment URLs are:
- Publicly accessible (no authentication required)
- Hosted on http://github.com (a trusted domain)
- Not easily monitored (outside of repository security tools)
While this feature is helpful for collaboration, it introduces a serious security risk when misused. Let’s demonstrate our POC below to showcase a potential flaw.
Proof of Concept: User Attachments
For the sake of demonstration, I will traverse to the velociraptor public repository and click “Issues.”
I will proceed to click “New issue”.
I will use the EOF (End Of File) command to write the string “SBT Says Hi” in a file named test.txt. Remember, we are not trying to cause significant damage.
I then “drag and drop” the file into the description box. Notice this: It is hosted automatically with a public URI (uniform resource identifier). There was no need to click “Create.” It is not shown here, but I canceled the issue, and the link was still accessible.
I moved to my Firefox browser and pasted the link. I immediately downloaded the resource; I was not given a prompt, warning, or anything. Imagine if this was a malicious script or something more harmful…
Proof of Concept: A Deeper Dive
The findings below will be interesting. I will switch to my Linux machine and start by creating a malicious .html file. This page presents itself as a cat, but secretly, in the background, it links the victims’ browser to a BeEF hook to establish a TCP connection once the page is loaded. The goal is to have GitHub host this in that “anonymous” repository and then share the link!
`
Oh, will you look here? GitHub has thankfully blocked that file type. The developers have taken precautions. Let’s say it went through, and someone fell for the trap. How would the attacker be identified? Indeed, this act itself is not anonymous; it can’t be. A company like GitHub would not allow that. I decided to boot up ZAP to proxy my browser traffic. Let’s dive deeper into it.
ZAP Proxy: The Shocking Findings
When uploading a file to the description page, much metadata is captured in the web traffic. The following IDs are related: 90, 93, 95. After much testing, I noticed these tend to subsequently follow with the respective HTTP codes: 201 (Created), 204 (No Content), and 200 (OK). In this first image, I will look into the latter—specifically, the response from GitHub (the server).
As you can see, the server has noted the unique ID, name, size, content_type, and href. This is pretty good for auditing. Let’s move over to the client’s request.
The HTTP PUT method creates a resource on a server. Pay attention to the URL:
https://github.com/upload/repository-files/18789627
It looks like our file was also saved in this directory (which was not present in the public URI). In the previous screenshot, our file is saved in the user-attachments/files/18789627
.
Fun fact: Computer scientist Phillip Hallam-Baker misspelled the referrer's original proposal to incorporate the field into the HTTP specification.
The referrer attribute (highlighted in yellow) identifies the request's origin. If you remember, we were on Velociraptor’s issues page, adding files to a blank template. This has been documented. I proceeded to block the cookies but showcased some key attributes (which dispel the anonymity in our first POC) like device_id, saved_user_sessions, user session, and dotcom_user. The latter refers to the GitHub username that uploaded the resources despite not creating an issue.
A Deeper, Deeper Dive?
After testing, I noticed that .txt files are also accepted alongside images (e.g., jpg, jpeg).
Images (and other accepted files) seem to be saved in a different location for this status code—Amazon. I am unsure why, but I thought it was interesting to note. Do you remember when we mentioned the 204 HTTP code? I believe it would be a great time to explain it! The HTTP 204 (No Content) is a successful response status code that indicates that a request has succeeded, but the client doesn’t need to navigate away from its current page per MDN Web Docs. This explains why we weren’t redirected after uploading content in the description of the issue page.
With images, unlike the .txt files we tested earlier, I did see the name changed into a complex string.
Discord CDN: Infinite Storage & Hacks
The Tragedy of the Commons is an economic concept describing a situation where individuals, acting in their interest, overexploit a shared resource. Discord, the online chat application that allows users to chat with each other in real time, utilizes a content distribution network (CDN) to serve static content to users.
Discord’s robust CDN infrastructure allows it to serve files quickly and reliably. CDNs are designed to serve content quickly by caching it in multiple locations worldwide. Like GitHub, the Discord domain is considered a trusted network, and hackers can piggyback on its good reputation by using its CDN.
Similar to GitHub but with more options (e.g., more file extensions allowed), attackers can upload malicious files on a Discord channel (public or private) and share its public link with others, including non-Discord users. Here is the format of the link (with an example):
https://cdn.discordapp.com/attachments/ChannelID/AttachmentID/filename.ext
This POC will cover how Discord attachments are formed and distributed. Discord’s Deceptive Practices Policy Explainer states, “Using Discord’s content-delivery network (CDN) to host malicious files, malware, or phishing resources for later distribution” is prohibited.
Proof of Concept: Hosting Files
In our SBT Discord, I placed a text file named “malware.txt” in a private channel and a PDF file called “Time_Mastery_by_Malik_Girondin.pdf” in a public channel.
If you notice, there is a link to the file hosted by Discord’s CDN where you have the “Download” button. We will dive deeper into this via ZAP.
ZAP Proxy: Interesting Findings
After clicking the button, your browsing client downloads the file from the CDN using a GET method. Discord recently (post-2023) changed to temporary file links to block malware delivery. Most of these changes will be explained below:
First, all links to files uploaded to Discord servers will expire after 24 hours. This prevents the use of Discord’s CDN for permanent file hosting. Second, three new parameters that add expiration timestamps and unique signatures will be appended to the CDN URLs. Let’s look at the example above:
https://cdn.discordapp.com/attachments/1156728791096381530/1333855309487280329/Time_Mastery_by_Malik_Girondin.pdf?
As explained earlier, 1156728791096381530
is the Channel ID, and 1333855309487280329
is the Attachment ID. This link alone will provide a 404 error should you request it without the three new parameters.
The three new parameters are ex, is, and hm.
ex: Expiration timestamp for the current link
hm: Given signature (e.g., hash)
is: Valid version presently
Here are the current parameters for our POC link:
ex=67b6188f&
hm=5407943956c410d83228abe46bd02e91a9054bd693ec7210e030c11d2768a853&
is=67b4c70f&
After grabbing the CDN URLs with the three new parameters appended, you can download the resource (which can be any file you can upload with Discord). The Discord upload limit for free users is ~10MB per file, ~50MB per file for Nitro Basic, and ~500MB for Full Nitro.
(The URL is not in the address bar because I opened the file within the browser. I will show an example with the “malware.txt” below.)
I also tried downloading this on my M3 Mac using the public link, and it worked (seeing that it was within the 24 hours). To download the file after the link expires, a new CDN URL must be created by downloading the resource from Discord and sharing that newly created URL.
Even though Discord is a free chat app, its hosting features are being abused to host malware, scripts, and programs to harm others. According to Trellix, they have found around 10,000 malware samples that utilize Discord’s CDN to deliver payloads.
Conclusion
GitHub remains a powerful platform for developers, and its user-attachments feature is not as significant a security risk as previously suspected. Our testing confirms that GitHub actively mitigates abuse by restricting certain file types (scripts) and logging upload activity (via the response information). Like GitHub, Discord can be a breeding ground for nefarious activities and malware distribution. Banning accounts won’t stop malicious actors from creating new ones and resuming their activities, especially given the vast file types that can be uploaded. However, awareness is still key. We must be cautious when downloading files from unknown sources. Thanks for reading!
About SBT
Security Blue Team is a leading online defensive cybersecurity training provider with over 100,000 students worldwide, and training security teams across governments, military units, law enforcement agencies, managed security providers, and many more industries.
This content is for educational purposes only and we do not endorse illegal activities. Only explore vulnerabilities with proper authorization. The author and Security Blue Team disclaims any liability for misuse.