1. 程式人生 > 其它 >How to Hack APIs in 2021(API漏洞利用)

How to Hack APIs in 2021(API漏洞利用)

How to Hack APIs in 2021

August 10, 2021

Detectify Crowdsource is not your average bug bounty platform. It’s an invite-only community of the best ethical hackers who are passionate about securing modern technologies and end users. Crowdsource hackers Hakluke and Farah Hawa have joined forces on this guest blog on how hackers and defenders can (safely) hack APIs to help make the Internet safer.

Baaackkk iiin myyy dayyyyy APIs were not nearly as common as they are now. This is due to the explosion in the popularity of Single Page Applications (SPAs). 10 years ago, web applications tended to follow a pattern where most of the application was generated on the server-side before being presented to the user. Any data that was needed would be gathered directly from a database by the same server that generates the UI. It might look something like this:

Chart: Web application model 10 years ago

Many modern web applications tend to follow a different model often referred to as an SPA (Single Page Application). In this model there is typically an API backend, a JavaScript UI, and database. The API simply serves as an interface between the webapp and the database. All requests to the API are made directly from the web browser.

Detectify product update: thefuzzing engine will cover public-facing APIs

圖表。10年前的網路應用程式模型

許多現代的網路應用往往遵循一種不同的模式,通常被稱為SPA(單頁應用)。在這種模式下,通常有一個API後端、一個JavaScript UI和資料庫。API只是作為網路應用程式和資料庫之間的一個介面。所有對API的請求都是直接從網路瀏覽器發出的。

Chart: Modern web applications tend to follow a different model often referred to as an SPA

This is often a better solution because it is easier to scale and allows more specialised developers to work on the project, i.e. frontend developers can work on the frontend while backend developers work on the API. These apps also tend to feel snappier because page loads are not required for every request.

Instead, different components of the same page will update magically, giving it a similar feel to a native application. This model has also become more popular because ten billion ⁽ᶜᶦᵗᵃᵗᶦᵒⁿ ⁿᵉᵉᵈᵉᵈ⁾ different frontend JavaScript frameworks (React, Vue and Angular, etc.) have come into existence. Suspicious minded folk might conclude that the ridiculous amount of JavaScript frameworks available today is a co-ordinated attempt to slow the progress of webapp development, instigated by the Illuminati. That’s probably not true though .

All this to say – there are APIs everywhere now, so we should know how to hack and secure them. If you’re still reading – your fingers are probably hovering over ctrl+w. Your brain is thinking “this article title promised to teach me to hack, not what a SPA is. I am an intellectual individual and the author’s attempts at humour are futile, life is short and I am wasting my time reading this stupi….” HOLD IT! We’re getting there. I promise. Cool your jets. Goooooosfraba.

圖。現代網路應用傾向於遵循一種不同的模式,通常被稱為SPA

這通常是一個更好的解決方案,因為它更容易擴充套件,並允許更多的專業開發人員在專案上工作,也就是說,前端開發人員可以從事前端的工作,而後端開發人員則從事API的工作。這些應用程式也往往感覺更敏捷,因為每一個請求都不需要頁面載入。

相反,同一個頁面的不同元件會神奇地更新,給人一種類似於本地應用程式的感覺。這種模式也變得更加流行,因為百億⁽ᶜᶦᵗᵃᵗᶦ ⁿᵉᵈ⁾ 不同的前端JavaScript框架(React、Vue和Angular等)已經出現了。心存疑慮的人可能會得出這樣的結論:今天的JavaScript框架數量之多令人髮指,這是由光照派煽動的一個協調的嘗試,旨在減緩Web應用開發的進展。但這可能不是真的。

所有這些都是為了說明--現在到處都是API,所以我們應該知道如何入侵和保護它們。如果你還在閱讀--你的手指可能正徘徊在ctrl+w上。你的大腦正在思考 "這篇文章的標題承諾教我黑客,而不是SPA是什麼。我是一個知識分子,作者的幽默嘗試是徒勞的,生命是短暫的,我正在浪費我的時間來閱讀這個stuppi...." 等一下!"。我們就要到了。我保證。冷卻你的噴氣。Goooooosfraba。

Setting up for testing APIs

Postman is a handy application that makes API security testing a breeze. You can download Postman from its official website.In essence, Postman is just another HTTP client which can be used to easily modify and send requests to APIs.

If you’ve got a collection of API requests in a file, you can start by importing them into Postman by clicking on the Import button on the top-left corner of the app:

為測試API進行設定

Postman是一個方便的應用程式,使API安全測試變得輕而易舉。你可以從其官方網站下載Postman。從本質上講,Postman只是另一個HTTP客戶端,可以用來輕鬆地修改和傳送對API的請求。

如果你有一個檔案中的API請求集合,你可以通過點選應用程式左上角的匯入按鈕,開始將它們匯入Postman。

After importing the collection, you will see the API calls loaded in Postman like so:

匯入集合後,你會看到在Postman中載入的API呼叫,像這樣。

By clicking on an individual API call, the full API request will be seen on the right. Moreover, different parts of the request will be broken down into sections like Params, Authorization, Headers, Request Body, etc. This will allow you to easily play around with each part.

通過點選單個的API呼叫,完整的API請求將在右邊被看到。此外,請求的不同部分將被分成若干部分,如引數、授權、頭資訊、請求正文等。這將允許你輕鬆地玩弄每個部分。

You can modify the request headers and body in the same manner as you would in BurpSuite. To analyze the responses to your test cases, you can simply hit the Send button on the top right.

你可以以與BurpSuite相同的方式修改請求頭和正文。要分析對你的測試案例的響應,你可以簡單地點選右上方的傳送按鈕。

The response view in Postman looks like this and the response is also bifurcated into different sections like Body, Cookies, Headers, etc. so you can analyze each part carefully.

Postman中的響應檢視是這樣的,響應也被分成不同的部分,如Body、Cookies、Headers等,所以你可以仔細分析每個部分。

Since Postman is meant for APIs specifically, you have a lot of in-built options to test for functions that are mostly present in APIs. For example, by clicking on this drop-down arrow next to the request method, you can see tons of different request verbs to test with:

由於Postman是專門為API設計的,你有很多內建的選項來測試API中大多存在的功能。例如,通過點選請求方法旁邊的這個下拉箭頭,你可以看到大量不同的請求動詞來測試。

For GET requests, you can add/remove as well as edit parameters via the Params tab. When you check/uncheck parameters, you can see them appear accordingly in the URI field.

對於GET請求,你可以通過Params標籤新增/刪除以及編輯引數。當你檢查/取消檢查引數時,你可以看到它們相應地出現在URI欄位中。

When it comes to adding Authorization values, Postman gives you a number of options to choose from and you can choose one depending on how the target API handles authorization.

當涉及到新增授權值時,Postman給了你一些選項,你可以根據目標API處理授權的方式來選擇一個。

You can also add Authorization values to the entire collection or sub-collection by following these steps:

  1. Click the three dots on the side of the collection/sub-collection name and choose the Edit option
  2. 你也可以通過以下步驟為整個集合或子集合新增授權值。

    點選集合/子集合名稱邊上的三個點,選擇編輯選項
  3. Go to the Authorization tab, select the type of auth and add its value.

  4. Lastly, go to an individual API request and select the Inherit auth from parent option ##最後,進入一個單獨的API請求,選擇從父級繼承授權選項

This will save you the trouble of setting Authorization values for every single request.

Another handy feature of Postman is that it allows users to proxy API requests with BurpSuite. In order to set that up, you need to follow these steps:

這將節省你為每一個請求設定授權值的麻煩。

Postman的另一個方便的功能是,它允許使用者用BurpSuite代理API請求。為了設定這一點,你需要遵循以下步驟。

  1. Click on the Settings option from the drop-down menu on the top-right corner
  2. Go to the Proxy tab and do this:
    • Switch Off Use the system proxy
    • Switch On Add a custom proxy configuration
    • Set the Proxy Server IP address & port to match your Burp Suite proxy settings. The default values are 127.0.0.1 and 8080.
      Your final settings should look like this:

  3. To proxy HTTPS requests without any errors, you can switch off SSL certificate validation under the General tab.

Detectify product update: thefuzzing engine will cover public-facing APIs

Types of API Vulnerabilities

APIs come in many shapes and sizes, the methods of attacking an API will vary greatly depending on these shapes, and sizes. It would be impossible to cover every attack type in a single blog, but we’re going to go through a bunch of them!

API Exposure

Much like web applications, APIs can have different levels of visibility. Some may be accessible to the internet while others are only available internally. One of the more rudimentary API hacks is simply gaining access to an API which should be inaccessible to you. This may be achieved through a variety of methods, including:

  • Forced browsing: If you are lucky, an API that is intended for internal use may be accidentally exposed to the internet, either through a misconfiguration or just because it was assumed that nobody would be able to find it. API locations may be discovered through many means including analysing JavaScript files, analysing exposed source code, observing host names (e.g. api.internal.example.com) and Google dorking.
  • Pivoting: Discovering an exploit like SSRF on an external host may allow you to pivot into an internal API.

Mitigation

There are many best practices that can help mitigate against unintentionally exposing APIs including the implementation of strict deployment practices, enforced principle of least privilege through IAM and network segmentation.

Misconfigured Caching

For APIs that require authentication, the data being returned is often dynamic and is scoped to each API key. For example, accessing /api/v1/userdetails as Bob should return Bob’s details, while accessing the same endpoint as Jane should return Jane’s details.

A common misconfiguration occurs when an API does not use the standard Authorization header, instead using a custom header such as X-API-Key. Caching servers may not recognise this as an authenticated request, and may cache it.

If this is the case, and there are no Cache-Control or Pragma headers, simply accessing /api/v1/userdetails may reveal the information of another user.

Mitigation

The fix for this is to implement Cache-Control or Pragma headers and utilise the standard Authorization header.

Exposed tokens

We shouldn’t discount the most rudimentary authentication hack. Discovering an API key through any means may provide you with access to the API. To make matters worse, APIs that are meant for internal use often have no need to implement complex authentication flows and thus may implement a static token as their authentication. Secret tokens may be discovered in code repositories, client-side JavaScript, intercepting traffic, etc.

Mitigation

Implementing code scanning into your devops pipeline will often catch API keys before they are deployed somewhere they shouldn’t be. Some code repository providers (including GitHub) also have the ability to detect API keys before they are pushed.

JWT Weaknesses

If your API token is three base64 blobs separated by two dots (.), it’s probably a JSON Web Token (JWT). Like many things, these tokens are secure in theory, but there are many ways to mess up the implementation in a way that introduces security issues. Before we delve into JWT attacks we’ll go through a very quick JWT primer.

Here’s an example JWT token, colorized for your aesthetic appreciation:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

There are three base64 encoded strings separated by dots, the first section (colored in red) is the header. The second (colored in purple) is the payload, and the third (colored in blue) is the signature.

If we decode the first section, also known as the header, we will see the following:

API漏洞的型別

API有很多形狀和大小,攻擊API的方法也因這些形狀和大小而大不相同。要在一篇部落格中涵蓋所有的攻擊型別是不可能的,但我們將對其中的一些型別進行分析。
API暴露

與網路應用程式一樣,API也有不同程度的可見性。有些可能是可以進入網際網路的,而有些則是隻能在內部使用。一個更初級的API黑客是簡單地獲得對你來說應該是不可訪問的API的訪問權。這可以通過各種方法實現,包括。

強制瀏覽。如果你很幸運,一個供內部使用的API可能會意外地暴露在網際網路上,要麼是通過錯誤的配置,要麼只是因為假定沒有人能夠找到它。API的位置可以通過很多方式發現,包括分析JavaScript檔案、分析暴露的原始碼、觀察主機名(如api.internal.example.com)和Google dorking。
樞軸。在外部主機上發現像SSRF這樣的漏洞,可以讓你轉向內部的API。

緩解措施

有許多最佳實踐可以幫助緩解無意中暴露的API,包括實施嚴格的部署實踐,通過IAM和網路分段強制執行最小許可權原則。
錯誤配置的快取

對於需要認證的API,返回的資料往往是動態的,並且是針對每個API金鑰的範圍。例如,以Bob的身份訪問/api/v1/userdetails應該返回Bob的詳細資訊,而以Jane的身份訪問同一終端應該返回Jane的詳細資訊。

一個常見的錯誤配置發生在API不使用標準的授權頭,而使用一個自定義的頭,如X-API-Key。快取伺服器可能無法識別這是一個經過驗證的請求,並可能將其快取。

如果是這種情況,並且沒有Cache-Control或Pragma頭,那麼簡單地訪問/api/v1/userdetails可能會顯示出另一個使用者的資訊。
補救措施

解決這個問題的方法是實現Cache-Control或Pragma頭並利用標準的Authorization頭。
暴露的令牌

我們不應該忽視最基本的認證黑客。通過任何方式發現一個API金鑰可能會讓你獲得對API的訪問。更糟糕的是,供內部使用的API往往不需要實施複雜的認證流程,因此可能會實施靜態令牌作為其認證。祕密令牌可能在程式碼庫、客戶端JavaScript、攔截流量等方面被發現。
緩解措施

在你的開發管道中實施程式碼掃描,通常會在API金鑰被部署到不應該部署的地方之前捕獲它們。一些程式碼庫供應商(包括GitHub)也有能力在推送API金鑰之前檢測它們。
JWT的弱點

如果你的API令牌是由兩個點(...)分開的三個base64 blobs,它可能是一個JSON網路令牌(JWT)。像許多東西一樣,這些令牌在理論上是安全的,但有許多方法可以擾亂實現,從而引入安全問題。在我們深入研究JWT攻擊之前,我們先來看看JWT的簡單介紹。

這裡有一個JWT令牌的例子,為了你的審美欣賞,它被著色了。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

有三個base64編碼的字串被點分開,第一部分(紅色)是標題。第二段(紫色)是有效載荷,第三段(藍色)是簽名。

如果我們對第一部分,也就是標題進行解碼,我們將看到以下內容。

{
"alg": "HS256",
"typ": "JWT"
}

This outlines the algorithm that we’re using (HS256) and the type of token (JWT). If you’ve not worked with JWTs before you might be thinking “why do we need an algorithm?”. We will get there soon!

If we decode the second section, also known as the payload, we will see the following:

{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}

This section could contain anything, but at minimum it needs to contain some kind of user identifier and a timeout (iat).

The third section (also known as the signature) signs the first two sections with a secret key. In this case it is signed with the HS256 algorithm, which can be determined by looking at the “alg” value in the header. The idea is that the secret key should only be known to the owner of the application. When the application receives a JWT token, it can verify that the token is legitimate by decrypting the signature and comparing it to the data in the header and payload. If the data matches up then the data is verified, otherwise it is invalid.

So why would anyone use JWT? It is because it negates the need for server-side session management. Traditionally when a user logs in, an application would assign a secret token to the user and store that same token in a database. Whenever the user makes a request with their token, the application needs to check if the token is in the database. If it is, the user is allowed to continue, otherwise they are not. When we use JWTs, we introduce a method of trusting data that is sent from the client instead of solely trusting information that is stored in the database. If the application receives a JWT token that can be verified using the secret key, then the application has no reason to distrust it.

As we touched on earlier, in theory JWT tokens are totally secure. The problem is that they are often implemented in a way that is insecure. Here are some examples:

  • The None algorithm: Some implementations of JWT will allow you to specify “None” as the algorithm. If the algorithm is “None”, the application will not check the validity with the signature, so you can simply update the payload to whatever you want. The most obvious exploitation of this would be to update the user id to another user to take control of their account.
  • Brute forcing: It is possible to brute force the secret key of JWT tokens. The feasibility of this attack will depend on the strength of the key. You can attempt to crack JWT tokens using this tool. A full write-up on the method can be found on Auth0’s blog.
  • Simply changing the payload: In some rare cases, the server may simply skip the token verification entirely and trust the data in the payload. While I have not seen this personally, I have read about it occuring in the wild!
  • Switching RS to HS: There’s a defect with some older JWT libraries where you can trick an application that is expecting tokens signed using asymmetric cryptography into accepting a symmetrically signed token. The symmetrically signed token that ends up being used is actually a public key which is often somehow obtainable, or reused from their HTTPS key. There is a great writeup of this method here.
  • The iat timeout is not honoured, so JWT tokens remain valid forever.

Mitigation

The best mitigation for JWT weaknesses is to utilise a widely-used, reputable JWT library for all JWT operations.

Authorization Issues / IDOR

Authorization is the process of checking whether an authenticated user has access to a specific user. A common authorization-related vulnerability is known as an Insecure Direct Object Reference (IDOR). For example, in an API for an invoicing application, we may have an endpoint that is used to get the details of an invoice:

/api/v1/invoices/?id=1234

The id parameter is the identifier for the invoice that should be returned. If this endpoint is secured, I should only be able to get the details of invoices that belong to me. For example if I created an invoice with an ID of 1234 then it should return the details. If I try to access an invoice that I did not create by browsing to /api/v1/invoices/?id=1233, it should return an error.

If I am able to change the identifier to view the invoice details of other users, this is a vulnerability known as an IDOR.

To counter IDOR issues, many APIs today are utilising UUIDs as object identifiers. A UUID looks like this:

f1af4910-e82f-11eb-beb2-0242ac130002

It’s important to note that utilising UUIDs as identifiers is not a valid method of mitigating IDOR issues. In fact, the UUID RFC specifically calls this out:

Do not assume that UUIDs are hard to guess; they should not be used as security capabilities (identifiers whose mere possession grants access), for example. A predictable random number source will exacerbate the situation.

While it is good practice to utilise UUIDs as object IDs instead of integers, they should never be used as the sole protection against IDOR attacks.

Mitigation

Authorization issues are typically difficult to detect in an automated fashion. The structure of the codebase should be set up in a way that it is difficult to make authorization errors on specific endpoints. To achieve this, authorization measures should be implemented as far up the stack as possible. Potentially at a class level, or using middleware.

Undocumented Endpoints

It is common to encounter situations where the API you are attacking has no documentation (or at least none that is accessible to you). It is also fairly common that an API with documentation will have endpoints beyond what is documented. Sometimes the very existence of these endpoints may be a security issue – for example, the endpoint may be designed for administrative purposes, and allow you to perform administrative tasks as an underprivileged user. Other times, these endpoints may present vulnerabilities simply because they have not been tested as thoroughly as the ones that are easy to discover.

Here are a few methods that we can utilise to uncover undocumented endpoints:

  • Use an application that interfaces with the API, and capture traffic. Perhaps the most commonly used tool for this today is Burp Suite.
    • Set up Burp Suite to proxy the application traffic
    • Use all the application features
    • Check the endpoints that were used by viewing the Target tab.
  • Observe errors. Many APIs will give errors that are verbose enough to enumerate undocumented endpoints and parameters. For example, sending a blank POST request to /api/v1/randomstring may result in an error that says something along the lines of Invalid route, valid routes are [/users,/invoices,/customers].
  • Analyse client-side JavaScript. If you know of an application that interacts with the API, you can analyse the JavaScript within that application to gather a list of API endpoints that may be accessible.
  • Use Kiterunner, a tool by Assetnote that is designed for content discovery on APIs
  • Brute force endpoints, there are some excellent API wordlists on Assetnote’s website

Mitigation

Having a strong, structured method and process for documenting API functionality can save a lot of headaches down the road. Swagger is an excellent standard for exactly this. Additionally, it’s best to plan out the API functionality with documentation before coding it instead of the other way around.

Different Versions

When an organisation releases an API, it may interface with many different applications. If the API is updated at any point, it may introduce breaking changes for one or more of those applications. For that reason, multiple API versions are often implemented as a means of supporting older API schemas while also upgrading the API over time for new users.

It is worth testing all versions of the API. Older versions may still have security issues that have since been fixed in the new version, and newer / bleeding edge / beta versions may have introduced new security issues.

A common schema for api versioning is:

/api/v1/
/api/v2/
/api/beta/

It’s always worth checking for non-production route names such as:

qa
devenv
devenv1
devenv2
preprod
pre-prod
test
testing
staging
stage
dev
development
deploy
slave
master
review
prod
uat
prep
Version2

This wordlist was taken from an example set in DNSCewl.

Mitigation

API versions can be supported with defined lifecycles. For example, when you release version 2 of the API, you may notify your users that version 1 will reach End of Life (EoL) and be deprecated at a specific date in the future. Pre-production/beta versions should only be publicly accessible if they have been thoroughly tested for security issues.

Rate Limiting

Most of the time, APIs do not have any protection on the number of times a user can request them. This is termed as “lack of rate limiting” and it occurs when an attacker can call the API thousands of times to cause some unintended behaviour. The server will attempt to fulfill each of these requests and this can potentially:

  • DOS the server by overloading it with requests
  • Allow the attacker to quickly exfiltrate sensitive user information such as: user IDs, usernames, emails etc.
  • Bypass authentication by brute-forcing a login API
  • Flood the victim’s inbox by brute-forcing a functionality which sends an email/SMS to the victim

Let’s look at an attack scenario where there is no rate limiting on an API endpoint that checks credentials:

GET /api/v1/user/1234/login/?password=mypassword

Normally you would not see a password sent in a GET request like this, but for the purposes of this demonstration, let’s say that you do. To brute force the password in the endpoint above, an attacker can use BurpSuite’s Intruder tool. This tool allows us to customize different kinds of brute-force attacks but for this example, we will be feeding it a simple list of passwords.

In the span of a few seconds, Intruder will make hundreds of API requests, attempting a different password on each request.

GET /api/v1/user/1234/login/?password=notmypassword1
GET /api/v1/user/1234/login/?password=notmypassword2
GET /api/v1/user/1234/login/?password=notmypassword3
.
.
.
GET /api/v1/user/1234/login/?password=correctpassword!

Since there is no rate limit, the server will happily serve the responses to all these requests and the attacker can continue to brute-force different passwords as quickly as possible until the correct one is found.

To protect against rate limiting bugs, the application should implement a limit on how often a user can request an API within a certain timeframe. The exact limit that is set will depend on the use case for that API or endpoint.

Mitigation

Rate-limiting can be implemented in many different ways. Per account, per IP address, per endpoint, for the entire API, etc. Some reverse proxies can also be used to implement application-wide throttling without additional development. The exact implementation that you use will depend on the requirements of your specific application.

Race Conditions

A race condition is when two or more requests are sent at the same millisecond to an API. When an API does not have a mechanism to handle this scenario, it can lead to the API processing the requests in an unintended manner.

A potential attack scenario for a race condition could arise while redeeming discounts or promo codes on a vulnerable e-commerce application.

POST /api/v1/discount
Target: www.ecommerce.com
Connection: close

{
"code","first10",
"amount":"10"
}

BurpSuite has an extension called Turbo Intruder which allows users to test for race conditions using the in-built race.py script.

After configuring the attack in Turbo Intruder, an attacker can send multiple concurrent requests to the API for redeeming this promo code.

POST /api/v1/discount		200 OK
POST /api/v1/discount		200 OK
POST /api/v1/discount		200 OK
POST /api/v1/discount		404 Not Found
POST /api/v1/discount		404 Not Found

If the API does not invalidate the promo code immediately after receiving the first concurrent request, the discount amount could be doubled, tripled, etc.

This attack is named “Race Condition” because it is a race between how fast an attacker sends requests to modify a resource and how fast the application updates that particular resource.

Mitigation

Unfortunately, mitigating race condition issues often means sacrificing performance. Methods to mitigate race conditions include utilizing locks and other thread-safe functionality. Most programming languages will have thread-safe functionality built in, but it usually needs to be manually specified.

XXE injection

XXE stands for XML External Entity and this injection vulnerability can be tested anywhere an API is used to process XML data. SOAP APIs could also be vulnerable to XXE injection because they are based in XML.

An API endpoint that uses XML looks something like this:

POST /soap/v2/user HTTP/1.1
Host: example.com
Content-Type: text/xml


 <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE dtd[<!ENTITY username SYSTEM "https://example.com/?username">]>
<SOAP-ENV:Envelope>
<SOAP-ENV:Body>
<getUser>
<id>&username;</id>
</getUser>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

An XML document uses entities to represent a single object of data and an XML document type definition (DTD) is used to define the entities to be used, structure of the document, etc.

XXE injection refers to when an attacker injects custom external entities that are outside of the specified DTD. Once these external entities are parsed by the API, it can allow an attacker to access the application’s internal files, escalate to SSRF, leak sensitive data to an attacker-controlled domain or DOS the server.

This is an example of a request where an attacker has injected an external custom entity called xxe and the purpose of this entity is to retrieve an internal file:

POST /soap/v2/user HTTP/1.1
Host: example.com
Content-Type: text/xml

 <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE aa [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<SOAP-ENV:Envelope>
<SOAP-ENV:Body>
<getUser>
<id>&xxe;</id>
</getUser>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

If the API allows the usage of a standard XML parser to process the data, then this injected external entity will be processed by the application and will return the content of /etc/passwd to the attacker.

Mitigation

Ensure that the XML parser being utilised is set up to not parse XML entities.

Switching Content Type

Even though an API may use JSON as a data format to communicate, the underlying server/framework may still accept other data formats like XML. Therefore, when you see an API with a content-Type of application/json, you can still try testing for XXE by switching its value to Content-Type: text/xml

For example, if an API uses JSON:

POST /api/v1/user HTTP/1.1
Host: example.com
Content-Type: application/json

It can be modified to send XML data in this manner:

POST /soap/v2/user HTTP/1.1
Host: example.com
Content-Type: text/xml

 <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE aa [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>

Mitigation

This bug only really exists on frameworks that are designed to accept multiple formats, where each format corresponds to a type of object. In these cases, the frameworks would normally have an option to whitelist available formats. It should be noted that this is quite rare in 2021.

HTTP Methods

APIs usually support various types of HTTP methods. Some of the common ones are GET, POST, PATCH, DELETE and OPTIONS.If a GET request is sent at a point where an application expects a POST request, then this can lead to the application responding in unexpected ways.

Let’s take CSRF as an example. CSRF (Cross-Site Request Forgery) is when an attacker tricks the victim into submitting a request which can cause state-changing actions to occur on the victim’s account. These changes can be anything from changing the victim’s personal details and in some cases, even changing the victim’s password leading to a full account takeover.

A normal request to change the victim’s personal details may look something like this:

POST /api/v1/user
Target: www.example.com
Content-Type: application/json

{
  "name","victim", 
  "email":"[email protected]", 
  "location":"AU", 
  "csrftoken":"76hhs683bki0"
}

After observing the above request, we might conclude that a CSRF attack will not be possible since the csrftoken value is being sent in the request body. However, if an API does not restrict the HTTP methods which can be used for this request, then it may be possible for an attacker to bypass this protection by sending this request using the GET method.

GET /api/v1/user?name=hacked&email=attacker@example.com&location=FR
Target: www.example.com
Connection: close

Additionally, sometimes the server simply does not validate the CSRF token, or accepts the request if no CSRF token parameter exists at all in the request.

Another common vulnerability in REST APIs is when permissions have been correctly applied on an endpoint, but only for one HTTP verb. For example, you may not be able to GET other people’s records, but you may be able to edit their records by utilizing the PATCH verb.

Mitigation

Manually specify HTTP verbs in your routes. Do not utilize verbs unnecessarily, and ensure that any endpoints that are specified do have the same controls applied against all verbs. Some frameworks will do this automatically, others will not.

Injection Vulnerabilities

Server-side injection flaws like SQL injection, RCE, command injection and SSRF can exist on APIs in the same way they exist on regular web applications. Whenever any injected data is directly passed to the interpreter on the backend, it leaves room for an attacker to send targeted queries and commands to access internal data or execute arbitrary code.

For example, let’s test SSRF on the following API request:

POST /api/v1/user
Target: www.example.com
Content-Type: application/json

{
  "name","victim", 
  "email":"[email protected]", 
  "profile_pic_url":"https://www.example.com/me.jpg"
}

An attacker can attempt SSRF on the website field by sending a request like this:

POST /api/v1/user
Target: www.example.com
Content-Type: application/json

{
  "name","victim", 
  "email":"[email protected]", 
  "profile_pic_url":"http://localhost/admin"
}

If the backend interpreter does not validate the profile_pic_url value properly, then this can lead to an attacker exploiting SSRF and successfully accessing internal data.

Similarly, an attacker can also attempt SQL injection if the data on this request is improperly validated:

POST /api/v1/orders
Target: www.example.com
Content-Type: application/json

{
  "offset":0,
  "limit":10,
  "scope":"orders_all",
}

By looking at the request body, we may get a hint that the values of these parameters are being sent to a backend database interpreter. Therefore, we can attempt SQL injection by modifying these values to targeted queries:

POST /api/v1/orders
Target: www.example.com
Content-Type: application/json

{
  "offset":0,
  "limit":10,
  "scope":"SELECT sleep(10)",
}

Once again, if the interpreter does not validate these values properly, then this can lead to a SQL injection where an attacker can cause a time delay in the database and even exfiltrate sensitive data.

Mitigation

Mitigating injection vulnerabilities in APIs is essentially the same as mitigating injection vulnerabilities in web applications. SAST/DAST scanners can help with this, but secure development practices are the best mitigation. Parameterize SQL queries, utilise ORMs and reputable libraries where possible, don’t trust user input!


Written by:
Hakluke and Farah Hawa
Detectify Crowdsource community members

Hakluke

My name is Luke Stephens but most know me as hakluke. I am currently living on the Sunshine Coast, in Australia. I recently resigned from my role as the Manager of Training and Quality Assurance for Bugcrowd to start my own consultancy, Haksec. I do a lot of penetration testing and bug bounties and create content for hackers. Check out my Youtube channel.

Farah Hawa

Farah Hawa is from Mumbai, India. She works as an Application Security Engineer at Bugcrowd. She’s a part-time bug bounty hunter and also creates technical content for bug bounty hunters & web application pentesters for her YouTube channel with more than 30000 subscribers. You can also find her on LinkedIn.