03 Dec 2024 - Posted by Dennis Goodlett
Doyensec’s Maxence Schmitt recently built a playground to go with his CSPT research. In this blog post, we will demonstrate how to find and exploit CSPT bugs with Eval Villain. For this purpose, we will leverage the second challenge of Maxence’s playground.
The next image shows what this methodology yields.
We’ve added some boxes and arrows in orange to better illustrate the current situation. First,
Eval Villain saw that part of the page’s path is being used in a fetch
request.
There, you can plainly see the asdf%2f..
was being URL decoded. Or if you prefer, you can expanded
the “Encoder function” group to check. Either way, Eval Villain had discovered the CSPT sink.
The second square is on top of a debug statement from evSourcer
. This was
where the response from the first fetch
was being added to Eval Villain’s
source bank. As a result, Eval Villain warned us that the _id
parameter from
the CSPT response had hit another fetch
sink. Again, you could get a bit more
details from the “Encoder function”.
From the arg[2/2]
of each fetch
we learned more. The first fetch
is a GET
that had "redirect":"follow"
and the second had "method":"POST"
. So we
controlled the path of a client-side GET
request and an open redirect could have sent
that request to our own server. The response of our own server would have then been
used in the path of an authenticated POST
request. This one image shows the
entire exploit chain for a CSPT2CSRF exploit.
All of this instrumentation stays around to help us with our exploit. Clicking the provided solution we see the following image. This shows exactly how the exploit works.
You will need Firefox with Eval Villain installed.
You’ll also need the CSPT playground,
which runs in Docker via docker compose up
. This should bring up a vulnerable
web app on http://127.0.0.1:3000/
. Read the README.md
for more info.
We really do recommend trying this out in the playground. CSPT is one of those bugs that seems easy when you read about it in a blog but feels daunting when you run into it on a test.
Log into the playground and visit the “CSPT2CSRF : GET to POST Sink” page. Open
the console with ctrl+shift+i
on Linux or cmd+option+i
on Mac. Ensure Eval
Villain is turned on. With the default configuration of Eval Villain, you
should just see [EV] Functions hooked for http://127.0.0.1:3000
in the
console.
In a real test though, we would see that there is obviously a parameter in the URL path. Eval Villain does not use the path as a source by default, due to false positives. So lets turn on “Path search” in the “Enable/Disable” pop-up menu (click the Eval Villain logo).
Now, after a page refresh, Eval Villain will tells us about two calls to fetch
,
each using the path. We don’t know if they are CSPT yet, we need to check if
../
is accepted, but it looks hopeful.
Note: You may only see one fetch
here, that is ok.
To test for actual CSPT, just add the string %2fasdf%2f..
to the end of the
path. This is a good tip, since this will normalize to the original path, the
website will act the same if it’s vulnerable. When you refresh the page you
will see this in the console.
It’s that easy to find a CSPT primitive. Had the source been in window.name
or a
URL parameter, Eval Villain would likely have found it right away.
Since the URL path was encoded, Eval Villain gives us an encoder function. You can paste that into your console and use it to try new payloads quickly. The function will automatically apply URL encoding.
With a CSPT primitive, the next step toward exploitation is learning how the response of this request is used. For that, we want to ingest the response as a new source for Eval Villain.
evSourcer
First you need to enable the evSourcer
global in Eval Villain. Go to the
configuration page from the pop-up menu and scroll to the globals table. Enable
the row that says “evSourcer”. Don’t forget to click save.
Now you can refresh the page and just run evSourcer.toString()
in the console
to verify the configuration change took.
You can run a quick test to try out the feature. Anything that goes into the
second parameter of this function will be put into the Eval Villain source
bank. Before using evSinker
the string foobar
does not generate a warning
from the eval
sink, afterward it does.
evSourcer
So, if we put the response of the CSPT request into evSourcer
, Eval Villain
can tell us if it hits eval
, .innerHTML
, fetch
or any other sink we have
hooked.
To find the response to the CSPT request, we just look at the stack trace Eval Villain gave us.
Here we have highlighted what we think of as the “magic zone”. When you see
function names go from minified garbage, to big readable strings, that is where
you typically want to start. That often means a transition from library code to
developer written code, either forward or back. One of those two functions are
probably what we want. Based on context, fetchNoteById
is probably returning the
info to Ko
. So go to the Ko
function in the debugger by clicking the link
next to it. Once you get there, beautify the code by clicking the {}
icon in
the lower left of the code pane.
You will see some code like this:
return (0, t.useEffect) (
(
() => {
r &&
ot.fetchNoteById(r).then((e => { // <-- fetchNoteById call here
ot.seenNote(e._id), // <-- so `e` is probably our JSON response
n(e)
})).catch((e => {
//...
fetchNoteById
apparently returns a promise. This makes sense,
so we would normally set a breakpoint in order to inspect e
and compare it with
the response from fetch
. Once you validate it, it’s time to instrument.
Right-click on the line number that contains ot.seenNote
and click “Add
Conditional breakpoint”. Add in the evSinker
call, using a name you can
recognize as injecting the e
variable. The evSinker
function always returns
false
so we will never actually hit this breakpoint.
Notice we have disabled source maps. Source maps can optimize out variables and make debugging harder. Also, Firefox sometimes takes a minute to work through beautifying code and putting breakpoints at the right spot, so just be patient.
Now we just refresh the page. Since we used true
as the last parameter to
evSinker
, we will use console debugging to tell us what got injected. Enable
“Debug” in the console. We can also enable XHR
in the console to see requests
and responses there. The requests we are interested in will directly follow
Eval Villain output to the console, so they are easy to find. This is what we see.
For the sake of room, we closed the first fetch
group. It does show the
asdf%2f..
payload hitting fetch. The “XHR” entry we have open there does not
show the directory traversal because it was normalized out. Eval Villain makes
it easy to find though. The response from the “XHR” can be seen injected in the
console debug below it. Then of course Eval Villain is able to spot it hitting
the fetch
sink.
You may notice that there is no arg[2/2]
output in the last picture. That
argument is a JavaScript object. Eval Villain by default is configured to only
look at strings. Open the pop-up menu, click types and enable objects. Then when
you refresh the page you can see from the Eval Villain output what options are
being passed to fetch
.
The playground makes finding gadgets easy. Just go to the “gadgets” drop down in the page. The real world does not have that, so Burp Suite’s Bambda search seems to be the best bet. See Maxence’s CSPT research for more on that.
Eval Villain is really just a JavaScript function, with config, that Firefox copy/pastes into each page before it loads. Once injected, it just uses the console to log output. So in theory, you could copy paste this same code manually into anywhere JavaScript is accepted.
Eval Villain 1.11 lets you do just that. Go to the configuration page and scroll to the very bottom. You will see a “Copy Injection” button. If you click it, the entire Eval Villain injection, along with the current configuration, will be put into your clipboard.
Using this we have gotten Eval Villain into an instrumented Electron App. The following screen shot shows Eval Villain running from a conditional breakpoint in Burp’s built-in Chrome browser.
Or you can use the HTTP Mock extension in Burp to paste Eval Villain into a web response. We have not tried it yet, but it will be cool to inject it into a Web View on Android using Frida.
Instrumenting the target code does not really take that long. This blog post explained step by step on how to leverage Eval Villain in order to find and exploit CSPT vulnerabilities. Even for learning new tricks using a playground, Eval Villain helps us debug little mistakes.
Make sure to use the right tool for the right job. For example, Eval Villain can’t decode everything (check out the fragment challenge). Maxence developed a great Burp Extension for CSPT, but it lacks insight into the DOM. Some other tools are Geko, DOMLogger++ and DOM Invader (enable xhr.open and fetch in sinks). Mix and match what works best for you.