其中笔记多为查询ai获得
ai直接解释不清楚的 为自行查找并总结
strops()
strpos()
是 PHP 用来查找字符串 的函数,它用于查找 某个字符串 在 另一个字符串 中 首次出现的位置。
if (strpos($xdmtql, 'sys nb') !== false) {
echo 'flag{*******}';
} else {
echo 'true .swp file?';
}
strops函数匹配上传的参数里有sys nb
如果
$xdmtql
不匹配/sys.*nb/
(即sys
和nb
不能被任何字符隔开,除非是空格)如果
$xdmtql
里面有 “sys nb”,输出 flag{*******}(获取 flag)
否则,输出
“true .swp file?”`。
preg_match()
preg_match('/^\W+$/', $v3)
:这行代码检查v3
是否只包含非字母数字字符(即不是字母、数字或下划线)。这样可以确保v3
是一个运算符或其他特殊字符。
^\W+$ 代表不能是字母 数字 下划线
preg_match函数处理的字符长度有限,如果超过这个长度就会返回false也就是没有匹配到。
必要时可以通过回溯 即让字符长度超长 使preg_match返回false来绕过该函数
PHP 默认的 pcre.backtrack_limit 是 1,000,000(1MB),即 preg_match()
最多检查 1MB 的内容,超出会 直接返回 false
,不会报错。 (有一些用.+?之类的了 不够就继续加)
php代码
1 | echo str_repeat('very','10000000').'36Dctfshow'; |
1 | str_repeat('very', '250000') |
- 这会重复字符串
'very'
250000 次,生成一个非常长的字符串。
1 | .'36Dctfshow' |
- 在上面重复的
'very'
250000 次的字符串后面,拼接'36Dctfshow'
。
绕过示例代码
1 | import requests |
1️⃣ data=data
作用:指定 要发送的 POST 数据,通常是一个 字典 (
dict
) 或 字符串 (str
)。格式
发送
表单数据
1
2data = {"username": "admin", "password": "123456"}
requests.post(url, data=data)这会
以
application/x-www-form-urlencoded
形式发送1
username=admin&password=123456
发送
纯文本
1
requests.post(url, data="raw_text_data")
2️⃣ allow_redirects=False
作用:禁用 HTTP 自动跳转(默认
True
)。为什么要关掉自动跳转?
- 有些 CTF 服务器如果 验证失败,会 302 重定向 到错误页面。
- 如果
allow_redirects=True
(默认),requests 会自动跟随跳转,你可能看不到 原始返回内容。 - 但如果
allow_redirects=False
,你可以手动检查 是否返回了 302 状态码。
示例
1
2res = requests.post("http://example.com/login", data={"user": "admin"}, allow_redirects=False)
print(res.status_code) # 可能返回 302如果
res.status_code == 302
,说明服务器 想跳转。你可以查看
Location 头
1
print(res.headers.get("Location")) # 可能是 "/error"
文件上传靶场出现函数
trim 函数能够对上传文件的空格,换行符(%0a %0b %0d %20 %09)等多余字符进行删除,返回修改后的字符,
deldot删除文件名末尾的点,都是对文件名进行优化。
strrchr() 函数能够查找子字符串在字符串最后一次出现的位置(r即最右的值),返回该字符串到整体字符串结尾的所有字符。这里是返回上传文件的最后一个后缀名。
strtolower() 函数能够将这最后一个后缀名全部转换为小写。
str_ireplace() 函数能够实现字符串的部分替换,它的三个参数分别为‘查找值’;‘替换值’;‘待查找字符串’
in_array() 就是在数组中搜索值,第一个参数为搜索值,第二个参数为搜索范围
除去第一个条件判断是否有上传路径,通过提示和查看源码可以发现,服务器设置了一个黑名单
[‘tmp_name’] 是服务器端储存的临时文件名
date() 能够格式化本地日期和时间,并返回已格式化的日期字符串,具体返回值与给定format值有关。
move_uploaded_file() 即将文件移至相应位置。
intval()函数(https://blog.csdn.net/wangyuxiang946/article/details/131156104)
一、进制自动转换
二、转换数组
三、转换小数
四、转换字符串
五、取反~
六、算数运算符
七、浮点数精度缺失问题
三、intval()绕过思路
intval() 函数可以获取变量的「整数值」。常用于强制类型转换。
语法
int intval( $var, $base )
参数
$var:需要转换成 integer 的「变量」
$base:转换所使用的「进制」
返回值
返回值为 integer 类型,可能是 0 或 1 或 其他integer 值。
0:失败 或 空array 返回 0
1:非空array 返回 1
其他integer值:成功时 返回 $var 的 integer 值。
返回值的「最大值」取决于系统
32 位系统(-2147483648 到 2147483647)
64 位系统(-9223372036854775808到9223372036854775807)
一、进制自动转换
第二个参数 $base 允许为空。
当 base 为空时,默认值是 0,会根据 $var 的格式来调整转换的进制。
如果 $var 以 0 开头,就使用 8进制
如果 $var 以0x开头,就使用 16进制
否则,就使用 10进制
实例:
10的 8进制是12
var_dump(intval(012));
10的 16进制是A
var_dump(intval(0xA));
10的 10进制是10
var_dump(intval(10));
输出:
int(10)
int(10)
int(10)
绕过思路:当某个数字被过滤时,可以使用它的 8进制/16进制来绕过。
二、转换数组
intval() 转换数组类型时,不关心数组中的内容,只判断数组中有没有元素。
「空数组」返回 0
「非空数组」返回 1
实例:
var_dump(intval(array()));
var_dump(intval(array(3,2)));
输出:
int(0)
int(1)
如果传入的 $var是数组中的某个值时,则当做变量来转换,而不是当做数组类型。
实例:
$arr1 = array(8,6);
var_dump(intval($arr1[0]));
输出:
int(8)
绕过思路:对于弱比较(a==b),可以给a、b两个参数传入空数组,使弱比较为true。
三、转换小数
intval() 转换小数类型时,只返回个位数,不遵循四舍五入的原则。
实例:
var_dump(intval(12));
var_dump(intval(1.2));
var_dump(intval(1.9));
输出:
int(12)
int(1)
int(1)
绕过思路:当某个数字被过滤时,可以给它增加小数位来绕过。
四、转换字符串
intval() 转换字符串类型时,会判断字符串是否以数字开头
如果以数字开头,就返回1个或多个连续的数字
如果以字母开头,就返回0
单双引号对转换结果没有影响,并且 0 或 0x 开头也只会当做普通字符串处理。
实例:
var_dump(intval(‘12abc’));
var_dump(intval(“12abc”));
var_dump(intval(‘abc123’));
var_dump(intval(‘1a2b3c’));
var_dump(intval(‘0101’));
var_dump(intval(“0x2b”));
输出:
int(12)
int(12)
int(0)
int(1)
int(101)
int(0)
五、取反~
intval() 函数支持一些特殊符号的,比如~取反。
实例:
var_dump(intval(~10));
var_dump(intval(~~10));
输出:
int(-11)
int(10)
绕过思路:当某个数字被过滤时,可以两次取反来绕过。
六、算数运算符
intval() 函数支持算数运算符,如果传入的 $var参数包含算数运算符,会先运算,再对运算结果进行转换。
实例:
var_dump(intval(5*5));
var_dump(intval(5+5));
var_dump(intval(05+5));
输出:
int(25)
int(10)
int(10)
绕过思路:当某个数字被过滤时,可以使用算数运算符绕过。
七、浮点数精度缺失问题
由于PHP中的浮点数是「弱类型」,存在「精度丢失」的问题,在转换时可能会出现意料之外的情况。
比如下面这个案例,第一个输出34正常,第二个以为会输出58,结果输出了57。
实例:
var_dump(intval(0.34100.0));
var_dump(intval(0.58100.0));
输出:
int(34)
int(57)
三、intval()绕过思路
最后汇总一下intval()函数漏洞的绕过思路:
1)当某个数字被过滤时,可以使用它的 8进制/16进制来绕过;比如过滤10,就用012(八进制)或0xA(十六进制)。
2)对于弱比较(a==b),可以给a、b两个参数传入空数组,使弱比较为true。
3)当某个数字被过滤时,可以给它增加小数位来绕过;比如过滤3,就用3.1。
4)当某个数字被过滤时,可以给它拼接字符串来绕过;比如过滤3,就用3ab。(GET请求的参数会自动拼接单引号)
5)当某个数字被过滤时,可以两次取反来绕过;比如过滤10,就用~~10。
6)当某个数字被过滤时,可以使用算数运算符绕过;比如过滤10,就用 5+5 或 2*5
intval(“+010574”, 0)==4476 True。 有时会用+号绕过 或者url编码的%2b
ctype_alpha()
ctype_alpha()
只检测字母,不会检查数字和符号。
1 | for ($i=0;$i<strlen($emp);$i++){ |
例如这个就是检查$emp里有没有字母 如果有字母终止下面代码的执行 执行die 输出:你不是hacker?那请去外场等候!
stripos()
stripos($try, "HACKER")
stripos()
是 PHP 的一个字符串查找函数,作用是查找子字符串在字符串中首次出现的位置,不区分大小写。
语法
1 | stripos(string $haystack, string $needle, int $offset = 0): int|false |
参数
$haystack
(必填):要搜索的字符串。$needle
(必填):要查找的子字符串。$offset
(可选):从哪个位置开始查找,默认为0
。
返回值
- 如果找到
needle
,返回它的索引位置(从0
开始)。 - 如果找不到,返回
false
。
SqlFileObject
SplFileObject
是 PHP 的一个内置类,它是 Standard PHP Library (SPL) 的一部分,专门用于 处理文件的读取、写入 和 文件信息获取。通过 SplFileObject
,可以轻松地以面向对象的方式操作文件,提供了一些常用的文件操作方法。
SplFileObject
类的常用方法
1. 打开文件
可以使用 __construct()
来打开一个文件:
1 | $file = new SplFileObject('path/to/file.txt', 'r'); // 只读模式打开文件 |
第二个参数是模式,类似于
1
fopen()
,常见的模式有:
'r'
: 只读'w'
: 写入'a'
: 追加'r+'
: 读写
2. 逐行读取文件
SplFileObject
可以像数组一样访问文件内容,也可以逐行读取:
1 | $file = new SplFileObject('path/to/file.txt'); |
3. 获取当前行
可以通过 current()
方法获取当前行:
1 | echo $file->current(); // 获取当前行 |
- 获取文件信息
文件大小:
1
echo $file->getSize(); // 获取文件大小
文件路径:
1
echo $file->getRealPath(); // 获取文件的绝对路径
5. 写入文件
通过 SplFileObject
可以轻松写入文件:
1 | $file = new SplFileObject('path/to/file.txt', 'w'); // 写入模式打开文件 |
6. 读取文件的特定行
使用 seek()
方法可以将指针移动到文件中的特定行:
1 | $file->seek(2); // 移动到第三行 |
结合 php://filter
实现文件内容读取
SplFileObject
的强大之处在于它可以与 PHP 的流过滤器(如 php://filter
)结合使用。这可以让你对文件内容进行特殊的处理,如编码转换。
例如,利用 php://filter
读取文件并将内容 base64 编码:
1 | $file = new SplFileObject('php://filter/read=convert.base64-encode/resource=flag.php'); |
php://filter
是 PHP 的一个特殊流协议,它允许你在读取文件时应用过滤器。convert.base64-encode
是一个过滤器,它会将文件内容编码成 base64。- 通过这种方式,可以避免直接输出文件内容,而是输出其编码后的内容。
fgets
fgets()
是 PHP 中的一个文件操作函数,用于 从文件指针 读取一行内容。它会 逐行读取文件,直到遇到 换行符(\n
)或 文件末尾(EOF)。这是一个常用的文件读取函数,特别是在需要逐行处理文件内容时。
基本用法
1 | $file = fopen('path/to/file.txt', 'r'); // 打开文件以只读模式 |
参数说明
fgets()
函数有两个参数:
1 | fgets($file, $length); |
- $file:一个有效的文件指针(由
fopen()
或fopen()
相关函数返回)。 - $length(可选):指定读取的最大字节数。如果省略,则默认读取一行直到遇到换行符或文件结尾。
行为
fgets()
会读取并返回文件中的 一行文本,包含换行符(\n
)。- 如果到达文件末尾,
fgets()
会返回false
。 - 如果指定了
$length
,则最多读取$length
个字节(包括换行符)。
示例 1:逐行读取文件
1 | $file = fopen('file.txt', 'r'); |
示例 2:指定最大读取字节数
1 | $file = fopen('file.txt', 'r'); |
返回值
- 成功时:返回读取到的一行内容(字符串)。
- 失败时:返回
false
,通常表示文件结尾(EOF)或出现错误。
与 fread()
的区别
fgets()
每次读取一行(以换行符为结束标志)。fread()
读取固定长度的字节,无论是否是行的结束。
总结
fgets()
是一个用于读取文件中一行文本的常用函数,特别适用于需要逐行处理文件内容的场景,常与 fopen()
一起使用。它的关键优势是 逐行读取,不会一次性将整个文件加载到内存中,适用于读取较大的文件。
fread
fread()
是 PHP 中的一个文件操作函数,用于 从文件指针中读取指定字节数的内容。它不是逐行读取,而是根据传入的长度读取文件的部分内容。
用法
1 | file = fopen('path/to/file.txt', 'r'); // 打开文件以只读模式 |
参数
- $file:有效的文件指针(通过
fopen()
获取)。 - $length:要读取的字节数。
fread()
会最多读取$length
个字节。
返回值
- 如果成功,返回读取的内容(字符串)。
- 如果失败,返回
false
,通常在遇到文件末尾或其他错误时发生。
特点
fread()
不会按行读取,它是按字节读取的,可以用来读取文件的一部分(不一定是以换行符为界)。- 如果读取的字节数超过文件的剩余部分,
fread()
会读取整个文件直到 EOF。
示例 1:读取指定字节数
1 | $file = fopen('file.txt', 'r'); |
示例 2:读取整个文件内容
1 | $file = fopen('file.txt', 'r'); |
current
current()
是 PHP 中的一个数组函数,用于 返回数组中当前元素的值。它并不直接与文件操作相关,但在处理可迭代对象(如 SplFileObject
)时常常被使用。
用法
current()
用于访问数组中的当前元素或 SplFileObject
(一个类数组对象)的当前行。
1 | php复制编辑$array = ['apple', 'banana', 'cherry']; |
与 SplFileObject
配合使用
当你用 SplFileObject
逐行读取文件时,current()
可以用来获取文件中的当前行。
1 | php复制编辑$file = new SplFileObject('file.txt'); |
特点
current()
不需要传递参数,它会根据 当前的内部指针 返回当前元素。- 如果你使用
SplFileObject
,则current()
返回的是文件的当前行内容(每次调用后文件指针会向下移动)。
与 fgets()
的关系
fgets()
是 逐行读取文件,而current()
是获取 当前行内容(在迭代器中)。- 使用
SplFileObject
时,你可以用current()
来获取当前行,next()
来跳到下一行。
fread()
适合在读取文件的任意部分时使用(按字节读取)。
current()
适合在使用数组或迭代器(如 SplFileObject
)时读取当前元素(如当前行)。
php传参绕过点符号的问题
在 PHP 中,$_GET['e_m.p']
实际上是一个键名。由于点 (.
) 符号通常在 PHP 中被用作数组的层级分隔符,因此直接使用 e_m.p
会在某些情况下引发问题或受到过滤。然而,[ ]
是数组的符号,可以用来绕过这个问题。
如何绕过:
- 当你直接传递
e_m.p
时,PHP 会把它当作 数组键 来解析。 - 如果你使用
e[m.p
作为键,PHP 会 正确地解析它为一个数组键e[m.p
,而不需要担心点 (.
) 会作为数组的层级分隔符。
例如,传递 e[m.p
会变成一个单独的键名,这样就绕过了点符号的潜在问题。
更详细的解释:
PHP 中,
$_GET
数组中的键可以是任意字符串,默认情况下,点(.
)用作多维数组的分隔符。例如:1
$_GET['foo.bar'] // 会被解析为 $_GET['foo']['bar']
但是,如果你使用
[ ]
来包裹点符号,它会被当作一个普通的字符来处理,而不是层级分隔符。1
$_GET['e[m.p'] // 这里 `e[m.p` 被当作一个整体的字符串键
这样,
e[m.p
就不会被分解成多个层级,而是一个单一的字符串。它被视为一个合法的查询参数,可以顺利通过 PHP 的解析。
举个例子:
1 | // 如果你使用以下 URL: |
总结:
你需要传递 e[m.p
而不是 e_m.p
,是为了绕过 PHP 默认的 点符号 解析规则。使用 [ ]
来包裹点符号,可以避免 PHP 将其当作数组层级分隔符解析,从而保证你能够正确地传递这个参数。
有一道题 [ 为什么被转换成立_
在 PHP 中,$_GET
数组中的键名可能会经历一些特殊的处理和转换,特别是在 HTTP 请求过程中。如果你传递的 URL 包含某些特殊字符(如 [
, ]
, .
等),PHP 会尝试根据其规则进行解析和标准化,通常会进行 URL 解码或替换不被允许的字符。
关键原因:URL 编码和字符替换
- URL 编码和解码:
- 在 HTTP 请求的 URL 中,特殊字符(如
[
和]
)是 URL 编码 的。例如,[
会被编码为%5B
,]
会被编码为%5D
。 - 如果你在 URL 中传递
e[m.p
,它可能会被浏览器或 PHP 自动编码成e%5Bm.p
。然后,PHP 在接收到请求时,会自动对这些编码字符进行 URL 解码。
- 在 HTTP 请求的 URL 中,特殊字符(如
- PHP 的自动解析和处理:
- PHP 在解析查询字符串时会自动进行一些字符替换,特别是当它遇到某些符号时。例如,
[
和]
通常会被替换或忽略,导致它们在$_GET
中被转换为下划线(_
)或其他符号。 - 这是 PHP 的一种 容错机制,旨在确保不会出现不合法的数组键。具体地,在某些配置下,PHP 会自动将
[
和]
转换为_
,以确保不会引发解析错误或混乱。
- PHP 在解析查询字符串时会自动进行一些字符替换,特别是当它遇到某些符号时。例如,
为什么 [
和 ]
被替换为 _
:
在某些 PHP 配置或框架中,存在对 URL 中特殊字符的处理机制。以下是可能的原因:
- PHP 变量命名规范: PHP 对一些字符(如
[
和]
)有解析限制,认为它们不是合法的变量或数组键的一部分。因此,PHP 会自动将这些符号转换为下划线_
来确保变量名的合法性。 - 框架或安全过滤: 如果你在使用某些框架或在 PHP 配置中启用了某些安全选项,它们可能会强制将某些字符(如
[
,]
)转义或替换成下划线_
,以防止潜在的注入攻击或解析问题。 - URL 参数处理: 一些服务器和浏览器可能会对
[
和]
做特殊的处理。它们会自动将这些字符进行转换,以避免 PHP 在处理数组参数时出错。
如何验证这一点:
你可以尝试以下的实验,看看 PHP 如何处理 URL 中的特殊字符:
传递参数时,编码特殊字符:
1
http://example.com/index.php?e%5Bm.p%5D=114514
PHP 解析时:
1
var_dump($_GET);
你可能会看到 PHP 会自动对 [ ]
进行转换,可能变成了 _
或其他字符。
总结:
- PHP 会对 URL 中的特殊字符(如
[
,]
)进行 URL 解码,并可能会自动 替换或转义 这些字符为下划线_
,以避免解析问题。 - 这种替换行为可能是由 PHP 的解析规则、URL 编码解码机制 或 框架的特殊处理 引起的。
因此,[ ]
被转换为 _
,通常是 PHP 在处理请求时为避免解析错误或不合法字符所做的自动调整。
在linux下面表示当前目录是 ./
当前目录访问 ./flag.php
md5绕过
https://blog.csdn.net/2301_80307383/article/details/140827718
强类型
数组绕过md5($a)===md5($b)且$a!==$b
原理:md5对数组加密结果为NULL
传入a[]=1&b[]=2即可绕过,数组绕过也可用于弱类型
is_numeric()
函数
is_numeric()
是 PHP 内置的一个函数,用于检查变量是否为 数字或数字字符串。
substr()
函数
substr()
是 PHP 内置函数,用于截取字符串的一部分。
array_push()
函数
🔹 语法
1 | array_push(array &$array, mixed ...$values): int |
$array
:目标数组(会被修改)$values
:要添加的值(可以是多个)- 返回值:返回新数组的长度
array_push()
返回新数组长度
1 | $colors = ["Red", "Blue"]; |
🛠 用途
🔹 动态数组扩展
1 | $stack = []; |
🔹 模拟栈(LIFO - 后进先出)
1 | $stack = []; |
🔹 合并多个值
1 | $list = []; |
rand
rand(1, $i)
生成一个介于 1
和 $i
之间的随机数
in_array($_GET['n'], $allow)
作用
in_array($_GET['n'], $allow)
用于检查 $_GET['n']
是否存在于 $allow
数组中。
in_array()
详解
1 | in_array(mixed $needle, array $haystack, bool $strict = false): bool |
$needle
:要搜索的值(这里是$_GET['n']
)。$haystack
:目标数组(这里是$allow
)。$strict
(可选):默认false
,如果设为true
,会进行类型严格比较(===
)。
ReflectionClass
的作用
ReflectionClass
是 PHP 的一个类,用来获取一个类的结构、方法、属性等信息。在这个场景下,new ReflectionClass('ctfshow')
会返回一个 ReflectionClass
对象,它包含了关于 ctfshow
类的反射信息。这个类可能包含 flag(如之前提到的注释所示)。
在某些情况下,ReflectionClass
可能会允许访问私有属性、方法,甚至是类中的 flag。
例如
eval(“echo new ReflectionClass(‘ctfshow’);”)
就会创建一个新的 ReflectionClass
对象,ReflectionClass
是 PHP 中用于反射类的一个类。
也就是将ctfshow类里的内容反射到ReflectionClass中
再通过echo输出
hex2bin()
函数
hex2bin可以将传入的十六进制数据解码为原始数据
可以传一下只能用数字传入的木马
1 | ... |
:这是 PHP 的短标签语法,用于输出表达式的结果。 相当于<?php echo””
`cat *`
:这是一个 shell 命令,会读取并输出当前目录下所有文件(`*`)的内容。1
2
3
4
5
:这是 PHP 中的反引号操作符,用于执行其中的命令作为 shell 命令。在这里,它会在 shell 中运行 `cat *`。
- ```php
`cat *`
PHP 中关于哈希函数的返回结果
不论是 md5 还是 sha1 函数,当其参数为一个数组时,函数不能将其转换为对应的哈希值,因此返回值为空(即 null),但并不是 false,而是 warning
false 的程序讨论其返回值是没有意义的,但 warning 的程序返回值会根据实际情况而有所不同
因此,当哈希函数的参数为数组时,其返回的值为 null 而不是 false
另一种解释sh1 两个数组的话指针在比较时不相等,但作为sha1的参数时,会调用方法变成字符串Array,故sha1相等
sh1碰撞 aaK1STfY aaO8zKZF
parse_str($v1,$v2);
parse_str($b);
的作用是解析 b=key=value
形式的字符串,并将变量 key
赋值为 value
。
比如这个要给a[0]赋值 但是不能直接传参 就可以通过parse_str函数 传参b=a[ ]=“xxx” 将 a[]赋值为xxx
parse_str()
是一个 PHP 内置函数,它的作用是将查询字符串解析为 PHP 变量
这行代码的作用是:
- 解析字符串
$v1
,并将解析结果存入数组$v2
。
示例 1:基本解析
1 | $v1 = "name=Darkxell&age=25"; |
输出:
1 | Array |
✅ $v2
成功存储了解析后的键值对。
示例 2:解析数组格式
1 | $v1 = "user[name]=Darkxell&user[age]=25"; |
输出:
1 | Array |
✅ parse_str()
能解析数组格式的查询字符串,将 user[name]
和 user[age]
解析成嵌套数组。
示例 3:处理特殊字符
1 | $v1 = "message=Hello%20World%21"; |
输出:
1 | Array |
✅ parse_str()
自动解码 URL 编码的字符,如 %20
→ 空格,%21
→ !
。
本题中**$v2['flag']
代表 $v2
数组中 flag
这个键对应的值**。
也就是我们传入的v1 要给它一个flag键
然后parse_str函数会将flag的键值存储到$v2里然后与md5($v3)比较
还可以v3[]=1 数组绕过 让其为NULL v1随便传
也可以v3=240610708 md5编码后为0e462097431906509019562988736854
v1=0 进行0e绕过
strrev()函数
strrev()
是 PHP 的一个内建函数,用于 反转字符串。
语法:
1 | string strrev(string $string) |
$string
:要反转的字符串。
返回值:
- 返回一个反转后的字符串。
ereg函数
功能基本和preg_match()函数一样
ereg现在在高版本php环境已废弃
它存在NULL截断漏洞,可以导致正则过滤被绕过,必要时可以使用%00截断正则匹配
例如a%00778 他就只会检查a 而不会检查%00后面的778
Exception
异常处理类
在 PHP 中,Exception
是 所有异常的基类。当代码运行时发生错误,我们可以 抛出(throw)一个 Exception
,然后在 catch
语句中捕获它,执行错误处理逻辑。
执行方法和Reflectionclass类似 但二者意义不同
Exception
本质上是一个普通的类,它包含了异常的信息,如错误消息、错误代码、发生的文件和行号等。
也就是Exception主要返回错误信息
Reflectionclass是动态调用一个方法
用途不同
类 | 作用 |
---|---|
Exception |
异常处理(用于错误捕获) |
ReflectionClass |
反射 API(用于动态分析和操作类) |
ReflectionFunction
也可以用于信息泄露
DOMDocument
也可以用于文件读取攻击
getcwd()
用法
getcwd()
是 PHP 内置函数,返回当前工作目录(Current Working Directory, CWD)。
GLOBALS
数组
在 PHP 中,GLOBALS
是一个特殊的超级全局数组,它包含了所有全局作用域中的变量。通过这个数组,你可以访问或修改任何全局变量,即使是在函数或方法内,也能跨作用域访问全局变量。
GLOBALS
数组
GLOBALS
数组是 PHP 内置的,所有全局变量都可以通过这个数组进行访问。其键是全局变量的名称,值是变量的值。
访问全局变量
假设有一个全局变量 $foo
:
1 | $foo = "Hello, World!"; |
在函数中,你可以通过 GLOBALS
数组访问它:
1 | function test() { |
也就是利用GLOBALS访问全局变量 因为不知道flag变量 访问它flag可能在其中
is_file()函数
is_file()的
的长度限制依赖于操作系统的最大路径长度限制。
在大多数操作系统中,路径的最大长度为 4096 字节(Linux 和 macOS)或 260 字符(Windows,除非启用长路径支持)。
如果路径超出了操作系统允许的最大长度,操作系统会返回错误,导致 is_file()
返回 false
。
语法:
1 | is_file(string $filename): bool |
参数:
$filename
是文件路径的字符串,可以是相对路径或绝对路径。返回值
:
true
:如果指定的路径指向一个存在的常规文件(而不是目录、符号链接或其他类型的文件)。false
:如果指定的路径不存在、是目录或其他类型的文件。
功能:
is_file()
用于检查指定路径是否是一个常规文件。它不检查目录、符号链接等。如果指定路径存在并且是一个文件(不是目录),则返回 true
;如果路径不存在或是一个目录,则返回 false
。
例子
- 检查文件是否存在并且是一个常规文件:
1 | $file = 'path/to/your/file.txt'; |
检查文件是否是目录: 如果你想检查文件是否是目录,可以使用
is_dir()
函数。1
2
3
4
5
6
7$dir = 'path/to/your/directory';
if (is_dir($dir)) {
echo "This is a directory.";
} else {
echo "This is not a directory.";
}示例:检查文件是否存在并读取内容:
1
2
3
4
5
6
7
8$file = 'example.txt';
if (is_file($file)) {
$content = file_get_contents($file); // 读取文件内容
echo $content;
} else {
echo "The file does not exist.";
}
常见用途
- 验证文件是否存在并且可以操作:确保路径指向的是一个有效的文件,而不是目录或不存在的路径。
- 读取文件前验证:在读取文件前使用
is_file()
来避免错误。 - 文件上传检查:上传文件时,可以用
is_file()
检查文件路径,确保上传的是文件。
注意事项
is_file()
不会检查文件是否具有读取权限。如果你需要检查文件的读取权限,可以结合使用is_readable()
函数:1
2
3if (is_file($file) && is_readable($file)) {
echo "The file exists and is readable.";
}is_file()
只能检查普通文件,无法检测符号链接或目录,若需要分别处理目录、符号链接等,可以使用is_dir()
和is_link()
等函数。
总结
is_file()
是一个用于检查路径是否是常规文件的有用函数,在处理文件时常常用来确保操作的是有效的文件,而不是目录或其他文件类型。
php文件读取新方法
php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
这个流包装器将对文件内容进行 字符编码转换。
convert.iconv.UCS-2LE.UCS-2BE
:- 将文件内容从 UCS-2 Little Endian (LE) 编码转换为 UCS-2 Big Endian (BE) 编码。
- 文件
flag.php
将在读取时应用这种编码转换。
- 用法:这种转换可以在读取文件时进行字符编码转换。
示例:
1 | $file = file_get_contents('php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php'); |
这将读取 flag.php
文件,并在读取时将其内容从 UCS-2 Little Endian 编码转换为 UCS-2 Big Endian 编码。
php://filter/read=convert.quoted-printable-encode/resource=flag.php
这个流包装器将文件内容 以 quoted-printable 编码进行转换。
convert.quoted-printable-encode
:- 将文件内容转换为 quoted-printable 编码,这种编码通常用于电子邮件中传输非 ASCII 字符或二进制数据。
- 用法:这种转换通常用于编码文件内容,以便将其传输到电子邮件或需要编码的其他系统。
示例:
1 | file = file_get_contents('php://filter/read=convert.quoted-printable-encode/resource=flag.php'); |
这将读取 flag.php
文件,并对其内容应用 quoted-printable 编码。
compress.zlib://flag.php
这个流包装器允许你读取 flag.php
文件 并应用 zlib 压缩。
compress.zlib://
:
- 这个包装器用于读取经过 zlib 压缩的文件,并且会在读取时自动解压缩文件内容。
- 它可以在读取压缩文件时避免手动解压。
示例:
1 | $file = file_get_contents('compress.zlib://flag.php'); |
这将读取 flag.php
文件的压缩内容,并使用 zlib 解压缩后返回其原始内容。
常见的过滤器:
convert.quoted-printable-encode
convert.iconv.*
zlib.deflate php://filter/zlib.deflate|zlib.inflate/resource=flag.php
bzip2.compress
string.rot13
string.tolower
convert.base64-decode
在php中/proc/self/root代表根目录
%0a
:这是 换行符 (LF)(ASCII 值 10)的 URL 编码,通常表示换行符 (\n
)。
%0b
:这是 垂直制表符 (VT)(ASCII 值 11)的 URL 编码,是一个不可打印字符,用于垂直制表。
%0d
:这是 回车符 (CR)(ASCII 值 13)的 URL 编码,通常用于将光标移动到行的开头 (\r
)。
%20
:这是 空格字符(ASCII 值 32)的 URL 编码,常用于表示单词之间的空格。
%09
:这是 水平制表符 (HT)(ASCII 值 9)的 URL 编码,通常表示水平制表空格(\t
)。
%0c
:这是 换页符 (FF)(ASCII 值 12)的 URL 编码,是一个不可打印字符,用于分页
$_SERVER[‘argv’]
$_SERVER['argv']
是一个包含命令行参数的数组,在 CLI(命令行接口)模式下使用。它存储了通过命令行传递给 PHP 脚本的所有参数。每个命令行参数都会作为数组中的一个元素,索引为整数。
$_SERVER['argv'][0]
是脚本的名称或路径(即运行的 PHP 脚本)。$_SERVER['argv'][1]
、$_SERVER['argv'][2]
等 是传递给脚本的其他参数。
isset()函数
主要检查一个变量是否存在
isset()
会返回 false
对于 null
和 未定义的变量,但它会返回 true
对于 空字符串 和 0 等。
empty()
函数
empty()
函数用于检查一个变量是否为空。“空” 的标准包括:
- 变量没有设置(未定义)。
- 变量的值为
null
。 - 变量的值为
""
(空字符串)。 - 变量的值为
0
或0.0
。 - 变量的值为
false
。 - 变量是空数组。
is_null()
函数
is_null()
用于检查变量是否为 null
。
[ 与. 特性
php中变量名只有数字字母下划线,被get或者post传入的变量名,如果含有空格、+、[则会被转化为_,所以按理来说我们构造不出CTF_SHOW.COM这个变量(因为含有.),但php中有个特性就是如果传入[,它被转化为_之后,后面的字符就会被保留下来不会被替换
get_defined_vars()
get_defined_vars()
是 PHP 内置的一个函数,它返回当前作用域内所有已定义的变量,以关联数组的形式返回,键是变量名,值是变量的内容。例如:
1 | $a = "hello"; |
输出:
1 | Array |
这个数组包含所有在当前作用域内的变量,包括用户定义的变量(如
$a, $b, $c
),以及一些 PHP 预定义的变量(如$_GET, $_POST, $_SERVER, $_SESSION
等)。
var_export()
var_export()
用于将变量的值以PHP 代码字符串的形式输出,类似于 print_r()
,但它的输出格式更适合直接用于代码中。例如:
1 | $a = [1, 2, 3]; |
输出:
1 | array ( |
这个输出格式可以直接复制粘贴到 PHP 代码中,并作为数组变量使用。
extract()
是什么
1. extract()
是什么?
extract()
是 PHP 的一个内置函数,它用于从数组中提取键值对,并将键作为变量名,值作为变量值赋值到当前作用域。
语法:
1 | extract(array $array, int $flags = EXTR_OVERWRITE, string $prefix = ""): int |
$array
:要提取的关联数组,键成为变量名,值成为变量值。$flags
(可选):定义如何处理已有变量(后面详解)。$prefix
(可选):如果键是无效变量名,可以添加前缀。
2. 基本用法
1 | $data = [ |
这里,extract($data)
相当于:
1 | $name = "Darkxell"; |
3. extract()
的 flags
参数
$flags
参数决定如何处理数组中的变量名如果已经存在的情况:
EXTR_OVERWRITE
(默认):覆盖已存在的同名变量。EXTR_SKIP
:跳过已有变量,不覆盖。EXTR_PREFIX_SAME
:如果变量名已存在,加前缀($prefix
)。EXTR_PREFIX_ALL
:所有变量都加前缀(无论是否冲突)。EXTR_PREFIX_INVALID
:仅对无效变量名加前缀。EXTR_IF_EXISTS
:仅覆盖已有变量,不创建新变量。EXTR_PREFIX_IF_EXISTS
:仅对已有变量加前缀,不创建新变量。
示例 1:防止覆盖
1 | $data = [ |
示例 2:使用前缀
1 | $data = [ |
示例 3:避免非法变量名
1 | $data = [ |
4. extract()
在 CTF 和安全中的应用
(1)信息泄露
extract()
可能导致变量覆盖或信息泄露,比如:
1 | extract($_GET); |
如果 GET
传递:
1 | ?flag=CTF{hidden_flag} |
那么 $flag
变量就会被赋值,直接显示出 CTF{hidden_flag}
。
(2)变量覆盖漏洞
如果代码如下:
1 | $admin = false; |
攻击者发送 POST
请求:
1 | admin=1 |
则 $admin
被覆盖为 1
,绕过了权限校验。
(3)配合 GLOBALS
变量劫持
1 | extract($_GET); |
GET 请求:
1 | ?flag=CTF{hacked} |
会导致 flag
变量被覆盖。
compact()
如果 extract()
是数组转变量,那么 compact()
就是变量转数组:
1 | $name = "Darkxell"; |
输出:
1 | Array |
extract() |
从数组创建变量 |
---|---|
compact() |
从变量创建数组 |
---|---|
assert()
函数
PHP 中,assert()
是一个内置函数,用于检查一个表达式是否为真。如果为真,则什么都不做;如果为假,则会抛出一个警告。
通常,assert()
用来进行调试,但是在某些情况下,assert()
也可以用来执行代码,这取决于它传递的参数。
1 | assert(expression); |
expression
是需要检查的一个表达式。一般来说,assert()
会判断这个表达式是否为真。- 如果表达式为
false
,assert()
会触发一个警告并且停止执行(通常这是在开发环境中用于调试的工具)。
- 通过
assert()
执行代码
在某些情况下,assert()
也可以用来执行代码。如果传入给 assert()
的是一个可以作为 PHP 代码执行的字符串,它将会作为一段 PHP 代码执行。
比如:
1 | assert('phpinfo();'); |
这将会执行 phpinfo()
函数,并显示 PHP 配置信息。
比如这个题里 我传入?$fl0g=flag_give_me
因为server 就相当与传入了一个 $_GET[‘fl0g’]=flag_give_me
但是现在还没有将flag_give_me真正储存到$fl0g这个变量里
所以我们就需要通过fun=assert($a[0]) 因为a=server[‘argv’] 相当于传入了一个数组
a[0]就是第一个元素 $fl0g=flag_give_me 而assert就会执行$f10g=flag_give_me 这个php的有效代码
成功把flag_give_me储存到$f10g里
从而成功执行echo $f10g
$_SERVER[‘QUERY_STRING’] $_SERVER[‘argv’]
$_SERVER['QUERY_STRING']
和 $_SERVER['argv']
都是 PHP 中常见的全局变量,但它们的使用场景和内容有所不同。我们来详细解释它们之间的区别:
$_SERVER['QUERY_STRING']
定义:
$_SERVER['QUERY_STRING']
包含 URL 中?
后面的部分,即查询字符串(query string)。查询字符串是 URL 中传递参数的一部分。例如,在 URLhttp://example.com/index.php?a=1&b=2
中,a=1&b=2
就是查询字符串。示例:
假设 URL 是http://example.com/index.php?user=admin&role=admin
,那么:1
echo $_SERVER['QUERY_STRING'];
输出将会是:
1
user=admin&role=admin
用途:
你通常使用$_SERVER['QUERY_STRING']
来获取 URL 中?
后面的所有参数及其值。它可以帮助你解析 URL 查询字符串,但它不会将这些参数转换为关联数组。你需要使用parse_str()
等函数将查询字符串解析为 PHP 数组。
$_SERVER['argv']
定义:
$_SERVER['argv']
包含通过命令行传递给 PHP 脚本的参数。也就是说,它用于 PHP 脚本在命令行模式下执行时,从命令行接收的参数。示例:
假设你在命令行中运行 PHP 脚本:1
php script.php arg1 arg2 arg3
那么在脚本中,
$_SERVER['argv']
将会是一个包含所有参数的数组:1
print_r($_SERVER['argv']);
输出:
1
2
3
4
5
6
7Array
(
[0] => script.php
[1] => arg1
[2] => arg2
[3] => arg3
)用途:
$_SERVER['argv']
主要用于命令行界面的 PHP 脚本,允许开发者从命令行传递参数。它不是用于处理 HTTP 请求。
3. 区别:
特性 | $_SERVER['QUERY_STRING'] |
$_SERVER['argv'] |
---|---|---|
来源 | 从 URL 的查询字符串中获取参数,通常用于 HTTP 请求 | 从命令行中传递给 PHP 脚本的参数 |
例子 | http://example.com/index.php?a=1&b=2 |
php script.php arg1 arg2 arg3 |
返回值 | 查询字符串,格式为键值对(例如 a=1&b=2 ) |
包含命令行参数的数组(例如 ['script.php', 'arg1', 'arg2'] ) |
用途 | 用于 Web 请求中获取 URL 参数 | 用于命令行脚本传递参数 |
可用性 | 在 Web 环境中使用 | 仅在命令行环境中使用 |
4. 代码示例对比:
$_SERVER['QUERY_STRING']
:
假设 URL 为http://example.com/index.php?a=1&b=2
,你可以这样访问查询字符串:1
echo $_SERVER['QUERY_STRING']; // 输出: a=1&b=2
$_SERVER['argv']
:
假设你在命令行中运行 PHP 脚本:1
php script.php arg1 arg2 arg3
你可以访问命令行参数:
1
print_r($_SERVER['argv']); // 输出: Array([0] => script.php, [1] => arg1, [2] => arg2, [3] => arg3)
5. 总结:
$_SERVER['QUERY_STRING']
是 Web 环境中的查询字符串,它包含了 URL 中?
后的所有参数。常用于 Web 页面的请求中。$_SERVER['argv']
是命令行环境中的参数,包含了从命令行传递给 PHP 脚本的所有参数。常用于命令行模式下执行的 PHP 脚本。
它们的主要区别在于来源和使用场景:$_SERVER['QUERY_STRING']
用于 Web 请求中的 URL 查询参数,而 $_SERVER['argv']
用于命令行中的参数。
call_user_func
var_dump(call_user_func(call_user_func($f1,$f2)));
call_user_func($f1, $f2) 会尝试执行 $f1 所代表的函数,并将 $f2 作为参数传递给该函数。
如果 $f1 是一个有效的函数名,并且该函数能够接受 $f2 作为参数,call_user_func($f1, $f2) 会执行该函数。
结果会进一步传递给 var_dump() 进行输出。
外层的 call_user_func
接下来,外层的 call_user_func
会接收到内层 call_user_func
返回的结果,并再次执行它。
假设内层的 call_user_func
返回的是 'HELLO'
,那么:
1 | call_user_func('HELLO'); |
PHP 会尝试执行 HELLO
作为函数名。然而,HELLO
不是一个有效的函数名,因此会导致一个错误。
问题的核心
这里的问题是,call_user_func(call_user_func($f1, $f2))
看起来是一个嵌套调用,但由于内层 call_user_func
的返回值并不是一个有效的函数名,它会引发错误。你需要确保内层 call_user_func
返回的值实际上是一个有效的函数名。
get_defined_vars
get_defined_vars()
是 PHP 的一个内置函数,它返回当前作用域(或范围)内所有已定义的变量及其值。这个函数可以用于检查当前 PHP 脚本中定义了哪些变量,包括全局变量、局部变量以及通过 $_GET
、$_POST
等超全局数组传递的变量。
mt_rand(1,0x36D)
mt_rand(min, max)
是 Mersenne Twister 伪随机数生成器,用于生成min
到max
之间的随机整数(包含min
和max
)。0x36D
是一个 十六进制数,等于 十进制的877
。
计算结果
1 | mt_rand(1, 0x36D) === mt_rand(1, 877) |
这个函数会返回 1 到 877 之间的随机整数。
URL 传参时的特殊字符处理规则(.绕过)
传上去的参数只会有数字 字母与下划线
[ ]
→ 变成_
(因为 PHP 解析时可能会把e[m.p]
认为是数组).
→ 变成_
(因为.
在 PHP 变量名中无效).
只有在 顶级键 才会变成_
-
→ 不会被转换(可以作为数组键)- 其他特殊字符(如
@
,#
,*
,+
等)不会被解析成键名,但可以作为值
tee命令
tee指令会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件
比如 exec(ls /|tee 1) 会将ls / 列出的目录 保存到1文件中
管道符 |
是在 Linux 和类 Unix 系统中用于连接多个命令的特殊符号,它的作用是将前一个命令的标准输出(stdout)作为下一个命令的标准输入(stdin)。
strripos()
strripos()
是 PHP 的一个函数,它用于查找字符串在另一个字符串中最后一次出现的位置(不区分大小写)。它返回的是子字符串的位置,如果没有找到,返回 false
。
strripos()
函数的语法:
1 | strripos($haystack, $needle) |
$haystack
:要搜索的字符串(大字符串)。$needle
:要查找的子字符串(小字符串)。- 如果找到了,返回子字符串在大字符串中最后出现的位置(以 0 为起始索引)。如果没有找到,返回
false
。
web137
call_user_func($_POST[‘ctfshow’]);
ctfshow[0]=ctfshow&ctfshow[1]=getFlag
在这种情况下,$_POST['ctfshow']
是一个数组:
1 | $_POST['ctfshow'] = array(0 => 'ctfshow', 1 => 'getFlag'); |
这里,$_POST['ctfshow']
数组包含两个元素:'ctfshow'
和 'getFlag'
。当 call_user_func()
被调用时,PHP 会解析数组并尝试将其作为方法调用。具体来说:
$_POST['ctfshow'][0]
是'ctfshow'
类。$_POST['ctfshow'][1]
是'getFlag'
方法。
所以,call_user_func($_POST['ctfshow'])
将等效于:
1 | ctfshow::getFlag(); |
web140
在 PHP 中,如果你将一个字符串与整数进行比较,PHP 会尝试将字符串转换为数值。
usleep()
函数:
usleep()
是一个 PHP 函数,接受一个整数参数并使程序暂停一段时间(微秒)。
如果 usleep()
被传递 null
或无效参数时,通常 PHP 会抛出一个警告。但是有可能,在某些情况下(如 null
被转换为 0
),usleep()
可能不抛出警告或 PHP 会以某种方式“静默”处理它。
switch循环语句传参
在 PHP 中,switch
语句的比较是通过 严格比较(==) 来进行的。当你传递一个非数字字符串(例如 "flag"
)时,PHP 会尝试将其转换为数字。这个转换过程有些特殊,通常会把任何非数字字符串转换为 0
。