• Say0l的安全开发-弱口令扫描工具-My-crack【红队工具】


    写在前面

    终于终于,安全开发也练习一年半了,有时间完善一下项目,写写中间踩过的坑。

    安全开发的系列全部都会上传至github,欢迎使用和star

    工具链接地址

    https://github.com/SAY0l/my-crack

    预览

    在这里插入图片描述

    My-Crack 工具介绍

    更适合中国宝宝的弱口令扫描器

    Help

    NAME:
       My-Crack - Weak password crack
    
    USAGE:
       main.exe [global options] command [command options] [arguments...]
    
    VERSION:
       1.1
    
    AUTHOR:
       sayol 
    
    COMMANDS:
       scan     let's crack weak password
       help, h  Shows a list of commands or help for one command
    
    GLOBAL OPTIONS:
       --debug, -d                    debug mode
       --timeout value, -t value      timeout (default: 3)
       --scan_num value, -n value     thread num (default: 5000)
       --ip_list value, -i value      ip_list (default: "./input/ip_list.txt")
       --user_dict value, -u value    user_dict (default: "./input/user.dic")
       --pass_dict value, -p value    pass_dict (default: "./input/pass.dic")
       --output_file value, -o value  scan result file (default: "./output/my_crack.txt")
       --help, -h                     show help
       --version, -v                  print the version
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    Use

    当前核心支持ftp/mongodb/mysql/mssql/postgre/redis/ssh的弱口令扫描

    提供了编译后的版本。

    后续版本开放代码

    Other

    思路借鉴于赵海峰老师的《白帽子安全开发实战》

    顺带回答赵海峰老师的小问题:

    1. 扫到一个弱口令后,如何取消相同IP\port和用户名请求,避免扫描效率低下
    2. 对于FTP匿名访问,如何只记录一个密码,而不是把所有用户名都记录下来
    3. 对于Redis这种没有用户名的服务,如何只记录一次密码,而不是记录所有的所有用户及正常的密码的组合
    4. 对于不支持设置超时的扫描插件,如何统一设置超时时间
    
    • 1
    • 2
    • 3
    • 4
    1. 利用hash来完成的。在redis,ftp这种可以仅记录密码的就以(ip-port-protocol)进行hash,其他带用户名的以(ip-port-username)进行hash,查hash来作为规避条件,一旦结果集的hash已经存在和当前扫描产生的hash值相同,就continue。

    2. 基于上述原理,就可以实现redis、ftp 只记录一个密码,不会把所有用户名都记录下来,因为是用ip-port-protocol 进行hash。

    3. 设计了一个WaitTimeout函数。

    c:= make(chan struct{})
    	go func(){
    		defer close(c)
    		wg.Wait()
    	}()
    	select{
    	case <-c:
    		return false //c仅仅作为一个flag,没有实际作用
    	case <-time.After(timeout):
    		return true
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    使用一个管道作为flagtime.After(timeout)也返回一个管道。一旦c关闭,就会触发select第一个case,返回false,表示正常关闭,wg.Wait正常完成。一旦超时时间到达,会触发select第二个case,就会返回true,表示已经超时。

    Ver 1.1 优化点

    1. 连接核心中,更替了一些不能用的版本、第三方库,使用了维护性高,连接稳定的版本库。

    例如mongodb的连接核心:

    “go.mongodb.org/mongo-driver/mongo”

    “go.mongodb.org/mongo-driver/mongo/options”

    抛弃了之前的个人维护库。

    1. 修复了一些问题
    ipListFile, err := os.Open(fileName)
    	if err != nil {
    		logger.Log.Fatalf("Open ip list file err, %v", err)
    	}
    
    	defer ipListFile.Close()
    
    减少大量代码冗余。上述代码为例,发生错误会直接终止,defer不会入栈,无需再做defer前的判断。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. 错误处理修复

    之前版本大量复用err,会出现err覆盖问题,有必要的err取消覆盖写法。

    Ver 1.2 待优化点

    代码上线

    并发算法需要优化

    更多服务的支持 smb,elastic …

    踩坑

    初始化结构体什么时候用指针,什么时候传呢?

    在Go语言中,当我们初始化一个结构体时,通常会使用取地址运算符&来创建一个指向该结构体的指针,然后将该指针赋值给一个变量。

    这样做的主要原因有以下几点:

    1.结构体是值类型:在Go语言中,结构体是值类型,也就是说当我们将一个结构体赋值给另一个结构体时,会进行值的拷贝。如果结构体较大,这将导致内存开销较大。而使用指针可以避免这种情况,因为指针只是保存了结构体的内存地址,赋值时只是复制了一个指针,而不是整个结构体。

    2.修改结构体的字段:如果我们希望在函数中修改结构体的字段值,直接传递结构体的值是不会改变原结构体的。但如果传递结构体的指针,就可以通过指针来修改结构体的字段。

    3.减少内存拷贝:在函数调用时,如果传递结构体的指针,避免了对整个结构体进行内存拷贝,提高了性能。特别是当结构体较大时,这种优化效果更为明显。

    函数出口编写

    无论是AI,还是我自己编写的,都是多出口写法,也就是函数弹出err时,检测err是否为nil,一旦出现错误,就弹出函数

    从程序设计的角度来说,多出口是很繁琐,也很不严谨的,但是如果设置flag变量来检测,不仅占用内存也很麻烦

    学习大佬编写时,提供了一个非常棒的思路,当你的true条件比较重要时,同时需要处理的err不算多,不需要保证每一次的错误精准处理时

    使用单个err变量,判断err==nil时的下一步动作,一旦出现错误,直接走整个函数的总出口返回。如果没有错误,继续执行语句,新的err会覆盖旧的err,一旦出错仍然走总出口返回
    
    • 1

    这样,整个函数就只有一个总出口,重点就会放在err==nil的正确情况下,不需要去繁琐的处理err

    但是这样也会产生一个问题,由于err是覆盖机制,一旦有两个语句同时发生错误,你并不能判断第一个语句是否真的发生了错误,它可能是被覆盖了,也可能是没有错误。所以上述写法在需要处理的err较少的情况下比较好用。

    defer与return

    在常规情况下,defer应该是写在return的前面,保证函数在进行返回之前做出defer中的操作。
    但是这并不能保证defer一定能够达到你的逻辑,因为写的不好的函数中,函数会有多个出口,例如

    if err!=nil{
        return false
    }
    defer fmt.Println("i am defer")
    
    • 1
    • 2
    • 3
    • 4

    这样的错误处理是很常规的,此时如果err没有问题,defer仍然能够正常入栈,可以达到正常的逻辑,但是一旦err发现错误,defer 就不能正常入栈了。

    所以defer一定要写在所有可能return的前面

  • 相关阅读:
    21-数据结构-内部排序-交换排序
    虚拟机ubuntu20.04连不上网
    C# 利用.NET 升级助手将.NET Framework项目升级为.NET 6
    postman调试注意事项
    软件架构设计的底层核心
    Scrum Master的职责
    Mycat分库分表分片方式
    msvcr120.dll丢失是什么意思,快速修复msvcr120.dll丢失的问题的方法分享
    Java使用lamda表达式简化代码
    8个独立键盘驱动程
  • 原文地址:https://blog.csdn.net/bin789456/article/details/132755942