Fscan 自实现loader免杀规避

admin 2024年4月22日03:06:36评论81 views字数 3807阅读12分41秒阅读模式
请您仔细阅读以下声明:
您在AtomsTeam查看信息以及使用AtomsTeam服务,表明您对以下内容的接受:
AtomsTeam提供程序(方法)可能带有攻击性,仅供安全研究与实验性教学之用。
用户将其信息做其他用途,由用户承担全部法律及连带责任,AtomsTeam不承担任何法律及连带责任。
与此同时,希望你能遵守如下的规范,因为这能帮助你走的更远:
1.不在没有直接或间接授权的情况下,对公网的任何设施进行安全检测。
2.在测试的过程中,不做任何可能导致业务遇到干扰的动作。
3.任何的测试中,不查看与下载任何敏感的数据。
4.发现漏洞后,第一时间通过企业SRC进行报告。
5.不在目标站点使用后门类工具,如需必要的测试,请获取目标网站官方授权,测试可通过替代的方案(如webshell替换为phpinfo页面等)。

0x01 基本思路

从github上拉取fscan源代码去除特征后编译成DLL文件

通过loader.exe加载DLL进行免杀

采用如上最朴素的操作后可暂时过部分杀软,但几天后便会报毒

于是进行一系列tricks

0x02 dll去除特征+混淆

首先拉取github上fscan的源代码

Fscan 自实现loader免杀规避

简单搜索可以看到存在的特征

"github.com/shadow1ng/fscan/Plugins""github.com/shadow1ng/fscan/common"

这里从mian.go处可以看到golang直接从github上拉取的common和plugins,所以我们更改为使用本地的包

Fscan 自实现loader免杀规避

将fscan目录下的common 和plugins文件夹移动到golang安装目录下的src目录下

Fscan 自实现loader免杀规避

然后将main.go中导入包改为如下即可

import (
"Plugins"
"common"
"fmt"
"time"
)

同时可以发现两文件夹下文件大多都含有特征

github.com/shadow1ng/fscan/common

于是通过如下python脚本分别对两个文件夹内容进行批量替换

import os
def replace_imports(dir, old, new):
  for root, dirs, files in os.walk(dir):
      for file in files:
          if file.endswith('.go'):
              file_path = os.path.join(root, file)
              with open(file_path, 'r', encoding='utf-8') as file:
                  content = file.read()

              content = content.replace(old, new)

              with open(file_path, 'w', encoding='utf-8') as file:
                  file.write(content)
directory = 'C:\Program Files\Go\bin\src\common'#要替换的文件目录

old_word = 'github.com/shadow1ng/fscan/common'#要替换的文字
new_word = 'common'#替换后的文字

replace_imports(directory, old_word ,new_word)

替换成功后可以看到已经没有原来的特征

Fscan 自实现loader免杀规避

接下来使用golang把fscan编译成dll文件,编译时需要golang具有GCO环境,需要提前配置好,配置过程可搜索Go语言的CGO环境搭建这里不再赘述,将main.go改为如下,主要是设置原来的将原来的mian函数置空,同时建立新的导出函数newmain()

Fscan 自实现loader免杀规避

package main

import "C"
/*
#include <stdlib.h>
*/
import (
"Plugins"
"common"
"fmt"
"time"
)

//export newmain
func newmain() {

start := time.Now()
var Info common.HostInfo
common.Flag(&Info)
common.Parse(&Info)
Plugins.Scan(Info)
t := time.Now().Sub(start)
fmt.Printf("[*] 扫描结束,耗时: %sn", t)
}

func main() {

}

在配置好环境后使用下列命令进行编译即可

go build -ldflags "-s -w" -buildmode=c-shared -o a.dll

同时如果配合garble食用效果更佳

gradle build -buildmode=c-shared -o a.dll

0x03 loader根据参数调用指定dll

采用loader进行直接加载dll时发现只能短暂免杀,loader过一段时间就会被检测到

于是这里采用由参数传递给loader.exe要加载的dll,通过传入的第一个参数控制要加载的dll

Fscan 自实现loader免杀规避

0x03 loader 自实现getprocess调用dll

这里我们需要loader调用dll中的newmain导出函数,忽略掉第一个传递进来的参数(因为第一个传递的参数是要加载的dll的名称)

首先自实现getprocess获取到newmain函数,代码如下

Fscan 自实现loader免杀规避

至此进行编译即可,同时要保证dll和loader的位数一致,这里go的位数设为386,c的位数x86,完整代码如下

#include <stdio.h>
#include <iostream>
#include <Windows.h>
#include <winternl.h>
#include <shellapi.h>
using namespace std;
typedef void (*DllEntryPointType)( char* argv[]);
PVOID GetPAT(HMODULE handle, LPCSTR Name) {

PBYTE pBase = (PBYTE)handle;
PIMAGE_DOS_HEADER pdosHader = (PIMAGE_DOS_HEADER)pBase;
if (pdosHader->e_magic != IMAGE_DOS_SIGNATURE) {
return NULL;
}

PIMAGE_NT_HEADERS pImageNtHeaders = (PIMAGE_NT_HEADERS)(pBase + pdosHader->e_lfanew);
if (pImageNtHeaders->Signature != IMAGE_NT_SIGNATURE) {
return NULL;
}
IMAGE_OPTIONAL_HEADER imageoptiopn = pImageNtHeaders->OptionalHeader;

PIMAGE_EXPORT_DIRECTORY pimageexport = (PIMAGE_EXPORT_DIRECTORY)(pBase + imageoptiopn.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
    PDWORD FunctionNameArray = (PDWORD)(pBase + pimageexport->AddressOfNames);
    PDWORD FunctionAddressArray = (PDWORD)(pBase + pimageexport->AddressOfFunctions);

PWORD ordinArray = (PWORD)(pBase + pimageexport->AddressOfNameOrdinals);

for (DWORD i = 0; i < pimageexport->NumberOfFunctions; i++) {
CHAR* pFunctionName = (CHAR*)(pBase + FunctionNameArray[i]);
PVOID functionAddress = (PVOID)(pBase + FunctionAddressArray[ordinArray[i]]);

if (strcmp(Name, pFunctionName) == 0) {
return functionAddress;
}
    }
return NULL;
}
int main(int argc, char* argv[]) {
    int dllArgc = argc - 2;
char** dllArgv = new char* [argc - 2];
for (int i = 0; i < argc - 1; ++i) {
dllArgv[i] = argv[i + 1];
}
dllArgv[argc - 1] = NULL;
    HMODULE PP;
PP = LoadLibrary(argv[1]);//加载dll
    if (PP != NULL) {;

DllEntryPointType newmain = (DllEntryPointType)GetPAT(PP, "newmain");
newmain(dllArgv);
FreeLibrary(PP);// 卸载 dll

}
    return 0;
}

0x04测试效果

因为第一个参数的是dll名称的传递问题,因为对于golang的不熟练,这里有一个遗留问题没有解决 ,fscan需要再原来多添加一个无用的参数进行调用,即此处3处需要多一个无实际作用的参数,即atoms可以是随机的字符

Fscan 自实现loader免杀规避

静态扫描效果

Fscan 自实现loader免杀规避

Fscan 自实现loader免杀规避

Fscan 自实现loader免杀规避

Fscan 自实现loader免杀规避

原文始发于微信公众号(flower安全):Fscan 自实现loader免杀规避

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年4月22日03:06:36
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Fscan 自实现loader免杀规避https://cn-sec.com/archives/2631778.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息