抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

0 x 00 思路

近期看了一些做渗透的同事们的报告,从别人的报告中汲取到了一些黑盒的思路。此次漏洞的挖掘,是黑盒+白盒的过程。

原本我是在复现 Lylme Spage 已经暴露出的一个漏洞,结果登陆的时候,用渗透的思路,发现了一些异常,搭配源代码稍微审计了一下,便挖掘出了该漏洞。是个很浅的登录的设计缺陷,代码非常简单。

这个系统的开发模式很简单,是传统的混合开发模式,将前端和后端逻辑混合在一起处理,代码非常好看懂。暴露在互联网上的资产数量也很多。适合逮住薅更多的漏洞出来。

申请了 CVE。


耶!纪念一下!水洞申请成功!

0 x 01 漏洞描述

LyLme Spage 是一个开源的导航页面,致力于简洁高效无广告的上网导航和搜索入口,支持后台添加链接、自定义搜索引擎等功能。
由于 admin/login.php 的登录验证逻辑不够严谨,未设置尝试登录的次数限制,且验证码在登陆失败后不会主动刷新,导致攻击者可以对用户名和密码进行爆破,并登录系统后台。影响的系统版本是 LyLme Spage v1.9.5.


0 x 02 环境搭建

系统版本:LyLme Spage v1.9.5
fofa 规则:body = “LyLme Spage”

PHP 版本:7.1.9(PHP 版本需 7.1 及以上,不支持 PHP 8)

  1. 前往 GitHub - LyLme/lylme_spage: 六零导航页下载 v1.9.5 版本源码压缩包,上传到网站根目录解压。并同步创建数据库

  2. 访问 http://域名/install

  3. 按提示配置数据库进行安装
    安装成功

  4. 后台地址:http://域名/admin

  5. 账号密码:admin/123456

0 x 03 漏洞验证

  1. 进入登录页面 /admin/login.php,输入任意用户名密码及验证码,并抓包

    1
    2
    3
    4
    5
    6
    7
    POST /admin/login.php HTTP/1.1
    Host: lylme:8056
    Content-Type: application/x-www-form-urlencoded
    Connection: keep-alive
    Content-Length: 37

    user=admin&pass=xxxxxx&authcode=63275
  2. 点击刷新验证码,抓包,获取最新验证码 /include/validatecode.php

  3. 保持页面不再刷新,对密码部分进行爆破

  4. 爆破成功

  5. 登陆成功


0 x 04 代码原理

混合开发模式(服务器端渲染 + 客户端增强)

该系统采用的是传统的混合开发模式,这种开发模式将前端和后端逻辑混合在一起处理,页面在服务器端生成,服务器端使用 PHP 处理业务逻辑并直接输出 HTML 页面,客户端使用少量的 JavaScript 进行交互。页面在服务器端完成渲染,每次用户请求页面时,服务器都会返回一个完整的 HTML 页面。

  • 服务器端渲染(SSR)
    页面的主要内容由服务器端(PHP)渲染生成,然后将完整的 HTML 发送给客户端。
  • 客户端增强
    在服务器渲染的基础上,使用 JavaScript 或 jQuery 添加一些动态功能,如表单验证、验证码刷新等。
  • PHP 和 HTML 紧密耦合
    PHP 嵌套在 HTML 中,服务器端不仅处理业务逻辑,还负责生成页面的结构和内容。
  • 少量的异步交互
    可以通过 AJAX 等方式在前端与后端进行部分数据交互,但核心流程还是基于完整的页面刷新。

PHP 系统中 JS 的作用

简单讲解就是:客户端和页面交互,点击提交表单按钮,触发 js,类似于把数据暂在 js 中,然后 js 会将数据初步处理之后(判断是否为空等)传递给 PHP,让他来做逻辑处理。而 ajax 的作用其实就是,在提交数据的时候,可以异步运行。比如,我只交一个小表单,但是不用刷新整个页面

PHP 是一种服务器端脚本语言,它主要用于生成动态网页内容、处理表单提交、访问数据库等。用户在浏览器访问页面时,PHP 代码会在服务器上执行,生成 HTML 并将其发送到用户的浏览器。
JavaScript 是一种客户端脚本语言,它在用户的浏览器中运行,主要用于操作 DOM(文档对象模型)、与用户交互、验证表单、异步加载内容等。

PHP 和 JavaScript 的合作方式

  1. 输出 JavaScript 代码

    PHP 可以动态生成 JavaScript 代码,并输出到网页中。当浏览器接收到服务器返回的页面时,JavaScript 会在客户端(用户的浏览器)执行。

    例如:PHP 可以动态生成 JavaScript 代码,并输出到网页中。当浏览器接收到服务器返回的页面时,JavaScript 会在客户端(用户的浏览器)执行。

    1
    2
    3
    4
    5
    //PHP 生成了一段包含 JavaScript 的 HTML 页面,当用户访问该页面时,浏览器会弹出一个欢迎框。
    <?php
    $username = "User";
    echo "<script>alert('欢迎 $username!');</script>";
    ?>
  2. PHP 处理用户请求,JavaScript 控制界面

    PHP 通常用于处理服务器端的逻辑,比如验证登录信息、存储数据等。

    JavaScript 则用于处理客户端的交互,如表单验证或页面的局部更新。

    例如:PHP 处理登录请求。JavaScript 验证表单输入,检查输入是否为空或者格式是否正确,避免不必要的服务器请求。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <!-- HTML form -->
    <form method="post" action="login.php" onsubmit="return validateForm()">
    用户名: <input type="text" id="username" name="username"><br>
    密码: <input type="password" id="password" name="password"><br>
    <input type="submit" value="登录">
    </form>

    <script>
    function validateForm() {
    var username = document.getElementById("username").value;
    var password = document.getElementById("password").value;
    if (username == "" || password == "") {
    alert("用户名和密码不能为空!");
    return false; // 阻止表单提交
    }
    return true; // 允许提交
    }
    </script>
  3. 通过 AJAX 实现异步数据交互
    AJAX 允许使用 JavaScript 进行异步 HTTP 请求,避免页面的完全刷新。这样,PHP 可以处理请求并返回数据,而页面的其他部分保持不变。
    例如:用户点击登录按钮后,JavaScript 使用 AJAX 发送登录信息给服务器。PHP 处理请求,并返回成功或错误信息,页面无需刷新即可动态更新结果。

  4. 通过 JavaScript 动态加载和处理 PHP 生成的数据
    有时,PHP 负责生成数据,而 JavaScript 则负责将这些数据动态呈现给用户。

    例如,使用 PHP 生成 JSON 数据,JavaScript 获取这些数据并在前端进行处理。

0 x 05 漏洞分析

漏洞产生原因

由于 admin/login.php 的登录验证逻辑不够严谨,未设置尝试登录的次数限制,且验证码在登陆失败后不会主动刷新,导致可以对用户名和密码进行爆破。

函数分析

登录逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
if (isset($_POST['user']) && isset($_POST['pass'])) {

if (isset($_REQUEST['authcode'])) {
session_start();
if (strtolower($_REQUEST['authcode']) == $_SESSION['authcode']) {
$user = daddslashes($_POST['user']);
$pass = md5('lylme' . daddslashes($_POST['pass']));
if ($user == $conf['admin_user'] && $pass == $conf['admin_pwd']) {
$session = md5($user . $pass);
$token = authcode("{$user}\t{$session}", 'ENCODE', SYS_KEY);
setcookie("admin_token", $token, time() + 604800, "/");
exit("<script language='javascript'>alert('登陆管理中心成功!');window.location.href='./';</script>");
} elseif ($pass != $conf['admin_pwd']) {
exit("<script language='javascript'>alert('用户名或密码不正确!');history.go(-1);</script>");
}
} else {
exit("<script language='javascript'>alert('验证码错误');history.go(-1);</script>");
}
}
}

会先验证用户名和密码是否不为空,如果存在的话,进入到验证码的判断逻辑。将用户输入的验证码和 session 中生成的验证码进行对比。如果验证码正确的话,进入用户名和密码的逻辑判断,否则报错验证码错误。

用户名和密码的判断:对用户传入的用户名进行转义处理。对密码进行转义处理后拼接上 lylme 字符串,整体进行 md5 加密。将传入数据与配置中系统预设的用户名和密码进行比对。如果匹配成功,设置 cookie,并返回登陆成功,否则登陆失败返回用户或密码不正确。

JS 客户端互动刷新验证码

点击验证码图片时,通过 JavaScript 刷新验证码,并清空验证码输入框的内容。

1
2
3
4
5
6
7
<img id="captcha_img" title="验证码" src='../include/validatecode.php' class="pull-right code" onclick="recode()" />
<script>
function recode() {
$('#captcha_img').attr('src', '../include/validatecode.php?r=' + Math.random());
$("input[name='authcode']").val('');
}
</script>

通过将一个随机数附加到验证码图片的 URL 中,每次调用函数时都生成一个不同的 URL。这有效避免了浏览器从缓存中加载同一张验证码图片,强制浏览器重新从服务器请求验证码图片,实现验证码的刷新。


0 x 06 案例

窄范围 fofa : body = “LyLme Spage” && body = “验证码” && title = “初心导航”

https://images.612ch.com/admin/login.php

https://fk.612ch.com/admin/login.php

https://wxapi.612ch.com/admin/login.php

https://iloveyouyq.612ch.com/admin/login.php

https://lwh.612ch.com/admin/login.php

评论