信息收集
[2025-08-27 11:08:37] [INFO] 暴力破解线程数: 1 [2025-08-27 11:08:37] [INFO] 开始信息扫描 [2025-08-27 11:08:38] [INFO] 最终有效主机数量: 1 [2025-08-27 11:08:38] [INFO] 开始主机扫描 [2025-08-27 11:08:38] [INFO] 有效端口数量: 233 [2025-08-27 11:08:38] [SUCCESS] 端口开放 10.10.233.28:22 [2025-08-27 11:08:38] [SUCCESS] 端口开放 10.10.233.28:80 [2025-08-27 11:08:38] [SUCCESS] 服务识别 10.10.233.28:22 => [ssh] 版本:8.9p1 Ubuntu 3ubuntu0.7 产品:OpenSSH 系统:Linux 信息:Ubuntu Linux; protocol 2.0 Banner:[SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.7.] [2025-08-27 11:08:43] [SUCCESS] 服务识别 10.10.233.28:80 => [http] [2025-08-27 11:08:43] [INFO] 存活端口数量: 2 [2025-08-27 11:08:43] [INFO] 开始漏洞扫描 [2025-08-27 11:08:44] [INFO] 加载的插件: ssh, webpoc, webtitle [2025-08-27 11:08:44] [SUCCESS] 网站标题 http://10.10.233.28 状态码:302 长度:0 标题:无标题 重定向地址: http://10.10.233.28/ [2025-08-27 11:09:41] [SUCCESS] 扫描已完成: 3/3
进去80端口看一下,说是guest,不能看cookie,扫一下目录
备份文件泄露
这里其实也给了提示,直接扫也能出来
[11:13:52] 200 - 0B - /config.php [11:14:17] 200 - 150B - /index.php [11:14:17] 200 - 150B - /index.php/login/ [11:14:17] 200 - 2KB - /index.php.bak
下载备份文件后分析逻辑
Cookie生成逻辑
这里代码有如何生成cookie的逻辑,然后如果是admin,就能打印flag。否则,会显示当前用户的登录信息。
让我们分析一下未设置 Cookie 的情况,并检查
generate_cookie
函数。首先,它调用generatesalt(2)
函数,该函数根据字母数字字符集生成一个随机的 2 字节盐值。之后,它使用提供的user
、User-Agent
和ENC_SECRET_KEY
创建一个字符串,并以:
分隔。然后,将此字符串与生成的盐值一起传递给make_secure_cookie
。最后,它将返回值设置为secure_cookie
cookie,并将user
设置为另一个 cookie。function generate_cookie($user, $ENC_SECRET_KEY) { $SALT = generatesalt(2); $secure_cookie_string = $user . ":" . $_SERVER["HTTP_USER_AGENT"] . ":" . $ENC_SECRET_KEY; $secure_cookie = make_secure_cookie($secure_cookie_string, $SALT); setcookie("secure_cookie", $secure_cookie, time() + 3600, "/", "", false); setcookie("user", "$user", time() + 3600, "/", "", false); }
make_secure_cookie函数,将输入字符串拆分成 8 字节的块,并为每个块调用
cryptstring
,同时使用提供的盐值function make_secure_cookie($text, $SALT) { $secure_cookie = ""; foreach (str_split($text, 8) as $el) { $secure_cookie .= cryptstring($el, $SALT); } return $secure_cookie; }
cryptstring,我们发现它只是调用 PHP 的
crypt()
函数来使用给定的盐对传递的字符串进行哈希处理function cryptstring($what, $SALT) { return crypt($what, $SALT); }
verify_cookie
函数,就是用user
和UA头
以及ENC_SECRET_KEY
拼接形成的function verify_cookie($ENC_SECRET_KEY) { $crypted_cookie = $_COOKIE["secure_cookie"]; $user = $_COOKIE["user"]; $string = $user . ":" . $_SERVER["HTTP_USER_AGENT"] . ":" . $ENC_SECRET_KEY; $salt = substr($_COOKIE["secure_cookie"], 0, 2); if (make_secure_cookie($string, $salt) === $crypted_cookie) { return true; } else { return false; } }
我的UA
是Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
,所以字符串就是
guest:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36:<ENC_SECRET_KEY>
获取管理员Cookie
这里其实就可以看出来问题了,它是分块加密的,所以即使我们没有ENC_SECRET_KEY的值也不影响我们拿到admin的cookie,当然也和题目设计有关,如果不是guest
和admin
长度一样,那么也没办法
拿到guset的cookie,然后确定guest:Mo
对应的加密值,替换为admin:Mo
加密的即可
1JeK/8RQjeULk1JZl/WpuLzhT21Jyfi76GyXF1g1JcA5o3HxIemE1JzKPU.rHbCXg1JjOByGwZtmN.1JJYoHonhcMqk1JoalkgYhnu3U1JcnWn.bQcuUM1JxaqsbY0Qqg.1JAKAYnCw6fHA1JPbSYpVKmqz21JWCJcpu9FX061J.LVMm4qoKYA1J5phSa3RXZYE1JCQR4cvlsjCQ1J3QBI93mIyXI1JPnrHOtDWuMs1JOLYsfY0apAc1JbYehUxpvFgM1JfmJECUDb9c61JhIBm3z0uv7E1JC.oZwlmNzG.1JfeT4Z4FQQbI1JLvnEhnqB8EI1JrgeYR3OFB/I1JYMaKSfr1.Y61Jc3o7/R/USi61J5P8nyb5etWY1JwWtxslsW51g1JUuY7sdxSRvI1Jzwm51CJhT1Y1JqErh9cbHXHs
前两位是盐的值
php > echo crypt("guest:Mo", "1J"); 1JeK/8RQjeULk php > echo crypt("admin:Mo", "1J"); 1Jt3Dq0Mrzwws
所以admin的Cookie
1Jt3Dq0Mrzwws1JZl/WpuLzhT21Jyfi76GyXF1g1JcA5o3HxIemE1JzKPU.rHbCXg1JjOByGwZtmN.1JJYoHonhcMqk1JoalkgYhnu3U1JcnWn.bQcuUM1JxaqsbY0Qqg.1JAKAYnCw6fHA1JPbSYpVKmqz21JWCJcpu9FX061J.LVMm4qoKYA1J5phSa3RXZYE1JCQR4cvlsjCQ1J3QBI93mIyXI1JPnrHOtDWuMs1JOLYsfY0apAc1JbYehUxpvFgM1JfmJECUDb9c61JhIBm3z0uv7E1JC.oZwlmNzG.1JfeT4Z4FQQbI1JLvnEhnqB8EI1JrgeYR3OFB/I1JYMaKSfr1.Y61Jc3o7/R/USi61J5P8nyb5etWY1JwWtxslsW51g1JUuY7sdxSRvI1Jzwm51CJhT1Y1JqErh9cbHXHs
获取key
我们可以利用字符串的哈希值计算方式来提高效率。由于待哈希的字符串以我们的输入开头,并以 8 字节为单位进行哈希处理,我们可以利用这一点,通过使用 User-Agent
作为填充,并更改其长度,使其始终只有一个 8 字节的块,并且我们知道前 7 个字节,从而简化暴力破解过程。这样,我们就可以只暴力破解该块的最后一个字符。
例如,如果我们向服务器发送一个空的 User-Agent
,则需要进行哈希处理的字符串将是: guest::<ENC_SECRET_KEY>
。由于它是以 8 字节块为单位进行哈希处理的,因此第一个哈希处理的块最终将为: guest::<First character of ENC_SECRET_KEY>
。然后,我们可以简单地遍历所有字符并将它们附加到 guest::
字符串,对其进行哈希处理,然后将得到的哈希值与服务器返回的 secure_cookie
中的第一个哈希值进行比对。如果匹配,我们就可以识别 ENC_SECRET_KEY
的第一个字符。通过对剩余字符重复此过程,我们可以通过逐个字符地暴力破解来发现密钥。
第一个字符的加密值
JKonAC2Jg6RMo
我们手动测一下
php > echo crypt("guest::T","JK"); JKonAC2Jg6RMo
符合,那么第一个字符就是T,那么第二个字符怎么办你,稍微换一下思路
我们用7个A填充UA
,那么第二块就是AAAAAAT?
,只爆破最后一个就可以啦,所以后面的就是一样的操作了,直接上脚本,参考的jaxafed的脚本
#!/usr/bin/env python3 import crypt import requests import urllib.parse import string BASE_URL = "http://10.10.126.159/" USERNAME = "guest:" SEPARATOR = ":" CHARSET = string.printable def get_secure_cookie(user_agent: str) -> str: session = requests.Session() response = session.get(BASE_URL, headers={"User-Agent": user_agent}) cookie = session.cookies.get("secure_cookie") return urllib.parse.unquote(cookie) def main(): discovered = "" while True: ua_padding_length = (7 - len(USERNAME + SEPARATOR + discovered)) % 8 user_agent = "A" * ua_padding_length prefix = USERNAME + user_agent + SEPARATOR + discovered block_index = len(prefix) // 8 secure_cookie = get_secure_cookie(user_agent) target_block = secure_cookie[block_index * 13:(block_index + 1) * 13] salt = target_block[:2] found_char = False for char in CHARSET: candidate = (prefix + char)[-8:] candidate_hash = crypt.crypt(candidate, salt) if candidate_hash == target_block: discovered += char print(char, end="", flush=True) found_char = True break if not found_char: break print() if __name__ == "__main__": main()
flag还挺长的