From hosts-file blocking to HTTPS interception
Two successive mod-validation bypasses in ARK: Survival Ascended. A C# proxy, traffic captures, and the client-side trust assumptions behind the investigation.
Infrastructure & Security Engineer
I build and maintain systems that connect people,
access, and operations.
Access automation and security engineering for a community application.
20,000+ community members
~70% less manual administration
Membership-based scale; project-reported reduction estimate.Two successive investigations into client-side content validation in ARK: Survival Ascended.
Research reported to Studio Wildcard
Technical report and public proof of concept. Current exposure unverified.Projects / Field notes
Technical reports, security investigations, and systems I’ve built.
Two successive mod-validation bypasses in ARK: Survival Ascended. A C# proxy, traffic captures, and the client-side trust assumptions behind the investigation.
Access automation and security engineering for a community application. An architectural project report covering identity, workflows, data integrity, and operations.
Resume / Leandro Pareja
Hands-on systems administration, application security,
and access automation.
Hours per week: Approx. 35–40 | Series/Grade: N/A (private sector)
Florida Atlantic University
GPA 3.80 / 4.00 · Dean’s List, Spring 2026
Palm Beach State College
GPA 3.50 / 4.00
Project / Infrastructure & application engineering
Access automation, application security & operations.
Leandro Pareja · Node.js · MongoDB · Systems administration
Posted
I built and maintained application workflows that connected community membership, access requests, staff decisions, and operational information. Legioner Solutions brought those activities into a structured system using Node.js, discord.js v14, MongoDB, and Google Sheets integrations.
My work combined application and integration development with systems administration: managing server configurations, deploying applications, maintaining services, and troubleshooting technical issues. The goal was to make recurring administrative work easier to coordinate while keeping staff responsible for approval decisions and in-game fulfillment.
Membership-based scale, not a concurrent-user or throughput measurement.
Project-reported estimate across automated activities.
The application coordinates the request. Staff make the decision and complete the fulfillment.
External trackers → operational displaysConfigured source filtering, parsed telemetry, server status, and scheduled updates.
Google Sheets ↔ application recordsService-account import/export utilities and safeguards for manually corrected data.
Membership approvals, access requests, and resource fulfillment involved recurring coordination between users and staff. Free-form intake made the status of outstanding work harder to follow. I replaced that intake with guided inputs, persistent requests, staff-facing queues, and completion notifications.
The system needed to connect several kinds of information: platform identity, configured community roles, request details, stored records, and external server telemetry. Each integration had a different purpose and needed to be handled within its own trust boundary.
The platform supplied an authenticated actor and interaction context. Selected commands checked configured role groups before modifying membership state. Cross-community onboarding checked primary-community membership and the required role before assigning secondary roles.
Commands, buttons, and forms routed interactions to the relevant application handler. Applicable request workflows checked for an existing pending request, created an incomplete MongoDB record, and published a staff work card. After fulfillment, the application updated completion state and coordinated the queue and requester notifications.
Configured tracker messages passed through sender and channel matching before being parsed into operational state. A public server feed supplied population and metadata for scheduled displays. External database reads supported player analytics and leaderboard aggregation.
MongoDB held member approvals, requests, and completion state. Google Sheets connected through a service account and import/export utilities. Local JSON stored tasks, timers, and snapshots; in-memory maps and sets held temporary state.
I separated workflow coordination from human fulfillment. The application made work visible and tracked its state, while staff retained responsibility for decisions that required their review.
Typed command options, required form inputs, duplicate-pending-request checks, private responses in selected workflows, and configured telemetry-source matching supported more predictable operations. Persisted reminders restored scheduled work after application restarts.
Character-record removals used a process-local mutex to serialize a helper’s read/modify/write path. Records included a lastModified marker, and spreadsheet imports skipped retained records already marked as manually corrected. RAW spreadsheet writes helped preserve the intended input values.
These safeguards addressed specific failure paths. The mutex was not a distributed lock, and the local JSON, maps, sets, and temporary state were process-scoped. Handler-level role checks also should not be read as evidence of a single, universal authorization layer.
The detailed diagram below connects the access workflow with telemetry, storage, and integrations. Open it to inspect the architecture, then select a node for an illustrative code example.
Identity context moves through event routing, handler-specific checks, persistent workflow state, and staff-facing output. Select any node to explore an illustrative proof of concept.
Configured tracker messages pass through sender and channel matching into parsed operational state. A public server feed supplies population and metadata for scheduled displays. External database reads support player analytics and leaderboard aggregation.
Google Sheets connects through a service account and import/export utilities. Local JSON stores tasks, timers, and snapshots; in-memory maps and sets hold temporary state.
Simplified logical architecture. Authorization checks are distributed across selected handlers. Local JSON, locks, and temporary state are process-scoped. External services supply identity and telemetry.
The application supported a community of more than 20,000 members. The reported reduction in manual administration was approximately 70% across automated activities. These figures describe community scale and a project-reported estimate; they are not independently benchmarked throughput, concurrency, or performance measurements.
The practical result was a more structured way to collect requests, track outstanding work, and communicate completion. The supporting project document explains the implementation context. The code examples on this page are independently authored illustrations, not production source or proof that every illustrated control was implemented.
The next hardening priorities follow the points where permissions, persistent state, and external messages meet:
This work connected access management, backend application logic, persistence, integrations, and day-to-day operations. It reinforced the importance of making state transitions explicit: who requested an action, what checks applied, who fulfilled it, and whether completion was recorded and communicated.
Engineering takeaway: reliable automation needs clear ownership of decisions, durable workflow state, and a recovery path when connected systems disagree.
Node.js · JavaScript · discord.js v14 · MongoDB · Google Sheets API · JSON persistence · async-mutex · Windows and server administration.
Read the supporting project document
Based on the supplied Legioner Solutions project document and resume. Implemented behavior and future hardening priorities are distinguished above. Homepage network activity is a separate visualization using simulated events.
Project / Application security research
Two successive mod-validation bypasses.
Leandro Pareja · C# · Network traffic analysis
Posted
I developed a C# tool to investigate a mod-validation weakness in ARK: Survival Ascended. My research focused on how the client handled mod metadata returned through CurseForge API workflows and whether changing that information could influence which content the client accepted.
In my original research, I identified an access-control bypass involving unapproved mods in an official multiplayer-server context and responsibly disclosed the finding to Studio Wildcard. This write-up explains the proxy-based technique behind my implementation. The accompanying code shows the interception and metadata manipulation; reproducing the complete game behavior also requires the matching client, mod files, and test environment.
Two approaches, one security question: what happens when a validation check cannot produce a trustworthy result?
After I reported the first bypass, an update required API connectivity for online access. Blocking the hostname then blocked online play.
Exact metadata request HTTP 403A selected request is answered locally.
Qualifying JSON response fingerprint replacementMetadata is rewritten before reaching the client.
The published PoC illustrates these related mechanisms; it does not include the complete three-check interruption test.
Engineering takeaway: a working connection is not an approval decision. Validate content authoritatively and reject missing or inconsistent results.
My first bypass was straightforward: users could add a blocking entry for the relevant CurseForge API hostname to the Windows hosts file. During my testing, the game did not enforce a check that the API connection had completed successfully before continuing. When the connection failed, it continued without the expected validation, allowing the mod-loading restriction to be bypassed.
The hosts file changes hostname resolution; it does not filter individual URLs or HTTP requests. In practical terms, my change prevented the relevant API traffic from reaching its intended destination for clients using that hostname mapping. It did not block every CurseForge service or every possible connection method.
The underlying issue was how the game handled a failed validation connection: it allowed the workflow to continue rather than rejecting content when the API was unreachable.
I reported this hosts-file bypass to Studio Wildcard. Following my disclosure, Studio Wildcard updated the game so it no longer allowed access to online services without reaching the API. Blocking the API through the hosts file now prevented online access entirely, making hosts-file blocking useless for bypassing mod restrictions.
This patch forced me to research a new workaround. To investigate how the game processed individual mod-validation traffic when API access was required, I developed my own custom C# proxy to filter traffic and inspect individual API calls used for server mod validation. During this research, I discovered that if you individually drop the connections of the 3 specific validation checks, you can load any mod while retaining online access.
To investigate further, I developed my own C# proxy tool using Titanium.Web.Proxy to filter and inspect the game's traffic. I examined the API calls used to validate mods for specific servers, separating those checks from the API connectivity required to access online services.
Through that analysis, I identified three validation checks. In my testing, selectively dropping the connections for those three checks individually allowed me to load any mod I chose while retaining access to online services. This became my second bypass: the game required general API connectivity, but the individual validation checks could still be interrupted without enforcing the intended mod restriction.
The accompanying C# implementation documents the proxy infrastructure and a related combination of exact-request blocking and fingerprint-response manipulation. It does not contain three separately identified connection-drop rules, so it should not be read as the complete reproduction of the three-check test described above.
The sequence above describes my testing experience. I have not included build numbers, patch dates, or vendor correspondence in this public report, so it should not be read as a claim about current exposure or a specific vendor patch implementation.
While loading into a server during my investigation, I observed the validation checks requesting information about verified mods and their attributes. These captures show the file metadata returned through that workflow, including file IDs, download URLs, availability, hashes, and module fingerprints.


While developing the interception rules, I tested a regex-based URL pattern against the metadata endpoint:
https://83374.api.curseforge.com/v1/mods/files
The screenshot below records the matching tool reporting, “The pattern matches the test URL.” This helped me check that the rule recognized the endpoint I intended to target.

This screenshot establishes a positive URL-match test. It does not, by itself, show an intercepted response or prove that the game accepted a mod. My published C# implementation uses an exact URL comparison in the request handler and substring checks in the response handler; it does not execute the regex shown in this separate development test. A successful match for one URL also does not establish that a pattern excludes unintended URLs.
A mod fingerprint is a value used to identify or compare mod content. I investigated what happened when I changed that fingerprint in an API response before the game received it.
The security question was whether the client could be persuaded to treat modified content as acceptable by changing the metadata used during validation. Information processed on a player's computer can be manipulated, so an authoritative content restriction needs validation that does not depend solely on those client-side inputs.
I used C#, Titanium.Web.Proxy, Windows proxy configuration, HTTPS interception, and JSON response manipulation to build my research tool.
Game client -> Local C# HTTPS proxy -> API
Game client <- Modified metadata <- API response
I matched one exact metadata URL and answered it locally with HTTP 403 Forbidden. I set the response's Content-Type header to text/html; charset=utf-8 and supplied a short HTML body.
This let me investigate how the validation workflow behaved when that request was blocked. The effect of request blocking needs to be distinguished from the effect of fingerprint substitution when reproducing the finding.
I selected GET requests whose URLs contained the configured API domain and /v1/mods/files path text. After checking that the response contained JSON, I read its decoded body and replaced the original fingerprint with a different fingerprint.
I then returned the rewritten body with HTTP 200 OK and a Content-Type of application/json; charset=utf-8. These were response headers; my implementation did not add authorization headers or replace API credentials. The proxy library handled the underlying HTTP exchange.
The exact replacement was "fingerprint":885506360 to "fingerprint":1925868458. It used literal text matching, so whitespace and formatting could affect whether the replacement occurred. A longer number beginning with the same digits could also be changed unintentionally.
My finding concerned the integrity of the mod-loading decision. Accepting unapproved client content in a restricted multiplayer environment undermines the intended content policy. That observation does not, by itself, establish server-side installation, remote code execution, or account compromise.
Installing a trusted local certificate and intercepting traffic does not establish a vulnerability in TLS. The issue I investigated was how the application used the altered metadata when deciding whether content was acceptable.
My code does not replace mod download URLs or patch game memory. Some comments and console messages in my original loader referred to those actions, but the relevant executable logic performs request blocking and fingerprint substitution.
My remediation focus is authoritative validation: verify approved content against authenticated metadata and bind that metadata to the actual artifact. A fingerprint supplied through the same mutable channel as the validation input is not an independent authenticity check. Blocked requests, missing metadata, and inconsistent validation results also need explicit rejection behavior.
I kept this PoC in C# because it represents the implementation I built and makes the Windows proxy and certificate work visible. A Python version could demonstrate the same concept, but changing languages would not make the finding stronger. The useful evidence is the reproducible request/response behavior and the resulting mod-loading outcome.
The code below extracts the proxy functionality from my original loader. It includes the HTTPS listener, certificate setup, Windows proxy configuration, request interception, response headers, HTTP status handling, and fingerprint replacement.
My full loader also included KeyAuth licensing, subscription checks, updates, Steam-folder discovery, and background checks. I left those supporting features out of this public PoC to keep the security mechanism easy to follow.
This version uses one listener port and restores the previous proxy settings on normal exit. My original implementation tried two ports and disabled the proxy on exit. The request and response filters retain the original substring matching and replacement behavior.
The PoC requires Windows, a .NET SDK, and the Titanium.Web.Proxy NuGet package. Create a console project, replace its Program.cs with the accompanying source, and add the package:
dotnet new console -n ModIntegrityResearch
cd ModIntegrityResearch
# Replace Program.cs with FingerprintPoC.cs from this report.
dotnet add package Titanium.Web.Proxy
dotnet run
Certificate trust setup may require elevation. The PoC changes Windows proxy settings and installs a trusted local root certificate, so I would run it in an authorized test environment. Escape performs normal proxy cleanup. The certificate remains installed afterward, and forced termination can prevent proxy restoration.
This published extraction has not been compiled or retested against a live game environment. Package compatibility, affected builds, and current exposure remain unverified. The source demonstrates the manipulation technique; the complete finding also depends on the observed game behavior.
using System;
using System.Collections.Generic;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Win32;
using Titanium.Web.Proxy;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Models;
// Proxy-focused extraction of my original mod-loader research code.
// Requires Windows, a .NET SDK, and Titanium.Web.Proxy.
// Changes the current user's proxy and installs a trusted local root CA.
internal static class Program
{
private const string OldFingerprint = "885506360";
private const string NewFingerprint = "1925868458";
private const string ApiDomain = "api.curseforge.com";
private const string BlockedUrl = "https://83374.api.curseforge.com/v1/mods/files";
private const string ProxyKey = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings";
private static readonly string[] Names = { "ProxyEnable", "ProxyServer", "ProxyOverride" };
private static readonly Dictionary<string, (object Value, RegistryValueKind Kind)> Saved = new();
[DllImport("wininet.dll", SetLastError = true)]
private static extern bool InternetSetOption(IntPtr handle, int option, IntPtr buffer, int length);
private static void Main()
{
var proxy = new ProxyServer();
bool settingsSaved = false;
proxy.BeforeRequest += OnBeforeRequest;
proxy.BeforeResponse += OnBeforeResponse;
try
{
// Inspect HTTPS for clients that use the proxy and trust this CA.
proxy.CertificateManager.CreateRootCertificate(true);
proxy.CertificateManager.TrustRootCertificate(true);
var endpoint = new ExplicitProxyEndPoint(IPAddress.Loopback, 6969, true);
proxy.AddEndPoint(endpoint);
proxy.Start();
SaveProxySettings();
settingsSaved = true;
EnableWindowsSystemProxy("127.0.0.1:6969");
Console.WriteLine("Research proxy listening on 127.0.0.1:6969.");
Console.WriteLine("Press Escape to stop and restore the previous proxy settings.");
while (Console.ReadKey(intercept: true).Key != ConsoleKey.Escape) { }
}
finally
{
try { if (settingsSaved) RestoreProxySettings(); }
finally
{
proxy.BeforeRequest -= OnBeforeRequest;
proxy.BeforeResponse -= OnBeforeResponse;
proxy.Stop();
}
// The trusted certificate remains installed. Forced termination
// can bypass cleanup; use an authorized test environment.
}
}
private static Task OnBeforeRequest(object sender, SessionEventArgs e)
{
string url = e.HttpClient.Request.RequestUri.AbsoluteUri;
if (url.Equals(BlockedUrl, StringComparison.OrdinalIgnoreCase))
{
e.Ok("<html><body><h1>403 Forbidden: Blocked by research proxy</h1></body></html>");
e.HttpClient.Response.StatusCode = 403;
e.HttpClient.Response.Headers.RemoveHeader("Content-Type");
e.HttpClient.Response.Headers.AddHeader("Content-Type", "text/html; charset=utf-8");
Console.WriteLine("Blocked the exact metadata endpoint with HTTP 403.");
}
return Task.CompletedTask;
}
private static async Task OnBeforeResponse(object sender, SessionEventArgs e)
{
string url = e.HttpClient.Request.RequestUri.AbsoluteUri;
// Preserve the original substring-based URL filtering.
if (!url.Contains(ApiDomain) || !url.Contains("/v1/mods/files") ||
!e.HttpClient.Request.Method.Equals("GET", StringComparison.OrdinalIgnoreCase))
return;
byte[] responseBytes = await e.GetResponseBody();
if (responseBytes == null || responseBytes.Length == 0) return;
string contentType = e.HttpClient.Response.ContentType ?? "";
if (!contentType.Contains("application/json")) return;
string originalJson = Encoding.UTF8.GetString(responseBytes);
string patchedJson = originalJson.Replace(
$"\"fingerprint\":{OldFingerprint}",
$"\"fingerprint\":{NewFingerprint}");
// Original status-code and response-header handling.
e.HttpClient.Response.StatusCode = 200;
e.HttpClient.Response.Headers.RemoveHeader("Content-Type");
e.HttpClient.Response.Headers.AddHeader("Content-Type", "application/json; charset=utf-8");
e.SetResponseBody(Encoding.UTF8.GetBytes(patchedJson));
Console.WriteLine(originalJson == patchedJson
? "Eligible response: no matching fingerprint found."
: "Replaced the fingerprint in an intercepted JSON response.");
}
private static void SaveProxySettings()
{
using var key = Registry.CurrentUser.OpenSubKey(ProxyKey)
?? throw new InvalidOperationException("Windows Internet Settings key is unavailable.");
foreach (string name in Names)
{
object value = key.GetValue(name);
if (value != null) Saved[name] = (value, key.GetValueKind(name));
}
}
private static void EnableWindowsSystemProxy(string address)
{
using var key = Registry.CurrentUser.OpenSubKey(ProxyKey, writable: true)
?? throw new InvalidOperationException("Cannot update Windows proxy settings.");
key.SetValue("ProxyEnable", 1, RegistryValueKind.DWord);
key.SetValue("ProxyServer", address, RegistryValueKind.String);
key.SetValue("ProxyOverride",
"steamcommunity.com;store.steampowered.com;*.steamcontent.com;*.valvesoftware.com;<local>",
RegistryValueKind.String);
RefreshProxySettings();
}
private static void RestoreProxySettings()
{
using var key = Registry.CurrentUser.OpenSubKey(ProxyKey, writable: true)
?? throw new InvalidOperationException("Cannot restore Windows proxy settings.");
foreach (string name in Names)
{
if (Saved.TryGetValue(name, out var saved)) key.SetValue(name, saved.Value, saved.Kind);
else key.DeleteValue(name, throwOnMissingValue: false);
}
RefreshProxySettings();
}
private static void RefreshProxySettings()
{
InternetSetOption(IntPtr.Zero, 39, IntPtr.Zero, 0);
InternetSetOption(IntPtr.Zero, 37, IntPtr.Zero, 0);
}
}
Start a conversation
Compose email