这题应该在出题的时候就有提供docker环境,也就是说比赛的时候就有考ctfer搭建docker环境的能力,我看了网上大佬的wp,也侧面验证了,所以从搭建环境开始
docker load < thinkshop.tar
docker导入环境
docker run -tid --name thinkshop -p 36000:80 -e FLAG=flag{test_flag} thinkshop
部署该docker环境
sudo docker start thinkshop
运行该环境
sudo docker exec -it thinkshop /bin/bash
进入该环境的终端
sudo docker stop thinkshop
停止该环境
访问http://127.0.01:36000/
图片显示的是我有进行更改的
在网站根目录进行观察,查看index.php
这与我们直接访问网站也就是http://127.0.01:36000/,自动跳转的路径是一样的,那么我们就找到了一点线索
我们到public查看index.php
根据图片我们能看出真正有用的代码在application目录
分析
/var/www/html
├── index.php ← Web 入口(跳转用)
├── public/
│ ├── index.php ← 真正的入口文件(ThinkPHP启动)
│
├── application/
│ ├── index/ ← 默认模块(index 模块)
│ ├── controller/ ← 控制器目录
│ ├── model/ ← 模型目录
│ ├── view/ ← 视图目录
│
├── thinkphp/ ← 框架核心
/index.php/index/index/index
└──模块 └控制器└方法
等价于调用
URL段 | 解析含义 |
---|---|
index | 模块名(/application/index/) |
index | 控制器名(/controller/Index.php) |
index | 方法名(Index 控制器中的 index() 函数) |
👉 最终执行:
application/index/controller/Index.php 里的 index() 方法
root@cb45d412bcad:/var/www/html/application/index/controller# ls ../model/
Goods.php Select.php Update.php
root@cb45d412bcad:/var/www/html/application/index/controller# cat ../model/Goods.php
public function getGoodsList(){$select = new Select();return $select->select('goods');}public function getGoodsById($id){$select = new Select();$data = $select->select('goods', 'id', $id);if (!empty($data[0]['data']) && substr($data[0]['data'], 0, 3) !== "YTo"){$this->error("数据错误" , 'index/admin/goods_edit');}return $data;}
根据代码我们可以看出存在数据库查询,我们到数据库查看
root/root
我们可以看到其存在shop的数据库,以及admin和goods的表
md5的123456
应该存在后台等登录的地方
/var/www/html/application/index/controller/Admin.php
根据前面分析的方法
我们调用Admin中的index方法校验身份
http://127.0.0.1:36000/public/index.php/index/admin/index
判断正确,跳转到
http://127.0.0.1:36000/public/index.php/index/admin/login.html
我们尝试用在数据库中的admin表中的数据
进行登录
很疑惑,为什么不对,去看看代码
这里存在获取参数校验,并写入session的操作
public function do_login(){$username = input('post.username');$password = input('post.password');if (empty($username) || empty($password)) {$this->error('用户名或密码不能为空');}if(strlen($password) > 100){$this->error('用户名或密码错误');}// 使用md5对输入的密码进行加密$encryptedPassword = md5($password);// 设置缓存键和有效期$Key = ["Login" , $username];$Expire = 600; // 缓存有效期为10分钟 (600秒)// 尝试从缓存中获取数据$adminData = Db::table('admin')->cache(true, $Expire)->find($username);//⬅️在这if ($adminData && $adminData['password'] === $encryptedPassword) {// 登录成功,设置sessionsession('admin', $adminData['username']);$this->success($username.'登录成功', 'index/admin/goods_edit');} else {$this->error('用户名或密码错误');}
一下是GPT的分析结果,关键在find()的使用上面
->find($username) —— 执行“查一条”操作(传参表示按主键查找)
● find() 在 ThinkPHP 中是一个用来获取单条记录的方法。它可以:
○ 不带参数时:以查询构造器当前条件为准,取第一条匹配记录(等同 limit 1)。
○ 带一个标量参数(如 find($id)):框架会把这个参数当作 主键值 来构建查询(等价于 WHERE <主_key> = $id),然后返回该条记录。
○ 也可以传数组作为条件(但这里传的是一个标量 $username)。
● 因此 find($username) 的语义是:以主键 = $username 去查 admin 表。这暗示要么:
○ admin 表的主键就是 username 字段(常见),
○ 要么主键是其他列(如 id),那此调用会变成 WHERE id = '
也就是说用户名应该是id
果然,username用的是数据库中的id的值
登录成功
,到这里就卡住了,应该是利用admin能够触发到的页面而不同用户触发不到的页面
到达该目录下
root@cb45d412bcad:/var/www/html/application/index/view/admin#
<textarea class="form-control" id="data" name="data" rows="3" required>{php}use app\index\model\Goods;$view=new Goods();echo $view->arrayToMarkdown(unserialize(base64_decode($goods['data'])));⬆️反序列化的地方{/php}
</textarea>
很明显这就是序列化后的数据
随便创建一个类,然后进行在数据库进行修改,
在这里我将数据库的id=2的data修改,
修改
看效果
能够反序列化,但是这是我们自己在自己的虚拟机上的测试,
在比赛的时候,还得考虑传参,以及哪来的链子
还是回到Admin.php中
public function do_edit(){if(!session('?admin')){$this->error('请先登录','index/admin/login');}$goodsModel = new Goods();$data = input('post.');$result = $goodsModel->saveGoods($data);if ($result) {$this->success('商品信息更新成功', 'index/index/index');} else {$this->error('商品信息更新失败');}}
public function saveGoods($data){$data['data'] = base64_encode(serialize($this->markdownToArray($data['data'])));return $this->save($data);}public function save($data){$update = new Update();return $update->updatedata($data , 'goods' , $data['id']);}
public function updatedata($data, $table, $id)
{if (!$this->connect()) {die('Error');} else {$sql = "UPDATE $table SET ";foreach ($data as $key => $value) {$sql .= "`$key` = unhex('" . bin2hex($value) . "'), ";}$sql = rtrim($sql, ', ') . " WHERE `id` = " . intval($id);return mysqli_query($this->connect(), $sql);}
}
基本就清楚了,抓包后,可以在data部分添加数据进行,但是接下来的就是thinkphp的链子部分,这里我还没学,得去学一下了
POST http://777.777.777.777:36000/public/index.php/index/admin/do_edit.html HTTP/1.1
Host: 777.777.777.777:36000
Content-Length: 145
Cache-Control: max-age=0
Accept-Language: zh-CN,zh;q=0.9
Origin: http://777.777.777.777:36000
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://777.777.777.777:36000/public/index.php/index/admin/goods_edit/id/2.html
Accept-Encoding: gzip, deflate, br
Cookie: PHPSESSID=c01ttalgd0co8lemafcplh3a24
Connection: keep-aliveid=2&name=cxz&price=3000.00&on_sale_time=2025-10-11T09%3A29&image=https%3A%2F%2Fi.postimg.cc%2FFzvNFBG8%2FR-6-HI3-YKR-UF-JG0-G-N.jpg&data=hello+w
大佬wp
大佬wp