RtlAdjustPrivilege 一行代码提升进程权限

来源:互联网 发布:linux 父目录 编辑:程序博客网 时间:2024/05/22 06:21
前言:今天逆向一个非常实用的函数RtlAdjustPrivliege这个函数封装在NtDll.dll中(在所有DLL加载之前加载),被微软严格保密,就是说你在MSDN上查不到关于他的任何信息。先来看看这个函数的定义(Winehq给出):NTSTATUS RtlAdjustPrivilege(ULONG    Privilege,BOOLEAN Enable,BOOLEAN CurrentThread,PBOOLEAN Enabled)参数的含义:Privilege [In] Privilege index to change.                        // 所需要的权限名称,可以到MSDN查找关于Process Token & Privilege内容可以查到Enable [In] If TRUE, then enable the privilege otherwise disable.// 如果为True 就是打开相应权限,如果为False 则是关闭相应权限CurrentThread [In] If TRUE, then enable in calling thread, otherwise process.// 如果为True 则仅提升当前线程权限,否则提升整个进程的权限Enabled [Out] Whether privilege was previously enabled or disabled.// 输出原来相应权限的状态(打开 | 关闭)很多人大概没有听说过他的大名,但是相信有很多人见过进程提权的过程拷一段我写的提权上来吧BOOL ImproveProcPriv(){    HANDLE token;    //提升权限    if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&token))    {        MessageBox(NULL,"打开进程令牌失败...","错误",MB_ICONSTOP);        return FALSE;    }    TOKEN_PRIVILEGES tkp;    tkp.PrivilegeCount = 1;    ::LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tkp.Privileges[0].Luid);    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;    if(!AdjustTokenPrivileges(token,FALSE,&tkp,sizeof(tkp),NULL,NULL))    {        MessageBox(NULL,"调整令牌权限失败...","错误",MB_ICONSTOP);        return FALSE;    }    CloseHandle(token);    return TRUE;}看看吧,这个提权快要累死了...但是 如果有这个函数就不一样了,你可以只用一个函数就实现这个功能,甚至功能远多于上面的代码...通过恰当的IDE设置和必要的Defination,上面这个函数的功能你完全可以通过一行代码来实现。RtlAdjustPrivilege(SE_DEBUG_NAME,1,0,NULL);正文:下面我们看一下这个函数是怎么运行的,顺便学习下强大的IDAIDA 载入ntdll.dll (我这里载入的是 WinDBG自动下载的 Symbol里面的英文版本 可能不同的Windows版本略有不同)先把函数的原型给输入IDA 方便一下阅读,然后开始阅读汇编代码了(党和国家考验我们的时候到了)。看看Graph View 真的是很牛啊...看看函数最开头...mov     edi, edi        ; 这句话是废指令push    ebpmov     ebp, espsub     esp, 30h        ; 48个字节的子过程域Auto变量cmp     [ebp+CurrentThread], 1 ; 判断CurrentThread参数是否被指定为1mov     eax, dword_7C97B0C8mov     [ebp+var_4], eaxmov     eax, [ebp+Enabled]mov     [ebp+IsEnabled], eax ; BOOL *IsEnabled = Enabled;lea     eax, [ebp+var_28]push    eaxjz      loc_7C93378B判断是调整进程权限还是线程权限,CurrentThread == TRUEpush    0push    28h             ; TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERYpush    0FFFFFFFEh      ; GetCurrentThread()call    ZwOpenThreadTokenjmp     loc_7C929A7ACurrentThread == FALSEpush    28h             ; TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERYpush    0FFFFFFFFh      ; GetCurrentProcess()call    NtOpenProcessToken然后两个代码块同时指向这里loc_7C929A7A:           ; 很明白了吧 判断进程/线程令牌是否成功被打开test    eax, eaxjl      short loc_7C929AE4 ; 没成功则跳若 执行成功mov     eax, [ebp+Privilege]mov     [ebp+dwPrivilege], eaxmov     al, [ebp+Enable]xor     ecx, ecx        ; ecx清零neg     alpush    esimov     [ebp+NewState], 1mov     [ebp+var_C], ecxsbb     eax, eaxand     eax, 2mov     [ebp+var_8], eaxlea     eax, [ebp+ReturnLength] ; 实际返回长度push    eaxlea     eax, [ebp+OldState]push    eax             ; 旧的特权 指针push    10h             ; sizeof(TOKEN_PRIVILEGES)lea     eax, [ebp+NewState]push    eax             ; 新的特权 指针push    ecx             ; FALSE 因为上面有xor ecx,ecxpush    [ebp+TokenHandle]call    NtAdjustPrivilegesToken ; 调用 AdjustPrivilegesToken提权push    [ebp+TokenHandle]mov     esi, eax        ; eax备份call    ZwClose         ; 关闭 内核对象句柄cmp     esi, 106h       ; 判断NtAdjustPrivilege执行情况 106h = STATUS_NOT_ALL_ASSIGNEDjz      loc_7C947DF2判断是否执行成功之后,开始输出最后一个参数cmp     [ebp+OldState], 0mov     ecx, [ebp+IsEnabled]jnz     loc_7C929E99若 OldState != 0 则mov     al, [ebp+Enable]         ; 应该很明显了 把Enable变量赋给al 也就是eax最后两位若 OldState == 0 则mov     eax, [ebp+var_18]shr     eax, 1and     al, 1jmp     loc_7C929ADF这个函数大致流程就是这样。到这里差不多可以按一下传说中的F5了int __stdcall RtlAdjustPrivilege(int Privilege, char Enable, char CurrentThread, int Enabled){int result; // eax@2signed int AdjustResult; // esi@4char returnValue; // al@7int v7; // [sp+2Ch] [bp-4h]@1int IsEnabled; // [sp+4h] [bp-2Ch]@1int TokenHandle; // [sp+8h] [bp-28h]@2int dwPrivilege; // [sp+20h] [bp-10h]@4signed int NewState; // [sp+1Ch] [bp-14h]@4int v12; // [sp+24h] [bp-Ch]@4int v13; // [sp+28h] [bp-8h]@4int OldState; // [sp+Ch] [bp-24h]@4char ReturnLength; // [sp+0h] [bp-30h]@4unsigned int v16; // [sp+18h] [bp-18h]@11v7 = dword_7C97B0C8;IsEnabled = Enabled;if ( CurrentThread == 1 )    result = ZwOpenThreadToken(-2, 40, 0, &TokenHandle);else    result = NtOpenProcessToken(-1, 40, &TokenHandle);if ( result >= 0 ){    dwPrivilege = Privilege;    NewState = 1;    v12 = 0;    v13 = -(Enable != 0) & 2;    AdjustResult = NtAdjustPrivilegesToken(TokenHandle, 0, &NewState, 16, &OldState, &ReturnLength);    ZwClose(TokenHandle);    if ( AdjustResult == 262 )      AdjustResult = -1073741727;    if ( AdjustResult >= 0 )    {      if ( OldState )        returnValue = (v16 >> 1) & 1;      else        returnValue = Enable;      *(_BYTE *)IsEnabled = returnValue;    }    result = AdjustResult;}return result;}可读性好像仍然不高,看看这个.../******************************************************************************* RtlAdjustPrivilege          [NTDLL.@]** Enables or disables a privilege from the calling thread or process.** PARAMS* Privilege     [I] Privilege index to change.* Enable        [I] If TRUE, then enable the privilege otherwise disable.* CurrentThread [I] If TRUE, then enable in calling thread, otherwise process.* Enabled       [O] Whether privilege was previously enabled or disabled.** RETURNS* Success: STATUS_SUCCESS.* Failure: NTSTATUS code.** SEE ALSO* NtAdjustPrivilegesToken, NtOpenThreadToken, NtOpenProcessToken.**/NTSTATUS WINAPIRtlAdjustPrivilege(ULONG Privilege,                   BOOLEAN Enable,                   BOOLEAN CurrentThread,                   PBOOLEAN Enabled){    TOKEN_PRIVILEGES NewState;    TOKEN_PRIVILEGES OldState;    ULONG ReturnLength;    HANDLE TokenHandle;    NTSTATUS Status;    TRACE("(%d, %s, %s, %p)\n", Privilege, Enable ? "TRUE" : "FALSE",        CurrentThread ? "TRUE" : "FALSE", Enabled);    if (CurrentThread)    {        Status = NtOpenThreadToken(GetCurrentThread(),                                   TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,                                   FALSE,                                   &TokenHandle);    }    else    {        Status = NtOpenProcessToken(GetCurrentProcess(),                                    TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,                                    &TokenHandle);    }    if (!NT_SUCCESS(Status))    {        WARN("Retrieving token handle failed (Status %x)\n", Status);        return Status;    }    OldState.PrivilegeCount = 1;    NewState.PrivilegeCount = 1;    NewState.Privileges[0].Luid.LowPart = Privilege;    NewState.Privileges[0].Luid.HighPart = 0;    NewState.Privileges[0].Attributes = (Enable) ? SE_PRIVILEGE_ENABLED : 0;    Status = NtAdjustPrivilegesToken(TokenHandle,                                     FALSE,                                     &NewState,                                     sizeof(TOKEN_PRIVILEGES),                                     &OldState,                                     &ReturnLength);    NtClose (TokenHandle);    if (Status == STATUS_NOT_ALL_ASSIGNED)    {        TRACE("Failed to assign all privileges\n");        return STATUS_PRIVILEGE_NOT_HELD;    }    if (!NT_SUCCESS(Status))    {        WARN("NtAdjustPrivilegesToken() failed (Status %x)\n", Status);        return Status;    }    if (OldState.PrivilegeCount == 0)        *Enabled = Enable;    else        *Enabled = (OldState.Privileges[0].Attributes & SE_PRIVILEGE_ENABLED);    return STATUS_SUCCESS;}http://hi.baidu.com/xuzheng1111/item/e328b2f0d16d042d743c4c1f


对比了一下,其实也就比以往的调用少了一个函数。

以下新的API调用:

 TOKEN_PRIVILEGES tp;tp.PrivilegeCount = 1;int res = LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);if (!res){int err = ::GetLastError();TRACE(_T("LookupPrivilegeValue(SE_DEBUG_NAME) Fail!!")); return -1; }        typedef int (__stdcall *PRtlAdjustPrivilege)(ULONG, BOOLEAN, BOOLEAN, PBOOLEAN);    PRtlAdjustPrivilege RtlAdjustPrivilege =         (PRtlAdjustPrivilege)GetProcAddress(GetModuleHandle(_T("ntdll")), "RtlAdjustPrivilege");    while (!RtlAdjustPrivilege)    {        int err = ::GetLastError();        TRACE(_T("GetProcAddress(ntdll.dll::RtlAdjustPrivilege) Fail!!"));        return -1;    }    // 实验了下 提权成功了 但是返回值还是NULL 如果这个时候验证返回值将不准确了.    BOOLEAN enabled = 0;    res = RtlAdjustPrivilege(tp.Privileges[0].Luid.LowPart, TRUE, FALSE, &enabled);//...LowPart   = 0x14    if (!res)    {    }    int err = ::GetLastError();


以下旧的API调用:

        TOKEN_PRIVILEGES tp;tp.PrivilegeCount = 1;int res = LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);if (!res){int err = ::GetLastError();TRACE(_T("LookupPrivilegeValue(SE_DEBUG_NAME) Fail!!")); return -1; }tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;res = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);if(!res){int err = ::GetLastError();                            TRACE(_T("OpenProcessToken(TOKEN_ADJUST_PRIVILEGES) Fail!!"));return -1;  }res = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);CloseHandle(hToken);if (!res){int err = ::GetLastError();TRACE(_T("AdjustTokenPrivileges(SE_PRIVILEGE_ENABLED) Fail!!"));CloseHandle(hToken);return -1;}

经过以上提权,能使用 ProcessIdToSessionId得到另一用户的进程的sessionid,

但 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);

还是不成功,可能还需要其它权限,继续研究中。。。


0 0
原创粉丝点击