二次入侵常藏在DDoS噪声里:应急时关掉的日志、临时放开的端口、随手加的白名单,都可能变成对手的入口。本文讲清被打后怎么查——时间线怎么对齐、文件和账号层看什么、数据外流怎么判断、临时放行怎么收回。
Q:为什么 DDoS 之后一定要查入侵?
- DDoS 消耗的是带宽、客服和你的注意力。对手真正想要的往往是数据,而不是让你掉线两个小时。
- 应急期通常会欠下四笔「安全债」:把 WAF 切到观察模式少误杀、放宽登录频率限制让运维能进后台、临时放开 22/3306/6379 方便处理、停掉日志采集省 IO。
- 攻击结束、站点恢复、告警归零,很多人就此收工——门还开着。
- 行业里有过太多这样的案例:被打的那两天网站只是慢,一周后才发现数据早被搬走。DDoS 的噪声正好盖住了拖库的流量。
Q:哪些信号说明可能已经被趁乱进来了?
- 攻击结束后资源不降:CPU、IO、出向带宽持续偏高,而访问量已回到正常水位。
- 出向明显大于入向:正常网站出向确实大于入向,但如果比例突然从 5:1 跳到 30:1,先怀疑数据在往外流。
- 后台出现陌生登录:新 IP、新时段、登录成功却没有任何操作记录。
- 上传目录出现脚本文件:
.php、.phtml、.jsp、.phar,或者伪装成图片的文件。 - 账号与计划任务有变化:新增系统账号、
authorized_keys被追加、多了一条你没写过的 cron。 - 站点表现异常:被挂马跳转、搜索结果里出现奇怪快照、老页面标题被改。
Q:第一步怎么把时间线对齐?
- 先从防护侧拿到攻击的起止时间,再看这段时间里主机和业务侧发生了什么。没有时间窗,翻日志等于大海捞针。
START="2026-11-07 21:00:00"; END="2026-11-08 02:30:00"
# 谁登录过、谁登录失败过(-F 打全时间)
last -F | head -30
lastb -F | head -30
# auditd:认证、提权、命令执行
ausearch -ts 11/07/2026 21:00:00 -te 11/08/2026 02:30:00 \
-m USER_LOGIN -m USER_AUTH -m EXECVE -i | tail -80
# 系统日志里该时段的重启、服务异常
journalctl --since "$START" --until "$END" -p warning | tail -50
- 把三个时间放在一张纸上:攻击开始、攻击结束、第一个可疑文件或登录出现的时间。第三个如果落在前两个之间,基本可以确定是趁乱进来的。
Q:文件层该查什么?
- 核心思路是「按时间窗找新东西」,而不是全盘扫特征。攻击窗口内新增或被改的文件,数量通常只有几十个,人工能看完。
# 攻击窗口内被改动的 Web 文件
find /var/www -type f -newermt "$START" ! -newermt "$END" -ls | head -50
# 上传目录里的可执行文件(最常见的落地点)
find /var/www -path "*/uploads/*" -type f \
\( -name "*.php" -o -name "*.phtml" -o -name "*.phar" -o -name "*.jsp" \) -ls
# 一句话木马的常见特征
grep -rlE '\$_(POST|GET|REQUEST)[[:space:]]*\[[^]]+\][[:space:]]*\(' \
/var/www --include=*.php 2>/dev/null | head -20
# /tmp 与 /dev/shm 里的近期可执行文件(内存马与挖矿的常见位置)
find /tmp /dev/shm -type f -newermt "-24 hours" -perm -u+x -ls 2>/dev/null
- 看到可疑文件先
cp到隔离目录再删,别直接rm。文件本身就是证据,stat里的三个时间戳能反推出落地时刻。 - 别忘了比对备份:用攻击前一天的备份跟现在
diff一次,改了什么一目了然,比 grep 特征可靠得多。
Q:账号和持久化怎么查?
- 对手进来之后几乎一定要留后路。持久化的位置就那么几个,逐个核对就行。
# UID 0 的账号,以及近期新建的普通账号
awk -F: '$3==0 {print}' /etc/passwd
awk -F: '$3>=1000 && $3<65534 {print $1,$3,$7}' /etc/passwd
# SSH 公钥有没有被追加(看修改时间最直观)
ls -l --time-style=long-iso /root/.ssh/authorized_keys /home/*/.ssh/authorized_keys 2>/dev/null
# 计划任务
crontab -l; ls -l /etc/cron.d/ /etc/cron.hourly/; ls -l /var/spool/cron/*/ 2>/dev/null
systemctl list-timers --all | head -20
# sudoers 免密提权
grep -rn "NOPASSWD" /etc/sudoers /etc/sudoers.d/ 2>/dev/null
- 重点不是「有没有 NOPASSWD」,而是「这条是不是我加的」。建议平时就把这几处的输出存成基线,出事时直接 diff。
- systemd 服务也常被用来做持久化:
systemctl list-unit-files里出现陌生服务名,就要看它的ExecStart指向哪里。
Q:Web 层能留下哪些证据?
- 如果攻击期间把日志留全了,Web 日志是最好用的证据源。重点看两类:上传类请求返回 200,以及上传目录里被执行过的脚本。
# 攻击窗口内成功的上传与导入请求
awk '$9==200 && $6 ~ /POST/' /var/log/nginx/access.log \
| grep -Ei 'upload|attach|editor|import|php' | head -40
# 上传目录里的脚本被访问过(且成功)
grep -E 'uploads/.*\.(php|phtml|phar)' /var/log/nginx/access.log \
| awk '$9==200' | head -20
# 当日最可疑的短 UA(脚本默认 UA 通常极短或为空)
awk -F'"' '{print $6}' /var/log/nginx/access.log \
| sort | uniq -c | sort -rn | head -20
- 注意日志格式差异:上面的
$6、$9是按 combined 格式的位置取的,如果你改过log_format,列号要跟着改。 - WAF 在应急期往往被切成观察模式或加了大量白名单,这段时间的恶意请求不会被拦,只会被记录。所以 WAF 日志和源站日志都要看,两边对不上就是缺口所在。
Q:怎么判断数据有没有被拖走?
- 三个方向:数据库里的大查询、主机上的导出文件、网络上的出向流量。
# 当前连接与大查询
mysql -e "SHOW FULL PROCESSLIST\G" | head -40
# 导出文件痕迹(INTO OUTFILE / dump)
mysql -e "SELECT event_time,user_host,argument FROM mysql.general_log \
WHERE argument LIKE '%INTO OUTFILE%' OR argument LIKE '%mysqldump%' LIMIT 20"
# 近期的大体积压缩包与 sql/csv(拖库后最常见的落地物)
find / -xdev -type f \( -name "*.sql" -o -name "*.csv" -o -name "*.zip" -o -name "*.tar.gz" \) \
-newermt "-48 hours" -size +10M -ls 2>/dev/null
# 出向流量 TOP 目的 IP(拖库是持续大流量出向)
ss -tn state established '( dport = :443 or dport = :80 )' | awk '{print $5}' \
| cut -d: -f1 | sort | uniq -c | sort -rn | head
mysql.general_log只有在开启 general log 时才有数据,没开就用 binlog 和慢查询日志反推:大批量SELECT、单次返回行数异常、连续扫同一张大表,都是特征。- 拖库最隐蔽的手法不是一条大 SQL,而是分页慢慢拉:每页 500 条、几分钟一轮,看起来像正常浏览。这种只能靠「单账号或单 IP 的累计返回行数」才能发现。
- 如果确认外流,先保留证据再断网:镜像磁盘、导出内存、复制日志,顺序错了后面就说不清了。
Q:应急期开的临时口子怎么收?
- 原则只有一条:临时放行必须带超时。裸
iptables -I加进去的规则,十有八九会忘。
# 运维临时放行统一走带超时的 ipset(2 小时后自动失效)
ipset create ops_tmp hash:ip timeout 7200
ipset add ops_tmp 203.0.113.10
iptables -I INPUT -m set --match-set ops_tmp src -j ACCEPT
# 看还有谁在白名单里
ipset list ops_tmp | tail -20
# 攻击结束后一次性清掉
ipset flush ops_tmp
iptables -D INPUT -m set --match-set ops_tmp src -j ACCEPT 2>/dev/null
# 核对是否还残留裸放行规则
iptables-save | grep -E 'ACCEPT' | grep -vE 'established|state|lo|icmp'
- 除了防火墙,还有五个地方最容易漏:云安全组(临时全开 0.0.0.0/0)、CDN 回源白名单(临时加了运维家里 IP)、WAF 白名单(把整个 /24 放过去了)、调试页面与备份文件(
info.php、db.sql、www.zip)、应急时新建的运维账号。 - 建议在预案里放一张「应急放行登记表」:谁、开了什么、为什么、多久、谁撤销。这张表在事后是最省事的东西。
Q:确认被入侵之后,处置顺序是什么?
- 先取证,再处置。
镜像磁盘 → 导出内存 → 复制日志 → 记录时间线,四步做完再动手。 - 断外联不要关业务:用安全组只保留运维 IP,把攻击者的通道切掉,业务面还能继续服务。
- 换掉所有可能已经泄露的凭据:数据库密码、API key、云 AK、SSH 密钥、后台管理员密码。只改一半等于没改。
- 只删木马不查入口,一定复发。找不到入口就重装,这是最省时间的做法,别舍不得。
Q:还有什么补充建议?
- 攻击期间别为了省 IO 关日志。真要降负载就改异步写入或降低采样率,日志断了,事后什么都查不出来。
- 平时每月存一份基线:文件哈希清单(aide 或
find -printf导出的结果)、账号列表、计划任务列表、开放端口列表。出事时一次 diff 顶半天排查。 - 打完 24 小时内做一轮对比巡检,重点看资源曲线是否回落、出向流量是否正常。拖库经常发生在攻击结束后的「安全期」,因为那时候没人盯着。
- 把「应急降级动作」和「撤销动作」成对写进预案。只写怎么开、不写怎么关的预案,等于给对手留门。