--set up referances
local servStore = game:GetService("ServerStorage")
local modules = servStore:WaitForChild("modules")
local services = modules:WaitForChild("services")
local utilityModules = modules:WaitForChild("utility")
local repStore = game:GetService("ReplicatedStorage")
local exploitHandler = require(modules.exploitHandler)
local vfx = workspace:WaitForChild("map"):WaitForChild("vfx")
local sparkEffect = require(modules:WaitForChild("effects"):WaitForChild("sparkEffect"))
--require needed modules
local ipUtil = require(utilityModules.ipUtil)
local networkStorage = require(modules:WaitForChild("storage"):WaitForChild("networkStorage"))
local serverHandler = require(modules:WaitForChild("serverHandler"))
local packetHandler = require(modules.packetHandler)
local sqlService = require(services.sqlService)
local hashUtil = require(utilityModules.hashUtil)
local mfaHandler = require(modules.mfaHandler)
--local sshService = require(services:WaitForChild("sshService"))
local networksStorage = {} --store created networks
--load cost of actions
local actionCost = {
ping = 1,
scan_port = 2, -- per port scanned
ssh_login = 3,
http_request = 5, -- base request
FILE_TRANSFER_PER_KB = 0.02, -- cost per KB
iot_heartbeat = 0.5, -- periodic background devices
ddos_packet = 1.5, -- single malicious packet baseline
}
local networkHandler = {}
networkHandler.__index = networkHandler
--table search utility function
local function contains(tbl: table, val)
if type(tbl) ~= "table" then
error("First argument must be a table", 2)
end
for _, v in pairs(tbl) do
if v == val then
return true
end
end
return false
end
--define vulns for each service
--set up new network instance for player
function networkHandler.new(entity, config: {openPorts: number?, isInternal: boolean?, gatewayNetworks: {string?}, maxLoad: number?, loadDecay: number?}?)
config = config or {}
local self = setmetatable({}, networkHandler)
local name = if typeof(entity) == "Instance" and entity:IsA("Player") then entity.Name else entity -- Use player name or direct string for servers
self.hostname = name
self.publicIp = ipUtil.GeneratePublic()
self.openPorts = config.openPorts or {}
self.iotDevices = {}
--network bandwith stuff
self.maxLoad = config.maxLoad or 100
self.load = 0
self.load = math.clamp(self.load, 0, self.maxLoad)
self.loadThread = task.spawn(function()
while task.wait(0.5) do
--calculate load
local load = (self.load/self.maxLoad)*100
--check if load is over 80%
if load >= 70 then
print("load over 70, applying fx")
if not sparkEffect.SparksActive(self.hostname) then
task.spawn(function()
sparkEffect.MakeSparks(self.hostname)
while task.wait(0.1) do
if self.load <= 40 then
sparkEffect.Destroy(self.hostname)
end
end
end)
end
end
if self.load > 0 then
self.load -= config.loadDecay or 1
end
end
end)
self.connectedServer = {
name = nil,
port = nil,
service = nil
}
self.pivotedFrom = nil --tracks gateway network used for pivot
packetHandler.ClearPackets(name) -- clean up packets for new network
if typeof(entity) == "Instance" and entity:IsA("Player") then
--player specifc properties of the netinstance object
self.discoveredNetworks = {}
self.hackingNetwork = nil
self.instanceType = "player"
self.wordlists = {}
else
--server specific properties
self.instanceType = "server"
self.isInternal = config.isInternal or false
self.gatewayNetworks = config.gatewayNetworks or nil
end
networksStorage[name] = self -- store all network instances globally
return self
end
function networkHandler:ApplyLoad(action: string, cost: string?) -- normally map to action but you can also provide load cost
if not action then return "please provide action" end
local cost = cost or actionCost[action] or nil
if not cost then return "action not found for load" end
self.load += cost
self.load = math.clamp(self.load, 0, self.maxLoad) -- make sure it stays between 0 and 100
end
function networkHandler.SetInternalNetwork(parentNetworks: {string}, targetNetwork)
if not parentNetworks or not targetNetwork then return end
--make sure parent networks are strings
for key, network in parentNetworks do
if type(network) ~= "string" then
return error "parent networks table must only contain strings"
end
end
if not targetNetwork.isInternal then
targetNetwork.isInternal = true
--get gateway network(s) names
local gatewayNames = {}
for key, networkName in pairs(parentNetworks) do
local parentNetwork = networkHandler.GetNetworkInfo(networkName)
assert(parentNetwork, "unable to match parent network to key")
table.insert(gatewayNames, networkName)
end
if #gatewayNames == 0 then
return error "unable to find hostname of parent network"
end
targetNetwork.gatewayNetworks = gatewayNames
return ("set network '%s' as internal with gateways: %s"):format(targetNetwork.hostname, table.concat(gatewayNames, ", "))
end
end
--wordlist stuff
function networkHandler:AddWordlist(wordlistName, wordlist: {string}?)
if not wordlistName then return error "provide wordlist name" end
local wordlists = self.wordlists
if type(wordlist) ~= "table" then
return error("wordlist must be a table")
end
if #wordlist == 0 then
return error("wordlist must have at least one word")
end
self.wordlists[wordlistName] = wordlist
end
function networkHandler:FetchWordlist(wordlistName)
if not wordlistName then return error "provide wordlist name" end
local wordlist = self.wordlists
wordlist = wordlist[wordlistName]
return wordlist or error "wordlists not found"
end
function networkHandler.OpenPort(networkName, port)
if not port then
return error("unable to open ports, no ports provided")
end
local netInst = networksStorage[networkName]
assert(netInst, "network instance not found, unable to open port")
netInst.openPorts = netInst.openPorts or {}
if table.find(netInst.openPorts, port) then
error("attempted to open port that is already open")
end
table.insert(netInst.openPorts, port)
return ("successfully opened port on network '%s'"):format(networkName)
end
function networkHandler.GetNetworkInfo(name: string?)
if not name then return networksStorage end
local network = networksStorage[name]
if not network then error(("network '%s' not found"):format(name)) end
return network
end
function networkHandler:DiscoverNetwork(name: string)
local targetNetwork = self.GetNetworkInfo(name)
if not targetNetwork then return error(("network '%s' does not exist"):format(targetNetwork)) end
if targetNetwork.isInternal then
local connectedNetwork = self.connectedServer
if connectedNetwork == nil then return error(("cannot discover internal network '%s' without being connected to a network"):format(name)) end
local gatewayNetwork = contains(targetNetwork.gatewayNetworks, connectedNetwork.name) -- if this returns true then assume the gateway is valid
if gatewayNetwork == false then
return error(("access to internal network '%s' denied. current connection '%s' is not in allowed list of gateway networks to access this network"):format(name, connectedNetwork.name))
end
gatewayNetwork = self.GetNetworkInfo(name) --directly pass name because it is in the gateway table for the internal network
self.discoveredNetworks[name] = gatewayNetwork
return ("discovered network: '%s'"):format(name)
end
--see if network exists
if targetNetwork then
self.discoveredNetworks[name] = targetNetwork
--disallow connection to a network by default
return ("discovered network: '%s'"):format(name)
else
return error(("network '%s' not found, unable to discover"):format(name))
end
end
--get open ports from network
function networkHandler:ScanNetwork(name)
local openPorts = {}
local targetNetwork = self.GetNetworkInfo(name)
if not targetNetwork then return error("target network not found") end
local services = targetNetwork.services
if not services then error "services not found in target network" end
--dynamically get open ports based on the open ports in every service
for servName, servData in pairs(services) do
if servData.port then
table.insert(openPorts, servData.port)
end
end
--see if network is in the discovered networks
if self.discoveredNetworks[name] then
self.discoveredNetworks[name].openPorts = openPorts
return openPorts
else
return error(("unable to scan, network '%s' not found or discovered"):format(name))
end
end
function networkHandler:ConnectToNetwork(name, port)
local targetNetwork = self.GetNetworkInfo(name)
if not targetNetwork then return error("target network not found") end -- make sure target network exists
--if the network your attempting to connect to is internal, make sure your connecting thru a valid gateway network
if targetNetwork.isInternal then
local connectedServer = self.connectedServer.name
if not connectedServer or not contains(targetNetwork.gatewayNetworks, connectedServer) then
error(("cannot connet to internal network '%s'. you must connect to it through a valid gateway network"):format(name))
end
end
--throw error if you dont provide a port
if not port then
error(("unable to connect to network: '%s' no port provided"):format(name))
end
--see if you have the network discovered
if not self.discoveredNetworks[name] then error(("unable to connect to %s, network not discovered or does not exist"):format(name)) end
--see if you have discovered any open ports:
if not self.discoveredNetworks[name].openPorts then error(("unable to connect to network %s, no ports found. please scan for open ports"):format(name)) end
--see if the port your attempting to connect on is open on the requested network
if not table.find(self.discoveredNetworks[name].openPorts, port) then
print(self.discoveredNetworks[name].openPorts)
error(("port '%s' on network: '%s' not open"):format(port,name))
end
local ssh = targetNetwork.services and targetNetwork.services.ssh
local http = targetNetwork.services and targetNetwork.services.http
if ssh and port == (ssh.port or 22) then
--handle ssh connection
if ssh.allowConnection then
self.connectedServer = {
name = targetNetwork.hostname,
port = port,
service = "ssh",
isInternal = targetNetwork.isInternal or false
}
return ("connected to network '%s' on port '%s' with 'ssh'"):format(name, port)
else --dont allow to connect if allow connection isnt enabled on the servers ssh service
return error(("connection not allowd on network '%s'. in order to connect you must first get the correct password"):format(name))
end
elseif http and port == (http.port or 80) then
if http.allowConnection then
self.connectedServer = {
name = targetNetwork.hostname,
port = port,
service = "http",
isInternal = targetNetwork.isInternal or false
}
return ("connected to network '%s' on port '%s' with 'http'"):format(name,port)
else
error(("HTTP connection not allowed on network %s"):format(name))
end
else
return error ("unsupported port/service")
end
end
function networkHandler:DisconnectFromNetwork()
self.connectedServer = nil
return ("disconnected from network")
end
function networkHandler:getServerFiles()
if self.connectedServer then
local serverInstance = serverHandler.GetServerInfo(self.connectedServer.name)
assert(serverInstance, "server instance not found")
local files = serverInstance:GetFiles()
assert(files, "files not found on server")
return files
else
return error("connect to a network to its see files!")
end
end
function networkHandler:AllowConnectionToNetwork(name)
if self.discoveredNetworks[name] then
else
return error(("network: '%s' not found or discovered"):format(name))
end
end
function networkHandler.ping(clientNetwork, network: string)
if not network then
return error "network not provided"
end
if not networkHandler.GetNetworkInfo(network) then
return error "ping failed, network not found"
end
local targetNetwork = networkHandler.GetNetworkInfo(network)
packetHandler.LogPacket({clientNetwork, network}, {
sourceIp = clientNetwork.publicIp,
destIp = targetNetwork.publicIp,
port = 0,
protocol = "ICMP",
method = "PING",
})
packetHandler.LogPacket({clientNetwork, network}, {
sourceIp = targetNetwork.publicIp,
destIp = clientNetwork.publicIp,
port = 0,
protocol = "ICMP",
method = "PING_REPLY",
})
print(targetNetwork:ApplyLoad("ping", 20))
return "ping success"
end
--handle services
function networkHandler:InitServices(config: {
ssh: {enabled: boolean?, port: number?, password: string?, allowConnection: boolean?, authType: "password" | "mfa"?}?,
http: {enabled: boolean?, port: number, allowConnection: boolean?, authRequired: boolean?, password: string?, authType: "password" | "mfa"?}?
})
config = config or {}
self.services = {}
--ssh------------------------------------
if config.ssh and config.ssh.enabled ~= false then
--database setup
local hashedPswrd = hashUtil.sha256(config.ssh.password or "passwrd123")
local sqlTable: sqlService.tableDef = {
schema = {"username", "password"},
types = {"string", "string"},
data = {
{"_sshPass", hashedPswrd},
{"_mfaCode", tostring(math.random(100000, 999999))}
}
}
sqlService.createTable(self.hostname, sqlTable)
self.services.ssh = {
enabled = true,
port = config.ssh.port or 22,
password = config.ssh.password or "passwrd123", -- no longer used
allowConnection = config.ssh.allowConnection or false,
loginAttempts = 0,
authType = config.ssh.authType or "password",
discoveredEndpoints = {}
}
if self.services.ssh.authType == "mfa" then
print("loop output:",mfaHandler.StartLoop(self.hostname, 10, "ssh"))
end
exploitHandler.AssignVulns(self, "ssh")
end
--http---------------------------------
if config.http and config.http.enabled ~= false then
--database setup
local hashedPswrd = hashUtil.sha256(config.http.password or "passwrd123")
local sqlTable: sqlService.tableDef = {
schema = {"username", "password"},
types = {"string", "string"},
data = {
{"default", hashedPswrd},
{"_mfaCode", tostring(math.random(100000, 999999))}
}
}
sqlService.createTable("http", sqlTable)
self.services.http = {
enabled = true,
port = config.http.port or 80,
allowConnection = config.http.allowConnection or false,
authRequired = config.http.authRequired or false,
authType = config.http.authType or "password",
password = config.password or "passwrd123",
loginAttempts = 0
}
if self.services.http.authType == "mfa" then
print("loop output:",mfaHandler.StartLoop(self.hostname, 2, "http"))
end
exploitHandler.AssignVulns(self, "http")
end
end
--helper login func
local function passwordCheck(tableName, username, password)
local sqlString = string.format("SELECT password, username FROM %s WHERE username = '%s' AND password = '%s'", tableName, username, password)
return sqlService.Execute(sqlString)
end
function networkHandler:AttemptLogin(service: string, password: string, username: string?)
local svc = self.services and self.services[service]
if not services or not svc.enabled then error(("service '%s' not enabled"):format(service)) end --svc is short for service
if service == "ssh" then
local ssh = self.services.ssh
if not ssh or not ssh.enabled then return error("ssh not enabled") end
if ssh.allowConnection then return "already authenticated on network" end --if your alr authed js return
if ssh.authType == "mfa" then
local correctPass = mfaHandler.GetCode(self.hostname, service)
print("correct code:", correctPass, "entered pass:", password)
password = password:match("^%s*(.-)%s*$") --remove whitespace and make the 2 things the same datatype
correctPass = tostring(correctPass):match("^%s*(.-)%s*$")
if password ~= correctPass then
return error("incorrect password")
else
print("login success")
ssh.allowConnection = true --if you got the password then allow player to connect to network via ssh
return (("login successful, you are now able to connect to network: '%s' on service '%s'"):format(self.hostname, service))
end
else
--hash password to check w database
password = hashUtil.sha256(password)
--check database for service password
local msg, output = passwordCheck(self.hostname, "_sshPass", password)
if output then
print("login success", output)
ssh.allowConnection = true --if you got the password then allow player to connect to network via ssh
return (("login successful, you are now able to connect to network: '%s' on service '%s'"):format(self.hostname, service))
else
ssh.loginAttempts += 1
local trigger = exploitHandler.CheckTrigger(self, "ssh", "login_attempt")
if not trigger then
if ssh.loginAttempts > 3 then return error("too many login attempts") end
return error("incorrect password")
end
end
end
elseif service == "http" then
local http = self.services.http
if not http or not http.enabled then return error("http not enabled") end
if http.allowConnection then return "already authenticated on network" end --if your alr authed js return
if password ~= http.password then
http.loginAttempts += 1
return error("incorrect password")
end
http.allowConnection = true
return (("login successful, you are now able to connect to network: '%s' on service '%s'"):format(self.hostname, service))
end
error(("service '%s' not supported"):format(service))
end
--function networkHandler:D
return networkHandler