【问题标题】:Go channels, tcp/ip portmap not returning all open ports转到通道,tcp/ip 端口映射不返回所有打开的端口
【发布时间】:2018-04-28 23:20:27
【问题描述】:

我一直在学习 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... 所以我怀疑有些事情正在超时,或者发生了一些奇怪的事情。

有很多糟糕的代码 hack,主要是因为我正在学习和搜索很多关于如何做事的知识,所以请随时提供提示甚至更改/拉取请求。谢谢

【问题讨论】:

  • 链接可以使用,但不是唯一的。链接可能不可靠。请将您的代码直接发布到问题中。

标签: go


【解决方案1】:

您的代码存在一些问题。在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

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

这里是对正在发生的事情的最新解释。首先,我们创建一个通道,将端口号推送到我们的工作人员处理。然后我们启动一个 go-routine,它将尽可能快地将范围内的端口写入该通道。一旦我们将每个可用端口写入该通道,我们就会关闭该通道,以便我们的读者能够退出。

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

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

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

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

【讨论】:

  • 谢谢!我很感激,我现在​​正在移动中,一旦我回到我的 apt,我的原始链接指向 github 是有原因的。如果你想看看那里,所有内容都在 main.go 上引用。我回家后会回复您,看看您的解决方案是否有效,关于我遵循 vim-go 提示的风格。再次感谢 PS:这里是整个项目的 github github.com/ReK2Fernandez/GoEnumerator/blob/master/portscan.go
  • 没问题。我现在看到了主文件。我强烈敦促您重新考虑过度使用全局变量。这似乎没有必要,并且您在不同的文件中都有函数,包括在其他文件中定义的阴影和设置全局变量。如果您尝试改编我的代码,请小心。
  • 谢谢,是的,我会尽快重构所有内容,使其按我的意愿工作。我“以某种方式”实现了您的代码,大声笑,因此可以像以前一样进行更改,但仍然没有获得较高的端口..我得到的最大值是 4444 等永远不会达到 50K 的东西。 github.com/ReK2Fernandez/GoEnumerator/blob/master/portscan.go 更改已提交。
  • 所以我测试了你的代码,我怀疑你遇到了和我一样的问题。您尝试一次打开约 65000 个连接,超出了打开文件的限制。您应该限制实际连接的 goroutine 的数量。
  • 嘿,非常感谢,我真的很喜欢你不仅给了我一个解决方案,还给了我风格和软件技巧,正如你所看到的,我不是软件工程师。我可以编码,但我会学习我自己并且来自技术的系统/网络/黑客方面,所以任何提示并指出我正确的方向总是非常感谢..我认为这是我在编码方面得到的最好的帮助之一。正如您在我的代码中看到的那样,主要是黑客和自己解决问题。所以你给我这么多好信息的事实是惊人的。如果端口映射有效,将测试和重构。交易
猜你喜欢
  • 2011-10-30
  • 2013-01-14
  • 2019-02-09
  • 1970-01-01
  • 1970-01-01
  • 2010-10-19
相关资源
最近更新 更多