THM Crypto Failures

发布于:2025-08-28 ⋅ 阅读:(26) ⋅ 点赞:(0)

信息收集

 [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 字节盐值。之后,它使用提供的 userUser-AgentENC_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 函数,就是用userUA头以及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;
         }
     }

我的UAMozilla/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,当然也和题目设计有关,如果不是guestadmin长度一样,那么也没办法

拿到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还挺长的


网站公告

今日签到

点亮在社区的每一天
去签到