任务门&&任务段(提权)

admin 2023年10月27日02:26:43评论7 views字数 3545阅读11分49秒阅读模式

任务段

什么是任务段

在说任务门之前,我们需要先了解任务段。

在前文说到,每当出现权限切换时,就会有堆栈的切换。而随着权限的切换,CS的CPL就会跟着发生切换,那么SS也会跟着切换。那么问题来了,SS与ESP都是从哪里来的?

答案就是:从TSS中来。

那么什么是TSS呢?

TSS是一种任务状态段,在这里,我们需要先明白 一个概念,我们平时所说的线程是对于操作系统而言的,而对于CPU来说,线程就是任务。

TSS

TSS是一块内存结构,他的大小有104个字节,其内部结构具体如下:

任务门&&任务段(提权)

我们可以看到,TSS内部存放了0-3环的所有寄存器,还有各种段寄存器等。因此,当进行任务切换时,寄存器的值就会从该结构中来。

相关结构介绍:

  • Previous Task Link :存储的是上一个TSS。

其中CR3需要我们手动获取。

因此,我们可以理解成:每个任务,都有一个TSS结构。

那么我们该如何找到TSS呢?

在说到如何找到TSS之前,我们还需要补充几个概念,一个是TR寄存器,一个是TSS段描述符

TSS段描述符

TSS段描述符,位于GDT表中,是系统段描述符的一种,具体结构如下:

任务门&&任务段(提权)

由图可知:

  • 其高32位的第4和第3个字节为:e9或eb,其中Type = 9时,表明TSS段描述符未被加载到TR段寄存器中;当Type = B时,则表明TSS段描述符已经被加载到TR段寄存器中。

  • Limit = 0x68

  • 因此,我们可以得出简单的构造出,TSS段描述符= XX00e9XX`XXXX0068


TR寄存器

在前面介绍中,我们提到了TR寄存器,我们可以通过TR寄存器找到TSS,如下图:

任务门&&任务段(提权)

TR寄存器是寄存器中的一种,他的值是当操作系统启动时,从TSS段描述符加载的。

而TR.Base = TSS结构的地址。

TR.Limit = TSS大小

TR寄存器的读写

将TSS段描述符加载到TR寄存器

指令:LTR

这里需要注意以下几点:

  • 用LTR指令,仅仅改变的是TR寄存器,并没有真正的改变TSS

  • LTR只能在0环使用

  • 加载后TSS段描述符的状态位会发生改变

介绍完TR寄存器、TSS段描述符、TSS,在回到这张图中

任务门&&任务段(提权)

我们可以从图中分析,TSS如何寻找?

首先CPU会通过TR寄存器找到TSS,结构,并根据TR寄存器的Limit确定TSS的大小。而TR寄存器的值从哪来呢?TR寄存器从TSS段描述符中来。

那么完整的过程就是,先从TSS段描述符加载TR寄存器,在从TR寄存器中的BaseAddr与Limit确定TSS的位置和大小。

读TR寄存器

指令:STR

如果用STR去读的话,只读了TR的16位,也就是选择子。

如何Ring3中利用任务段中提权?

我们可以使用CALL指令,或者JMP指令。

JMP指令

用JMP访问一个代码段时,改变的是CS和EIP。

如:JMP 0x48:0x123456

如果0x48是代码段,执行后:CS->0x48 EIP->0x123456。

用JMP 去访问一个任务段的时候,如果0x48是TSS描述符,先修改TR寄存器,在用TR.Base指向的TSS中的值修改当前的寄存器。

但是,因为JMP过去之后,PTL并不会记录前一个TSS的位置,所以,我们需要再JMP之前,先记录一下当前TSS的位置。

那么如何实现呢?

代码如下:

// RenWuMenJMP.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>

DWORD iTSS[26];
DWORD ESP0[0x1000];
DWORD ESP3[0x1000];

DWORD dwESP;
DWORD dwCS;
DWORD dwCR3;
BYTE dwBack[6];
WORD dwTSS;

_declspec(naked) void Call(){
_asm{

mov dwESP,ESP
mov ax,cs
mov dwCS,eax

jmp fword ptr[dwBack]
}
}
int main(int argc, char* argv[])
{
memset(iTSS,0,sizeof(iTSS));
memset(ESP0,0,sizeof(ESP0));
memset(ESP3,0,sizeof(ESP3));

dwESP = 0;
dwCS = 0;
dwCR3 = 0;
dwTSS = 0;

_asm
{

str dwTSS
}
dwBack[4] = dwTSS;
printf("%xn",dwBack[4]);
printf("%xn",dwTSS);
iTSS[1] = (DWORD)(ESP0+0x900); // ESP
iTSS[2] = 0x10; // SS0
iTSS[8] = (DWORD)Call; // EIP
iTSS[14] = (DWORD)(ESP3+0x900); // ESP3
iTSS[18] = 0x23; // ES
iTSS[19] = 0x08; // CS
iTSS[20] = 0x10; // SS
iTSS[21] = 0x23; // DS
iTSS[22] = 0x30; // FS

printf("iTSS:%x ESP3:%x ESP0:%xn",iTSS,(ESP3+0x900),(ESP0+0x900));
printf("input cr3:");
scanf("%x",&dwCR3);
iTSS[7] = dwCR3; // cr3

char buf[6] = {0,0,0,0,0x48,0};
_asm{
jmp fword ptr [buf];
}
printf("ESP:%x CS:%xn",dwESP,dwCS);
getchar();
return 0;

}

任务门&&任务段(提权)

任务门&&任务段(提权)

CALL指令

// RenWuDuanCall.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>

DWORD iTSS[26];
DWORD ESP0[0x1000];
DWORD ESP3[0x1000];

DWORD dwESP;
DWORD dwCS;
DWORD dwCR3;

_declspec(naked) void Call(){
_asm{

mov dwESP,ESP
mov ax,cs
mov dwCS,eax

iretd
}
}
int main(int argc, char* argv[])
{
memset(iTSS,0,sizeof(iTSS));
memset(ESP0,0,sizeof(ESP0));
memset(ESP3,0,sizeof(ESP3));

dwESP = 0;
dwCS = 0;
dwCR3 = 0;

iTSS[1] = (DWORD)(ESP0+0x900); // ESP
iTSS[2] = 0x10; // SS0
iTSS[8] = (DWORD)Call; // EIP
iTSS[14] = (DWORD)(ESP3+0x900); // ESP3
iTSS[18] = 0x23; // ES
iTSS[19] = 0x08; // CS
iTSS[20] = 0x10; // SS
iTSS[21] = 0x23; // DS
iTSS[22] = 0x30; // FS

printf("iTSS:%x ESP3:%x ESP0:%xn",iTSS,(ESP3+0x900),(ESP0+0x900));
printf("input cr3:");
scanf("%x",&dwCR3);
iTSS[7] = dwCR3; // cr3

char buf[6] = {0,0,0,0,0x48,0};
_asm{
call fword ptr buf;
}
printf("ESP:%x CS:%xn",dwESP,dwCS);
getchar();
return 0;

}

任务门&&任务段(提权)

使用JMP 与 使用CALL的区别

  • JMP指令TSS中的Previous Task Link为0,系统不会帮忙自动添加上一个TSS的位置。如果是CALL的话,该位置为原来TSS的段选择子。

  • JMP 指令 NT位不变;CALL 指令 NT位被置1。而NT位 会对iret指令有影响。

  • NT = 0 IRET返回值会从堆栈中取,相当于中断返回

  • NT = 1 IRET不是中断返回,cpu会找TSS的PTL,也就是第一个值,通过他来返回。


任务门

任务门&&任务段(提权)

任务门灰色部分为保留部分,而任务门的构造如下:

  • 0000 e500 0048 0000

  • 其中0048为TSS段选择子(可修改)

  • TSS段中存放的是:XX00e9XX`XXXX0068

而任务段了解之后,那么任务门就比较简单了。我们直接说任务门执行过程。

  • 执行指令INT N

  • 通过INT指令进入IDT表,查询第N个中断门描述符

  • 通过中断门描述符,查GDT表,找到任务段描述符

  • 通过任务段描述符加载TR寄存器

  • 通过TR寄存器找到TSS

  • 通过TSS段中的值,修改寄存器并进行提权

  • IRETD返回。

这里就不放代码了,都差不多。



原文始发于微信公众号(loochSec):任务门&&任务段(提权)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年10月27日02:26:43
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   任务门&&任务段(提权)https://cn-sec.com/archives/2146522.html

发表评论

匿名网友 填写信息