JS Reverse - Analysis of Little Sweet Potato XS Environment

foreword

I heard that this is a relatively strict website, so I won’t make a deep analysis here, just analyze some environmental points.
For details, you can see the big guy’s (playing is a risk transfer)
analysis of the new version of Xiaohongshu xs (expired on 2023-05-30)

1. Analysis

Look at the wave cover
insert image description here

The encryption point is here, no skills, just follow up and see
insert image description here

window._webmsxywThis is just to pick out the whole js, make up the environment, pay attention to roughness!
l and a can be copied on the webpage, which is probably like this.
insert image description here

Let him run normally first, let’s talk about it, and try to run the results. To be on the safe side, use vm2 to run first to prevent it from detecting our node environment

const fs = require('fs')
const {
    
     VM, VMScript } = require('vm2');
// console.time('myTimer');
const sandbox = {
    
    
    require: require,
    console: console
};
const codeFile = `${
      
      __dirname}/test.js`;
const vm = new VM({
    
    
    sandbox: sandbox,
    require: {
    
    
        external: true
    }
});
const script = new VMScript(fs.readFileSync(codeFile), `${
      
      __dirname}/我正在调试的代码.js`);
debugger;
result = vm.run(script);
console.log(result)

Let’s make up some basic objects first, and then add simple agents one by one (why not use your own chain agent, it’s too long, I’m bored, just be lazy and randomize the whole thing)
insert image description here

Then you can start building, the following is a simple proxy

better.proxy = function (o, callerName){
    
    
    return new Proxy(o, {
    
    
            set(target, property, value){
    
    
                console.table([{
    
    "类型":"set-->","调用者":callerName,"属性":property,"值":value}]);
                return Reflect.set(...arguments);
            },
            get(target, property, receiver){
    
    
                if (property!=="Math"){
    
    
                    console.table([{
    
    "类型":"get<--","调用者":callerName,"属性":property,"值":target[property]}]);
                }
                return Reflect.get(...arguments);
            },
        }
    );
};

Run it, and you can see that it has detected its Symbol (Symbol.toStringTag) attribute.
insert image description here
Open the browser and take a look. This attribute has value, so we can make it up. The above two attributes cannot be obtained. Value, Symbol(nodejs.util.inspect.custom) This property is the Symbol used in Node.js for the string representation of the custom object.
insert image description here
So we are going to make up this Symbol (Symbol.toStringTag) next, and make up his prototype chain by the way (here is a simple layer, not too deep, asking, I am lazy)

var Window = function Window(){
    
    
    throw new TypeError("Illegal constructor");
};

Object.defineProperties(Window.prototype, {
    
    
    [Symbol.toStringTag]:{
    
    
        value:"Window",
        configurable:false, // 通常为 false,不允许删除属性并修改其特性。
        writable:true, // 通常为 true,允许重新赋值。
        enumerable:true // 通常为 true,可以出现在 for...in 循环或 Object.keys() 方法中。
    }
});
window.__proto__ = Window.prototype;

The above one is solved, but looking down, these three are missing again. Make up, navigator and window are the same way to make up.
insert image description here
Here is a small detail Navigator, you will encounter it later, just configure it normally first. There are two remaining methods, just add them directly. Don't give the code anymore, just fill it up honestly.
insert image description here
If you encounter this kind of situation later, everyone must have learned how to make it up. The next one will not take you to make up this toString. This is a parameter method that
insert image description here
needs to be made up after some toStrings above are done. AudioContextThe principle we adhere to is that if the object is an agent, just throw one up to him (the next operation related to the object must be done in this way) and then we have
insert image description here
reached this step. Many people have different ambiguities in this method, so
insert image description hereI will show it. , I understand that this method is on the prototype of Document, and its function is to create an element, that is, an object. As the saying goes, everything is an object, then I will return an object, and then proxy it to see if it is fetched Is it okay to have any value? Here we take canvas as an example to proxy this canvas_objobject. (What to do if it is a div or span object, create it, return the object of the object in the past, and fill in the prototype if there is a detection prototype, and you’re done. Make up what is missing, don’t waste time to make up if you don’t detect it) Through my above
insert image description here
supplement Environmental thinking, just add a little bit casually, and the data will come out. At this time, it will definitely not work if you use it (I tried it). Everyone observes that the avatar at the beginning of ey after XYW does not look like base64 encryption. If it does, use it to decrypt it. See what is
insert image description here
the base64 decrypted URL
insert image description here
. You can see that only this payload needs us to figure out how it came from, and other parameters can almost be fixed. Then we need to go back to the webpage to see how it was encrypted.
insert image description here

Put the js first, and then you can see that the first point is where our XS comes out. _ace_dcca5[0]The second point is the return value, then we need to do a hook operation on this at the first point

_ace_dcca5 = new Proxy(_ace_dcca5,{
    
    
    get: function(target, prop) {
    
    
        return target[prop];
        // 返回属性的值
    },
    set: function(target, prop, value) {
    
    
        // debugger; // 在这里设置断点或执行自定义逻辑
        target[prop] = value;
        // 设置属性的值
        try {
    
    
            if (value['_ace_4de55'] && value['_ace_4de55'].startsWith("XYW")) {
    
    
                debugger ;
            }
        } catch (error) {
    
    
        }
        return true;
    }
});

Be optimistic here. The first time was XYW.
insert image description here
The second time is XYW_, and the next time is the base64 encrypted thing we want.
insert image description here
Here, we will follow up step by step. If you keep following here, you can find that this base64 has appeared.
insert image description here
Let's follow up _ace_34d1(3, 49), don't forget to bring in the parameters.
insert image description here
Following this step, we can actually find some clues. I have already followed it. The effect is to assign _ace_66a value to _ace_936this value. It can be found that this _ace_66is not an assignment, so what does it mean? This thing is global.
insert image description here
insert image description here
The subscript 49 in this is the base64 encrypted thing we want, then we will hook it next.
insert image description here
This is the hook code, and it won't work if you directly use it for a brainless hook. Follow up.

_ace_66 = new Proxy(_ace_66,{
    
    
    get: function(target, prop) {
    
    
        return target[prop];
        // 返回属性的值
    },
  set: function(target, prop, value) {
    
    
    if (prop === "49") {
    
    
      debugger; // 在第 49 个下标被设置时设置断点或执行自定义逻辑
    }
    console.log(value)
    target[prop] = value; // 设置属性的值
    return true; // 表示设置成功
  }
});

insert image description here
This is how it looks like successfully hooked. You are familiar with this data. It is the source data that was used as base64.
insert image description here
Apart from anything else, hook it
insert image description here

_ace_66 = new Proxy(_ace_66,{
    
    
    get: function(target, prop) {
    
    
        return target[prop];
        // 返回属性的值
    },
    set: function(target, prop, value) {
    
    
        try {
    
    
            if (value && value.startsWith("cd")) {
    
    
                debugger ;
            }
        } catch (error) {
    
    }
        if (prop === "49") {
    
    
            debugger ;// 在第 49 个下标被设置时设置断点或执行自定义逻辑
        }
        console.log(value)
        target[prop] = value;
        // 设置属性的值
        return true;
        // 表示设置成功
    }
});

Follow up, and you can find that it is this _ace_936 again, and you have come to the end here. After looking at this, it _ace_dcca5is an overall situation. Good guy, continue to hook.
insert image description here
Keep going up
insert image description here
here again, boy
insert image description here

In fact, it can be found that we have followed a dead end again, then we will continue to hook and print out the value of this global variable directly. By printing the log, we can find that there is a sum. Good guy, this is the truth. It is based stackInputon stackOutputthis
encryption x1=f060e018ee22aede6cdd2393dadd54f9;x2=0|0|0|1|0|0|1|0|0|0|1|0|0|0|0;x3=18995b7979bwbgf099rbj41o4mscuki8wn6a4ep3250000419053;x4=1690954928490;.
insert image description here
Then decompose this parameter, you can get x1, x2, x3, x4, x4 is obviously the timestamp, don't worry about it, x3 is the a1 of the cookie, don't worry about it, only x1 and x2 don't know how to get it. But don’t panic, in the process of printing above, everyone must have seen x1, x2, x3, x4, that is, when _ace_66hooking, then we will go back and hook _ace_66 again to see.
insert image description here
It can be found that x1 and the length of x1 appear only after the url string appears. Experienced readers should think of md5. Indeed, this is the encrypted md5 of the url string. Now x1 is Solved, only one x2 left.
MD5 encrypted address
insert image description here
Don’t worry, the printed information is not only x1, but also x2.
insert image description here
But looking at the above, x2 has no clue, then we can go back to our own nodejs environment to see what is different from the browser

Two, verification

There is no need to go back and hook our own program. According to the hook _ace_66, we can find that they are all produced through this function. We can go back and print this function.
insert image description here
Remember to use cmd to read and filter the information, otherwise you won’t be able to see it. You
insert image description here
can find that only x2 in nodejs is different from the browser, then let’s look up and print the information, and you will find that after passing through this set->userAgentx2 The value becomes 1. This is the small detail mentioned earlier. It is impossible to do this in the browser.
insert image description here
So we need to take some measures to directly set it as unacceptable set, and then take a look

Object.defineProperty(Navigator.prototype, "userAgent", {
    
    
    value:'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36',
    writable: false
  });

Look, this becomes 0, and there is another 1 that is different from the browser, let's look up again. The browser is 0|0|0|1|0|0|1|0|0|0|1|0|0|0|0ours. 0|0|0|1|0|0|1|0|0|1|1|0|0|0|0
insert image description here
Look up, where is the different 1? Look, after calling window.Window, it becomes 1. There must be a problem. The window.Window of our nodejs is

var Window = function Window(){
    
    
    throw new TypeError("Illegal constructor");
};

insert image description here
The browser is the window. Window is the window itself, so just operate it lightly window.Window=window, the good guy is the same as the browser this time
insert image description here
, and then send the result to the request. At this point, the XS environment of Xiaohongshu is completed, and the quack kills. Remember to request dataand encrypt 一样it!
insert image description here

insert image description here
There are about 300 lines in the js environment, and some of them are useless. It's very simple, you can try it yourself.

learn from

Analysis of the new version of Xiaohongshu xs (expired on 2023-05-30)

Guess you like

Origin blog.csdn.net/qq_41866988/article/details/132058203
XS