Toby Chui 4 місяців тому
батько
коміт
4ed6ea2747

+ 3 - 1
api.go

@@ -8,6 +8,7 @@ import (
 	"imuslab.com/zoraxy/mod/acme/acmedns"
 	"imuslab.com/zoraxy/mod/acme/acmewizard"
 	"imuslab.com/zoraxy/mod/auth"
+	"imuslab.com/zoraxy/mod/ipscan"
 	"imuslab.com/zoraxy/mod/netstat"
 	"imuslab.com/zoraxy/mod/netutils"
 	"imuslab.com/zoraxy/mod/utils"
@@ -187,7 +188,8 @@ func initAPIs(targetMux *http.ServeMux) {
 	authRouter.HandleFunc("/api/analytic/resetRange", AnalyticLoader.HandleRangeReset)
 
 	//Network utilities
-	authRouter.HandleFunc("/api/tools/ipscan", HandleIpScan)
+	authRouter.HandleFunc("/api/tools/ipscan", ipscan.HandleIpScan)
+	authRouter.HandleFunc("/api/tools/portscan", ipscan.HandleScanPort)
 	authRouter.HandleFunc("/api/tools/traceroute", netutils.HandleTraceRoute)
 	authRouter.HandleFunc("/api/tools/ping", netutils.HandlePing)
 	authRouter.HandleFunc("/api/tools/whois", netutils.HandleWhois)

+ 79 - 0
mod/ipscan/handlers.go

@@ -0,0 +1,79 @@
+package ipscan
+
+/*
+	ipscan http handlers
+
+	This script provide http handlers for ipscan module
+*/
+
+import (
+	"encoding/json"
+	"net"
+	"net/http"
+
+	"imuslab.com/zoraxy/mod/utils"
+)
+
+// HandleScanPort is the HTTP handler for scanning opened ports on a given IP address
+func HandleScanPort(w http.ResponseWriter, r *http.Request) {
+	targetIp, err := utils.GetPara(r, "ip")
+	if err != nil {
+		utils.SendErrorResponse(w, "target IP address not given")
+		return
+	}
+
+	// Check if the IP is a valid IP address
+	ip := net.ParseIP(targetIp)
+	if ip == nil {
+		utils.SendErrorResponse(w, "invalid IP address")
+		return
+	}
+
+	// Scan the ports
+	openPorts := ScanPorts(targetIp)
+	jsonData, err := json.Marshal(openPorts)
+	if err != nil {
+		utils.SendErrorResponse(w, "failed to marshal JSON")
+		return
+	}
+
+	utils.SendJSONResponse(w, string(jsonData))
+}
+
+// HandleIpScan is the HTTP handler for scanning IP addresses in a given range or CIDR
+func HandleIpScan(w http.ResponseWriter, r *http.Request) {
+	cidr, err := utils.PostPara(r, "cidr")
+	if err != nil {
+		//Ip range mode
+		start, err := utils.PostPara(r, "start")
+		if err != nil {
+			utils.SendErrorResponse(w, "missing start ip")
+			return
+		}
+
+		end, err := utils.PostPara(r, "end")
+		if err != nil {
+			utils.SendErrorResponse(w, "missing end ip")
+			return
+		}
+
+		discoveredHosts, err := ScanIpRange(start, end)
+		if err != nil {
+			utils.SendErrorResponse(w, err.Error())
+			return
+		}
+
+		js, _ := json.Marshal(discoveredHosts)
+		utils.SendJSONResponse(w, string(js))
+	} else {
+		//CIDR mode
+		discoveredHosts, err := ScanCIDRRange(cidr)
+		if err != nil {
+			utils.SendErrorResponse(w, err.Error())
+			return
+		}
+
+		js, _ := json.Marshal(discoveredHosts)
+		utils.SendJSONResponse(w, string(js))
+	}
+}

+ 2 - 3
mod/ipscan/ipscan.go

@@ -27,7 +27,7 @@ type DiscoveredHost struct {
 	HttpsPortDetected bool
 }
 
-//Scan an IP range given the start and ending ip address
+// Scan an IP range given the start and ending ip address
 func ScanIpRange(start, end string) ([]*DiscoveredHost, error) {
 	ipStart := net.ParseIP(start)
 	ipEnd := net.ParseIP(end)
@@ -57,7 +57,6 @@ func ScanIpRange(start, end string) ([]*DiscoveredHost, error) {
 			host.CheckHostname()
 			host.CheckPort("http", 80, &host.HttpPortDetected)
 			host.CheckPort("https", 443, &host.HttpsPortDetected)
-			fmt.Println("OK", host)
 			hosts = append(hosts, host)
 
 		}(thisIp)
@@ -118,7 +117,7 @@ func (host *DiscoveredHost) CheckPing() error {
 func (host *DiscoveredHost) CheckHostname() {
 	// lookup the hostname for the IP address
 	names, err := net.LookupAddr(host.IP)
-	fmt.Println(names, err)
+	//fmt.Println(names, err)
 	if err == nil && len(names) > 0 {
 		host.Hostname = names[0]
 	}

+ 48 - 0
mod/ipscan/portscan.go

@@ -0,0 +1,48 @@
+package ipscan
+
+/*
+	Port Scanner
+
+	This module scan the given IP address and scan all the opened port
+
+*/
+
+import (
+	"fmt"
+	"net"
+	"sync"
+	"time"
+)
+
+// OpenedPort holds information about an open port and its service type
+type OpenedPort struct {
+	Port  int
+	IsTCP bool
+}
+
+// ScanPorts scans all the opened ports on a given host IP (both IPv4 and IPv6)
+func ScanPorts(host string) []*OpenedPort {
+	var openPorts []*OpenedPort
+	var wg sync.WaitGroup
+	var mu sync.Mutex
+
+	for port := 1; port <= 65535; port++ {
+		wg.Add(1)
+		go func(port int) {
+			defer wg.Done()
+			address := fmt.Sprintf("%s:%d", host, port)
+
+			// Check TCP
+			conn, err := net.DialTimeout("tcp", address, 5*time.Second)
+			if err == nil {
+				mu.Lock()
+				openPorts = append(openPorts, &OpenedPort{Port: port, IsTCP: true})
+				mu.Unlock()
+				conn.Close()
+			}
+		}(port)
+	}
+
+	wg.Wait()
+	return openPorts
+}

+ 15 - 4
web/components/networktools.html

@@ -17,10 +17,21 @@
         <p>Discover mDNS enabled service in this gateway forwarded network</p>
         <button class="ui basic larger circular button" onclick="launchToolWithSize('./tools/mdns.html',1000, 640);">View Discovery</button>
         <div class="ui divider"></div>
-        <!-- IP Scanner-->
-        <h2>IP Scanner</h2>
-        <p>Discover local area network devices by pinging them one by one</p>
-        <button class="ui basic larger circular button" onclick="launchToolWithSize('./tools/ipscan.html',1000, 640);">Start Scanner</button>
+        <div class="ui stackable grid">
+            <div class="eight wide column">
+                 <!-- IP Scanner-->
+                <h2>IP Scanner</h2>
+                <p>Discover local area network devices by pinging them one by one</p>
+                <button class="ui basic larger circular button" onclick="launchToolWithSize('./tools/ipscan.html',1000, 640);">Start Scanner</button>
+            </div>  
+            <div class="eight wide column">
+                 <!-- Port Scanner-->
+                <h2>Port Scanner</h2>
+                <p>Scan for open ports on a given IP address</p>
+                <button class="ui basic larger circular button" onclick="launchToolWithSize('./tools/portscan.html',1000, 640);">Start Scanner</button>
+            </div>
+        </div>  
+       
         <div class="ui divider"></div>
         <!-- Traceroute-->
         <h2>Traceroute / Ping</h2>

+ 28 - 3
web/tools/ipscan.html

@@ -10,7 +10,6 @@
         <title>IP Scanner | Zoraxy</title>
         <link rel="stylesheet" href="../script/semantic/semantic.min.css">
         <script src="../script/jquery-3.6.0.min.js"></script>
-        <script src="../../script/ao_module.js"></script>
         <script src="../script/semantic/semantic.min.js"></script>
         <script src="../script/tablesort.js"></script>
         <link rel="stylesheet" href="../main.css">
@@ -149,13 +148,23 @@
 
                 function displayResults(data) {
                     var table = $('<table class="ui celled unstackable table"></table>');
-                    var header = $('<thead><tr><th>IP Address</th><th>Ping</th><th>Hostname</th><th>HTTP Detected</th><th>HTTPS Detected</th></tr></thead>');
+                    var header = $(`<thead>
+                        <tr>
+                            <th>IP Address</th>
+                            <th>Ping</th>
+                            <th>Hostname</th>
+                            <th>HTTP Detected</th>
+                            <th>HTTPS Detected</th>
+                            <th>Port Scan</th>
+                        </tr>
+                    </thead>`);
                     table.append(header);
                     var body = $('<tbody></tbody>');
                     var offlineHostCounter = 0;
                     for (var i = 0; i < data.length; i++) {
                         var classname = "offlinehost";
-                        if (data[i].Ping>=0){
+                        let hostIsOnline = data[i].Ping >= 0;
+                        if (hostIsOnline){
                             classname = "onlinehost";
                         }else{
                             offlineHostCounter++;
@@ -167,6 +176,7 @@
                         row.append($('<td>' + data[i].Hostname + '</td>'));
                         row.append($('<td>' + (data[i].HttpPortDetected ? '<i class="green check icon"></i>' : '') + '</td>'));
                         row.append($('<td>' + (data[i].HttpsPortDetected ? '<i class="green check icon"></i>' : '') + '</td>'));
+                        row.append($(`<td>${hostIsOnline ? `<button class="ui small basic button" onclick='launchToolWithSize("portscan.html?ip=${data[i].IP}", 1000, 640);'>Scan</button>`:''}</td>`));
                         body.append(row);
                     }
 
@@ -198,6 +208,21 @@
             function toggleOfflineHost(){
                 $(".offlinehost").toggle();
             }
+
+            function launchToolWithSize(url, width, height){
+                let windowName = Date.now();
+                window.open(url,'w'+windowName,
+                `toolbar=no,
+                location=no,
+                status=no,
+                menubar=no,
+                scrollbars=yes,
+                resizable=yes,
+                width=${width},
+                height=${height}`);
+            }
+
+
         </script>
     </body>
 </html>

+ 120 - 0
web/tools/portscan.html

@@ -0,0 +1,120 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
+    <meta name="apple-mobile-web-app-capable" content="yes" />
+    <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"/>
+    <meta name="theme-color" content="#4b75ff">
+    <link rel="icon" type="image/png" href="./favicon.png" />
+    <title>Port Scanner | Zoraxy</title>
+    <link rel="stylesheet" href="../script/semantic/semantic.min.css">
+    <script src="../script/jquery-3.6.0.min.js"></script>
+    <script src="../script/semantic/semantic.min.js"></script>
+    <script src="../script/tablesort.js"></script>
+    <link rel="stylesheet" href="../main.css">
+    <script src="../script/utils.js"></script>
+    </head>
+</head>
+<body>
+    <div class="ui container">
+        <br>
+            <div class="ui segment">
+                <p>Enter the IP address you want to scan for open ports. This tool only scans for open TCP ports.</p>
+                <div class="ui fluid action input">
+                    <input id="scanningIP" type="text" placeholder="IP Address">
+                    <button class="ui basic blue button" onclick="startScan()">Start Scan</button>
+                </div>
+                <div class="ui yellow message">
+                    <h4 class="ui header">
+                        <i class="exclamation triangle icon"></i>
+                        <div class="content">
+                            Port Scan Warning
+                            <div class="sub header">Please ensure that you only scan IP addresses that you own or have explicit permission to scan. Unauthorized scanning may be considered a network attack and could result in legal consequences.</div>
+                        </div>
+                    </h4>
+                </div>
+                
+                <table class="ui celled compact table">
+                    <thead>
+                        <tr>
+                            <th>Port</th>
+                            <th>TCP Port Open</th>
+                            <th>Full Path</th>
+                        </tr>
+                    </thead>
+                    <tbody id="resultTable">
+                        <tr>
+                            <td colspan="3"><i class="ui green circle check icon"></i> Click the "Start Scan" to start port scan on given IP address</td>
+                        </tr>
+                    </tbody>
+                </table>
+                <button class="ui right floated basic button" onclick="exitTool();">Exit</button>
+                <br><br>
+            </div>
+        </div>
+        <br>
+        <br>
+    </div>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.js"></script>
+    <script>
+
+        //If the URL has a query parameter, fill the input box with that value
+        $(document).ready(function(){
+            let urlParams = new URLSearchParams(window.location.search);
+            let ipToScan = urlParams.get("ip");
+            if (ipToScan != null){
+                $("#scanningIP").val(ipToScan);
+                startScan();
+            }
+        });
+
+        function startScan(button=undefined){
+            let ipToScan = $("#scanningIP").val();
+            ipToScan = ipToScan.trim();
+            let table = $("#resultTable");
+            table.empty();
+            table.append($("<tr><td colspan='3'><i class='ui loading spinner icon'></i> Scanning</td></tr>"));
+
+            if (button != undefined){
+                button.addClass("loading");
+            }
+            $.get("/api/tools/portscan?ip=" + ipToScan, function(data){
+                if (button != undefined){
+                    button.removeClass("loading");
+                }
+
+                if (data.error != undefined){
+                    alert(data.error);
+                    return;
+                }else{
+                    table.empty();
+
+                    //Entries are in the form of {Port: 80, IsTCP: true, IsUDP: false}
+                    //if both TCP and UDP are open, there will be two entries
+                    for (let i = 0; i < data.length; i++){
+                        let row = $("<tr></tr>");
+                        row.append($("<td></td>").text(data[i].Port));
+                        row.append($("<td><i class='ui green check icon'></i></td>"));
+                        row.append($("<td></td>").html(`<a href="//${ipToScan + ":" + data[i].Port}" target="_blank">${ipToScan + ":" + data[i].Port}</a>`));
+                        table.append(row);
+                    }
+
+                    if (data.length == 0){
+                        table.append($("<tr><td colspan='3'><i class='ui green circle check icon'></i> No open ports found on given IP address</td></tr>"));
+                    }
+                }
+                console.log(data);
+            });
+        }
+
+        function exitTool(){
+            //Close the current window
+            window.open('', '_self', '');
+            window.close();
+        }
+    </script>
+</body>
+</html>

+ 0 - 39
wrappers.go

@@ -26,7 +26,6 @@ import (
 
 	"imuslab.com/zoraxy/mod/dynamicproxy"
 	"imuslab.com/zoraxy/mod/dynamicproxy/loadbalance"
-	"imuslab.com/zoraxy/mod/ipscan"
 	"imuslab.com/zoraxy/mod/mdns"
 	"imuslab.com/zoraxy/mod/uptime"
 	"imuslab.com/zoraxy/mod/utils"
@@ -267,44 +266,6 @@ func HandleMdnsScanning(w http.ResponseWriter, r *http.Request) {
 	utils.SendJSONResponse(w, string(js))
 }
 
-// handle ip scanning
-func HandleIpScan(w http.ResponseWriter, r *http.Request) {
-	cidr, err := utils.PostPara(r, "cidr")
-	if err != nil {
-		//Ip range mode
-		start, err := utils.PostPara(r, "start")
-		if err != nil {
-			utils.SendErrorResponse(w, "missing start ip")
-			return
-		}
-
-		end, err := utils.PostPara(r, "end")
-		if err != nil {
-			utils.SendErrorResponse(w, "missing end ip")
-			return
-		}
-
-		discoveredHosts, err := ipscan.ScanIpRange(start, end)
-		if err != nil {
-			utils.SendErrorResponse(w, err.Error())
-			return
-		}
-
-		js, _ := json.Marshal(discoveredHosts)
-		utils.SendJSONResponse(w, string(js))
-	} else {
-		//CIDR mode
-		discoveredHosts, err := ipscan.ScanCIDRRange(cidr)
-		if err != nil {
-			utils.SendErrorResponse(w, err.Error())
-			return
-		}
-
-		js, _ := json.Marshal(discoveredHosts)
-		utils.SendJSONResponse(w, string(js))
-	}
-}
-
 /*
 	WAKE ON LAN