October 23, 2026
When the PDF Started Oversharing: From XXE to Reading /etc/passwd
A bug bounty journey that started with an ordinary Export button and ended with a server putting the contents of its own files inside aโฆ

By Omar Elsayed
10 min read
A bug bounty journey that started with an ordinary Export button and ended with a server putting the contents of its own files inside a PDF. Talk about going above and beyond.
In the world of bug bounty hunting, not every vulnerability hides behind a complicated dashboard or some obscure API endpoint. Sometimes, you find a security issue in a feature that looks completely harmless , so harmless that the last thing you would expect is for it to expose files stored on the server itself.
This time, it started with a simple feature: Export as PDF.
The feature was supposed to take some data, format it nicely, and generate a professional-looking report. Nothing dramatic. Just another day at the office.
But as we all know, not everything that looks innocent actually is. Sometimes, a PDF has more secrets than someone who says, "I'm fine. Nothing's wrong."
In this write-up, I'll walk through how I investigated a PDF generation feature, identified indicators of XML External Entity Injection (XXE), and demonstrated how the issue could expose local file contents through the generated PDF.
01 โ The Starting Point: What Are We Actually Testing?
I started by exploring the application and understanding its available features. One feature caught my attention: exporting documents as PDF.
The workflow seemed straightforward. A user submits some data, the application processes it, and a finished document is generated.
From a security perspective, however, a few questions were worth investigating:
- Does the application accept XML input?
- Is an XML parser used on the server?
- Does the parser allow external entity processing?
- Can the results of XML processing appear in the generated PDF?
- Could this behavior allow access to local files on the server?
These questions don't automatically mean a vulnerability exists. They simply help define a direction for testing.
That's one of the most important principles in reconnaissance: the goal isn't to collect as much information as possible; it's to identify information that can lead to a real security finding.
02 โ Reconnaissance: Before Testing Anything
One mistake security researchers can make is rushing to throw payloads at every endpoint they discover.
Reconnaissance isn't just about collecting subdomains and declaring victory. The real goal is to understand how an application works and identify features whose underlying logic deserves closer inspection.
First: Understanding the Attack Surface
I began by identifying functionality related to document processing and PDF exports, focusing on endpoints within the authorized testing scope.
Endpoints that accept files or structured data, such as XML, can be particularly interesting โ especially when the server performs additional processing on that data.
However, an endpoint accepting XML doesn't automatically mean it's vulnerable to XXE. It's simply a starting point for investigation.
Second: Understanding the Technology Stack
Next, I looked for information about the technologies responsible for processing requests and generating PDFs, using information available through the application and authorized testing.
Understanding the technology stack helps identify which components might be responsible for processing the input.
For example, if an application uses Java, the configuration of its XML parser may be relevant when investigating potential XXE vulnerabilities.
If PDF generation is handled by a separate component, it's also worth understanding whether that component processes XML directly or receives data that has already been processed by another layer.
The important thing is to distinguish confirmed facts from assumptions. Identifying a technology doesn't prove that it's vulnerable.
Third: Using the Application Normally
After exploring the relevant functionality, I used the PDF export feature as a normal user while monitoring HTTP requests through Burp Suite.
That's when I noticed something worth investigating: the request contained XML data.
This was the first useful clue. Instead of treating the feature as a simple export operation, I started looking more closely at how the application processed the input.
03 โ The Discovery: Wait, We're Actually Sending XML?
When I inspected the request in Burp Suite, I found that the application accepted XML in the request body.
A simplified example of the request looks like this:
POST /api/generate-pdf HTTP/1.1
Host: [TARGET_HOST]
Content-Type: application/xml
Authorization: Bearer [REDACTED]
<?xml version="1.0" encoding="UTF-8"?>
<document>
<title>Security Report</title>
<author>Researcher</author>
<content>Report content goes here.</content>
</document>POST /api/generate-pdf HTTP/1.1
Host: [TARGET_HOST]
Content-Type: application/xml
Authorization: Bearer [REDACTED]
<?xml version="1.0" encoding="UTF-8"?>
<document>
<title>Security Report</title>
<author>Researcher</author>
<content>Report content goes here.</content>
</document>Note: This is an illustrative example of the request structure, not a verbatim copy of the original request.
The application accepts XML containing fields such as title, author, and content.
That means some component needs to parse the data before it can be transformed into a document.
But let's make one thing clear: an XML request does not automatically mean an application is vulnerable.
The actual risk depends on how the XML is processed, how the parser is configured, and whether appropriate security restrictions are enforced.
This is where XXE enters the picture.
04 โ XXE Explained Without Making It Complicated
Let's keep it simple.
XML is a format for structuring data using tags, like this:
<document>
<title>Security Report</title>
<author>Researcher</author>
</document><document>
<title>Security Report</title>
<author>Researcher</author>
</document>Nothing complicated here: a title, an author, and a document.
XML also supports something called entities, which allow values to be defined and referenced elsewhere in a document.
One type is an external entity, which can refer to an external resource, such as a network resource or a local file.
If an XML parser processes external entities without appropriate security restrictions, a user may be able to influence the server into accessing resources that shouldn't be accessible through that feature.
That's the core idea behind XXE.
Instead of simply processing the data you submitted, the server may follow additional instructions that cause it to retrieve information from somewhere else.
In plain English: You asked the server to generate a report, and because its parser wasn't configured securely, it might decide to fetch information from its own machine, too.
That definitely wasn't part of the original feature request.
05 โ Testing the Hypothesis: Does the Parser Process External Entities?
At this stage, I needed to validate the hypothesis rather than assume that accepting XML meant XXE was present.
One possible testing method is out-of-band testing using a tool such as Burp Collaborator, provided the technique is permitted by the program's scope and rules.
The idea is to monitor whether processing a request produces an external interaction associated with the test.
An expected DNS lookup or HTTP interaction can indicate that the server attempted to access the external resource.
However, there are a few important caveats:
- An interaction does not automatically prove that every form of XXE is possible.
- The absence of an interaction does not conclusively prove that the vulnerability doesn't exist.
- The observed result must be correlated with the submitted request, and alternative explanations should be considered.
- Testing must remain within the authorized scope and avoid unnecessary exposure of data or disruption.
During my investigation, I observed indicators associated with external entity processing.
That led to the next question: was the impact limited to an external interaction, or could it extend to reading local files?
06 โ From XXE to Local File Disclosure
After identifying indicators that the parser was processing external entities, the next question was straightforward:
Could this behavior expose files stored on the same server?
First, it's important to distinguish between two concepts.
XXE: The underlying vulnerability caused by insecure processing of external entities.
Local File Disclosure: A potential impact in which the vulnerability exposes the contents of local files.
You'll often see people use the term LFI in this context. However, the more technically precise description is that XXE resulted in local file disclosure.
This is not necessarily the same as a traditional Local File Inclusion vulnerability, which typically involves unsafe handling of file paths within an application.
The terminology matters because it helps explain the actual root cause instead of putting every file-reading issue into the same category.
Why Does the PDF Matter?
This is what made the behavior particularly interesting.
The application didn't simply return a text response; it generated a PDF as the result of processing the input.
If the contents of an entity were processed and inserted into a document field, those contents could appear in the resulting PDF.
Therefore, even if the information wasn't displayed directly on the web page, that didn't necessarily mean the application wasn't exposing it.
Sometimes, you need to inspect the artifact an application generates rather than looking only at the HTTP response.
In this case, the generated PDF was an important part of understanding the impact.
07 โ Demonstrating the Impact: When the File Contents Appeared in the Document
After validating the parser's behavior, I needed to demonstrate the impact in a clear and controlled way.
I started with a relatively low-sensitivity file:
/etc/hostname
On Linux systems, this file typically contains the system's hostname. Its contents can therefore help demonstrate whether processing has reached a local file on the server.
The file's contents appeared in the generated PDF.
That was an important indicator: the result was no longer just an external interaction. It suggested that local file contents were being disclosed through the document generation process.
I then verified the impact using another file:
/etc/passwd
This file commonly exists on Linux systems and contains account information, including usernames, user and group IDs, and other account-related fields.
A typical example looks like this:
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologinroot:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologinLet's break down the first line:
root:x:0:0:root:/root:/bin/bashroot:x:0:0:root:/root:/bin/bashroot: The account username.x: A marker indicating that password information is stored separately, depending on the system configuration.0: The user ID (UID).0: The group ID (GID).root: The account description./root: The account's home directory./bin/bash: The configured login shell.
Of course, not every account listed in this file belongs to a human who uses the machine daily. Many are system or service accounts.
It's also important to understand that the presence of account information does not mean we have obtained users' passwords. The file generally contains account metadata, not the actual passwords.
The key finding wasn't simply the name of the file. It was that the application exposed local file contents through a feature designed to generate PDFs.
08 โ Why Is Reading /etc/passwd a Problem?
Someone might ask, "But that file doesn't contain the actual passwords. What's the big deal?"
Fair question.
The concern isn't necessarily that /etc/passwd alone is an extremely sensitive file. The more important point is that it demonstrates that the application was able to access a local file outside the data the user was supposed to control.
In other words, the application had a problem with the boundary between user-supplied input and resources accessible to the server.
That raises concerns about whether other files could also be accessible, depending on the service account's permissions and the system's configuration.
For example, application configuration files or certain environment files may contain application secrets or credentials.
However, we need to be precise:
Being able to read /etc/passwd does not automatically prove that every other file is readable.
Each file has its own permissions, services may run under different accounts, and security isolation mechanisms may prevent access to particular resources.
In bug bounty reports, the distinction between what you actually demonstrated and what you believe might be possible is extremely important.
We shouldn't claim access to cloud credentials or database secrets simply because local file disclosure is possible. Every additional claim needs supporting evidence, and further testing should not unnecessarily expose sensitive information.
09 โ Assessing the Impact: From a Bug to a Security Finding
Let's summarize the discovery:
- I investigated a PDF generation feature.
- I identified that the application accepted XML input.
- Testing revealed indicators of external entity processing.
- I verified that local file contents could be exposed.
- The contents appeared in the generated PDF.
- These results demonstrated that the document generation feature could expose data beyond its intended purpose.
This type of vulnerability can have a significant impact, particularly when files accessible to the service account contain sensitive operational information or application secrets.
However, there's an important distinction between demonstrated impact and potential impact.
The demonstrated impact here was the disclosure of local file contents through the PDF generator.
Access to database credentials, signing keys, or cloud credentials would depend on which files were actually accessible and what permissions the service had. Those possibilities should not be presented as confirmed results without evidence.
This distinction also matters when assigning severity. A proper assessment should consider the evidence, exploitability, sensitivity of the exposed data, and the potential consequences.
Not every XXE vulnerability is Critical, and not every local file disclosure means the entire server is compromised.
A strong report explains the real impact without exaggerating it or downplaying it.
10 โ How Can We Fix This?
Now for the question developers actually want answered: how do we prevent this from happening?
First: Disable External Entity Processing
This is one of the most important defensive measures in this scenario.
If the application doesn't require external entities, the XML parser should be configured to prevent their processing.
In Java, secure settings for DocumentBuilderFactory can be used to disable DTD processing and external entities. The exact configuration should be verified against the parser implementation and version in use.
For example:
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
factory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl",
true
);
factory.setFeature(
"http://xml.org/sax/features/external-general-entities",
false
);
factory.setFeature(
"http://xml.org/sax/features/external-parameter-entities",
false
);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
factory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl",
true
);
factory.setFeature(
"http://xml.org/sax/features/external-general-entities",
false
);
factory.setFeature(
"http://xml.org/sax/features/external-parameter-entities",
false
);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);This example illustrates the general direction of the fix, but it isn't a substitute for reviewing the parser's documentation and verifying that the settings are supported and actually enforced.
If a critical security setting cannot be enabled, the application should fail securely rather than continue with uncertain protections.
Second: Use a Secure XML Parser Configuration
If the application doesn't actually need XML, consider whether a simpler data format would be appropriate for the use case.
If XML is necessary, use a suitable parser and secure configuration instead of relying on default settings without reviewing them.
XML itself isn't the problem. The way the application processes it determines the security risk.
Third: Apply the Principle of Least Privilege
Even if the parser is misconfigured, the service responsible for generating PDFs shouldn't have permission to read files unrelated to its function.
Run the service with restricted permissions, isolate it from unnecessary files and secrets, and limit its access to network resources according to its actual requirements.
Defense in depth matters because a single configuration mistake shouldn't turn into a much larger security incident.
Fourth: Test the Fix
After applying the changes, verify that the parser no longer allows the unsafe behavior and that normal PDF generation still works as expected.
Also, review other parts of the application that process XML. Fixing one endpoint doesn't necessarily mean every component is secure.
11 โ The Lessons I Took Away
For me, the most valuable part of this kind of discovery isn't simply accessing a local file. It's the reasoning that leads to the finding.
Lesson 1: Test features, not just endpoints.
A seemingly ordinary feature like Export as PDF can involve several processing stages. Each stage may introduce additional security considerations.
Lesson 2: Understand the data flow.
Ask yourself: Where does the data enter? Which component processes it? Is it transformed into another format? What happens before the result reaches the user?
The better you understand the data flow, the more effectively you can identify where to focus your testing.
Lesson 3: Evidence matters more than assumptions.
An XML request doesn't prove XXE, and XXE doesn't automatically mean every local file is readable. Each stage needs its own supporting evidence.
Lesson 4: Artifacts can reveal what the response doesn't.
In this case, the generated PDF was an important part of understanding the impact. Looking only at the HTTP response wouldn't necessarily tell the whole story.
Lesson 5: Accuracy makes a stronger report.
When writing a bug bounty report, clearly explain what actually happened and what remains a possibility. A precise report helps the security team understand the issue, reproduce it, and fix it. It also makes your impact assessment more credible.
Final Thoughts
This investigation started with a simple PDF export feature, but examining how the application processed XML revealed a security issue that went beyond the feature's intended functionality.
What made the finding important was that the impact wasn't limited to the presence of XML or an external interaction. The testing demonstrated that local file contents could be exposed through the generated document.
It's a good reminder that vulnerabilities don't always live where you expect them to. Sometimes, the real problem lies in how an application handles data before producing the result the user sees.
And remember: not every PDF has secrets, but when a parser is configured incorrectly, the document might start sharing more than it was asked to.
Happy Hunting! ๐น