1. 环境准备
写好的两篇博客忘了备份,结果自己捣鼓来捣鼓去不小心删除掉了…而且我没有保留到回收站的习惯,所以找不回来了,于是重新写吧…
环境准备:
- 一台云主机
- 牧云主机管理助手(https://rivers.chaitin.cn/app/collie/home)
- 长亭雷池WAF社区版 (https://waf-ce.chaitin.cn/)
- 开源 HFish 蜜罐 (https://hfish.net/)
我的部署方式为 SaaS + docker ; 当然也可以用其他方式安装,可以查看官方链接使用相应部署方式
登录牧云主机管理助手(https://rivers.chaitin.cn/app/collie/home),绑定云主机后进入应用市场
选择雷池社区版安装
根据指引成功安装好WAF,后台web界面如下
安装HFish蜜罐,参考链接 https://hfish.net/#/2-1-docker
安装好后蜜罐web管理页面如下
到这一步,SaaS + docker的WAF和蜜罐已经搭建好。
2. 如何让低交互蜜罐变得稍微高可信
由于HFish的开源版本中蜜罐大多数都是低交互的,所以当把IP发给朋友的时候,朋友看到80端口为tomcat的时候,说了句:“这一眼看上去就是蜜罐”。 于是蜜罐 “出师未捷身先死”… 因此需要将低交互蜜罐做的逼真点。
2.1 蜜罐与WAF的配合
2.1.1 缩小蜜罐端口暴露面
HFish有环境模板,尽可能的减少了端口的暴露,使其更像真实环境
随意选择一个模板,在其模板的基础上进行调整。关闭了一些TCP常用端口,比如22、23、445、3389等
然后修改高交互Mysql蜜罐的端口,设置默认3306的话,遇到有经验的攻击者会被识别出为蜜罐。
2.1.2 修改相应的特征
2.1.2.1 修改telnet协议特征或者使用高交互的telnet蜜罐
Hfish 中的低交互telnet蜜罐,默认监听在23端口。模拟的该协议默认无需验证,并且对各个命令的结果都做了响应的模板来做应答。在命令为空或者直接回车换行时,会响应default模板,该模板内容为test。因此可以利用这个特征进行该蜜罐在telnet服务上的检测如图所示。
解决办法:
-
修改配置文件中的默认模版内容
-
更换为高交互telnet蜜罐
2.1.2.2 修改明显的web特征
HFIsh在默认8080端口实现了一个WordPress登录页面,页面中由一个名为x.js的javascript文件用来记录尝试爆破的登录名密码。直接通过判断wordpress登录页是否存在x.js文件就可判断是否为蜜罐。
解决办法:
- 修改x.js的命名和代码内容
- 关闭WordPress这个蜜罐,使用其他蜜罐
2.1.3 在部分web蜜罐上设置WAF防护
拿宝塔蜜罐和Zabbix监控蜜罐举例
现在宝塔蜜罐和Zabbix的蜜罐IP为 9224 和 9192
在雷池上设置对宝塔蜜罐和Zabbix蜜罐web页面的防护
宝塔蜜罐页面的防护设置:
Zabbix蜜罐的防护设置:
然后分别去访问相应的页面,如:https://<你的域名>:<雷池监听端口>
如果能正常访问,接着测试攻击发现被拦截,即表示两个蜜罐web站点已经被waf防护
并且蜜罐也成功检测到攻击:
这样的话,攻击者在访问IP的特定web页面的时候发现有WAF,或许在思考自己是否踩中蜜罐的情况下有所迟疑。
另外:
在雷池WAF的 防护配置-语义分析中,可以加将 “机器人检测”设置为仅观察,如果策略太严,FOFA、zoomeye、quake等空间测绘的IP节点也会被WAF拦截,这样的话,蜜罐端口就少了一个暴露方向。
3. 从蜜罐上获取高价值IP
思路如下
3.1 对踩中蜜罐的攻击IP进行整理分类
注意:以下分类仅代表个人观点 ,如有异议,以你为准。
-
踩中蜜罐,并且IP的威胁情报为以下的,可直接在WAF侧进行拉黑封禁
Scanner-扫描Botnet-僵尸网络uspicious-可疑Mobile-移动基站 -
踩中蜜罐,并且IP的威胁情报为以下的,为高价值IP,可尝试进行反制溯源,需不需要放入WAF黑名单视情况(傀儡机通常会进行大量扫描,也可以拉黑)。
Spam-垃圾邮件Zombie-傀儡机Exploit-漏洞利用IDC-IDC服务器Edu-教育BTtracker-BT服务器Backbone-骨干网
可通过蜜罐的API直接调用,提前需要在 平台管理 ->系统信息 处将 HFish社区云情报计划 勾上
然后进入 平台管理 -> 系统配置 -> API配置选择已开放的API,根据使用示例中进行API调用
用于在WAF侧进行拉黑封禁的IP,其获取脚本可用
import requestsimport json
url = "https://Server_IP/api/v1/attack/ip?api_key=YOUR_API_KEY"
payload = json.dumps( { "start_time": 0, "end_time": 0, "intranet": 0, "threat_label": [ "Scanner", "Botnet", "uspicious", "Mobile", ], })headers = {"Content-Type": "application/json"}
response = requests.request("POST", url, headers=headers, data=payload, verify=False)print(response.text)
获取高价值可溯源反制IP,API脚本可用
import requestsimport json
url = "https://Server_IP/api/v1/attack/ip?api_key=YOUR_API_KEY"
payload = json.dumps( { "start_time": 0, "end_time": 0, "intranet": 0, "threat_label": [ "IDC", "Spam", "Zombie", "Exploit", "Edu", "BTtracker", "Backbone", ], })headers = {"Content-Type": "application/json"}
response = requests.request("POST", url, headers=headers, data=payload, verify=False)print(response.text)
后续会在对应的脚本上进行添加功能。
3.2 将获取到的IP通过webhook形式发送到飞书
根据之前的脚本进行添加调整,基本的demo已经弄好,如下:
import requestsimport jsonimport reimport loggingimport osimport warnings
# 忽略警告warnings.filterwarnings("ignore")
# 获取当前脚本所在的目录script_dir = os.path.dirname(os.path.realpath(__file__))
# 设置日志logging.basicConfig(level=logging.INFO, format="%(message)s")logger = logging.getLogger(__name__)
# 配置选项RESULT_DIR = os.path.join(script_dir, "result")
# 检查并创建结果目录if not os.path.exists(RESULT_DIR): os.makedirs(RESULT_DIR)
BASE_URL = "https://蜜罐的API链接" # 此处为蜜罐的APIHEADERS = {"Content-Type": "application/json"}THREAT_LABELS_ATTACK = [ "IDC", "Spam", "Zombie", "Exploit", "Edu", "BTtracker", "Backbone",]THREAT_LABELS_SCANNER = ["Scanner", "Botnet", "uspicious", "Mobile"]IP_REGEX_PATTERN = r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"
def fetch_data(payload): try: response = requests.post(BASE_URL, headers=HEADERS, data=payload, verify=False) response.raise_for_status() return response.text except requests.exceptions.RequestException as e: logger.error(f"Error fetching data: {e}") return ""
def match_ips(data): return re.findall(IP_REGEX_PATTERN, data)
def find_new_ips(old_ips, new_ips): return list(set(new_ips) - set(old_ips))
def write_ips_to_file(file_path, ips): with open(file_path, "w") as file: file.write("\n".join(ips))
def send_notification(webhook_url, content): headers = {"Content-Type": "application/json", "Charset": "UTF-8"} message = {"msg_type": "text", "content": {"text": content}} response = requests.post(url=webhook_url, headers=headers, data=json.dumps(message))
def main(): attack_webhook = ( "https://open.feishu.cn/open-apis/bot/v2/hook/xxxx" # 接收攻击告警机器人的 webhook ) scan_webhook = ( "https://open.feishu.cn/open-apis/bot/v2/hook/xxxxx" # 接收扫描告警机器人的 webhook )
payload_attacker = json.dumps( { "start_time": 0, "end_time": 0, "intranet": 0, "source": 0, "threat_label": THREAT_LABELS_ATTACK, } ) payload_scanner = json.dumps( { "start_time": 0, "end_time": 0, "intranet": 0, "source": 0, "threat_label": THREAT_LABELS_SCANNER, } )
attack_data = fetch_data(payload_attacker) scanner_data = fetch_data(payload_scanner)
attacker_ips = match_ips(attack_data) scanner_ips = match_ips(scanner_data)
attacker_ips_path = os.path.join(RESULT_DIR, "attack_ips.txt") scan_ips_path = os.path.join(RESULT_DIR, "scan_ips.txt")
previous_attacker_ips = [] previous_scan_ips = []
if os.path.exists(attacker_ips_path): with open(attacker_ips_path, "r") as file: previous_attacker_ips = file.read().splitlines()
if os.path.exists(scan_ips_path): with open(scan_ips_path, "r") as file: previous_scan_ips = file.read().splitlines()
new_attack_ips = find_new_ips(previous_attacker_ips, attacker_ips) new_scan_ips = find_new_ips(previous_scan_ips, scanner_ips)
write_ips_to_file(attacker_ips_path, attacker_ips)
if new_attack_ips: logger.info("有憨包踩蜜罐咯,尝试下溯源吧! hacking!") logger.info("\n".join(new_attack_ips)) attack_content = "有憨包踩蜜罐咯,尝试下溯源吧! hacking!\n" + "\n".join(new_attack_ips) else: logger.info("还没新的憨包踩到蜜罐! 请静待有缘人") attack_content = "还没新的憨包踩到蜜罐! 请静待有缘人\n"
write_ips_to_file(scan_ips_path, scanner_ips)
if new_scan_ips: logger.info("快把这些扫描器、爬虫封了!") logger.info("\n".join(new_scan_ips)) scan_content = "快把这些扫描器、爬虫封了!\n" + "\n".join(new_scan_ips) else: logger.info("目前没得需要封禁的IP") scan_content = "目前没得需要封禁的IP\n"
send_notification(attack_webhook, attack_content) send_notification(scan_webhook, scan_content)
if __name__ == "__main__": main()
将代码放置你的云主机服务器上,使用crontab设置定时任务即可。
4. 参考
- http://tttang.com/archive/1832 [企业蜜罐建设实践]
- https://www.anquanke.com/post/id/227833#h2-12 [浅析开源蜜罐识别与全网测绘]
- https://waf-ce.chaitin.cn/ [长亭雷池WAF社区版]
- https://hfish.net/ [开源 HFish 蜜罐]
- https://rivers.chaitin.cn/app/collie/home [牧云主机管理助手]
- https://docs.rivers.chaitin.cn [长亭百川云平台]