- Critical Thinking - Bug Bounty Podcast
- Posts
- [HackerNotes Ep. 186] Is GPT-5.6 Sol Superhuman? wp2shell, Rails RCE and the Shrinking Bounty Table
[HackerNotes Ep. 186] Is GPT-5.6 Sol Superhuman? wp2shell, Rails RCE and the Shrinking Bounty Table
Pre-auth RCE in WordPress, Rails and GitHub, and we dig how to have the best setup at the moment
Hacker TL;DR
Programs are cutting lows and mediums and blaming AI for it. GitHub moved the real money behind a VIP gate: $7,500 mediums and $20k highs, but only once you are in.
wp2shell is a pre-auth WordPress RCE chain built in about 10 hours with GPT-5.6 Sol, and the researcher who found it says no human could have done it in that window.
KindaRails2Shell (CVE-2026-66066) turns any Rails image upload into arbitrary file read, through a fake MATLAB header that ends up in HDF5 external storage.
The new edge is deep prompting plus exclusive scope: keep pushing when the model says there is no bug, and point it at surface nobody else can reach.

This episode's sponsor is Zero Trust Network Access. Check out the ZTNA rundown.
The Bounty Table Is Shrinking
Ian Carroll does not talk about the bug bounty industry much anymore, but when he does it lands. Here is a recent post from him:
There's a trend of bug bounty programs reducing rewards under the guise that they can find issues themselves with AI. This is basically absurd, and you don't have to pay bounties for issues you've already found.
The trigger is Coinbase. Their new policy says low and medium severity issues are moving out of scope for the public program because their own internal tooling has matured and can catch this class of issue at scale, continuously.
But if you really catch mediums internally, then catch them, fix them, and they never reach a hunter's report. You do not need a policy change for that. The defensible version of this move is "we are pausing lows and mediums while we dig out" which makes sense right now, because everyone is in triage mode. But that is not what the policy says.
The part that hurts hunters is not the missing mediums, it is the gap between tiers. Mediums pay the mortgage, crits go to savings. Some programs run $400 mediums against $5,000 highs, and a single CVSS point decides which one you get. Privileges-required-low alone can turn a $3,000 high into a $300 medium. You spent a week on it because it looked like a high, and the downgrade reads as "your report is not worth much."
Pro Tip: pick your programs on the medium and the critical, not the high. The medium tells you what a downgrade costs you, the critical tells you the ceiling. If the two are far apart, a strict VRT triager is a real risk to your income.
GitHub's VIP Tier
GitHub restructured along the same lines, but kept the money in the system instead of removing it. The new table:
Severity | Public | VIP |
|---|---|---|
Low | $250 | $1,000 |
Medium | $2,000 | $7,500 |
High | $5,000 | $20,000 |
Critical | $10,000 | $30,000+ |
To get into VIP you need at least one of: one critical, two highs, four mediums, or seven lows. Reports submitted before July 27, 2026 are honored under the old structure.
The gate is the problem. Working a public scope just to earn access to the program you wanted in the first place is dead time, especially if you already have a track record on Fortune 100 targets. AWS works this way, and it is not motivating. The upside here is that GitHub's public tier still pays real money, and the VIP numbers change the math: two mediums a month at $7,500 is $15k a month. That is a reasonable target if you decide to specialize on GitHub.
As a target, GitHub is split in two. The web app is hard, and classic IDORs and broken access control are mostly gone. The bugs are in the technically complex products, in how tokens are scoped, and above all in parsing. Actions, Pages, issues and runners all take fields from somewhere, translate them, and run code in a sandbox. That is where things break.
We do subs at $25, $10, and $5, premium subscribers get access to:
– Hackalongs: live bug bounty hacking on real programs, VODs available
– Live data streams, exploits, tools, scripts & un-redacted bug reports
Need a Pentest? We just launched CTBB Pentests!
Hack full time? Check out the Full-Time Hunter’s Guild!
CVE-2026-3854: One Missing Semicolon Filter
Wiz found a cross-tenant RCE affecting both github.com and GitHub Enterprise Server. A git push goes through four components: babeld (the git proxy handling SSH), gitauth (credential and policy checks), gitrpcd (parses the security metadata headers), and a compiled Go pre-receive hook that enforces policy.
Everything turns on the X-Stat header. It carries security-critical fields as semicolon-delimited key=value pairs, and parsing is last-write-wins: if a key appears twice, the later value silently overrides the earlier one. babeld copies git push option values into X-Stat without sanitizing semicolons. So:
git push -o 'anything;rails_env=development' origin master
The semicolon breaks out of the push option field, and you can append fields that override the legitimate ones downstream.
Escalation to RCE takes three injected fields:
rails_envset to a non-production value. The pre-receive binary has two paths: production runs sandboxed, non-production runs directly as the git user.custom_hooks_dirto control where the binary looks for hook scripts.repo_pre_receive_hookswith path traversal (../../../bin/bash) to run an arbitrary binary from the filesystem.
You end up with unsandboxed execution as the git service user, and read access to other tenants' repositories on shared infrastructure.
Here the researchers had GitHub Enterprise Server to reverse. Wiz credits its own research team and says they used AI-augmented reverse engineering (IDA MCP) on the closed-source binaries, which they describe as one of the first critical vulns found this way.
Pro Tip: if a target ships an on-prem or enterprise build of the same product, get it. Having the binaries turns guessing into an actual research loop.
wp2shell and the ASI Question
Adam Kues (@hash_kitten) of Searchlight Cyber built a fully unauthenticated WordPress core RCE chain with GPT-5.6 Sol, in a little over 10 hours, for about $25 in API cost. It is two CVEs: CVE-2026-63030, the REST API batch-route confusion, and CVE-2026-60137, a SQL injection in the author__not_in parameter of WP_Query. The full chain only works on 6.9.x and 7.0.x.
When reading through the chain it produced, I was astonished by several creative exploitation techniques that I would previously have thought were only the domain of humans: the use of a recursive batch call to avoid the restriction on GET; the cache abuse to apply a changeset and temporarily escalate to administrator; and the choice of a fake post that ends up calling the parse_request hook to replay the request.
He adds that no security researcher could have found and completed this chain in 10 hours, and that even handed the original bug and asked to exploit it for RCE, he is not sure it would be possible in that time frame. That is what started the AGI versus ASI discussion in the episode.
The chain, step by step:
Batch desync.
POST /wp-json/batch/v1splits validation and execution into two loops, backed by a$matchesarray and a$validationarray. Whenis_wp_error()triggers acontinue;, only the validation array advances. The two arrays drift out of alignment, and a request gets executed against the wrong validated route.The SQLi.
GET /wp/v2/postswithauthor__not_in. Arrays getabsint()filtering, scalar strings do not, so"author_exclude": "0) OR 1=1 -- "reaches the query.Recursion to get GET. The batch API does not support GET, but method validation is implemented as parameter validation, the same mechanism the desync already breaks. A batch call inside a batch call gets you the GET.
Cache poisoning. Fake posts returned by the SQLi are cached in memory as
WP_Postobjects. Embeds inside those cached posts get written to the database asoembed_cacheentries. You now have a database write without a database write primitive.Changeset privesc. When the database row and the in-memory cache disagree, WordPress prefers the in-memory fields, which you fully control. A
customize_changesetpost carries auser_id, and applying it temporarily sets the current user to that ID. Set it to 1 and you are admin. The catch is that changesets live inpost_content, the one field you do not control, which is why cycle detection in the post hierarchy callingwp_update_post()without overwritingpost_contentmatters.Replay as admin. Fabricate a post with type
requestand statusparse. WordPress hooks are named{status}_{type}, so this fires theparse_requesthook, which replays the whole batch request from the beginning, now as admin. The original batch already contained a "create a new administrator" call that failed on the first pass as a guest. On the second pass it succeeds.
From there you log in as the new admin and install a malicious PHP plugin.
The practical takeaway: 5.6 Sol is a real step up from 5.5 for security work, and Sol Ultra in particular has been finding things on hardened targets that were not expected to be findable. If you are still on the previous generation, that is worth changing this week.
KindaRails2Shell: CVE-2026-66066
Ethiack (André Baptista @0xacb, Bruno Mendes, Rafael Castilho) published a near-default Rails RCE, found independently a few days later by RyotaK at GMO Flatt Security.
The precondition is very low: any file upload, on any Rails app using Active Storage with libvips, which is the default in Rails Docker images and on Debian and Ubuntu.
The mechanism is a decoder sniff. libvips picks a loader based on file contents, not on the extension or the declared type. A file whose first ten bytes claim MATLAB 5.0 goes to the libvips MATLAB loader, which hands it to libmatio, which looks at a different byte range, finds MAT 7.3, and MAT 7.3 is HDF5. HDF5 has an External File List feature, where a dataset's bytes can live in another file, named by path and offset. So rendering your image reads a file you chose off the server and returns it as pixels.
Read /etc/passwd, or read the environment and secret_key_base, and the file read becomes RCE. Fixed in 7.2.3.2, 8.0.5.1 and 8.1.3.1. Workaround on libvips 8.13+ is VIPS_BLOCK_UNTRUSTED, or Vips.block_untrusted(true) with ruby-vips 2.2.1+.
Pro Tip: the file format is the transferable part. HDF5 external storage lets you point a dataset at an arbitrary path, and libvips is not the only thing that will accept a MATLAB file. Try it on other frameworks and other image pipelines. Joseph hit the same pattern before as a local file disclosure in Magento, where changing your profile picture would fetch /etc/passwd, store it as your avatar asset, and let you download it.
Practical AI Workflow Notes
Joseph moved to Codex since 5.6 Sol shipped. A few reasons worth stealing:
Native mobile sync. No remote-control flag needed, it stays synced to your phone. That matters when your agents run while you are away from the desk.
Native image generation. Codex calls Image Gen under the hood, so you say "make the art" and it does it. No separate Nano Banana Pro step.
Longer agentic runs. GPT-5.6 runs longer before stopping.
Project folders, not panes. Instead of four panes in one project, keep a folder per project and switch between them. Four monitors and four projects for three hours is too much context switching.
You still bring your own skills and your own AGENTS.md.
On TAC and refusals
Trusted Access for Cyber is OpenAI's gated tier for cyber-permissive models. The useful detail is that applications get denied for new accounts. A GitHub staff member and known bug bounty hunter got rejected, and the reason was account age. She had been on Claude Code all year and created a fresh OpenAI account right before applying.
Pro Tip: apply at chatgpt.com/cyber with your oldest existing account, not a new one. It is an ID verification, not a resume review, and a new account looks like a red flag.
The other half of the refusal problem is your system prompt. Joseph gets almost no blocks, and his AGENTS.md states who he is by name, links his website, his blog and his HackerOne profile, and explains why the work is authorized. These models respond well to real, checkable proof of credentials. A lot of people getting blocked have nothing like that in their prompt. Justin runs a shorter version, one paragraph with handle, stats, and where the verified testing happens, and also sees almost no denials.
The Gauntlet Loop
Matt Shumer (@mattshumer_) got Claude Opus 5 to one-shot a Three.js FPS that looks like Modern Warfare, audio included, no external assets. The prompt is short, and two lines carry the technique:
Fan out subagents and have subagents tackle each one individually so that it's perfect. You should loop on each one and have each subagent check it visually to ensure it looks AAA.
Plus the acceptance criterion:
Don't stop until each subagent is utterly wowed with the quality when compared to the actual game.
He calls the pattern the Gauntlet Loop, and the abstract version is the useful one:
The agent (not you!!) breaks the goal into parts, gives each part a specialist builder and a ruthless blind critic sub-agent, with a mandate to only pass if the generated artifact is better than some real-world equivalent.
None of that is specific to games. Build it as a skill and say "use the gauntlet loop to hack this." The part most hacking prompts are missing is the critic with a real-world bar: a subagent whose only job is to reject a finding that is weaker than published research on the same bug class.
A cheaper version works today. When your agent asks "should I go route A or route B," stop answering. Tell it to spin up a subagent for both branches and keep consulting subagents until it decides. It works.
Where the Edge Is Now
Justin raised a question he has been sitting on: are live hacking events still the best use of his time? Bounties used to be inflated enough to cover the dupe risk. With everyone running AI, dupe risk is much higher, and going deep no longer protects you like it used to. Xssdoctor and Douglas have said the same thing. The alternative is putting that time into the hackbot instead.
The old advantages still hold (be faster, be the expert in one niche, be well networked so leads come to you), they just run through AI now. What is actually new:
Deep prompting. Not "find bugs," but "these specific bugs have been found in this class, we need an equal-level bug that completely bypasses this auth structure." Set the bar at the level of published research and let it work.
Persistence. 5.6 Sol Ultra told JD there was no bug, several times, at a live event. He kept saying "yes there is, I know there is," and it eventually found real bugs. Most people would have stopped after the third refusal.
Exclusive scope. Always true, but decisive now. Hidden subdomains, scope behind an expensive business purchase, access you have because of your country or your network, and hardware. These models are very good at finding RCE in hardware. Anything that puts you in front of surface nobody else can reach beats another pass on a public scope.
Pro Tip: the general point is that trying things got cheap. Anytime you think "it would be nice to have this," build it. Anytime you think "I should try this," tell the agent to try it. Most people are still budgeting their effort at pre-frontier-model prices.
Resources
wp2shell writeup - the full chain plus the exact prompt used, from Adam Kues.
Wiz on CVE-2026-3854 - X-Stat injection to cross-tenant RCE on GitHub.
KindaRails2Shell - Ethiack's Rails Active Storage RCE.
GitHub bug bounty restructure - the new public and VIP tables.
The Gauntlet Loop - Matt Shumer's abstract formulation.
That's it for the week, keep hacking!
