去通道,tcp / ip portmap没有返回所有开放端口

时间:2018-04-28 23:20:28

标签: go

我一直在学习Golang将我所有的渗透测试工具都移到它身上。由于我喜欢编写自己的工具,因此这是学习新语言的完美方式。在这种特殊情况下,我认为我使用频道的方式有问题。我知道一个事实是没有完成端口映射,因为我在ruby上写的其他工具是找到所有开放端口但我的golang工具不是。有人可以帮我理解我做错了什么吗?渠道是正确的做法吗?

package main

import (
    "fmt"
    "log"
    "net"
    "strconv"
    "time"
)

func portScan(TargetToScan string, PortStart int, PortEnd int, openPorts []int) []int {
    activeThreads := 0
    doneChannel := make(chan bool)

    for port := PortStart; port <= PortEnd; port++ {
        go grabBanner(TargetToScan, port, doneChannel)
        activeThreads++
    }

    // Wait for all threads to finish
    for activeThreads > 0 {
        <-doneChannel
        activeThreads--
    }
    return openPorts
}

func grabBanner(ip string, port int, doneChannel chan bool) {
    connection, err := net.DialTimeout(
        "tcp",
        ip+":"+strconv.Itoa(port),
        time.Second*10)
    if err != nil {
        doneChannel <- true
        return
    }
    // append open port to slice
    openPorts = append(openPorts, port)

    fmt.Printf("+ Port %d: Open\n", port)
    // See if server offers anything to read
    buffer := make([]byte, 4096)
    connection.SetReadDeadline(time.Now().Add(time.Second * 5))
    // Set timeout
    numBytesRead, err := connection.Read(buffer)
    if err != nil {
        doneChannel <- true
        return
    }
    log.Printf("+ Banner of port %d\n%s\n", port,
        buffer[0:numBytesRead])
    // here we add to map port and banner
    targetPorts[port] = string(buffer[0:numBytesRead])

    doneChannel <- true
    return
}

注意:似乎找到第一批端口,但不是高于数字示例8080的那些,但它通常会得到80和443 ...... 所以我怀疑某些事情已经超时,或者发生了奇怪的事情。

有许多糟糕的代码,主要是因为我在如何做事方面学习和搜索很多,所以请随意提供提示,甚至更改/拉取请求。感谢

1 个答案:

答案 0 :(得分:2)

您的代码存在一些问题。在grabBanner中,您似乎引用了openPorts,但未在任何地方定义。您可能正在引用一个全局变量,并且此追加操作不会是线程安全的。除了线程安全问题之外,您还可能会耗尽文件描述符限制。也许你应该通过这样做来限制并发工作量:

package main

import (
    "fmt"
    "net"
    "strconv"
    "sync"
    "time"
)

func main() {
    fmt.Println(portScan("127.0.0.1", 1, 65535))
}

// startBanner spins up a handful of async workers
func startBannerGrabbers(num int, target string, portsIn <-chan int) <-chan int {
    portsOut := make(chan int)

    var wg sync.WaitGroup

    wg.Add(num)

    for i := 0; i < num; i++ {
        go func() {
            for p := range portsIn {
                if grabBanner(target, p) {
                    portsOut <- p
                }
            }
            wg.Done()
        }()
    }

    go func() {
        wg.Wait()
        close(portsOut)
    }()

    return portsOut

}

func portScan(targetToScan string, portStart int, portEnd int) []int {
    ports := make(chan int)

    go func() {
        for port := portStart; port <= portEnd; port++ {
            ports <- port
        }
        close(ports)
    }()

    resultChan := startBannerGrabbers(16, targetToScan, ports)

    var openPorts []int
    for port := range resultChan {
        openPorts = append(openPorts, port)
    }

    return openPorts
}

var targetPorts = make(map[int]string)

func grabBanner(ip string, port int) bool {
    connection, err := net.DialTimeout(
        "tcp",
        ip+":"+strconv.Itoa(port),
        time.Second*20)

    if err != nil {
        return false
    }
    defer connection.Close() // you should close this!

    buffer := make([]byte, 4096)
    connection.SetReadDeadline(time.Now().Add(time.Second * 5))
    numBytesRead, err := connection.Read(buffer)

    if err != nil {
        return true
    }

    // here we add to map port and banner
    // ******* MAPS ARE NOT SAFE FOR CONCURRENT WRITERS ******
    // *******************  DO NOT DO THIS *******************
    targetPorts[port] = string(buffer[0:numBytesRead])

    return true
}

您对var open bool的使用并不断设置,然后返回它既不必要又非惯用。此外,检查if someBoolVar != false是一种非惯用且冗长的撰写方式if someBoolVar

此外,映射对于并发访问并不安全,但是您的grabBanner函数正在以并发方式从许多go例程写入映射。 请停止改变函数内的全局状态。而是返回值。

以下是对正在发生的事情的最新解释。首先,我们建立一个渠道,我们将把端口号推送给我们的工人进行处理。然后我们开始一个go-routine,它会尽可能快地将该范围内的端口写入该通道。一旦我们将每个可用端口写入该频道,我们就会关闭该频道,以便我们的读者可以退出。

然后我们调用一个方法来启动可配置数量的bannerGrabber个工作者。我们传递ip地址和通道来读取候选端口号。此函数生成num个goroutine,每个goroutine都在portsIn通道上传递,调用grab banner函数,然后如果成功则将端口推送到出站通道。最后,我们再开始一个等待sync.WaitGroup完成的例程,这样我们就可以在所有工作完成后关闭传出(结果)通道。

返回portScan函数我们收到出站通道作为startBannerGrabbers函数的返回值。然后,我们调整返回给我们的结果通道的范围,将所有打开的端口附加到列表中,然后返回结果。

我还改变了一些风格,比如缩小你的函数参数名称。

听起来像是破纪录的风险,我将再次强调以下内容。 停止改变全局状态。您应该以并发安全的方式累积这些值,并将它们返回给调用者以供使用,而不是设置targetPorts。看来你在这种情况下对全局变量的使用是不方便的,并没有考虑如何解决没有全局变量的问题。