
Second-order vulnerabilities are not a distinct type of vulnerability in their own right, as a command injection might be. Rather, they refer to a specific way of exploiting existing vulnerabilities.
They arise when user input is stored by an application and then reused later in a different context without being properly validated or processed.
A classic example is stored XSS, which in many cases constitutes a second-order exploit. User input is first stored in the application without being properly sanitised. It is then reused or displayed in another part of the application, or even in another application—such as a back-office system—without being properly escaped on output. It is at this point that the vulnerability manifests itself.
Many types of vulnerabilities can thus be exploited in a second-order manner, including XSS, SQL injection, LFI/RFI and RCE. Below, we will detail two specific scenarios, relating to SSRF and SQL injection respectively. The principle, however, remains applicable to these other types of vulnerabilities.
The main difference between a conventional attack and a second-order attack lies in when and where the exploit is triggered.
In the case of a traditional attack, the injection immediately causes unexpected behaviour, either directly on the server side or in the application’s response. The presence of the vulnerability can therefore be observed and measured at the moment the malicious payload is injected.
Conversely, in a second-order attack, the injection and execution occur at two distinct moments. The payload is first stored or transmitted without necessarily causing any visible behaviour, and is then executed at a later stage when it is reused in a different context.
This is particularly the case with a Blind XSS attack.
Second-order attacks are particularly difficult to identify, and the detection approach varies significantly depending on whether one is working in a black-box, grey-box or white-box context.
In black-box or grey-box scenarios, automated tools are often ineffective, as they rely primarily on the immediate response to a request. However, in the case of a second-order attack, this response generally shows no visible signs of the vulnerability.
One method therefore involves injecting unique markers into each of the fields whose data is recorded by the application, then exploring the various functionalities to identify where these markers reappear. This helps to pinpoint the contexts in which user-controlled data is reused.
When the vulnerability is triggered blindly, it is necessary to rely on an external server to detect any interaction. Tools such as Burp Collaborator, for example, can identify this type of trigger.
In white-box testing, code review remains the most reliable approach. However, it requires a holistic view of the data flows within the application, rather than analysing each function in isolation. The first step is to identify the various sources from which user input can be entered into the application, then to trace the path of this data to the locations where it is stored and reused. The aim is, in particular, to identify instances where user-controlled data subsequently reaches a sensitive context, such as an SQL query.
Let’s take the BookVault app as an example, where any user can create an account.

Once an account has been created, the user can add a book to their library using a URL. This type of functionality is frequently encountered during our audits and is often a source of vulnerabilities.
By analysing the application’s source code, we can identify a configuration issue here.

In addition to the HTTP and HTTPS protocols, the developer has also enabled the file protocol. This makes it possible to access files on the server directly, which constitutes a vulnerability.
To verify this, we can use the entry file:///etc/passwd. This file is present on UNIX systems and can generally be read by all users.

However, the application returns an error stating that no <title> tag was found in the file. To understand why this is happening, we need to examine the extract_title() function.

A regular expression (regex) is applied to the retrieved content in order to extract everything contained between the <title></title> tags.
Although it is possible to read files on the server, few of them are likely to contain such tags. It is even less likely that the data contained between them would be of particular interest to an attacker.
However, a more in-depth analysis of the source code reveals two interesting behaviours:
/app/app.log.Based on these two elements, it is possible to envisage a scenario whereby the administrator’s session could be compromised:
<title> as the username. This value is then recorded in the log file and corresponds to the start of the regular expression used by the application. <title> as the username. This value corresponds to the end of the regular expression. All the data recorded between our two attempts is therefore enclosed within the tags <title> and <title>.file:///app/app.log. The extract_title() function then extracts all the content located between the two tags we injected, including the administrator’s session cookie. This can then be used to access their account.
This scenario clearly illustrates the principle of a second-order attack. The various actions carried out separately do not, on their own, allow the vulnerability to be exploited directly. It is the sequence of these actions, together with the subsequent reuse of the injected data, that makes exploitation possible.
This scenario is based on a real vulnerability that we identified during an audit. It had enabled the retrieval of several megabytes of logs generated by the application over a given period.
In this second scenario, we will exploit an SQL injection via the username.
The SupportDesk application allows users to create an account, edit their profile details and contact support. They can also view their ticket history from the /support page.

From the profile section, users can change their username, email address and password. These are therefore standard features found in many web applications.
An analysis of the source code reveals that both account creation and profile editing rely on parameterised queries, thereby preventing direct SQL injection at this stage.

The code shown above is therefore not vulnerable as it stands. It should be noted, however, that the three fields are stored in the database as they are, without any specific sanitisation.
On further analysis of the source code, however, it can be seen that, on line 208, the username is concatenated directly into an SQL query.

An important point is that this value is not retrieved directly from user input when the query is executed: it is read from the database.
However, as we have just seen, the user can previously modify their username and store arbitrary characters within it. It is therefore possible to inject an SQL payload, which will be stored in the database without immediately triggering any specific behaviour.
The vulnerability arises when this value is reused by the /support route. For example, if a quotation mark is stored in the username, and a GET or POST request is then made to this route, the stored value is concatenated into the SQL query. This then becomes invalid and causes an error to be returned to the client.

The injection is therefore indirect and less obvious to identify, but it is certainly present. The payload is injected when the profile is modified, whilst its execution only takes place later, when the username is retrieved from the database and then reused in the SQL query.
A basic automated scanner may fail to detect this vulnerability, as it generally relies on the immediate response to the query containing the payload. However, Sqlmap allows you to handle this type of scenario by using options such as --second-url or --second-req.
From there, it becomes possible to modify the username with a suitable payload in order, for example, to retrieve information about other users. By injecting a UNION-based SQL injection into the username and then revisiting the /support page, the payload is executed and the query returns the list of users.

By its very nature, the impact of a second-order attack is no different from that of the underlying vulnerability: an SQL injection remains an SQL injection, an XSS remains an XSS, and so on. What does change, however, is the context in which the malicious payload is ultimately executed.
This is precisely what makes these attacks particularly interesting. Execution may take place in a context with higher privileges than that of the initial injection point. For example, data may be saved by a standard user and then reused later in a back-office system accessible to an administrator.
This is particularly the case with Blind XSS, but also with the BookVault scenario described earlier: the attack starts from a simple user account but ultimately allows the administrator’s session cookie to be retrieved.
Another important feature is that the malicious payload is not usually present in the request that triggers its execution. The request itself may be entirely legitimate and contain no suspicious elements. A WAF therefore finds it difficult to identify the attack at the time it is triggered, as the malicious data was introduced earlier in the application.
This behaviour also complicates incident response. A malicious payload may remain stored for several hours or days before being reused and executed, making it more difficult to correlate the initial point of injection with the event that triggered the incident.
The ultimate impact therefore depends mainly on the context in which the data is reused. In our previous examples, the exploit enables, respectively, the theft of an administrator session and the exfiltration of user information. Depending on the underlying vulnerability and the execution context, however, a secondary attack could lead to other consequences, such as code execution or privilege escalation.
There is no single remedy for second-order attacks. As we have seen, these do not constitute a specific category of vulnerability: the remedy therefore depends primarily on the underlying vulnerability.
The fact that an attack is second-order does not necessarily imply any specific protective measures. It is, above all, the vulnerability present at the point where the data is reused that must be addressed: parameterised queries to prevent SQL injections, context-sensitive output encoding for XSS, validation of the protocol and destination for SSRF, and so on.
The key principle to bear in mind is that data considered safe in one context is not necessarily safe in another. In our SupportDesk example, the username is harmless as long as it is used on its own, but becomes dangerous as soon as it is concatenated into an SQL query.
Data should therefore never be regarded as secure simply because it comes from a source deemed trustworthy, such as the application’s database. The database may simply contain data entered by a user and recorded at an earlier time.
Finally, as these vulnerabilities are particularly difficult to identify, code review remains one of the most effective approaches. The analysis must be carried out by thinking in terms of data flow: this involves identifying the points at which user input is entered and stored, then tracking the various points at which this data is subsequently reused in order to verify that context-appropriate safeguards are indeed in place.
Author: Théo ARCHIMBAUD – Pentester @Vaadata