CCCPaste

lua script demo

--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