红蓝对抗必备的基础技能:PEB&PPID欺骗(一)

渗透技巧 2年前 (2022) admin
584 0 0


红蓝对抗必备的基础技能:PEB&PPID欺骗(一)

一、

前言

    PEB欺骗和父进程欺骗常在一些渗透的场景中应用,这系列的文章主要是让大家了解如何进行 PPID spoofing(父进程欺骗)和 PEB欺骗,并且把这两个结合起来运用到渗透里。

红蓝对抗必备的基础技能:PEB&PPID欺骗(一)

    在此之前你必须要了解一些基础知识,以便更好的阅读下一篇文章:

  • 什么是PEB?

  • 什么是PPID spoofing?

  • 什么是Command-line spoofing?

  • P/Invoke   

  • C++ 到c#类型转换的问题


二、

正文


►►►

01. 什么是 PEB?

    进程环境块(PEB)Windows NT操作系统内部使用的数据结构,用以存储每个进程运行时数据。简单来说就是进程环境信息块,这里包含了一些进程的信息,例如进程的启动参数、进程名等。为了更好的理解PEB,你还可以利用 Windbg attach 一个cmd,然后输入 dt _peb @$peb 来查看 PEB在内存中的具体内容。

红蓝对抗必备的基础技能:PEB&PPID欺骗(一)


    如果你还想进一步了解PEB,可以自行阅读下面的链接:

https://en.wikipedia.org/wiki/Process_Environment_Block
https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb
https://422926799.github.io/posts/165f8274.html


►►►

02. 什么是PPID spoofing?

    监控父进程和子进程之间的关系是威胁检测团队检测恶意活动的常用技术,例如,如果powershell是子进程,而Microsoft Word是父进程,这种异常行为各种EDR可以很容易地检测到,这时红队可以考虑使用父进程PID欺骗作为逃避方法。我们在渗透和免杀绕过中经常会遇到这种情况,明明这行命令在cmd下不拦截的,为什么复制到别的地方运行就报毒拦截了呢?这种情况大概率EDR或者杀软进行了父进程合法性检测。

    简单来说比如某杀软,当你在mshta下运行一个powershell,那么它就会认为你这个操作可疑,就把你拦截了;这个时候,父进程欺骗就起到了作用。能让你创建进程的父进程和实际的不一样。例如刚才的例子,mshta下运行一个powershell,那么当我们运用了父进程欺骗以后,powershell的父进程就不再是mshta,而是你选择的一个相对合法的进程(有点像com组件bypass UAC),像explorer。


    如果你还想进一步了解父进程欺骗,可以自行阅读下面的链接:

https://www.4hou.com/posts/wZBg
https://www.c0bra.xyz/2020/04/23/技巧-Parent-PID-Spoofing/


►►►

03. 什么是 Command-line spoofing?

    Commandline spoofing,简单来说就是运行一个程序,并且隐藏这个程序运行的实际参数,EDR或者Sysmon记录下来的程序运行参数实际上只是一个假的参数,而记录不到实际运行的。例如,我们运行powershell start-process mspaint, 但是实际上我们的真实命令是powershell calc,因此最终我们会打开一个计算器而不是画图,但是sysmon会记录下我们打开了一个画图,过程大概就是这样。


    如果你还想进一步了解Command-line spoofing,可以自行阅读下面的链接:

https://github.com/christophetd/spoofing-office-macro
https://blog.xpnsec.com/how-to-argue-like-cobalt-strike/


►►►

04. P/Invoke

    P/Invoke是一项允许你从托管代码中访问非托管库(即DLL这样的文件)中的结构、回调以及函数的技术。大多数允许这种操作的P/Invoke API都包含在两个命名空间中,即SystemSystem.Runtime.InteropServices。因为commandline spoofing 和 ppid spoofing 涉及到很多对内存和进程里结构体的修改,而这些内存操作的函数原本并不是用C#定义的,因此你可以把 P/invoke 理解为这些函数和结构体转换到c# 的桥梁。


例如 CreateProcess 的原始定义:

BOOL CreateProcess(  LPCSTR                lpApplicationName,  LPSTR                 lpCommandLine,  LPSECURITY_ATTRIBUTES lpProcessAttributes,  LPSECURITY_ATTRIBUTES lpThreadAttributes,  BOOL                  bInheritHandles,  DWORD                 dwCreationFlags,  LPVOID                lpEnvironment,  LPCSTR                lpCurrentDirectory,  LPSTARTUPINFOA        lpStartupInfo,  LPPROCESS_INFORMATION lpProcessInformation);


而通过 P/invoke转换到C#以后就变成如下:

[DllImport("kernel32.dll")]    [return: MarshalAs(UnmanagedType.Bool)]    static extern bool CreateProcess(        string lpApplicationName,         string lpCommandLine,         IntPtr lpProcessAttributes,        IntPtr lpThreadAttributes,         bool bInheritHandles,        CreateProcessFlags dwCreationFlags,        IntPtr lpEnvironment,         string lpCurrentDirectory,         [In] ref STARTUPINFOEX lpStartupInfo,        out PROCESS_INFORMATION lpProcessInformation);


这里要注意几点:

  1. 在导入非托管函数(CreateProcess)之前,我们还使用了DllImport属性。该属性十分重要,因为它告诉了运行时的环境应该加载非托管DLL,要注意这个函数在哪个dll里并且正确地引入;

  2. 我们使用extern关键字创建了一个私有的静态CreateProcess函数,该extern修饰符用于声明在外部实现的方法。它告诉了运行时的环境,在调用这个函数的时候,应在DllImport属性中指定的DLL中寻找该函数,在此例中函数位于kernel32.dll;

  3. 注意函数的返回值;

  4. 还有每个函数的变量类型在c#中对应的类型(我们下面马上会讨论到);

  5. 为了更好得理解 P/invoke你可以自行阅读下面的链接:

https://jhalon.github.io/utilizing-syscalls-in-csharp-1/
https://posts.specterops.io/offensive-p-invoke-leveraging-the-win32-api-from-managed-code-7eef4fdef16d

►►►

05. C++/C 到c#类型转换的问题

    看到这里你一定会很疑惑,结构体是怎么转换过来的呢?我们用PEB结构来举例:

在windbg下查看 peb在内存中的偏移和结构(64位)

0:006> dt _peb @$pebntdll!_PEB   +0x000 InheritedAddressSpace : 0 ''   +0x001 ReadImageFileExecOptions : 0 ''   +0x002 BeingDebugged    : 0x1 ''   +0x003 BitField         : 0 ''   +0x003 ImageUsesLargePages : 0y0   +0x003 IsProtectedProcess : 0y0   +0x003 IsImageDynamicallyRelocated : 0y0   +0x003 SkipPatchingUser32Forwarders : 0y0   +0x003 IsPackagedProcess : 0y0   +0x003 IsAppContainer   : 0y0   +0x003 IsProtectedProcessLight : 0y0   +0x003 IsLongPathAwareProcess : 0y0   +0x004 Padding0         : [4]  ""   +0x008 Mutant           : 0xffffffff`ffffffff Void   +0x010 ImageBaseAddress : 0x00000001`00400000 Void   +0x018 Ldr              : 0x00007ffc`7a5da4c0 _PEB_LDR_DATA   +0x020 ProcessParameters : 0x00000000`00762170 _RTL_USER_PROCESS_PARAMETERS   +0x028 SubSystemData    : (null)    +0x030 ProcessHeap      : 0x00000000`00760000 Void   +0x038 FastPebLock      : 0x00007ffc`7a5da0e0 _RTL_CRITICAL_SECTION

    一般情况下我们不需要所有变量, 因此我们定义PEB结构体的时候最好的方法应该是只定义你想要的变量,并且把偏移和结构体的大小设置好。但是首先你要了解在内存中结构体存放的方式:基址 + 偏移, 基址就是指向这个结构体的内存地址,而偏移就是在这个内存地址上的位移,例如 Ldr 实际也是一个结构体,0x00007ffc`7a5da4c0 就是这个结构体的基址,而 +0x018 就是 Ldr的偏移。当然32位情况下的偏移和64位是不一样的。


    例如我们只想要得到PEB结构体中 Ldr,ProcessParameters,FastPebLock 这三个的实际内容,那么我们的结构体在 C# 中应该写成 (64位情况下)

  [StructLayout(LayoutKind.Explicit, Size = 64)]    public struct PEB    {        [FieldOffset(24)]        public IntPtr Ldr64;        [FieldOffset(32)]        public IntPtr ProcessParameters64;        [FieldOffset(56)]        public IntPtr FastPebLock64;    }

    其中 [FieldOffset(24)] 表示 Ldr64 偏移是十进制 24,也就是我们上面说的16进制 +0x018 ,当然这个变量名不重要,只是为了在看代码时更好的理解这个是64位下的偏移。[StructLayout(LayoutKind.Explicit, Size = 64)] 表示这个结构体的大小,一般情况下你只要在结构体里最后一个变量的偏移上,加上最后一个变量的实际大小,也就是结构体的大小即可。


    举个例子:最后一个变量的偏移 [FieldOffset(56)], 是56 ,然后再加上 IntPtr 的大小,就等于64,所以结构体的Size = 64就是这么来的。如果你不知道最后一个变量的实际大小。你可以用下面的代码来得到,或者直接Google也行

IntPtr test = IntPtr.Zero;Marshal.SizeOf(test);
IntPtr test = IntPtr.Zero;Marshal.SizeOf(test);0:006> dx -r1 (*((ntdll!_UNICODE_STRING *)0x7621e0))(*((ntdll!_UNICODE_STRING *)0x7621e0)) [Type: _UNICODE_STRING] [+0x000] Length : 0x1c2 [Type: unsigned short] [+0x002] MaximumLength : 0x1c4 [Type: unsigned short] [+0x008] Buffer : 0x762808 : "usrbinmintty.exe

    这个结构体只有三个变量 Length ,MaximumLength,Buffer,我们可以看到 Length , MaximumLength都是unsigned short类型,我们可以去查找c/c++到c#之间的类型转换也可以参考以下链接:

https://www.cnblogs.com/yiki/archive/2008/10/29/1321848.html


    而Buffer是一个指针类型存放着一个地址,而C# 中的指针类型一般都用IntPtr,因此我们在C#中可以这样定义这个结构体。

[StructLayout(LayoutKind.Sequential)]public struct UNICODE_STRING{    public ushort Length;    public ushort MaximumLength;    public IntPtr buffer;}

    因为我们已经把这个结构体全部变量都定义了,所以不需要再去定义这个结构体的大小了。


二、

总结

    好了到这里我相信你对我们需要的背景知识已经有一定的了解,我知道涉及到的内容有点多,而且老实讲,一开始看到的时候也会比较复杂。所以请花些时间阅读这篇文章并确保你已经理解了这些概念。


    在下一篇文章中,我们会专注于实际编写代码,利用在本文中学到的内容,实现一个有效的PEB&PPID欺骗。

►►►

Reference link

https://medium.com/@r3n_hat/parent-pid-spoofing-b0b17317168e

https://www.ired.team/offensive-security/initial-access/phishing-with-ms-office/bypassing-malicious-macro-detections-by-defeating-child-parent-process-relationships

https://www.pinvoke.net

https://blog.nviso.eu/2020/02/04/the-return-of-the-spoof-part-2-command-line-spoofing/

https://blog.xpnsec.com/how-to-argue-like-cobalt-strike/

https://www.ired.team/offensive-security/defense-evasion/masquerading-processes-in-userland-through-_peb

https://blog.christophetd.fr/building-an-office-macro-to-spoof-process-parent-and-command-line/

https://gist.github.com/xpn/1c51c2bfe19d33c169fe0431770f3020#file-argument_spoofing-cpp

https://github.com/christophetd/spoofing-office-macro

https://github.com/FuzzySecurity/Sharp-Suite/tree/master/SwampThing


下篇预告:

红蓝对抗必备的基础技能:PEB&PPID欺骗(一)


作者Github link:

https://github.com/Kara-4search/PEB-PPIDspoofing_Csharp





关注我们,获取SFSRC最新咨询

红蓝对抗必备的基础技能:PEB&PPID欺骗(一)

原文始发于微信公众号(顺丰安全应急响应中心):红蓝对抗必备的基础技能:PEB&PPID欺骗(一)

版权声明:admin 发表于 2022年10月21日 上午11:59。
转载请注明:红蓝对抗必备的基础技能:PEB&PPID欺骗(一) | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...