天天看點

PassUAC的簡單實作(一)

一、前言

在我的GitHub中放了一個PassUAC的項目(​​https://github.com/xyddnljydd/PassUAC​​),其實早就放上去了,隻是沒有時間來寫這篇部落格(你打遊戲的時候可不是說沒有時間哦!),這裡簡單來講解一下它的實作原理和防護手段。

二、背景知識

首先來了解一下UAC是個什麼東西,其實就是使用者帳戶控制(User Account Control),它能幹什麼?不知道小夥伴有木有這樣一種經曆,就是在打開某些程式的時候會有彈窗(會發出 當當當 的聲音),有的是藍色的,有的為什麼又是黃色的呢?

一般情況下藍色是受信的軟體,比如windows自己的cmd

PassUAC的簡單實作(一)

如果是黃色的一般就是未知的軟體

PassUAC的簡單實作(一)

那還有一個問題,為什麼有的有彈出,有的又沒有彈窗呢?其實是因為在編譯可執行檔案的時候我們能夠手動的指定是否強制使用者使用管理者身份執行,第二個一般沒用,它的意思是如果是在能擷取高權限的情況下就擷取,這是給白名單軟體用的。

PassUAC的簡單實作(一)

三、管理者和非管理者的權限差異

左邊非管理者,右邊是管理者,幾乎一眼看出差距,非管理者真是一個”弟弟“

PassUAC的簡單實作(一)

那麼這些權限都是記錄在哪裡的呢?這些東西其實都記錄在token裡面了(每一個程序都有一個令牌,用來辨別它的權限),簡單的用windbg看一下非管理者的token結構,好了,fine!我看到了,但是我看不懂具體的含義。

PassUAC的簡單實作(一)

沒關系,既然我們都用上了windbg就得讓它自己幫我們解析一下這個token,說得好像用!token解析之後我就能看懂一樣,沒關系,這裡簡單說明一下,每一個使用者都有一個sid用來辨別它,當然這個使用者可以從屬于下面很多的組,每一個組也有一個專門的sid用來标志,我們可以查考一下msdn關于token的sid的描述(​​https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/security-identifiers-in-windows​​)

PassUAC的簡單實作(一)

在msdn中我們可以看到S-1-5-32-544這個組就是管理者的組,為什麼把他挑出來呢?因為從上圖中我們可以看到S-1-5-32-544的屬性是DenyOnly,意思就是被禁止了,不讓用,這裡也符合我們的認識,我們打開cmd就是非管理者。

PassUAC的簡單實作(一)

在來看看它的權限,還是有不少,那這些字段是從哪裡來的,其實是+0x40的Privileges 

PassUAC的簡單實作(一)
PassUAC的簡單實作(一)

我們應該如何解析呢?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)      
PassUAC的簡單實作(一)

不知道小夥伴有沒有用過提權函數,類似下面這種,之前寫程序注入和調試器的時候看别人都在用,是以稀裡糊塗的加上去,如果不加好像真的還不行。

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是無法修改的,下面就是程序所擁有的是以權限了。

PassUAC的簡單實作(一)

最後對比一下非管理者和管理者token的差別,不難發現它們的使用者sid和使用者組的sid是一模一樣的,隻是非管理者禁用了一些組導緻了最後的權限和管理者的權限相差很大,同時也驗證了上面的說法,提權函數提的是目前程序有的權限,就算是管理者有的權限不是預設打開也需要手動調用一下函數才行。

PassUAC的簡單實作(一)