首页
关于
友链
Search
1
设备部署-NIDS入侵检测系统-Snort&Suricata
5 阅读
2
从方法重写到SQL注入-信呼OA审计
5 阅读
3
CVE-2025-11001
4 阅读
4
设备部署-HIDS入侵检测系统-Elkeid&Wazuh
4 阅读
5
Web入侵分析入口思路
3 阅读
蓝队
应急响应
设备部署
二进制
基础
代码审计
基础理论
实践分析
登录
Search
标签搜索
学习笔记
蓝队
应急响应
代码审计
设备部署
内存马查杀
IDS
漏洞复现
JumpServer
HFish
雷池WAF
HIDS
Snort
Suricata
NIDS
Yara规则识别样本
ELK日志分析系统
Rookit查杀
容器应急
docker
N0va7
累计撰写
31
篇文章
累计收到
1
条评论
首页
栏目
蓝队
应急响应
设备部署
二进制
基础
代码审计
基础理论
实践分析
页面
关于
友链
搜索到
2
篇与
的结果
2025-11-23
从方法重写到SQL注入-信呼OA审计
本文从信呼OAv2.6.5版本的路由,鉴权开始一步一步分析,最后发现因方法重写导致的SQL注入
2025年11月23日
5 阅读
0 评论
0 点赞
2025-09-21
phpcms变量覆盖到SQL注入
前言无论进行什么语言的代码审计,首先第一步都是分析这份代码的路由结构,然后再进一步分析路由分析入口点分析phpcms是⼀个⼀分为⼆的cms,有⼀套类似于应⽤的东⻄,包括phpcms,还有⼀套后台的管控中⼼,叫phpsso_server,在⼤多数情况下,它俩部署在⼀台主机上。两套代码的路由结构是⼀样的,我们来看看phpcms的。一般来说就是通过modules中的模块找到对应的controller触发路由。对于PHP来说,直接访问文件也可以触发该文件代码的流程,但是这样的话我们就得在每个文件的头部都加上一个权限校验,这样非常不便于程序员的开发。所以程序员会统一一个/几个入口点,只有从这些入口点进来的才能进行正确的访问,直接访问路径的会被退出在统一了入口点的情况下,程序员只需要对入口点进行权限判断即可,大大节省工作量。比如说这边都是通过index.php进来的,⽤这种⽅式来保障所有的路由都是从index.php进来,这样权限管控就⽐较好做了,以防有漏做权限控制的⻛险。主入口包含base.php,然后设置IN_PHPCMS为true,其他的路由中会去判断该变量是否存在,存在才能访问除了index.php还有其他的入口点,通过代码我们可以看出来他们也包含了base.php可以看到存在三个入口点:index.php、plugin.php、api.php,但是主要入口基本是从index.php来的正式路由分析我们在网站随便点击会出现对应的路由结构http://192.168.139.156:8000/index.php?m=member&c=index&a=register&siteid=1我们的首要任务就是搞懂这个到底是怎么进行路由映射的。在base.php中可以看到都是一些基本配置的引入,那么我们可以不需要在这边花太多时间,去index.php走下一步先看phpcms发现引入了一个类,看看这个方法一步一步走下来发现这边包含了一个文件,最后还进行了实例化new通过动调看看这个路径是什么进入这个类看看,发现这边又加载了一个param类并调用了其中的三种方法,同样的方式找到路径进去看看实例化过程中对这些提交的数据包进行过滤下面那些基本就是一些引入配置信息的内容看看刚刚调用的三个方法:route_m、route_c、route_a上面三个函数分别获取了GET请求中的值,现在进行初始化$filepath = PC_PATH.'modules'.DIRECTORY_SEPARATOR.$m.DIRECTORY_SEPARATOR.$filename.'.php';根据这个构造我们可以大致知道m对应modules,c对应的是controller直接包含对应的class,不断步入之后进入到这个方法到这里可以看出a就是对应的controller中的事件所以现在整个路由结构我们基本明确了http://192.168.139.156:8000/index.php?m=member&c=index&a=register&siteid=1index.php是入口点,m是对应的module,c是controller,a是controller下的事件action业务分析我们现在已经把路由搞清楚了,接下来就是把业务弄明白。phpcms是一个一分为二的cms,每当有一些用户权限、用户信息相关的业务发生的时候,phpcms就会与phpsso_server进行通信。在这个通信的过程中我们看看它做了些什么先看看phpsso_server的代码这部分逻辑一模一样与phpcms不同的点在于sso_server只有两个module并且都调用了父类的构造函数看看父类的构造函数有些什么东西<?php define('IN_PHPSSO', true); class phpsso { public $db, $settings, $applist, $appid, $data; /** * 构造函数 */ public function __construct() { $this->db = pc_base::load_model('member_model'); pc_base::load_app_func('global'); /*获取系统配置*/ $this->settings = getcache('settings', 'admin'); $this->applist = getcache('applist', 'admin'); if(isset($_GET) && is_array($_GET) && count($_GET) > 0) { foreach($_GET as $k=>$v) { if(!in_array($k, array('m','c','a'))) { $_POST[$k] = $v; } } } if(isset($_POST['appid'])) { $this->appid = intval($_POST['appid']); } else { exit('0'); } if(isset($_POST['data'])) { parse_str(sys_auth($_POST['data'], 'DECODE', $this->applist[$this->appid]['authkey']), $this->data); if(get_magic_quotes_gpc()) { $this->data = new_stripslashes($this->data); } if(!is_array($this->data)) { exit('0'); } } else { exit('0'); } if(isset($GLOBALS['HTTP_RAW_POST_DATA'])) { $this->data['avatardata'] = $GLOBALS['HTTP_RAW_POST_DATA']; if($this->applist[$this->appid]['authkey'] != $this->data['ps_auth_key']) { exit('0'); } } } }在这里面我们可以发现一个风险函数parse_str,这个函数存在变量覆盖的风险if(isset($_POST['data'])) { parse_str(sys_auth($_POST['data'], 'DECODE', $this->applist[$this->appid]['authkey']), $this->data); if(get_magic_quotes_gpc()) { $this->data = new_stripslashes($this->data); } if(!is_array($this->data)) { exit('0'); } } else { exit('0'); }但是这里面还有一个sys_auth的函数,根据观察是加解密函数,且没有硬编码,所以说我们暂时不能操控这个加解密算法所以这边的逻辑就是将解密完毕的数据进行变量覆盖,但是目前加解密函数不可控,所以我们无法直接请求这个路由发送数据进行解密,我们先标记一下后续再看既然他存有这个东⻄这个api,必然是phpcms⾃⼰能向他发送请求,从这个方法也能看出来如果我们能在某个位置控制请求(我们与phpcms通信,phpcms在后端完成加密的动作,并且能把我们的参数传递到phpsso上),是否就可以跟phpsso通信了呢?与phpsso进行通信寻找交互点那么哪里会出现这种通信点呢?肯定是和用户信息相关的操作了,我们最开始分析路由的那个注册页面就是一个跟用户信息相关的操作,进去看看有没有出现phpcms和phpsso交互的点这是加载配置的点,下面这个就可能是交互点了,跟进看看发现了auth_key现在我们先继续调试,看看能不能不通过这个实现变量覆盖出现了sys_auth加解密函数,所以上面的_ps_send就是交互函数请求地址是phpsso,这就是交互点!!!既然确定了这个点是交互点,那么我们可以回到phpsso的代码下断点继续分析通过交互点函数往回查找调用点发现public_checkemail_ajax函数public function public_checkemail_ajax() { $this->_init_phpsso(); $email = isset($_GET['email']) && trim($_GET['email']) ? trim($_GET['email']) : exit(0); $status = $this->client->ps_checkemail($email); if($status == -5) { //禁止注册 exit('0'); } elseif($status == -1) { //用户名已存在,但是修改用户的时候需要判断邮箱是否是当前用户的 if(isset($_GET['phpssouid'])) { //修改用户传入phpssouid $status = $this->client->ps_get_member_info($email, 3); if($status) { $status = unserialize($status); //接口返回序列化,进行判断 if (isset($status['uid']) && $status['uid'] == intval($_GET['phpssouid'])) { exit('1'); } else { exit('0'); } } else { exit('0'); } } else { exit('0'); } } else { exit('1'); } }这时候我们的路由就是http://192.168.139.156:8000/index.php?m=member&c=index&a=public_checkemail_ajax&email=test访问这个地址会触发下面的POST数据包,先通过tcpdump看一下流量tcpdump -i lo port 8000 -v -nne -Ahttp://192.168.139.156:8000/phpsso_server/index.php?m=phpsso&c=index&a=checkemail要实现debug还得加上这个Cookie可以看到传递到checkemail函数,parse_str已经将data中的email变量覆盖且数据已经被解密了继续往下看看有没有什么操作,发现一个数据库的操作,看着挺像变量绑定那么回事的,好像没漏洞的样子,不管怎么说,这都是一个数据平面的交汇处,进去看看可以看到这里是拼接写法,故存在SQL注入漏洞,现在我们基本可以去尝试一下单引号了绕过可以看到这里加了过滤,还记得在本文开头对提交参数分析发现存在对提交参数进行单引号过滤吗?似乎我们的路子被堵死了?我知道你很急,但是你先别急,parse_str有一个特性,他会对传入的字符串进行自动的URL编码,这时候我们就可以尝试绕过GET /index.php?m=member&c=index&a=public_checkemail_ajax&email=test%2527 HTTP/1.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cache-Control: no-cache Connection: keep-alive Cookie: XDEBUG_SESSION=PHPSTORM Host: 192.168.139.156:8000 Pragma: no-cache Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36%25转化过去是%所以说我们这边没有添加单引号不会被转义过滤,成功绕过乌云镜像的原文:https://wy.zone.ci/bug_detail.php?wybug_id=wooyun-2015-0131548抓住_ps_send这个函数可以找到更多入口点,如下面这个点这些我就不一一展示了,这是15年的文章了,作为一个初学者我感觉也是学到很多的,仅作为个人学习记录,如有错误请多多指教
2025年09月21日
1 阅读
0 评论
0 点赞