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)
前往 GitHub - LyLme/lylme_spage: 六零导航页下载 v1.9.5 版本源码压缩包,上传到网站根目录解压。并同步创建数据库
访问 http://域名/install
按提示配置数据库进行安装
安装成功
后台地址:http://域名/admin
账号密码:admin/123456
0 x 03 漏洞验证
进入登录页面
/admin/login.php
,输入任意用户名密码及验证码,并抓包1
2
3
4
5
6
7POST /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点击刷新验证码,抓包,获取最新验证码
/include/validatecode.php
保持页面不再刷新,对密码部分进行爆破
爆破成功
登陆成功
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 的合作方式
输出 JavaScript 代码
PHP 可以动态生成 JavaScript 代码,并输出到网页中。当浏览器接收到服务器返回的页面时,JavaScript 会在客户端(用户的浏览器)执行。
例如:PHP 可以动态生成 JavaScript 代码,并输出到网页中。当浏览器接收到服务器返回的页面时,JavaScript 会在客户端(用户的浏览器)执行。
1
2
3
4
5//PHP 生成了一段包含 JavaScript 的 HTML 页面,当用户访问该页面时,浏览器会弹出一个欢迎框。
$username = "User";
echo "<script>alert('欢迎 $username!');</script>";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>通过 AJAX 实现异步数据交互
AJAX 允许使用 JavaScript 进行异步 HTTP 请求,避免页面的完全刷新。这样,PHP 可以处理请求并返回数据,而页面的其他部分保持不变。
例如:用户点击登录按钮后,JavaScript 使用 AJAX 发送登录信息给服务器。PHP 处理请求,并返回成功或错误信息,页面无需刷新即可动态更新结果。通过 JavaScript 动态加载和处理 PHP 生成的数据
有时,PHP 负责生成数据,而 JavaScript 则负责将这些数据动态呈现给用户。例如,使用 PHP 生成 JSON 数据,JavaScript 获取这些数据并在前端进行处理。
0 x 05 漏洞分析
漏洞产生原因
由于 admin/login.php
的登录验证逻辑不够严谨,未设置尝试登录的次数限制,且验证码在登陆失败后不会主动刷新,导致可以对用户名和密码进行爆破。
函数分析
登录逻辑
1 | if (isset($_POST['user']) && isset($_POST['pass'])) { |
会先验证用户名和密码是否不为空,如果存在的话,进入到验证码的判断逻辑。将用户输入的验证码和 session 中生成的验证码进行对比。如果验证码正确的话,进入用户名和密码的逻辑判断,否则报错验证码错误。
用户名和密码的判断:对用户传入的用户名进行转义处理。对密码进行转义处理后拼接上 lylme 字符串,整体进行 md5 加密。将传入数据与配置中系统预设的用户名和密码进行比对。如果匹配成功,设置 cookie,并返回登陆成功,否则登陆失败返回用户或密码不正确。
JS 客户端互动刷新验证码
点击验证码图片时,通过 JavaScript 刷新验证码,并清空验证码输入框的内容。
1 | <img id="captcha_img" title="验证码" src='../include/validatecode.php' class="pull-right code" onclick="recode()" /> |
通过将一个随机数附加到验证码图片的 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