SOC L2 · Detection Engineering · Vuln Research

Jakub Kozub
read the source.

SOC L2 analyst at Jagiellonian University. By day I tune detections and hunt threats; by night I read open-source code to find the access-control bugs scanners miss.

Kraków, Poland · remote 4× CTF podium · 2× 1st MSc IT · AI phishing detection 2× Critical in WordPress core · disclosed ↗
L2
SOC Analyst
Threat Hunting · DFIR
4×
CTF podium
incl. 2× 1st place
4
Certifications
blue team + offensive

01 / whoami

From the alert to the source code.

By day I'm a SOC L2 analyst: triage, threat hunting, and writing or tuning the detections that catch the next one. What holds my attention is the part the logs only hint at — and chasing that far enough usually ends with the source open, working out why something is genuinely exploitable. That habit is what pulled me from defense into vulnerability research, and each side keeps sharpening the other.

After hours I hunt broken authorization — missing or incorrect access control — in open-source software a lot of people quietly depend on: WordPress, Elastic, GitLab, Matomo. I keep coming back to this class because it hides well. Nothing about the request looks malformed, so it almost never shows up in a scanner; you only see the bug once you understand the application's logic and its trust boundaries.

Nothing gets filed until I've rebuilt it in a lab and reproduced the proof of concept. Coordinated disclosure on HackerOne has so far produced two Critical findings in WordPress core — CVSS 9.9 and 9.0, both fixed, both publicly disclosed, both paid. An early record, but a real one, and I'm still building on it.

“I'd rather send one clean, reproducible report than ten noisy ones.”// disclosure discipline

I read my own findings from the other side too: not just whether a bug can be exploited, but what it leaves in the logs and what would actually stop it in production. Detection is the day job, so that question comes with me into every report — and it's the perspective I bring to application security and detection engineering alike.

// profile

Role
SOC L2 Analyst
Org
Jagiellonian University
Based
Kraków, Poland
Focus
Detection engineering, threat hunting, DFIR
Research
Access-control flaws in WordPress, Elastic, GitLab, Matomo
Disclosure
HackerOne · lab-verified PoCs
Langs
EN · ES · PL

02 / things I have shipped

What I build.

The security work comes first: two Critical findings in WordPress core, the open-source tools I wrote for my own engagements, and the classifier from my thesis. Then the products — every one of them live, and every one written by me end to end, the engine, the copy, the deployment. Most started as somebody’s real problem: a bridge partner in another city, a friend learning Polish from Spanish, a student who would not open a vocabulary list but will open a game.

Security

OSS vulnerability research HackerOne

Two Critical findings in WordPress core — arbitrary file deletion leading to site takeover (CVSS 9.9) and a stored XSS in wp-admin (9.0). Both were fixed, paid and publicly disclosed; neither was a duplicate. More reports are in triage across WordPress, Elastic, GitLab and Matomo. The full write-up of the 9.9 is in the next section.

Security tooling OSS

Small, single-purpose tools I wrote for my own engagements — MIT, Python standard library only, no dependencies and no telemetry. Three of them: wp-authz-audit maps the WordPress plugin code an attacker can reach and reports which handlers nothing guards, ranked by the privilege needed to reach them. evidence-redaction-gate reads a directory of evidence and fails the export if a session cookie or a token is still in it. redact-request prints the shape of a captured HTTP request — method, host, path with identifiers replaced, header names without values — and never its contents. The others are on the GitHub profile below.

Phishing detector MSc thesis

Three ways to catch phishing, benchmarked on one corpus for my MSc in Information Technology at AHE Łódź: TF-IDF with logistic regression, a fine-tuned DistilBERT, and a zero-shot local LLM. The transformer scores highest (F1 0.98 vs 0.74) — but only the LLM says in plain Polish why a message is phishing. Runs fully offline, no external API.

Products I ship

brydz.uk Web · Durable Objects

Duplicate bridge for two people in two cities. One Durable Object per table owns the deal and redacts every hand before it leaves the server, so the cards you are not entitled to see never reach your browser. Robots take the empty chairs, scoring follows the Laws, and a full board can be played on the keyboard alone.

MATURA Android

English vocabulary for the Polish matura, built as a game rather than a list. Speech recognition marks the spoken answers. Published on Google Play.

francuski.org Web · course

French B1/B2 explained in English — and a second edition in Polish generated from the same source. 10 units, 419 vocabulary entries, 290 exercises, and pronunciation graded word by word through the microphone.

dziendobry.org Web · course

Polish for Spanish speakers, A0–A1, as one self-contained page: 16 units, dialogues read aloud by a neural voice, exercises marked the moment you press Enter. Ships with 32 audio tracks split by unit and a 365-page printable book.

Sites for other people Web

aleimamartinez.com began as somebody else’s problem and ended as a site that loads in under a second, stores nothing and needs no cookie banner. If you want one like it, write to me.

03 / disclosed research

Two Critical findings in WordPress core.

Reported to the WordPress security team through HackerOne, fixed, and now public — the full write-ups and triage threads are readable by anyone. Both came out of one audit of freshly added media-handling code, where the same attacker-controlled value in attachment metadata reached two different sinks.

Critical · CVSS 9.9 report #3931777

Author → arbitrary file deletion anywhere on disk, leading to site takeover

A newly added REST route, POST /wp/v2/media/<id>/finalize, stored an attacker-supplied path verbatim in attachment metadata. Laundering it through the image editor moved it into protected meta, where the deletion routine built its directory guard from that same poisoned value — so the guard constrained nothing. The lowest role allowed to upload files could delete wp-config.php, or files outside the document root entirely.

Class
Path traversal · CWE-22
Privilege
Author — lowest role with upload_files
Status
Resolved · disclosed 28 Aug 2026
Critical · CVSS 9.0 report #3931771

Author → stored XSS in wp-admin via an unescaped sub-size filename

The same controllable filename in attachment metadata was rendered unescaped by get_media_item(), breaking out of the src attribute in the admin media UI. Script then ran in the session of any administrator who merely opened the media library — no click on anything unusual, and nothing malformed in the request to catch.

Class
Stored XSS · CWE-79
Privilege
Author, executing as Administrator
Status
Resolved · disclosed 28 Aug 2026

Neither was a duplicate. More reports are in triage across WordPress, Elastic, GitLab and Matomo — hackerone.com/jakubk

How the 9.9 actually worked

Three hops, none of them a bug on its own. Step through them — the value in orange is the one the attacker controls, and you can watch it move from an ordinary upload into a filesystem delete.

A brand-new REST route stores the path verbatim

POST /wp/v2/media/<id>/finalize arrived with client-side media processing. It takes the sub-size filenames the browser reports and writes them into attachment metadata exactly as sent — no path sanitisation, because at this point it is just a filename among filenames.

// simplified to the essence
foreach ( $request['sub_sizes'] as $name => $size ) {
    $meta['sizes'][ $name ]['file'] = $size['file'];   // attacker-controlled
}
wp_update_attachment_metadata( $id, $meta );

On its own: harmless. Metadata full of odd strings is not a vulnerability.

Where it lands. Author is the lowest role with upload_files. Deleting wp-config.php makes WordPress redirect to setup-config.php, where anyone can point the site at their own database — a full takeover. Files outside the document root go too, which on shared hosting crosses into someone else's security authority. That escalation is the difference between Medium and 9.9.

The exploit, start to finish

The real console session from the report, replayed. An Author — the lowest role allowed to upload — hands the whole site to an unauthenticated installer. Nothing here is edited for effect; the poisoned value is the one the attacker controls.

21 / 21 CVSS 9.9 · #3931777

What this leaves in the logs

Same chain, from the other chair. Every request here is authenticated, authorised and successful: the Author holds upload_files, owns the attachment, and WordPress printed the editor nonce to them. Nothing 403s. Nothing is malformed. A standard access log records the request line, not the body, so the poisoned value never reaches the log at all — it travels in a JSON body field, where URL inspection does not look. The laundering hop is worse: it is a POST /wp-admin/admin-ajax.php, the endpoint every admin screen talks to, and the attachment id it acts on is not in the request line. WordPress core keeps no audit log of its own, so the move from _wp_attachment_metadata into _wp_attachment_backup_sizes — the hop the whole finding turns on — is recorded nowhere in the application. The first unambiguous evidence is on the filesystem, after the delete. So there is no request signature to write here. There is a sequence, an outcome and a state change: one signal before impact, one at it, one after.

  1. 01 · sequence — web access log One authenticated user, one media id: POST /wp/v2/media/<id>/finalize and then DELETE /wp/v2/media/<id>?force=true inside one short window. Both request lines carry the id, so they join with no body capture. A hunt rather than a page — finalise-then-force-delete is uncommon, not impossible — and triage is one question: did anything outside the uploads tree disappear at that timestamp?
  2. 02 · outcome — host file telemetry The PHP worker unlinking a path outside wp-content/uploads. This chain ends in a routine meant to confine every delete to one attachment's own directory; the finding is that one branch does not. Scope file-delete telemetry to the web root and the PHP identity, exclude the uploads tree, and the only legitimate remainder is core, plugin and theme updates — known paths, in a window you can suppress. Cheapest version: integrity monitoring on wp-config.php.
  3. 03 · aftermath — web access log The site root answering 302 to /wp-admin/setup-config.php — the last line of the replay above. A live site does not redirect its front page to the installer, so there is no baseline to tune. Late by design, because the file is already gone. But that redirect is the takeover window opening, and it holds for any bug that ends with wp-config.php missing.

04 / what you can order

Three things you can hire me for.

Contract work, taken alongside the day job. The two research engagements end the same way: a written report in the shape the WordPress reports took — steps to reproduce, a proof of concept rebuilt and verified in a lab, the impact stated plainly, and the fix. If nothing is exploitable, you still get the map of what was reachable and what held. The detection work ends somewhere else: rules that run on your own logs, each one written down with the log sources it needs and what to do when it fires.

Access-control review Authorization

One application — a plugin, an internal tool, a REST API. I work out which code an unauthenticated or low-privileged request can actually reach, then which guard dominates each of those paths. Two substitutions come up again and again: a nonce standing in for authorization, and a primitive capability check standing in for an ownership check. Neither makes the request look malformed, which is why scanners walk past both.

Scope
One codebase · every entry point a request can reach
You get
The reachable entry points, ranked by who can reach them, and a report per confirmed finding

Research on a component you depend on Vuln research

You run code you did not write — a plugin, a library, a service. I audit it the way the two WordPress findings came about: read what was added recently, follow one attacker-controlled value across subsystem boundaries, and look for the one branch that differs from its neighbours. Nothing is filed until it is rebuilt in a lab. If the component is not yours, the same report can go to its maintainer through coordinated disclosure — the route both WordPress reports took.

Scope
One component you depend on · usually open source
You get
A report per finding, in the form of report #3931777

Detection engineering Blue team

Detection is the day job: triage, threat hunting, and writing or tuning the rules that catch the next one. As contract work that means turning a finding, an incident or a threat model into detections that fire on your own logs — each one written down with the log sources it needs, tested against traffic you already have, and tuned so it stays quiet until it matters. I read my own vulnerability findings from this side too: what the exploit leaves behind, and what would have stopped it in production.

Scope
One finding, incident or threat model · your own log sources
You get
The rules, the log sources each one needs, and what to do when one fires

Remote, from Kraków. This is my own work, taken on independently of my employer and not on their behalf. Say what you run and what worries you — jakubkozub1@gmail.com

05 / credentials

The paper trail, briefly.

Certificates, competitions and degrees, kept short on purpose. The work itself is in the section above.

Certifications

  • eWPTX — Web Application Penetration Tester eXtreme INE
  • eCTHP — Certified Threat Hunting Professional INE
  • CDSA — Certified Defensive Security Analyst Hack The Box
  • BTL1 — Blue Team Level 1 Security Blue Team

CTF — 4× podium, 2× 1st

  • 1st ThreatOps CTF SIEM / Threat Intel
  • 1st SIEM / TI Cloud CTF cloud detection
  • 2nd XDR CTF Trend Micro UK
  • 3rd Vision One European CTF
  • finalist Splunk SIEM CTF Warsaw

Education

  • MSc Information Technology AHE Łódź · AI phishing thesis
  • Postgrad Cybersecurity Univ. of Warsaw · red-team, OSCP prep
  • MA English Philology Univ. of Warsaw · CALL thesis

Career

  • SOC L2 Jagiellonian University detection & response
  • Before that SOC L1 → L2, teaching, translation
  • Full history on LinkedIn — no point keeping two copies

Languages

  • English full professional
  • Spanish professional working
  • Polish native

06 / establish connection

Let's talk detection & disclosure.

Write if you work on detection engineering, DFIR, application security or coordinated disclosure. I'm not looking to move roles — I'm settled at Jagiellonian University — but I take on contract work: security research, access-control reviews and detection engineering.

jakubkozub1@gmail.com