Bypassing API Restrictions Once Again for Fun and Profit

Arnav Tripathy
4 min readMar 26, 2023

--

Image credits: https://blog.open-xchange.com/ox-bug-bounty-programs-two-years-in/

In my previous blog , I talk about how I wrote a script to bypass API restrictions. Would recommend everyone to go through the previous blog to understand the nuances and challenges of bypassing it before going forward. Recently the application got updated and the X-Api-Token field is no longer hard-coded in the application, rather it changes everyday. This becomes quite problematic because now I had to figure out a way to fetch the token dynamically for the bypass to work. After some reconnaissance, I figured out two ways to fix the API token problem. Let’s look at both ways and update the script.

The Reconnaissance

I opened burp and intercepted the request while opening the dashboard URL. While a lot of URLs was intercepted, one request interested me particularly:

GET /server/status
Host: redacted.com
Content-Length: 3144
Sec-Ch-Ua: “Chromium”;v=”109", “Not_A Brand”;v=”99"
Sec-Ch-Ua-Platform: “macOS”
X-Api-Token: "$api_token"
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.5414.120 Safari/537.36
Content-Type: application/json
Accept: */*
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://redacted.com/
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Connection: close

This URL was interesting to me because I was not yet logged in, yet the API token was showing up. The problem was that it was in the request header and not response. If it were in the response header, I could have created a request using requests module and just read the token from the response. Accordingly , I used two methods to fetch the token from request header

Using Selenium

Selenium is a tool for browser automation. I will write a separate blog about using Selenium as I use a lot of it for my automation work. Here, I used Selenium to open the URL dashboard and capture all the requests flowing through the Selenium browser. The below code helped me achieve it:

from seleniumwire import webdriver  # Import from seleniumwire
import time
from selenium.webdriver.common.by import By
# Create a new instance of the Chrome driver
driver = webdriver.Chrome(executable_path= "/usr/local/bin/chromedriver")

# Go to the dashboard home page
driver.get('https://redacted.com/')
time.sleep(4)
# Access requests via the `requests` attribute
for request in driver.requests:
if request.url == "https://redacted.com/server/status":
if request.response:
x_api_token=request.headers['X-Api-Token']
break

Selenium-wire is a library which assisted in fetching all requests through a browser. Using the above code, I was able to fetch the X-Api-Token value dynamically.

Adding the above code, the updated code became:

from seleniumwire import webdriver  # Import from seleniumwire
import time
from selenium.webdriver.common.by import By
import requests
import json
#Credentials needed to automate session
username= "admin"
password= "admin"
# Create a new instance of the Chrome driver
driver = webdriver.Chrome(executable_path= "/usr/local/bin/chromedriver")

# Go to the dashboard home page
driver.get('https://redacted.com/')
time.sleep(4)
# Access requests via the `requests` attribute
for request in driver.requests:
if request.url == "https://redacted.com/server/status":
if request.response:
x_api_token=request.headers['X-Api-Token']
break

#Function to generate headers for Login
def headers_data(request_data,if_login,token=0):
data_len= str(len(request_data))
headers_request = {
'Content-Length': data_len,
'Content-Type': 'application/json',
'X-API-Token': x_api_token
}
if if_login:
return headers_request
else:
headers_request['X-Cookie']= 'token='+token
return headers_request

#Login to application and store token
login_data= '{"username":' + '"' + username + '"'+',"password":' + '"' + password + '"'+'}'
login_headers = headers_data(login_data,True)
try:
login_tokens = requests.post('https://redacted.com/session', headers=login_headers, data=login_data, verify=False)
token= login_tokens.json().get('token')
print("Login Successful !")
except:
raise Exception("Login Automation failed")

#Post request to stop endpoint.
payload_data= { "Some json data to be posted to stop transaction"}
update_ip_headers = headers_data(payload_data,False,token)
stop_transaction_response= requests.put('https://redacted.com/stop/transaction', headers=update_ip_headers, data=payload_data, verify=False)
print("Transaction stopped via API")

Using JS Files

With some poking around, a JS file was discovered in the dashboard URL page source. The X-Api-Token was present in the JS file. Now the automation can be achieved easily through requests module. Below code helped in fetching the token:

import re
import requests

js_response = requests.get('https://redacted.com/redacted.js', verify=False)
token =re.findall('(?<=key:"Token",value:function\(\){return").*?(?="}},{key:"Headers")', js_response.text) #Regex in the file to fecth the token
x_api_token= token[0]

The updated code would become:


import requests
import json
import re
#Credentials needed to automate session
username= "admin"
password= "admin"
js_response = requests.get('https://redacted.com/redacted.js', verify=False)
token =re.findall('(?<=key:"Token",value:function\(\){return").*?(?="}},{key:"Headers")', js_response.text) #Regex in the file to fecth the token
x_api_token= token[0]

#Function to generate headers for Login
def headers_data(request_data,if_login,token=0):
data_len= str(len(request_data))
headers_request = {
'Content-Length': data_len,
'Content-Type': 'application/json',
'X-API-Token': x_api_token
}
if if_login:
return headers_request
else:
headers_request['X-Cookie']= 'token='+token
return headers_request

#Login to application and store token
login_data= '{"username":' + '"' + username + '"'+',"password":' + '"' + password + '"'+'}'
login_headers = headers_data(login_data,True)
try:
login_tokens = requests.post('https://redacted.com/session', headers=login_headers, data=login_data, verify=False)
token= login_tokens.json().get('token')
print("Login Successful !")
except:
raise Exception("Login Automation failed")

#Post request to stop endpoint.
payload_data= { "Some json data to be posted to stop transaction"}
update_ip_headers = headers_data(payload_data,False,token)
stop_transaction_response= requests.put('https://redacted.com/stop/transaction', headers=update_ip_headers, data=payload_data, verify=False)
print("Transaction stopped via API")

Hope this helps security automation enthusiasts :)

--

--

Arnav Tripathy

Feline powered security engineer . Follow me for a wide variety of topics in the field of cyber security and dev(sec)ops. Kubestronaut FTW!