Telemetry and what to do about it.
#1
Preamble

Whether you're on Windows or Mac, it seems to be a given these days that you will be subjected to telemetry to some degree or another. Indeed even Ubuntu is known to run operations that collect user and system usage information to varying degrees. Whether you're a hacker or a regular user; having your privacy compromised in such a way is something i strongly feel the end-user should be in control over.

Fortunately there are a number of excellent Linux Distributions that allow their users this level of control.

Windows

As you know Windows isn't a *Nix Distro, but when it comes to what i consider to be privacy violations Microsoft is the worst offender. Therefore Windows Telemetry and measures to hopefully counter-act it is what we will be discussing going forward.

There are a lot of things i dislike about Windows, but one of the more insidious aspects of the OS is what basically amounts to data mining user and systems usage information. There was a Batch script on Github that used to have all the telemetry associated domains in it that automated the process of blacklisting those domains with the Windows firewall. However it didn't take long for Microsoft to wise up to this, so now it will just ignore the rule set when applied IIRC. The best way to block those domains is to make a blacklist in your router. However i want to try something else and have it automated.

Bits, bytes, scripts, and code.

To that end i wrote a PAC Script, on the off chance you never heard of a PAC script, it's a Proxy Auto Configuration script and it's written in JS, what's neat is that it allows you to specify very specific proxy rules for very specific domains/hosts. The OS will read the script and apply the proxy rules you defined there. In the case of Windows it used to be that you could just write a PAC script and point to it's location on your HDD in your Network Settings. Since Win10 however you are obliged to serve the script over HTTP, what's more, you need to make sure legacy proxy config support is enabled. We do that by creating the proper subkey in the Windows registry.


Below is my PowerShell implementation of the HTTP server and registry operations.

Code:
# PowerShell RegEdit and HTTP Server
$registryPath = "HKLM\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\"

$Name = "EnableLegacyAutoProxyFeatures"

$value = "1"

# Check to see if an entry exists and set value
# If it doesn't create the appropriate subkey
if(!(Test-Path $registryPath)) {
    New-Item -Path $registryPath -Force | Out-Null
    New-ItemProperty -Path $registryPath -Name $name -Value $value `

    -PropertyType DWORD -Force | Out-Null
    }

else {
    New-ItemProperty -Path $registryPath -Name $name -Value $value `
    -PropertyType DWORD -Force | Out-Null
    }

# Http Server
$http = [System.Net.HttpListener]::new()

# Listen at 8080
$http.Prefixes.Add("http://localhost:8080/")

# Start the Http Server
$http.Start()


# Confirm
if ($http.IsListening) {
    write-host " HTTP Server Listening " -f 'gre'
    write-host " Please direct Windows Auto Proxy Config to http://127.0.0.1/:8080 " -f 'gre'
    }


# Server Loop
while ($http.IsListening) {

    $context = $http.GetContext()

    if ($context.Request.HttpMethod -eq 'GET' -and $context.Request.RawUrl -eq '/') {

        # Log to terminal
        write-host "$($context.Request.UserHostAddress)  =>  $($context.Request.Url)" -f 'gre'

# Get proxy.pac data
        [string]$data = Get-Content "C:\some\path\proxy.pac" -Raw
       
        # Field the request
        $buffer = [System.Text.Encoding]::UTF8.GetBytes($data)
        $context.Response.ContentLength64 = $buffer.Length
        $context.Response.OutputStream.Write($buffer, 0, $buffer.Length) # Data stream
        $context.Response.OutputStream.Close() # Close
   
    }

I'm no PS wizard but i think this should do it, haven't had a chance to test this yet so if anything stands out to you please let me know and i will remedy any issues. Same goes for the PAC Script i have below.

Code:
<script type="application/x-ns-proxy-autoconfig">

//Special PAC functions:
// Hostname:
// isPlainHostName(host)
// dnsDomainIs(host, domain)
// localHostOrDomainIs(host, hostdom)
// isResolvable(host)
// isInNet(host, pattern, mask)
// Utility:
// dnsResolve(host)
// myIpAddress()
// dnsDomainLevels(host)
// URL:
// shExpMatch(str, shexp)
// Time:
// weekdayRange(wd1, wd2, gmt)
// dateRange(...)
// timeRange(...)

// Declare vars
var normal = "DIRECT";
var torsocks = "PROXY 127.0.0.1:9050"
var blackhole = "PROXY 127.0.0.1:55555"; 

function FindProxyForURL(url, host) {
   
    if (dnsResolve(host) || shExpMatch(host, '*.local')
|| isInNet(dnsResolve(host), '10.0.0.0', '255.0.0.0')
|| isInNet(dnsResolve(host), '172.16.0.0',  '255.240.0.0')
|| isInNet(dnsResolve(host), '192.168.0.0',  '255.255.0.0')
|| isInNet(dnsResolve(host), '127.0.0.0', '255.255.255.0')) {
    return normal;
}

    if (shExpMatch((url, "*oca.telemetry.microsoft.com*")
|| shExpMatch(url, "*telecommand.telemetry.microsoft.com*")
|| shExpMatch(url, "*oca.telemetry.microsoft.com.nsatc.net*")
|| shExpMatch(url, "*a-0001.a-msedge.net*")
|| shExpMatch(url, "*a-0002.a-msedge.net*")
|| shExpMatch(url, "*a-0003.a-msedge.net*")
|| shExpMatch(url, "*a-0004.a-msedge.net*")
|| shExpMatch(url, "*a-0004.a-msedge.net*")
|| shExpMatch(url, "*a-0005.a-msedge.net*")
|| shExpMatch(url, "*a-0006.a-msedge.net*")
|| shExpMatch(url, "*a-0006.a-msedge.net*")
|| shExpMatch(url, "*a-0007.a-msedge.net*")
|| shExpMatch(url, "*a-0008.a-msedge.net*")
|| shExpMatch(url, "*a-0009.a-msedge.net*")
|| shExpMatch(url, "*i1.services.social.microsoft.com")
|| shExpMatch(url, "*telecommand.telemetry.microsoft.com.nsatc.net*")
|| shExpMatch(url, "*sqm.df.telemetry.microsoft.com*")
|| shExpMatch(url, "telemetry.appex.bing.net*")
|| shExpMatch(url, "*settings-sandbox.data.microsoft.com*")
|| shExpMatch(url, "*pre.footprintpredict.com")
|| shExpMatch(url, "*aidps.atdmt.com*")
|| shExpMatch(url, "*aka-cdn-ns.adtech.de*")
|| shExpMatch(url, "*a-msedge.net*")
|| shExpMatch(url, "*b.rad.msn.com*")
|| shExpMatch(url, "*az361816.vo.msecnd.net*")
|| shExpMatch(url, "*b.ads1.msn.com*")
|| shExpMatch(url, "*b.ads2.msads.net*")
|| shExpMatch(url, "*watson.telemetry.microsoft.com*")
|| shExpMatch(url, "*wes.df.telemetry.microsoft.com*")
|| shExpMatch(url, "*bs.serving-sys.com*")
|| shExpMatch(url, "*redir.metaservices.microsoft.com*")
|| shExpMatch(url, "*reports.wes.df.telemetry.microsoft.com*")
|| shExpMatch(url, "*cs1.wpc.v0cdn.net*")
|| shExpMatch(url, "*corpext.msitadfs.glbdns2.microsoft.comt*")
|| shExpMatch(url, "*df.telemetry.microsoft.com*")
|| shExpMatch(url, "*services.wes.df.telemetry.microsoft.com*")
|| shExpMatch(url, "*watson.telemetry.microsoft.com.nsatc.net*")
|| shExpMatch(url, "*sqm.telemetry.microsoft.com*")
|| shExpMatch(url, "*sqm.telemetry.microsoft.com.nsatc.net*")
|| shExpMatch(url, "*watson.ppe.telemetry.microsoft.com*")
|| shExpMatch(url, "*telemetry.microsoft.com*")
|| shExpMatch(url, "*telemetry.urs.microsoft.com*")
|| shExpMatch(url, "*survey.watson.microsoft.com*")
|| shExpMatch(url, "*watson.live.com*")
|| shExpMatch(url, "*vortex-sandbox.data.microsoft.com*")
|| shExpMatch(url, "*settings-win.data.microsoft.com*")
|| shExpMatch(url, "*watson.microsoft.com*")
|| shExpMatch(url, "*db3aqu.atdmt.com*")
|| shExpMatch(url, "*diagnostics.support.microsoft.com*")
|| shExpMatch(url, "*statsfe2.update.microsoft.com.akadns.net*")
|| shExpMatch(url, "*fe2.update.microsoft.com.akadns.net*")
|| shExpMatch(url, "*schemas.microsoft.akadns.net*")
|| shExpMatch(url, "*feedback.microsoft-hohm.com*")
|| shExpMatch(url, "*feedback.search.microsoft.com*")
|| shExpMatch(url, "*statsfe1.ws.microsoft.com*")
|| shExpMatch(url, "*statsfe1.ws.microsoft.com*")
|| shExpMatch(url, "*c.atdmt.com*")) {
// More to be added, maybe check against
// _dnsDomainIs as well
    return blackhole;
   
    } else {
    return torsocks;
    }
}
</script>

As you can tell i have all the telemetry related domains null routed. I also kept the list of special PAC functions there as a reminder and to get you guys' opinion on which one seems the most appropriate to use. Futhermore every other connection is going to be routed through the Tor network.

In order to do so i will be adding operations to my powershell script to invoke a special Tor binary. This binary doesn't come with the browser bundle so we don't need to have the Tor Browser running in the background wasting resources.

The binary in question:
Code:
https://dist.torproject.org/torbrowser/9.0.4/tor-win64-0.4.2.5.zip
https://dist.torproject.org/torbrowser/9.0.4/tor-win64-0.4.2.5.zip.asc

We might also want to get rid of any updates that are related to telemetry by running the `wusa` utility from a Batch file, which could probably also be done in powershell but whatever.

Code:
@ECHO OFF
SETLOCAL

REM #-KILL IT WITH FIRE-#

echo Uninstalling updates relevant to telemetry ops
echo Delete KB2902907 (Microsoft Security Essentials)
start "title" /b /wait wusa.exe /kb:2902907 /uninstall /quiet /norestart

echo Delete KB3022345 (telemetry)
start "title" /b /wait wusa.exe /kb:3022345 /uninstall /quiet /norestart

echo Delete KB3068708 (telemetry)
start "title" /b /wait wusa.exe /kb:3068708 /uninstall /quiet /norestart

echo Delete KB3080149 (Telemetry)
start "title" /b /wait wusa.exe /kb:3080149 /uninstall /quiet /norestart


Finally to make sure the HTTP server and Tor binary start at logon we can add a scheduled task with these commands.

Code:
schtasks /create /tn 'StageProxyOps' /tr powershell %USERPROFILE%\HTTP-Proxy-Server.ps1 /sc onstart /ru System

But since we're on the topic of persistence allow me to indulge for a second. I happened upon C++ source that came from Microsoft where they were talking about starting a sub process from a service. So I took the liberty to cut out any irrelevant bits and substitute the appropriate values, where needed, and now, should we want to we could probably just compile this and start our PowerShell script as a subprocess from the service after installing it with the `sc` utility.

Code:
/*******************************************************************/

#define _WIN32_WINNT 0x0501

#include <windows.h>

/*******************************************************************/

// See http://msdn.microsoft.com/en-us/library/windows/desktop/aa379608%28v=vs.85%29.aspx
// "Starting an Interactive Client Process in C++"

BOOL AddAceToWindowStation(HWINSTA hwinsta, PSID psid);
BOOL AddAceToDesktop(HDESK hdesk, PSID psid);
BOOL GetLogonSID (HANDLE hToken, PSID *ppsid);
VOID FreeLogonSID (PSID *ppsid);
BOOL StartInteractiveClientProcess (
    LPTSTR lpszUsername,    // client to log on
    LPTSTR lpszDomain,      // domain of client's account
    LPTSTR lpszPassword,    // client's password
    LPTSTR lpCommandLine    // command line to execute
);

/*******************************************************************/

const wchar_t displayname[] = L"Service to CreateProcessWithLogonW";
const wchar_t servicename[] = L"StageProxyOps";

DWORD dwWin32ExitCode = 0, dwServiceSpecificExitCode = 0;

/*******************************************************************/

#define EXCEPTION_USER 0xE0000000
#define FACILITY_USER_DEMOSVC 0x0001
#define EXCEPTION_USER_LINENUMBER (EXCEPTION_USER | (FACILITY_USER_DEMOSVC << 16))

HANDLE eventloghandle;

/*******************************************************************/

wchar_t subprocess_username[] = L"SubProc_User";
wchar_t subprocess_domain[] = L"scms";
wchar_t subprocess_password[] = L"SubProcUser_Pass";
wchar_t subprocess_command[] = L"cmd.exe /c powershell %USERPROFILE%\HTTP-Proxy-Server.ps1";

void proxysvc(void)
{
    if (!StartInteractiveClientProcess(subprocess_username, subprocess_domain, subprocess_password, subprocess_command))
    {
        const wchar_t * strings[] = {L"Creating subprocess failed."};
        DWORD err = GetLastError();
        ReportEventW(eventloghandle,
                    EVENTLOG_ERROR_TYPE,
                    0,
                    2,
                    NULL,
                    _countof(strings),
                    sizeof(err),
                    strings,
                    &err);
        return;
    }

    {
        const wchar_t * strings[] = {L"Creating subprocess succeeded!"};
        ReportEventW(eventloghandle,
                    EVENTLOG_INFORMATION_TYPE,
                    0,
                    1,
                    NULL,
                    _countof(strings),
                    0,
                    strings,
                    NULL);
    }

    return;
}

/*******************************************************************/

CRITICAL_SECTION service_section;

SERVICE_STATUS service_status;                    // Protected by service_section

SERVICE_STATUS_HANDLE service_handle = 0;          // Constant once set, so can be used from any thread

static DWORD WINAPI ServiceHandlerEx(DWORD control, DWORD eventtype, LPVOID lpEventData, LPVOID lpContext)
{
    if (control == SERVICE_CONTROL_INTERROGATE)
    {
        EnterCriticalSection(&service_section);
        if (service_status.dwCurrentState != SERVICE_STOPPED)
        {
        SetServiceStatus(service_handle, &service_status);
        }
        LeaveCriticalSection(&service_section);
        return NO_ERROR;
    }

    return ERROR_CALL_NOT_IMPLEMENTED;
}

static VOID WINAPI ServiceMain(DWORD argc, LPTSTR * argv)
{
    SERVICE_STATUS status;

    EnterCriticalSection(&service_section);

    service_handle = RegisterServiceCtrlHandlerEx(argv[0], ServiceHandlerEx, NULL);
    if (!service_handle) RaiseException(EXCEPTION_USER_LINENUMBER | __LINE__, EXCEPTION_NONCONTINUABLE, 0, NULL);

    service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    service_status.dwCurrentState = SERVICE_RUNNING;
    service_status.dwControlsAccepted = 0;
    service_status.dwWin32ExitCode = STILL_ACTIVE;
    service_status.dwServiceSpecificExitCode = 0;
    service_status.dwCheckPoint = 0;
    service_status.dwWaitHint = 500;

    SetServiceStatus(service_handle, &service_status);

    LeaveCriticalSection(&service_section);

    /************** service main function **************/

    {
        const wchar_t * strings[] = {L"Service started!"};
        ReportEventW(eventloghandle,
                    EVENTLOG_INFORMATION_TYPE,
                    0,
                    2,
                    NULL,
                    _countof(strings),
                    0,
                    strings,
                    NULL);
    }

    proxysvc();

    /************** service shutdown **************/

    EnterCriticalSection(&service_section);   

    status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    status.dwCurrentState = service_status.dwCurrentState = SERVICE_STOPPED;
    status.dwControlsAccepted = 0;
    status.dwCheckPoint = 0;
    status.dwWaitHint = 500;
    status.dwWin32ExitCode = dwWin32ExitCode;
    status.dwServiceSpecificExitCode = dwServiceSpecificExitCode;

    LeaveCriticalSection(&service_section);

    SetServiceStatus(service_handle, &status);      /* NB: SetServiceStatus does not return here if successful,
                                                    so any code after this point will not normally run. */
    return;
}

int wmain(int argc, wchar_t * argv[])
{
    const static SERVICE_TABLE_ENTRY servicetable[2] = {
        {(wchar_t *)servicename, ServiceMain},
        {NULL, NULL}
    };

    InitializeCriticalSection(&service_section);

    eventloghandle = RegisterEventSource(NULL, displayname);
    if (!eventloghandle) return GetLastError();

    {
        const wchar_t * strings[] = {L"Executable started!"};
        ReportEventW(eventloghandle,
                    EVENTLOG_INFORMATION_TYPE,
                    0,
                    2,
                    NULL,
                    _countof(strings),
                    0,
                    strings,
                    NULL);
    }

    if (StartServiceCtrlDispatcher(servicetable)) return 0;
    return GetLastError();
}



Anyway, that's turbo over kill but i thought it was pretty neat so i figured i'd include it. To conclude, feel free to point out that i'm a big dumb if and where i went astray with any of this, that last one was included for the lulz but i'll happily receive constructive criticism regarding that as well.
Reply
#2
I expanded the PowerShell script a bit, i've added operations to download and unzip the Tor binary. Now obviously if we want to start this at boot i need to add operations that make sure the binary isn't downloaded every time the script starts. I could probably check the directory for the presence of the binary or write something to the registry to check against.



I also need to add some logic that invokes the binary. But i figured i'd post what i have written so far in this thread as well. If anyone feels like contributing some code, or pointing out some things that need to be improved i am all ears.



Code:
#Function to unzip the Tor binary
Function unzip {
    param (
        [string]$archiveFilePath,
        [string]$destinationPath
    )

    if ($archiveFilePath -notlike '?:\*') {
        $archiveFilePath = [System.IO.Path]::Combine($PWD, $archiveFilePath)
    }

    if ($destinationPath -notlike '?:\*') {
        $destinationPath = [System.IO.Path]::Combine($PWD, $destinationPath)
    }

    Add-Type -AssemblyName System.IO.Compression
    Add-Type -AssemblyName System.IO.Compression.FileSystem

    $archiveFile = [System.IO.File]::Open($archiveFilePath, [System.IO.FileMode]::Open)
    $archive = [System.IO.Compression.ZipArchive]::new($archiveFile)

    if (Test-Path $destinationPath) {
        foreach ($item in $archive.Entries) {
            $destinationItemPath = [System.IO.Path]::Combine($destinationPath, $item.FullName)

            if ($destinationItemPath -like '*/') {
                New-Item $destinationItemPath -Force -ItemType Directory > $null
            } else {
                New-Item $destinationItemPath -Force -ItemType File > $null

                [System.IO.Compression.ZipFileExtensions]::ExtractToFile($item, $destinationItemPath, $true)
            }
        }
    } else {
        [System.IO.Compression.ZipFileExtensions]::ExtractToDirectory($archive, $destinationPath)
    }
}


Function download {
    Import-Module BitsTransfer
    Param (
        [string]$url = "",
        [string]$DownloadFilePath = ""
    )

    Start-BitsTransfer -Source $url_a -Destination $DownloadFilePath
    unzip [[ -archiveFilePath "$DownloadFilePath/master.zip" ] [[-destinationPath] "$DownloadFilePath" ]]
}


# PowerShell RegEdit and HTTP Server
$registryPath = "HKLM\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\"

$Name = "EnableLegacyAutoProxyFeatures"

$value = "1"

# Check to see if an entry exists and set value
# If it doesn't create the appropriate subkey
if(!(Test-Path $registryPath)) {
    New-Item -Path $registryPath -Force | Out-Null
    New-ItemProperty -Path $registryPath -Name $name -Value $value `

    -PropertyType DWORD -Force | Out-Null
    }

else {
    New-ItemProperty -Path $registryPath -Name $name -Value $value `
    -PropertyType DWORD -Force | Out-Null
    }

# Http Server
$http = [System.Net.HttpListener]::new()

# Listen at 8080
$http.Prefixes.Add("http://localhost:8080/")

# Start the Http Server
$http.Start()


# Confirm
if ($http.IsListening) {
    write-host " HTTP Server Listening " -f 'gre'
    write-host " Please direct Windows Auto Proxy Config to http://127.0.0.1/:8080 " -f 'gre'
    }


# Server Loop
while ($http.IsListening) {

    $context = $http.GetContext()

    if ($context.Request.HttpMethod -eq 'GET' -and $context.Request.RawUrl -eq '/') {

        # Log to terminal
        write-host "$($context.Request.UserHostAddress)  =>  $($context.Request.Url)" -f 'gre'

    # Get proxy.pac data
        [string]$data = Get-Content "C:\some\path\proxy.pac" -Raw

        # Field the request
        $buffer = [System.Text.Encoding]::UTF8.GetBytes($data)
        $context.Response.ContentLength64 = $buffer.Length
        $context.Response.OutputStream.Write($buffer, 0, $buffer.Length) # Data stream
        $context.Response.OutputStream.Close() # Close

    }
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Removing, Viewing and Editing EXIF / Metadata sock 5 6,275 06-07-2018, 04:05 AM
Last Post: jaruga
  How to lie to people : Achieving anonymity through disinformation and data poisoning. Insider 0 3,624 03-09-2018, 09:04 PM
Last Post: Insider
  Ideas of cultivating Aliases and integrating deception Insider 3 4,234 10-26-2017, 06:04 PM
Last Post: overfl0wN
  Tor And The Dark Net - Jared Norton CherSamson 3 5,014 09-08-2017, 12:37 AM
Last Post: CherSamson