[代码审计]PCWAP
为什么想要审计这套源码呐?之前看到某大佬在做反钓鱼网站的时候,发现钓鱼网站的后台用的就是PCWAP,所以我觉得有必要审计一下,顺便记录,打击网络犯罪!
0x00 PCAWAP:
PCWAP手机网站建站平台是一套可以实现PC和WAP手机版网站同一后台管理的PHP免费开源手机建站CMS系统。我简单看了一下,和ThinkPHP差不多的结构。
PCWAP
官网下载地址:http://www.pcwap.cn/1.html
下面我简单根据漏洞类型来审计…
小东在审计的过程中,发现有很多的漏洞,不好文字描述,所以下面的东西,复杂的,就直接给出 EXP
…
0X01 敏感信息泄漏:
1.安装完成后,虽然不存在重装漏洞,但是并未删除Install/pcwap.sql
文件,导致数据库字段信息泄漏
2.默认后台路径:http://www.test.com/index.php?s=/Admin
3.默认数据库备份文件地址:http://www.test.com/Data/pcwap.sql
http://www.test.com/index.php?Data/pcwap_admin.sql
如果没对这个目录做限制,有这两个东西,还不是…
4.默认是开启了Debug模式,当访问不存在的模块时,会爆出绝对路径。
EXP: http://www.test.com/index.php?s=/9%27
EXP: http://www.test.com/Lib/Action/EmptyAction.class.php
0x02 XSS:
首先来到留言板,黑盒留言测试
火狐浏览器作为管理员已登录,来到后台查看评论就弹窗:
这样一个存储性XSS
就确定存在了,可以打到管理员的cookie
看 Tpl\Admin\Message\index.html
其中的留言等各个参数都是直接以变量输出,未做过滤,那么再去看看存入数据库的呐?
在 Lib\Action\Home\MessageAction.class.php
<?php
class MessageAction extends CommAction {
public function index(){ if($this->ispost()){ if(session('code') != md5(htmlspecialchars(addslashes($_POST['code']),ENT_QUOTES))){
$this->error('验证码错误');
}
if($_POST['title']==false ){
$this->error('标题不能为空');
}
if($_POST['username']==false ){
$this->error('姓名不能为空');
}
if($_POST['mail']==false ){
$this->error('邮箱不能为空');
}
if($_POST['content']==false ){
$this->error('内容不能为空');
}
$data=$_POST;
$data['time']=time();
if(M('message')->data($data)->add()){
$this->success('留言成功');
}else{
$this->error('留言失败');
} }else{
$this->display();
}
}
} //data() 函数
public function data($data=''){
if('' === $data && !empty($this->data)) {
return $this->data;
}
if(is_object($data)){
$data = get_object_vars($data);
}elseif(is_string($data)){
parse_str($data,$data);
}elseif(!is_array($data)){
throw_exception(L('_DATA_TYPE_INVALID_'));
}
$this->data = $data;
return $this;
}
data()
函数只是将字符串格式化,add()
函数写入数据库…
不只是XSS,还有可能存在SQL注入呐~
0x03 SQL注入:
在 Common\common.php
中看到过滤函数
function inject_check($sql_str) {
return eregi ( 'select|inert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|UNION|into|load_file|outfile', $sql_str );
}
有上面这个函数调用的话,就不存在什么注入了!
直接看登陆是否存在SQL注入,这样就可以绕过了!
登录验证在\Lib\Action\Admin\LoginAction.class.php
中
是做了过滤的。
继续看看留言板!这是用户和后台交互的地方!
MYSQL数据库监控得到如下结果:
这里做了转义,再看看代码!
data()
函数:
/**
* 设置数据对象值
* @access public
* @param mixed $data 数据
* @return Model
*/
public function data($data=''){
if('' === $data && !empty($this->data)) {
return $this->data;
}
if(is_object($data)){
$data = get_object_vars($data);
}elseif(is_string($data)){
parse_str($data,$data); //在GPC开启下会调用addslashes() 转义函数
}elseif(!is_array($data)){
throw_exception(L('_DATA_TYPE_INVALID_'));
}
$this->data = $data;
return $this;
}
再看 add()
函数:
* 新增数据
* @access public
* @param mixed $data 数据
* @param array $options 表达式
* @param boolean $replace 是否replace
* @return mixed
*/
public function add($data='',$options=array(),$replace=false) {
if(empty($data)) {
// 没有传递数据,获取当前数据对象的值
if(!empty($this->data)) {
$data = $this->data;
// 重置数据
$this->data = array();
}else{
$this->error = L('_DATA_TYPE_INVALID_');
return false;
}
}
// 分析表达式
$options = $this->_parseOptions($options);
// 数据处理
$data = $this->_facade($data);
if(false === $this->_before_insert($data,$options)) {
return false;
}
// 写入数据到数据库
$result = $this->db->insert($data,$options,$replace);
if(false !== $result ) {
$insertId = $this->getLastInsID();
if($insertId) {
// 自增主键返回插入ID
$data[$this->getPk()] = $insertId;
$this->_after_insert($data,$options);
return $insertId;
}
$this->_after_insert($data,$options);
}
return $result;
}
再追溯 insert()
函数:
* 插入记录
* @access public
* @param mixed $data 数据
* @param array $options 参数表达式
* @param boolean $replace 是否replace
* @return false | integer
*/
public function insert($data,$options=array(),$replace=false) {
$values = $fields = array();
$this->model = $options['model'];
foreach ($data as $key=>$val){
if(is_array($val) && 'exp' == $val[0]){
$fields[] = $this->parseKey($key);
$values[] = $val[1];
}elseif(is_scalar($val) || is_null(($val))) { // 过滤非标量数据
$fields[] = $this->parseKey($key);
if(C('DB_BIND_PARAM') && 0 !== strpos($val,':')){
$name = md5($key);
$values[] = ':'.$name;
$this->bindParam($name,$val);
}else{
$values[] = $this->parseValue($val);
}
}
}
$sql = ($replace?'REPLACE':'INSERT').' INTO '.$this->parseTable($options['table']).' ('.implode(',', $fields).') VALUES ('.implode(',', $values).')';
$sql .= $this->parseLock(isset($options['lock'])?$options['lock']:false);
$sql .= $this->parseComment(!empty($options['comment'])?$options['comment']:'');
return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array()));
}
进一步看 parserKey()
函数:
/**
* 字段和表名处理添加`
* @access protected
* @param string $key
* @return string
*/
protected function parseKey(&$key) {
$key = trim($key);
if(!preg_match('/[,\'\"\*\(\)`.\s]/',$key)) {
$key = '`'.$key.'`';
}
return $key;
}
原来是在 before_insert()
函数,做了转义:
* 对保存到数据库的数据进行处理
* @access protected
* @param mixed $data 要操作的数据
* @return boolean
*/
protected function _facade($data) {
// 检查非数据字段
if(!empty($this->fields)) {
foreach ($data as $key=>$val){
if(!in_array($key,$this->fields,true)){
unset($data[$key]);
}elseif(is_scalar($val)) {
// 字段类型检查
$this->_parseType($data,$key);
}
}
}
// 安全过滤
if(!empty($this->options['filter'])) {
$data = array_map($this->options['filter'],$data);
unset($this->options['filter']);
}
$this->_before_write($data);
return $data;
}
OJBK,此处没有注入!
0x04 任意文件下载:
function downloadBak() {
$file_name = $_GET['file'];
$file_dir = $this->config['path'];
if (!file_exists($file_dir . "/" . $file_name)) { //检查文件是否存在
return false;
exit;
} else {
$file = fopen($file_dir . "/" . $file_name, "r"); // 打开文件
// 输入文件标签
header('Content-Encoding: none');
header("Content-type: application/octet-stream");
header("Accept-Ranges: bytes");
header("Accept-Length: " . filesize($file_dir . "/" . $file_name));
header('Content-Transfer-Encoding: binary');
header("Content-Disposition: attachment; filename=" . $file_name); //以真实文件名提供给浏览器下载
header('Pragma: no-cache');
header('Expires: 0');
//输出文件内容
echo fread($file, filesize($file_dir . "/" . $file_name));
fclose($file);
exit;
}
}
EXP:http://www.test.com/index.php?s=/Admin/Sqlback/downloadBak/file/..\index.php
这样就可以下载任意文件,但是需要管理员权限~
来看看权限验证吧!
public function _initialize(){
if(session('adminuser')!=C('webuser')){
$this->error('你没有权限',U('/Admin/Index/home'));
}
}
验证的是session,没办法绕过!这个漏洞只有在后台可利用!
0x05 任意文件删除:
//删除数据备份
function deletebak() {
if (unlink($this->config['path'] . $this->dir_sep . $_GET['file'])) {
$this->success('删除备份成功!');
} else {
$this->error('删除备份失败!');
}
}
EXP: http://www.test.com/index.php?s=/Admin/Sqlback/deletebak/file/..\index.php
同样需要管理员的权限!
再看了一下命令注入,也没法儿利用…
0x06 总结:
虽然此次审计没发现什么特别致命的东西,如果想要 getshell
有这样的方法!
1.首先就是需要管理员权限,(弱口令第一考虑,其次就是 XSS
打管理员 Cookie
)
2.通过任意文件下载网站配置信息:http://www.test.com/index.php?s=/Admin/Sqlback/downloadBak/file/..\Conf\pcwap.php
,可以得到网站配置信息(数据库连接信息),这里可通过 Mysql
写文件拿到shell,(网站的物理路径可通过报错信息得到)
3.通过任意文件删除漏洞,删除文件配置文件可重装:http://www.test.com/index.php?s=/Admin/Sqlback/deletebak/file/..\Conf\pcwap.php
,即可重装,然后安装到自己的远程数据库,MYSQL
写 Shell 即可。
就这样吧~
[代码审计]PCWAP的更多相关文章
- PHP代码审计中你不知道的牛叉技术点
一.前言 php代码审计如字面意思,对php源代码进行审查,理解代码的逻辑,发现其中的安全漏洞.如审计代码中是否存在sql注入,则检查代码中sql语句到数据库的传输 和调用过程. 入门php代码审计实 ...
- 技术专题-PHP代码审计
作者:坏蛋链接:https://zhuanlan.zhihu.com/p/24472674来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 一.前言 php代码审计如字面 ...
- 关于PHP代码审计和漏洞挖掘的一点思考
这里对PHP的代码审计和漏洞挖掘的思路做一下总结,都是个人观点,有不对的地方请多多指出. PHP的漏洞有很大一部分是来自于程序员本身的经验不足,当然和服务器的配置有关,但那属于系统安全范畴了,我不太懂 ...
- Kindeditor 代码审计
<?php /** * KindEditor PHP * * 本PHP程序是演示程序,建议不要直接在实际项目中使用. * 如果您确定直接使用本程序,使用之前请仔细确认相关安全设置. * */ r ...
- 一个CMS案例实战讲解PHP代码审计入门
前言 php代码审计介绍:顾名思义就是检查php源代码中的缺点和错误信息,分析并找到这些问题引发的安全漏洞. 1.环境搭建: 工欲善其事必先利其器,先介绍代码审计必要的环境搭建 审计环境 window ...
- php代码审计基础笔记
出处: 九零SEC连接:http://forum.90sec.org/forum.php?mod=viewthread&tid=8059 --------------------------- ...
- 【PHP代码审计】 那些年我们一起挖掘SQL注入 - 3.全局防护Bypass之Base64Decode
0x01 背景 现在的WEB程序基本都有对SQL注入的全局过滤,像PHP开启了GPC或者在全局文件common.php上使用addslashes()函数对接收的参数进行过滤,尤其是单引号.同上一篇,我 ...
- 【PHP代码审计】 那些年我们一起挖掘SQL注入 - 2.全局防护Bypass之UrlDecode
0x01 背景 现在的WEB程序基本都有对SQL注入的全局过滤,像PHP开启了GPC或者在全局文件common.php上使用addslashes()函数对接收的参数进行过滤,尤其是单引号.遇到这种情况 ...
- PHP代码审计】 那些年我们一起挖掘SQL注入 - 1.什么都没过滤的入门情况
0x01 背景 首先恭喜Seay法师的力作<代码审计:企业级web代码安全架构>,读了两天后深有感触.想了想自己也做审计有2年了,决定写个PHP代码审计实例教程的系列,希望能够帮助到新人更 ...
随机推荐
- 那些年我们一起踩过的坑(javascript常见的陷阱)
1.object最后一个逗号 定义object直接量或json,最后一个逗号多写了,在ie下会报错,高级浏览器则不会,给只使用chrome调试的同学敲个警钟.踩了无数次这个坑了. 2.自动加分号 ...
- 解决Java POI 导出Excel时文件名中文乱码,兼容浏览器
String agent = request.getHeader("USER-AGENT").toLowerCase(); response.setContentType(&quo ...
- 浅谈Python之sys.argv
(1)sys.argv是什么 sys模块为进入解释器维护或使用的变量,以及与解释器相关的函数提供了途径.sys.argv在脚本程序中扮演了这样一个角色:将命令行输入的参数作为一个list传入脚本程序, ...
- 吴裕雄 Bootstrap 前端框架开发——Bootstrap 字体图标(Glyphicons):glyphicon glyphicon-italic
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...
- 【剑指Offer】面试题10- I. 斐波那契数列
题目 写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项.斐波那契数列的定义如下: F(0) = 0, F(1) = 1 F(N) = F(N - 1) + F(N - 2) ...
- UVA - 116 Unidirectional TSP (单向TSP)(dp---多段图的最短路)
题意:给一个m行n列(m<=10, n<=100)的整数矩阵,从第一列任何一个位置出发每次往右,右上或右下走一格,最终到达最后一列.要求经过的整数之和最小.第一行的上一行是最后一行,最后一 ...
- BZOJ 2226 [Spoj 5971] LCMSum
题解:枚举gcd,算每个gcd对答案的贡献,贡献用到欧拉函数的一个结论 最后用nlogn预处理一下,O(1)出答案 把long long 打成int 竟然没看出来QWQ #include<ios ...
- 大二暑假第七周总结--开始学习Hadoop基础(六)
复习关于Hadoop的操作语句以及重点 Shell版 跳转目录到Hadoop: cd /usr/local/hadoop 启动Hadoop: ./sbin/start-dfs.sh 注意:Hadoop ...
- JPA 开发中遇到的错误
JPA 开发中遇到的错误 (2011-07-13 16:56:12) 转载▼ 标签: 杂谈 分类: Java/J2EE 常见异常1.异常信息:org.hibernate.hql.ast.QuerySy ...
- CountDownLatch、CyclicBarrier、Semaphore的使用
CountDownLatch(计数器) 主线程等待另外三个线程执行完成后再执行 public static void main(String[] args) { //定义一个CountDownLatc ...