TLDW logo

Deep inside React's source code - How the attack really works

By Mehul - Codedamn

Summary

## Key takeaways - **React Trusts User Payloads Blindly**: React server components code involves a lot of trust on the data that the user is sending in the first place, especially property access and property set, and that is where the vulnerability comes into picture. [00:22], [00:35] - **$@0 Enables Chunk Self-Reference**: The second chunk with dollar at the rate zero points back to the first chunk itself, waking up placeholders in the resolve listeners that were waiting to be resolved. [07:37], [16:20] - **Unvalidated Property Access on Proto**: React has no validation on what property is accessed, allowing access to __proto__ or constructor via recursive path application like 'proto:then' or 'constructor:constructor'. [18:01], [23:20] - **Constructor Chain Yields Eval Function**: Accessing constructor twice on a Promise-like object reaches the Function constructor, which acts as an eval: new Function('console.log(1337)') executes arbitrary code. [24:08], [25:00] - **Promise 'then' Auto-Triggers RCE**: Overwriting 'then' with a controlled payload and 'get' with Function constructor creates a thenable; JavaScript runtime auto-evaluates the then chain, executing the RCE. [30:13], [34:04] - **Exploit Chains 3 Key Weaknesses**: Attack exploits chunk dependencies creating null placeholders, unvalidated property lookup polluting prototype chains, and controlled chunks with response/formData/get for eval via binary mode. [35:21], [36:55]

Topics Covered

  • React Trusts User Payloads Blindly
  • Proto Chain Hijacks Promise Then
  • Eval via Double Constructor
  • Runtime Auto-Evals Then Chain
  • No Property Validation Enables RCE

Full Transcript

All right, guys. I'm back with the second video on React to Shell. And in

this video, let's look at how this exploit exactly works. I spent a lot of time understanding the details of React works. And to be honest, I'm not really

works. And to be honest, I'm not really impressed on the code quality as well as, you know, how the attack actually plays out because the code that you will see in this video about how React server

components works is slightly bad in my opinion because it involves a lot of trust on the data that the user is sending in the first place, right? The

property access and the property set mostly, right? And that is where the

mostly, right? And that is where the vulnerability also comes into picture.

But I'll show you I'll show you how everything works because now I have gone through this entire codebase or at least the relevant parts in React's framework and I'm going to show you what exactly

happens like what's going on over here.

So we are not going to go into the proof of concepts that other people have released. We're going to create our own

released. We're going to create our own proof of concept and I'm going to start by saying that in order to run this you need to have a react version which is vulnerable right react 19 and next 16

for example. Now over here you would see

for example. Now over here you would see that inside here I have added a new option called node options as inspect break flag. Now the reason I'm doing

break flag. Now the reason I'm doing this is because I want to show you exactly what's going on inside NodeJS server inside react as it's happening right. So and that is how I also studied

right. So and that is how I also studied it for the most part. So I'm going to start this server over here and you're going to see that it's waiting for us to you know attach the debugger first. So

in order to do that now I'm going to go to Chrome inspect and I'm going to attach the debugger. You can see that it's running on 9230 right now. So I'm

going to inspect that and you can see that it is something which is working.

All right. I might have to restart it.

Just give me a second because I'll just try if I have the same break points available or not. All right. Let me

check if this works or not. And yeah it works fine. Okay. So we can move

works fine. Okay. So we can move forward. So I already have a bunch of

forward. So I already have a bunch of these breakpoints already established.

But let me first of all show you what's going on, right? So imagine that this is the only code that we have. I just

started a regular Nex.js server on localhost 3000. Let me open this in the

localhost 3000. Let me open this in the browser as well. This comes with a single page which has this click as a button. And when I click on this button,

button. And when I click on this button, you will see let me just bypass this for now. you will see that a request is made

now. you will see that a request is made a post request is made to local host itself which has some data going on right so let me actually show you what the actual code is as well so this is

the this is a simple page right so it only has use server it has a debugger statement which we can remove it has a console log one and it has a math random over here which it returns right and when I click on this this function is

called from the server side right so this mathrandom is running on the NodeJS over here that is why you also see this one when I press on this button right so if I keep pressing on this button for

example you'll keep seeing one in my console log because this is the code which is actually running in NodeJS world and inside these you will see that this is a typical React server function

call where I have no clue as a user as a developer what is going on I just create this as a function I have this function and this automatically starts to run right now let's just take a look at

what's happening inside this function call so you would see over here first of all this is a post request on the same page where you have defined the function. So that is one thing. Second

function. So that is one thing. Second

thing is that you will see that this request has a very important thing called next action. This is the value that you want to copy and this is the value that will change across every action that you are running. That is why

this value for example if you see the shell.ts you will see that this is hardcoded over here. Right? This is uh another website that is on versel which is vulnerable because I wanted to also

test out w of warsel. I'll show you that by the end of this video. So this is a value that will trigger this next action and you need that because otherwise be because if you don't do that then the

attack itself does not work. Now what

happens here is that if you look into the payload you will see that the raw payload that you are sending in this case is just a dollar t right and this dollar t is basically nothing but it just means that there is no argument.

There is nothing going on inside the function call because you can see literally when I call this handle summit there is no function there is no argument that we are accepting right so it's just an empty function call directly to whatever function is defined

on this route but anyway we are not really interested in doing this we are interested in crafting our own payload crafting our own stuff right now if I do something like this let's say if I cancel this request and if I do

something like you know the same thing that we are doing right now so if I have for example this dollar t over here, right? And if I run this, let's say if I

right? And if I run this, let's say if I remove this and if I run this, you will see that we get the same response over here that we got from the server actions, right? And this is our

actions, right? And this is our math.random value. This value that you

math.random value. This value that you see is the math.random value over here.

Now, obviously, we can start playing around this this React server component.

And you'll start to see that it starts to break sometimes here and there. For

example, let's say if I just pass a string instead of a array or instead of an object, you will see that now we have started to get some errors. Create list

from array like called on an nonobject and so on and so forth. Right? So this

is what you start this is where you start to poke around and this is where you how you start to figure out you will see that in case of objects it'll not crash. Right? So again in case of object

crash. Right? So again in case of object it just works fine. So what we have to do is first of all we have to understand that what exactly is this payload? What

exactly is going on here? And in order to do that, a simpler way instead of me explaining you the whole React server architecture in general is to show you

how this works, right? So let me enable the debugger points that I uh figured out were relevant in my debugging and in my understanding as well. And let me show you the journey of this request.

Let's say I hit this, right? Um I think we need a little bit more uh parameters in the journey because I've removed some of these

uh let's see some of these endpoints right so let me add an endpoint over here let me add it over here let me also add it

maybe yeah I think that should be fine let me just show you the request again and before that actually let before going into the codebase. Let me actually

show you what's happening. You see this console log 1337. If I re if I run this, you will see that I get 1337 over here which is basically remote code execution, right? And that is what is

execution, right? And that is what is happening. That is what is wrong in this

happening. That is what is wrong in this whole thing. For example, I can do you

whole thing. For example, I can do you know once I have this I can do something like fetch to evil URL and then you know in the env I can just pass in like

process. Envically I can get the

process. Envically I can get the process.v NV and I can pass it in a

process.v NV and I can pass it in a network request and that is how I can exfiltrate the data. But in this case, I mean just running console log 1337 should suffice. I mean you'll get an

should suffice. I mean you'll get an idea of what's going on and you can see like you're basically able to execute arbitrary code that the user can specify on the server. Now let's see how the journey of this request looks like. Let

me enable this payload breakpoints again and hit this. All right. So you see that there are certain phases um in this journey of you know when the payload is sent you see that over here we are

sending two payloads we are sending two lines basically inside a form first is a zero which is a zero chunk and this has the value of this whole thing that is the string that you see over here and

then there is a first chunk which is this first key and it's a value the special value of this dollar at the rate zero which means that it's basically pointing back to the same chunk chunk

right so the force chunk is pointing back to the same chunk itself now what's happening over here is that you see that this initialize model chunk is one of the functions inside this react server

which is called for every single chunk that you pass in right so even if you have for example even if you have 2 3 4 5 more chunks over here it'll be called for every one of them now inside this

you would see that react does something interesting so I don't understand why they do it in the way they do it obviously they'll have their own reasons. But the way this works is that

reasons. But the way this works is that they construct a raw model of the you know whatever value is passed and they construct a very weird thing called a value which is like which they call as a

revive models return value. Now let me show you what this means and what this looks like. So you see that raw model

looks like. So you see that raw model over here is just nothing but a parsed version of whatever payload we are sending them. Right? So so far it's

sending them. Right? So so far it's safe, right? Because JSON parse

safe, right? Because JSON parse obviously is safe. You can't do any sort of code evaluation with that. But the

value over here if we go inside this revive model uh let me see if we have break points inside this or not. Not

really. So let me add a bunch of break points over here.

Yeah. So inside of revive model you see that we what we do is that we sort of construct an object and for whatever object value so we construct this object

again. Basically we reconstruct this

again. Basically we reconstruct this object but with a twist. What we do is that we look into every single value that we find and we see that if it has

special identifier this dollar symbol basically and it has a number associated with it that means that the value of this property actually depends on

something inside the chunk that is yet to come. Right? So that is where the

to come. Right? So that is where the server function call and the you know asynchronous and stuff and stuff like that comes into picture like you you can stream the input and output and all

those sort of things that react server components promises comes from the fact that I sent this chunk before I had this chunk from the user side. So this what

it basically means as you see over here if I can explain quickly for every single field it's going to run this function this revive model it'll run it for every single field inside this it'll

run it for then it'll run it for status reason value response and then inside prefix inside form data so it's a recursive function you will find this later but what it's doing is that it's

trying to get value of every single field that it encounters and if the value is string it then pars processes it further. Let me add a break point

it further. Let me add a break point over here. I'll show you. Otherwise, if

over here. I'll show you. Otherwise, if

it's an object, which in this case we have for response, it'll go inside this block. Otherwise, if it's an array,

block. Otherwise, if it's an array, it'll go inside this block. But it's not an array. So, it will not go inside this

an array. So, it will not go inside this block. It'll just stay inside this

block. It'll just stay inside this object area. Right? So, I'll let me just

object area. Right? So, I'll let me just Yeah, we have the break points. Yeah.

So, let me just play this. So you see that for the first object because this whole thing itself is an object it'll jump directly inside this for loop for objects right it's not an array it's

just an object so we'll go into this loop now what happens over here is that you recursively keep on calling this for every property right so the next time

for example this is called you will see um let me just play this again you will see that the next time this is called you will see that the value now the key

is Then the value is this proto thing which is what we passed over here right the first key and then this value. So

based on this because now this is a string it'll go and it'll parse this model string. So let me just play this

model string. So let me just play this and let me actually jump inside this parse model and you will see now that based on the value like you know because this is like a dollar symbol to start

off with. If it's if the value starts

off with. If it's if the value starts with a dollar symbol it has a special meaning right? Right? So if it starts

meaning right? Right? So if it starts with a dollar symbol, you do this whole thing, the whole bunch of things.

Otherwise, you just return the value. If

it's like a normal number like a zero or a resolved model or even something like this, this is also just returned as normally, right? Similarly for prefix u

normally, right? Similarly for prefix u but not forget because again get starts with a dollar symbol. So in case of dollar symbol, what it effectively does

is that calls um let me see it basically calls this part, right? So

in case of dollar one you will see that it's not matching one is not anywhere near here right? So we just have a dollar symbol as a value zero and then value one is one because value is okay

the debugger is probably pointing it to the wrong stack trace right now but the value is right now just one right so you it'll

not go inside any of these special blocks over here so let me just keep playing it let me just keep pushing this over here and let me actually point add

a break point on the value as well Right, great. So, let's just keep going on.

great. So, let's just keep going on.

Let's just keep going on and let's just wait for the final value that we get.

Right? So, this is the get that we have which is like an internal object. Now,

we'll start to construct the object from inside out. Right? Now if I keep playing

inside out. Right? Now if I keep playing this you will see in the next one we have the form data now which has a get null and if I keep playing this again you will see that now we have reason

status then and I think something is wrong. I think I messed up something

wrong. I think I messed up something because this this should also be null.

I'm assuming that we missed a dollar or something somewhere. Let me just play

something somewhere. Let me just play this back again and let me just run this again. Yeah something something messed

again. Yeah something something messed up. That's why you know you can see like

up. That's why you know you can see like we also did not get it 1337. In fact, we got one there. So, let's just play this back because I want to show you the real

object. Let's just keep playing. Let's

object. Let's just keep playing. Let's

just keep playing and let's just keep playing.

Okay. So,

let's just wait for return value to trigger again.

And here we are. So get is null. And

then we have form data with get as null.

And then we have final object. And now

you can see that then is also null. So

what's going on over here is that if I understand react properly, you see that these properties start with a dollar and a chunk number, right? And that chunk number is not there. it's not parsed yet

by React because it's obviously sequentially parsing the chunks. So what

it does is that it just blanks them out.

It'll keep them as null. That's why you also see that inside this get it's null, right? Even though this has the value of

right? Even though this has the value of whatever this specific string, but the rest of the values are still there, right? So it'll just keep rest of the

right? So it'll just keep rest of the values. Now the attack is this. The

values. Now the attack is this. The

attack is the fact that you're able to specify these underscore response and underscore form data and underscore you know these sort of things. That is the

first problem that is there that React allows you to specify these values for some reason right and then it by mistake acts on those these values as well. This

is the first problem. The second problem is that React allows you to specify these parameters these then and these constructor constructor and so on. Let

me show you where the problem begins.

Now once the second chunk comes into picture. So right now this this is our

picture. So right now this this is our first chunk right. The first chunk has resolved. Raw model looks something like

resolved. Raw model looks something like this. Uh result model sorry is this. Raw

this. Uh result model sorry is this. Raw

model looks something like this.

This is actually wrong because you know the debug this is like offiscated code.

So it's also overwriting the variable names that is there. So that that's why it's like slightly wrong. But raw model initially would have been clean implementation. And it'll just have this

implementation. And it'll just have this string and then once this value has run it has updated raw model again and again over and over again because the value finally is just like raw model. In fact

I think value and raw model are same objects if I'm not wrong.

Yeah. So you can see value and raw model are effectively pointing to the same object in the memory. Um and obviously like it's slightly difficult to do these things with offiscated code. A little

bit of offiscation but it's mostly clean right? So we can still make sense of the

right? So we can still make sense of the things over here. So let's keep moving forward now. So now what you see is that

forward now. So now what you see is that we get our second chunk which is this dollar at the rate zero, right? Which is

the second chunk. Now what this means what this dollar at the rate zero means is basically you're pointing back to this chunk again, right? And I'm not I've not spent a lot of time understanding what or how and why does

React creates these dependencies amongst chunks. But what it basically

chunks. But what it basically exploits is that this call or this thing can now go and it can wake up the placeholders which were supposed to be

filled with the presence of the first chunk. Right? So this dollar one

chunk. Right? So this dollar one effectively means nothing. But it's

saying that wait for this object whatever this is to come into the server and then go to these properties inside this object. Right? So to give you an

this object. Right? So to give you an example of um you know a a genuine payload how this might look like is probably something like this. Let's say

name Mahul and age you know 27 for example. So if I had something like this

example. So if I had something like this and then I had dollar one and then let's say name full name of user. So what this

effectively means is that it's waiting for the first chunk to come in and it'll go and pick up this property. Similarly,

if this was dollar one and age, it will mean that wait for this chunk to come in and then go pick up the property. So

what React is doing is using this column as a symbol as as a way for going and picking up the properties inside that specific chunk that has just come in.

But the problem happens is that there is no validation at all of any sorts like what property are you actually accessing? Does that even exist on the

accessing? Does that even exist on the object or is it like even ledger like can you access proto or this constructor which actually relates to eval right? So

let me show you where in the codebase exactly that happens. So let's say let's say that we keep playing over here. We

do the same thing. So we construct the same raw model of the second chunk. Um

this time but this time this is interesting right because now we have the string as dollar add the rate and zero right. So this is definitely a

zero right. So this is definitely a string. So we go into this sparse model

string. So we go into this sparse model string directly. Um let's just jump into

string directly. Um let's just jump into it. Our values first letter is

it. Our values first letter is definitely dollar. So we jump into this

definitely dollar. So we jump into this and the second letter is add the rate right which has again like a specific use case. So if you jump into add the

use case. So if you jump into add the rate part here you will see that we get the object first. Object is something which is basically you know just the number that is there right. So if you

hover over this value you will see that object is zero because it's parent value. slice 2 which gives us zero into

value. slice 2 which gives us zero into base 16. So I'm assuming that basically

base 16. So I'm assuming that basically a technical attack uh would also be fine if you do something like this. I'm

assuming like because it's doing like base 16 over here right and then you have this get chunk right which is the original response and then the chunk ID

which is going to be zero in this case again this is offiscated code so slightly it's pointing to the wrong um object right now because it has not been computed yet but this will give you the

chunk zero.

Now once you have this chunk, React then starts to act a little weird and starts to like start calling these vake chunk methods, right? Because now we have the

methods, right? Because now we have the chunk and now we have to go back and we have to wake them up. Hey, you have your value. Now let's just go ahead and let's

value. Now let's just go ahead and let's just fill that value. This is where the magic happens. See this object is

magic happens. See this object is basically nothing. It's it's just a

basically nothing. It's it's just a blank object. It's nothing. We are

blank object. It's nothing. We are

passing nothing. But we are still trying to access some properties over here, right? So if I keep playing this you

right? So if I keep playing this you would see that at some point we get into this part called wake chunk which has this resolve listeners and chunk dov value. Now this is this is very

value. Now this is this is very critical. This is where the

critical. This is where the vulnerability starts to creep in because you see that this resolve listeners is basically collection that react maintains of these dollar symbols.

Right? So wherever in the in the fields for example even if you have like a new field over here and you have written dollar one here it'll go inside this resolve listeners because these are listeners that are waiting to be

resolved by the next chunk when it comes into the picture. Now as these chunks are resolved, you will see that this also gets this chunk dot value which is again interesting because this is this

is the value of the first chunk the chunk which we have just passed right now right and if you go inside this wake chunk you will see that this is just a for loop which just iterates over these

listeners and it calls them with values.

It is even safe so far. Let me go inside one of these chunks values and show you what the call back is. This is what causes remote code execution. The

inability to clear out what exactly property are we accessing. Look at this.

Look at this stupid logic over here that we are implementing right now. Um where

we get the value right and we have the path right which is again split with a colon. So whatever you're specifying

colon. So whatever you're specifying over here split with a colon and these values are recursively applied to the these these in order to extract out that

value it's recursively applied right so what's happening over here effectively it's it's saying just like I gave you this example that let's say if I had person

name type let's say ID mayul let's say I had something like this right and I had this

in the legit payload I had something like person name type ID over here. So

what it's doing over here is that it's effectively just this path basically consists of one person uh type ID whatever I have defined as a separate

thing and it'll just keep on going inside this object what we have passed just now recursively. So it'll keep on not recursively, iteratively with a for

loop. It'll keep on going that object

loop. It'll keep on going that object and it'll assign the parent object which is this one. It'll override the key. In

this case, it was then let me just revert it back. So it'll try to iterate the field you have asked here.

[laughter] It'll try to iterate the field that you have asked here inside this object and it'll go back and update the original object. Remember that I showed you that

object. Remember that I showed you that you know the object is blanked out for the these fields. It's null, right? That

is where it has to fill back the values, right? And this is where the problem

right? And this is where the problem happens because now if you see that uh if I let me just step it a little bit and maybe like get to the part where we

have the parent object. So now if you see in the first case we now have then as a function and why do we have this as a function? Because you see that we have

a function? Because you see that we have said that go to the object that we have just passed you. Go to that prototypes chain. This underscore proto is a

chain. This underscore proto is a special prototype chain which exists on objects in JavaScript. Read about this if you don't know this. And then access

its then property. Right? Similarly,

similarly as we keep going over here, you will see that when we get to the let's see yeah so when we get to this part one constructor constructor this is

this is what causes remote code execution. Pay attention guys here. So

execution. Pay attention guys here. So

when we keep stepping on this you will see that at this point parent object key which is get becomes function constructor. Why? Why does it

function constructor. Why? Why does it become function constructor? Because

what we are saying is that go to the object that we have just passed you and access its constructor two times. You

know what that means? That means let me show you. Let's say if I have a object X

show you. Let's say if I have a object X which is just a promise, right? What I'm

this is what this is what is the value right? So what I'm saying is that go

right? So what I'm saying is that go ahead and access its constructor which is just a function that can create promise but then access its constructor also which is now a function. You know

what you have got over here? It's an

eval function because I can just say something like console log hacked and I can run this function. Do you see that you get a console log of hated over

here and the reason you got this is because this is nothing but a new function construct over here. Right? You

are basically calling just a function keyword over here because the constructor property takes you to the constructor of promise which is the promise function first and then it takes

you to the function object itself and now you have to somehow get the remote code execution. It's not still rce by

code execution. It's not still rce by the way. You have only assigned what you

the way. You have only assigned what you have done right now although it's a blunder but what you have done is that inside the memory this then suddenly has

changed from a string to a then function like a real then function of this promise and this function this form data.get has actually changed to a

data.get has actually changed to a function that will just eval anything that will be passed inside this. So

anything you pass inside this argument now and you call this get function by mistake if you do that then it'll get remote code execution and that is exactly what this then does right

because you you would be uh you know thinking about that what is this then and why haven't we played out this one yet right and that's that's what we are coming up now so let's just keep going

right let's just keep going remote code execution still hasn't happened if you go back to my console you will see that we still did not get 1337 yet. Right?

Still the code has not executed. So we

are still left to go. Let's say we keep on going over here. Right? Right. So

let's say we step it over a little bit more. And you can see now that now we

more. And you can see now that now we are inside this chunk prototype. Now

this is a call which was made because of this then right because you were able to modify the then of the function itself.

Now this is the call that lands us into this chunk.prototype.n then section

this chunk.prototype.n then section which then if you hover over this status you will see is as resolved model and this is why we pass the status as resolved model because if you don't pass

this then this initialize model chunk will not be called and this rest of the part actually fails the execution you can try it out on your own also by messing around with this resolve model

field. So it's very important for you to

field. So it's very important for you to pass this. It's very important you for

pass this. It's very important you for you to pass this reason can be anything.

it it's not important that this has to be zero or anything. And the value also has to be at least dollar B. It can be just dollar B. This number is not really required. You can change this to

required. You can change this to anything. I mean any number basically or

anything. I mean any number basically or even any string I think it will work fine as well. Uh but once we have because of this then once we are inside this chunk.prototype.n

this chunk.prototype.n

and we get inside this initialize model chunk you will see that we are back in the same u function that we have started so far. Right? But the difference this

so far. Right? But the difference this time is that this chunk this chunk that we have this chunk is controlled by us.

Right? This chunk is controlled by us.

Now, so if you go ahead, you see that the value the chunk dov value which is what we specified over here. Right? This

is the reason that we specified because now we have created basically a chunk that is fully controlled by us. It's not

controlled by the end whatever like the react server component. And this

resolved model in this case is a den with a dollar b of 1337 right and we have seen that revive model again um because this will pass out as a string

you will see that inside the revive model let's say if we go into this you will see that the value is then and dollar b1337 and that means that it'll go inside this

parse model string now um it's an object right so it'll first go into this then it'll go into this string thing dollar B1337. Then we'll go into parse model

B1337. Then we'll go into parse model string and over here now we have a special meaning. Now we have a special

special meaning. Now we have a special meaning because you see that the first symbol is dollar but the second symbol is B. So B in this case

is B. So B in this case is actually this thing. And if you look at this closely, what it's doing inside React server components codebase, it's

taking the first two characters, which is this one, and then we had like a 1337 over here, and it's parsing this as an object. And for some reason, it's

object. And for some reason, it's appending it to there, right? But it

doesn't matter because we already have the workload or the, you know, the exploit inside console log like in the first thing. So you might as well just

first thing. So you might as well just pass in like a comment over here because what you're effectively getting at this point um is uh let's see is basically

response form data.get which again you have polluted because remember you control the whole chunk right so you have this response over here form data you have already passing in get remember

that it automatically changed this to the function constructor. So this form data is not actually the form data that React is expecting it to be like a legit

form data. It's a form data that will

form data. It's a form data that will invoke the function. It'll invoke uh it'll you know just create a function

like this like this. But remember

remember that this is still not remote code execution because what you are doing is that you are creating this value is basically what this value is is something like this. something like

this. You still need somebody to call this function, right? And the way this happens, if I step forward over here, you would see that we return the value

which is this then function. But you see that this then is then evaluated internally by JavaScript as a promise.

And JavaScript has a feature or in this case which is used as an exploit is that it'll automatically

run the then sequences of promise.

Right? So what's going on over here is let me actually show you. Let me see if I can get it before.

Okay, it actually executed. But but uh if we run this again, what you will see is that this is a part this is a state where you have created this value

function. Basically you have created

function. Basically you have created this function which is a payload uh which just when once this is executed this will do remote code execution but

this payload is actually called with the then function that is something we have also exploited right over here with this prototype chain with with this value sorry not the

prototype chain that is used earlier. So

this value you see that let me just show you again. So this value if we see at

you again. So this value if we see at the very end let's just keep jumping.

So here we are again, right? So you see that this value that we have is a den, right? And this den is a function which

right? And this den is a function which will when when it'll execute, it'll actually run this 1337, right? Because

if you look in into this, let's see if we can find this or not. Yeah. So if I open this function location, you see that this function points to that, right? And you can exactly see what's

right? And you can exactly see what's going on. we have this 4919

going on. we have this 4919 for some reason. I thought it should be

1337 but anyway so you see um this is then this then which points to dollar B

oh I think it's 4919 because uh parse in 16 yeah because it's b in base 16 base

in base 1637 is 4919 and that's why you get this function like this because remember that the value is resolved to

be dollar b which is read by form data.get Get binary data exploiting that

data.get Get binary data exploiting that fact. Get is already overwritten with

fact. Get is already overwritten with the fact it's the constructor of this whatever you know you are passing over here which effectively becomes a new function and you make that function but

the exploit is still not done. The

exploit is still not done because you see that until and unless somebody calls this function just like I mentioned again earlier until and unless you call this value it will not be executed but

then we finally take fact of help of the fact that in JavaScript then chains are automatically evaluated by the runtime itself you don't have to call them in

case of promises right and yeah that's that's what happens so you know if you keep going down for example let's say let me show you If we just keep

stepping down inside this, you will see that at some point we will go inside um NodeJS internals. See now we are

inside NodeJS internal async hook because now it's going to evaluate the then right. So you can even keep going

then right. So you can even keep going down over here but there is no vulnerability here because this is how it is supposed to work. You know this is all now NodeJS internal codebase but we

have still not gotten the 1337 code log right because it's yet to evaluate the final dot then which triggers the vulnerability. So if I play this now you

vulnerability. So if I play this now you will see that we get 1337 finally at the log. That is why it's critically

log. That is why it's critically important that if you name this something wrong you will see that if I let me just do this you will see that there is no vulnerability because this

final then is what triggers the code.

This is what triggers the vulnerability.

So you can have the whole codebase running but if this final then is then five or something else you will see that the code will not run because this is the console log coming from here. But if

you change this to then it'll run and the server is compromised.

Extremely sophisticated attack I would say. I mean it requires a lot of

say. I mean it requires a lot of tinkering if you are doing it brute force. Um I would say if somebody you

force. Um I would say if somebody you know somebody who even knows like how react server components works I mean they would probably have a better idea in constructing this attack but I mean

this is something that will take a lot of time to think out loud it's it's even harder to explain right even after this is there and I've spent like multiple hours myself understanding this setting

up break points that's why I created this video but this is this is very cool this is very cool so you're exploiting like two three things in a similar way.

First is like you know the ability that chunks can be pointed that is one thing and this creates like a placeholder. So

this will automatically become null this will become null. Second is the property mishap lookup, right? The fact that React just automatically just points to

any chunk and it is able to just reiterate over you know whatever property the end user is passing. You are able you are accessing the property directly. That

was that I feel is the biggest problem in this right and once you have done that once you have polluted the chain the

prototype chain to pass back to myself then I can create a chunk which I'm in fully control of and that is what this that is why this includes this response response and so on because this is the

minimum viable payload that you need in order to run the attack right because for example if I show you you know messing around let's say if you remove this form data And if you try to run this attack, you will see that you'll

get the error cannot get read properties of undefined reading get because React's internal code expects the chunk to always have this underscore form data because you are not supposed to create

this in the first place by yourself.

This then prototype then should not be possible in the first place. This

prototype then basically this allows this to create act as an independent chunk. then the value itself then this

chunk. then the value itself then this response um form data get creates this as a new it's an eval function over here effectively and then finally because the

value points to the ability to read this form data through this binary mode you're able to trigger a prefix attack yeah that's that's how the attack works and if you like this make sure you leave

a like and subscribe to the channel and I will see you in the next video very

Loading...

Loading video analysis...