我一直在学习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 ...... 所以我怀疑某些事情已经超时,或者发生了奇怪的事情。
有许多糟糕的代码,主要是因为我在如何做事方面学习和搜索很多,所以请随意提供提示,甚至更改/拉取请求。感谢
答案 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
。看来你在这种情况下对全局变量的使用是不方便的,并没有考虑如何解决没有全局变量的问题。