• PassUAC的简单实现(一)


    一、前言

    在我的GitHub中放了一个PassUAC的项目(https://github.com/xyddnljydd/PassUAC),其实早就放上去了,只是没有时间来写这篇博客(你打游戏的时候可不是说没有时间哦!),这里简单来讲解一下它的实现原理和防护手段。

    二、背景知识

    首先来了解一下UAC是个什么东西,其实就是用户帐户控制(User Account Control),它能干什么?不知道小伙伴有木有这样一种经历,就是在打开某些程序的时候会有弹窗(会发出 当当当 的声音),有的是蓝色的,有的为什么又是黄色的呢?

    一般情况下蓝色是受信的软件,比如windows自己的cmd

    如果是黄色的一般就是未知的软件

    那还有一个问题,为什么有的有弹出,有的又没有弹窗呢?其实是因为在编译可执行文件的时候我们能够手动的指定是否强制用户使用管理员身份执行,第二个一般没用,它的意思是如果是在能获取高权限的情况下就获取,这是给白名单软件用的。

    三、管理员和非管理员的权限差异

    左边非管理员,右边是管理员,几乎一眼看出差距,非管理员真是一个”弟弟“

    那么这些权限都是记录在哪里的呢?这些东西其实都记录在token里面了(每一个进程都有一个令牌,用来标识它的权限),简单的用windbg看一下非管理员的token结构,好了,fine!我看到了,但是我看不懂具体的含义。

    没关系,既然我们都用上了windbg就得让它自己帮我们解析一下这个token,说得好像用!token解析之后我就能看懂一样,没关系,这里简单说明一下,每一个用户都有一个sid用来标识它,当然这个用户可以从属于下面很多的组,每一个组也有一个专门的sid用来标志,我们可以查考一下msdn关于token的sid的描述(https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/security-identifiers-in-windows

    在msdn中我们可以看到S-1-5-32-544这个组就是管理员的组,为什么把他挑出来呢?因为从上图中我们可以看到S-1-5-32-544的属性是DenyOnly,意思就是被禁止了,不让用,这里也符合我们的认识,我们打开cmd就是非管理员。

    在来看看它的权限,还是有不少,那这些字段是从哪里来的,其实是+0x40的Privileges 

    我们应该如何解析呢?Present字段代表的是当前token拥有的权限(用户层无法修改,什么意思?等下讲),Enabled代表当前已经开启的权限,EnabledByDefault代表默认启动的权限是什么。将0x602880000格式化为二进制之后会发现19 23 25 33 34位都是1,说明这些权限是当前进程能够得到的也就是,其中 Enabled==0x800000,刚好是23位是1,是不是就和!token解析出来的东西对应上了

    Privs: 
     19 0x000000013 SeShutdownPrivilege               Attributes - 
     23 0x000000017 SeChangeNotifyPrivilege           Attributes - Enabled Default 
     25 0x000000019 SeUndockPrivilege                 Attributes - 
     33 0x000000021 SeIncreaseWorkingSetPrivilege     Attributes - 
     34 0x000000022 SeTimeZonePrivilege               Attributes - 
    Authentication ID:         (0,c4efc)
    

    不知道小伙伴有没有用过提权函数,类似下面这种,之前写进程注入和调试器的时候看别人都在用,所以稀里糊涂的加上去,如果不加好像真的还不行。

    HANDLE token_handle;
        //打开访问令牌
        if(!OpenProcessToken(GetCurrentProcess(),       //要修改权限的进程句柄
                             TOKEN_ALL_ACCESS,          //要对令牌进行何种操作
                             &token_handle              //访问令牌
                             ))
        {
            printf("openProcessToken error");
        }
    
        LUID luid;
        if(!LookupPrivilegeValue(NULL,                 //查看的系统,本地为NULL
                                 SE_DEBUG_NAME,        //要查看的特权名称
                                 &luid                 //用来接收标识符
                                 ))
        {
            printf("lookupPrivilegevalue error");
        }
    
        TOKEN_PRIVILEGES tkp;
        tkp.PrivilegeCount = 1;
        tkp.Privileges[0].Luid = luid;
        tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        //调整访问令牌权限
        if(!AdjustTokenPrivileges(token_handle,    //令牌句柄
                                  FALSE,           //是否禁用权限
                                  &tkp,            //新的特权的权限信息
                                  sizeof(tkp),     //特权信息大小
                                  NULL,            //用来接收特权信息当前状态的buffer
                                  NULL             //缓冲区大小
                                  ))
        {
            printf("adjust error");
        }

    这里将解释提权函数到底提的是什么权,其实从上文一直往下读,就能发现,提权提的是你当前进程有的权限,没有的就是没有,提权也提权不了,所以进程注入和调试器的时候还是要用管理员。Present就是你当前进程能得到的所以权限了,r3是无法修改的,下面就是进程所拥有的所以权限了。

    最后对比一下非管理员和管理员token的区别,不难发现它们的用户sid和用户组的sid是一模一样的,只是非管理员禁用了一些组导致了最后的权限和管理员的权限相差很大,同时也验证了上面的说法,提权函数提的是当前进程有的权限,就算是管理员有的权限不是默认打开也需要手动调用一下函数才行。

    写的也够长了,剩下的之后再说

  • 相关阅读:
    20191003 尚硅谷Spring Cloud教学视频
    20190928 On Java8 第二十三章 注解
    C# 输出结果有System.Byte[]
    linux 命令
    用go run命令启动main package中的多个文件
    Docker使用入门
    Go实现mqtt服务
    MongoDB的Go语言驱动注意点
    Go实现发送解析GET与POST请求
    用Go实现RabbitMQ消息收发
  • 原文地址:https://www.cnblogs.com/csnd/p/16675590.html
Copyright © 2020-2023  润新知