这是Windows kernel exploit系列的第二部分 从UAF开始 因为之前有过pwn的经验 所以堆UAF的原理也是清楚 所以我也就直接从漏洞开始了
其实UAF跟其名字一样 释放后重利用 在内存块释放之后 其对应的指针没有被设置为NULL 但是在下次使用之前 有代码堆这块内存进行了修改 比如将内存指针修改为dangling pointer 从而执行我们的shellcode
我们来分析几个对堆块操作的函数
首先这个函数是分配一个非分页的池块(内存中的池分配机制是按块来的 假如每个块的大小是64bytes 需要分配70bytes的内存 就会分配两个块 虽然比较浪费内存 但速度快)并用'A'填充 并将池块编辑为"Hack"(可以在IDA中看到)
AllocateUaFObject
FreeUaFObject
这个函数是用来释放池块 图中是安全版本 所以将g_UseAfterFreeObjectNonPagedPool = NULL 而漏洞驱动中 未将此置为NULL
UseUaFObject
其实函数名已经很明显了 就是使用UAF对象 即就是使用刚才的g_UseAfterFreeObject
AllocateFakeObject
该函数分配fake对象 将对象分配到UAF对象所在的位置
我们对上面的函数分别下断点 来观察利用过程
然后执行exp WinDbg断下 然后单步执行到这里查看池的状态 已经被'A'占用
再次g 运行到第二个断点 单步执行到这里再次查看池的状态 我们的"HACK"已经是Free了 但没有置为NULL
再次g 运行到第三个断点处 将对象分配到UAF对象所在的位置
再次g 此时已经完成堆喷射 执行回调函数 我们再次观察池 可见callbake地址被覆盖为shellcode的地址 自此 我们就完成了利用
我们就根据上面的流程来利用编写exp 我们只要将自己申请的池块大小跟内核中池块大小相同 就有可能会利用它 只伪造一块的话肯定是不够的 我们可以通过在0day2上面学到的“堆喷射”技术 多伪造一些堆块来增加概率 然后释放的时候指向我们的shellcode即可利用漏洞
对于IO控制码 我们可以用IDA来查看IrpDeviceIoCtlHandler函数
最后我们发现利用成功了
其实在分析过程中我已经提到过就是补丁版本用完后将g_UseAfterFreeObjectNonPagedPool置为NULL了 导致无法利用
这次的UAF漏洞是在我看完漏洞战争之后的 这个也简单 容易理解
NTSTATUS AllocateUaFObjectNonPagedPool(VOID){
NTSTATUS Status
=
STATUS_UNSUCCESSFUL;
PUSE_AFTER_FREE_NON_PAGED_POOL UseAfterFree
=
NULL;
PAGED_CODE();
__try
{
DbgPrint(
"[+] Allocating UaF Object\n"
);
/
/
Allocate Pool chunk
UseAfterFree
=
(PUSE_AFTER_FREE_NON_PAGED_POOL)ExAllocatePoolWithTag(
NonPagedPool,
sizeof(USE_AFTER_FREE_NON_PAGED_POOL),
(ULONG)POOL_TAG
);
if
(!UseAfterFree)
{
/
/
Unable to allocate Pool chunk
DbgPrint(
"[-] Unable to allocate Pool chunk\n"
);
Status
=
STATUS_NO_MEMORY;
return
Status;
}
else
{
DbgPrint(
"[+] Pool Tag: %s\n"
, STRINGIFY(POOL_TAG));
DbgPrint(
"[+] Pool Type: %s\n"
, STRINGIFY(NonPagedPool));
DbgPrint(
"[+] Pool Size: 0x%X\n"
, sizeof(USE_AFTER_FREE_NON_PAGED_POOL));
DbgPrint(
"[+] Pool Chunk: 0x%p\n"
, UseAfterFree);
}
/
/
Fill the
buffer
with ASCII
'A'
RtlFillMemory((PVOID)UseAfterFree
-
>
Buffer
, sizeof(UseAfterFree
-
>
Buffer
),
0x41
);
/
/
Null terminate the char
buffer
UseAfterFree
-
>
Buffer
[sizeof(UseAfterFree
-
>
Buffer
)
-
1
]
=
'\0'
;
/
/
Set
the
object
Callback function
UseAfterFree
-
>Callback
=
&UaFObjectCallbackNonPagedPool;
/
/
Assign the address of UseAfterFree to a
global
variable
g_UseAfterFreeObjectNonPagedPool
=
UseAfterFree;
DbgPrint(
"[+] UseAfterFree Object: 0x%p\n"
, UseAfterFree);
DbgPrint(
"[+] g_UseAfterFreeObjectNonPagedPool: 0x%p\n"
, g_UseAfterFreeObjectNonPagedPool);
DbgPrint(
"[+] UseAfterFree->Callback: 0x%p\n"
, UseAfterFree
-
>Callback);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Status
=
GetExceptionCode();
DbgPrint(
"[-] Exception Code: 0x%X\n"
, Status);
}
return
Status;
}
NTSTATUS AllocateUaFObjectNonPagedPool(VOID){
NTSTATUS Status
=
STATUS_UNSUCCESSFUL;
PUSE_AFTER_FREE_NON_PAGED_POOL UseAfterFree
=
NULL;
PAGED_CODE();
__try
{
DbgPrint(
"[+] Allocating UaF Object\n"
);
/
/
Allocate Pool chunk
UseAfterFree
=
(PUSE_AFTER_FREE_NON_PAGED_POOL)ExAllocatePoolWithTag(
NonPagedPool,
sizeof(USE_AFTER_FREE_NON_PAGED_POOL),
(ULONG)POOL_TAG
);
if
(!UseAfterFree)
{
/
/
Unable to allocate Pool chunk
DbgPrint(
"[-] Unable to allocate Pool chunk\n"
);
Status
=
STATUS_NO_MEMORY;
return
Status;
}
else
{
DbgPrint(
"[+] Pool Tag: %s\n"
, STRINGIFY(POOL_TAG));
DbgPrint(
"[+] Pool Type: %s\n"
, STRINGIFY(NonPagedPool));
DbgPrint(
"[+] Pool Size: 0x%X\n"
, sizeof(USE_AFTER_FREE_NON_PAGED_POOL));
DbgPrint(
"[+] Pool Chunk: 0x%p\n"
, UseAfterFree);
}
/
/
Fill the
buffer
with ASCII
'A'
RtlFillMemory((PVOID)UseAfterFree
-
>
Buffer
, sizeof(UseAfterFree
-
>
Buffer
),
0x41
);
/
/
Null terminate the char
buffer
UseAfterFree
-
>
Buffer
[sizeof(UseAfterFree
-
>
Buffer
)
-
1
]
=
'\0'
;
/
/
Set
the
object
Callback function
UseAfterFree
-
>Callback
=
&UaFObjectCallbackNonPagedPool;
/
/
Assign the address of UseAfterFree to a
global
variable
g_UseAfterFreeObjectNonPagedPool
=
UseAfterFree;
DbgPrint(
"[+] UseAfterFree Object: 0x%p\n"
, UseAfterFree);
DbgPrint(
"[+] g_UseAfterFreeObjectNonPagedPool: 0x%p\n"
, g_UseAfterFreeObjectNonPagedPool);
DbgPrint(
"[+] UseAfterFree->Callback: 0x%p\n"
, UseAfterFree
-
>Callback);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Status
=
GetExceptionCode();
DbgPrint(
"[-] Exception Code: 0x%X\n"
, Status);
}
return
Status;
}
FreeUaFObjectNonPagedPool(
VOID
)
{
NTSTATUS Status
=
STATUS_UNSUCCESSFUL;
PAGED_CODE();
__try
{
if
(g_UseAfterFreeObjectNonPagedPool)
{
DbgPrint(
"[+] Freeing UaF Object\n"
);
DbgPrint(
"[+] Pool Tag: %s\n"
, STRINGIFY(POOL_TAG));
DbgPrint(
"[+] Pool Chunk: 0x%p\n"
, g_UseAfterFreeObjectNonPagedPool);
/
/
/
/
Secure Note: This
is
secure because the developer
is
setting
/
/
'g_UseAfterFreeObjectNonPagedPool'
to NULL once the Pool chunk
is
being freed
/
/
ExFreePoolWithTag((PVOID)g_UseAfterFreeObjectNonPagedPool, (ULONG)POOL_TAG);
/
/
/
/
Set
to NULL to avoid dangling pointer
/
/
g_UseAfterFreeObjectNonPagedPool
=
NULL;
/
/
漏洞版本未置NULL
/
/
/
/
Vulnerability Note: This
is
a vanilla Use After Free vulnerability
/
/
because the developer
is
not
setting
'g_UseAfterFreeObjectNonPagedPool'
to NULL.
/
/
Hence, g_UseAfterFreeObjectNonPagedPool still holds the reference to stale pointer
/
/
(dangling pointer)
/
/
ExFreePoolWithTag((PVOID)g_UseAfterFreeObjectNonPagedPool, (ULONG)POOL_TAG);
Status
=
STATUS_SUCCESS;
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Status
=
GetExceptionCode();
DbgPrint(
"[-] Exception Code: 0x%X\n"
, Status);
}
return
Status;
}
FreeUaFObjectNonPagedPool(
VOID
)
{
NTSTATUS Status
=
STATUS_UNSUCCESSFUL;
PAGED_CODE();
__try
{
if
(g_UseAfterFreeObjectNonPagedPool)
{
DbgPrint(
"[+] Freeing UaF Object\n"
);
DbgPrint(
"[+] Pool Tag: %s\n"
, STRINGIFY(POOL_TAG));
DbgPrint(
"[+] Pool Chunk: 0x%p\n"
, g_UseAfterFreeObjectNonPagedPool);
/
/
/
/
Secure Note: This
is
secure because the developer
is
setting
/
/
'g_UseAfterFreeObjectNonPagedPool'
to NULL once the Pool chunk
is
being freed
/
/
ExFreePoolWithTag((PVOID)g_UseAfterFreeObjectNonPagedPool, (ULONG)POOL_TAG);
/
/
/
/
Set
to NULL to avoid dangling pointer
/
/
g_UseAfterFreeObjectNonPagedPool
=
NULL;
/
/
漏洞版本未置NULL
/
/
/
/
Vulnerability Note: This
is
a vanilla Use After Free vulnerability
/
/
because the developer
is
not
setting
'g_UseAfterFreeObjectNonPagedPool'
to NULL.
/
/
Hence, g_UseAfterFreeObjectNonPagedPool still holds the reference to stale pointer
/
/
(dangling pointer)
/
/
ExFreePoolWithTag((PVOID)g_UseAfterFreeObjectNonPagedPool, (ULONG)POOL_TAG);
Status
=
STATUS_SUCCESS;
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Status
=
GetExceptionCode();
DbgPrint(
"[-] Exception Code: 0x%X\n"
, Status);
}
return
Status;
}
NTSTATUS UseUaFObjectNonPagedPool(VOID){
NTSTATUS Status
=
STATUS_UNSUCCESSFUL;
PAGED_CODE();
__try
{
if
(g_UseAfterFreeObjectNonPagedPool)
{
DbgPrint(
"[+] Using UaF Object\n"
);
DbgPrint(
"[+] g_UseAfterFreeObjectNonPagedPool: 0x%p\n"
, g_UseAfterFreeObjectNonPagedPool);
DbgPrint(
"[+] g_UseAfterFreeObjectNonPagedPool->Callback: 0x%p\n"
, g_UseAfterFreeObjectNonPagedPool
-
>Callback);
DbgPrint(
"[+] Calling Callback\n"
);
if
(g_UseAfterFreeObjectNonPagedPool
-
>Callback)
{
g_UseAfterFreeObjectNonPagedPool
-
>Callback();
}
Status
=
STATUS_SUCCESS;
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Status
=
GetExceptionCode();
DbgPrint(
"[-] Exception Code: 0x%X\n"
, Status);
}
return
Status;
}
NTSTATUS UseUaFObjectNonPagedPool(VOID){
NTSTATUS Status
=
STATUS_UNSUCCESSFUL;
PAGED_CODE();
__try
{
if
(g_UseAfterFreeObjectNonPagedPool)
{
DbgPrint(
"[+] Using UaF Object\n"
);
DbgPrint(
"[+] g_UseAfterFreeObjectNonPagedPool: 0x%p\n"
, g_UseAfterFreeObjectNonPagedPool);
DbgPrint(
"[+] g_UseAfterFreeObjectNonPagedPool->Callback: 0x%p\n"
, g_UseAfterFreeObjectNonPagedPool
-
>Callback);
DbgPrint(
"[+] Calling Callback\n"
);
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2020-10-26 20:21
被Ring3编辑
,原因: