In this Sherlock, you’ll step into the role of a DFIR specialist, tracing the steps of an external contractor who breached Forela’s internal forum. Utilizing provided forum logs and an SQLite3 database dump, you’ll unravel how the perpetrator exploited the Guest WiFi to steal administrative credentials. This easy task tests the your ability to analyze forensic data, follow digital footprints, and unmask the identity of the intruder.
An external contractor on the Guest WiFi registered a normal forum account on Forela’s phpBB instance and published a post containing JavaScript that rendered a fake login form pointed back at their own machine. An administrator’s credentials were captured through it, and the contractor used them to log in as the administrator from a Mac browser. Within a minute they added their own account to the Administrators group, then requested a database backup from the web-accessible store directory and downloaded it. The forum configuration also held a service-account password in plaintext, readable to anyone with admin rights. The whole chain is visible in the SQLite dump and the web server log, provided the log’s timezone offset is handled correctly.
sqlite3. Tables used: phpbb_users, phpbb_posts, phpbb_log, phpbb_configaccess.log → read with grep and awk. Cut long lines, some contain full HTML bodiesTimestamp note:
access.logtimestamps carry a+0100offset. Database timestamps are UTC epochs. Convert everything to UTC and answer inDD/MM/YYYY HH:mm:ss.
Start in phpbb_users. The newest account is a lookalike of a staff name, and a forum post by “Alex Poole” describes them as an external contractor. That ties the contractor to the lookalike username, which has the digit-suffixed shape the format hint implies.
sqlite3 phpbb.sqlite3 "select user_id, username, user_ip from phpbb_users order by user_id desc limit 5;"
The user_ip column on the Q1 account holds the registration address. Confirm it against the web log: the registration POSTs in access.log come from the same source.
grep 'POST' access.log | grep -i register | cut -c1-200
Gotcha: Some
access.loglines embed whole HTML pages. A baregrepfloods the terminal, so pipe throughcut.
From Q1 the contractor’s user_id is 52. Filter phpbb_posts on poster_id and read the post bodies. One post contains a <script> block that builds a login form, which marks it as the malicious one.
sqlite3 phpbb.sqlite3 "select post_id, poster_id, post_subject from phpbb_posts where poster_id=52;"
Pull the post_text of the Q3 post and read the form’s action attribute. It points at a PHP collector on the contractor’s own address from Q2, so captured credentials went straight back to their machine.
sqlite3 phpbb.sqlite3 "select post_text from phpbb_posts where post_id=9;" | grep -o 'action=[^ ]*'
Once the contractor held admin rights, the board configuration was theirs to read. phpbb_config stores the LDAP bind password in clear text under ldap_password.
sqlite3 phpbb.sqlite3 "select config_name, config_value from phpbb_config where config_name like 'ldap%';"
phpbb_log is the board’s own audit trail. Filter on the Q2 address and look for an admin authentication success entry. log_time is an epoch, so convert it to UTC rather than trusting the web log’s +0100 clock. This answer rests on reading that entry type as the administrator login.
sqlite3 phpbb.sqlite3 "select datetime(log_time,'unixepoch'), log_operation, log_ip from phpbb_log where log_ip='10.10.0.78' order by log_time;"
Count user agents in access.log and look for the one that fits the admin session. Exactly one agent in the file is a Macintosh browser, with 456 hits: Chrome 112 on macOS. That is the browser behind the admin session.
grep Macintosh access.log | awk -F'"' '{print $6}' | sort | uniq -c
Tip: The format hint gives the full string shape (
Mozilla/5.0 (Macintosh; ...)). Match the log’s value exactly, spaces included.
Stay in phpbb_log after the Q5 login. A LOG_USERS_ADDED entry for the Administrators group names the contractor’s second account, apoole, 39 seconds after the admin login. Convert the epoch to UTC.
sqlite3 phpbb.sqlite3 "select datetime(log_time,'unixepoch'), log_operation, log_data from phpbb_log where log_operation='LOG_USERS_ADDED';"
Search access.log for requests to the /store/ directory. A single GET fetches a compressed SQL backup. The line is stamped 12:01:38 +0100, so the UTC time is one hour earlier.
grep '/store/' access.log | cut -c1-200
Gotcha: Submitting the log’s own
12:01:38is wrong. Subtract the+0100offset first.
Same line as Q9. The byte count is the field right after the status code.
grep '/store/' access.log | awk '{print $9, $10}'
| Phase | Action |
|---|---|
| Persistence | Contractor registers a lookalike forum account from the Guest WiFi |
| Credential Access | Publishes a post with JavaScript that renders a fake login form posting to their own host |
| Initial Access | Logs in as the administrator from a Mac Chrome session |
| Privilege Escalation | Adds their own second account to the Administrators group 39 seconds later |
| Credential Access | Reads the plaintext LDAP bind password from the board configuration |
| Collection | Downloads a database backup from the web-accessible /store/ directory |
| Type | Value |
|---|---|
| IP (contractor, stealer host) | 10.10.0[.]78 |
| URL (credential collector) | hxxp[://]10[.]10[.]0[.]78/update[.]php |
| Account | apoole1 (user id 52) |
| Account (added to Administrators) | apoole |
| Forum post | post_id 9 |
| User agent | Chrome 112 on macOS 10.15.7 |
| Path | /store/ (database backup, 34707 bytes) |
| Technique | ID | Description |
|---|---|---|
| Create Account: Local Account | T1136.001 | Contractor registered a forum account |
| Input Capture: Web Portal Capture | T1056.003 | Fake login form in a forum post posting to a collector script |
| Valid Accounts | T1078 | Administrator login with the captured credentials |
| Account Manipulation | T1098 | Own account added to the Administrators group |
| Unsecured Credentials: Credentials In Files | T1552.001 | LDAP bind password stored in plaintext in phpbb_config |
| Data from Local System | T1005 | Database backup downloaded from /store/ |
Guest WiFi must not reach internal services. The contractor came in over the guest network and registered on an internal forum. Segmenting guest traffic away from internal web apps removes this path entirely.
Forum posts shouldn’t be able to carry script content. A post with an inline form and JavaScript harvested the administrator’s credentials. Sanitise or restrict post HTML and add a Content Security Policy that blocks form submission to unknown hosts.
Alert on Administrators group changes. The group add came 39 seconds after an admin login from a new address. A rule on LOG_USERS_ADDED into a privileged group, or on an admin login from an unfamiliar IP, would have fired before the backup download.
Keep secrets out of the application config and backups out of the web root. The LDAP bind password sat in clear text for any admin to read, and the database backup was fetchable over HTTP. Use a secrets store and keep backups outside the document root, or require authentication for them.
Normalise timezones at ingestion. The web log used +0100 while the database used UTC epochs. Mixing them silently shifts a timeline by an hour.
A guest-network contractor turned one unsanitised forum post into administrator access and walked away with a database backup. Nothing was exploited technically; the weak points were a trusting post editor, a flat network and a backup sitting in a web-readable folder.