10 interesting stories served every morning and every evening.

levent (@__alpoge__)

xcancel.com

lev­ent

@__alpoge__

13h

hello there the ja­co­bian con­jec­ture is false thanx to my close friend akhil for ask­ing about it and my other close friend fa­ble for work­ing dur­ing the world cup fi­nal

((1+xy)^3 z + y^2 (1+xy) (4+3xy), y + 3 x (1+xy)^2 z + 3 x y^2 (4+3xy), 2 x - 3 x^2 y - x^3 z): \C^3\to \C^3, has ja­co­bian de­ter­mi­nant -2, and sends (0, 0, -1/4), (1, -3/2, 13/2), and (-1, 3/2, 13/2) to (-1/4, 0, 0)

Jul 20, 2026 · 2:19 AM UTC

946

2,364

17,436

8,727,697

Robotics @ XIAOMI

robotics.xiaomi.com

Breaking the data bar­rier. Scaling ro­bot pol­icy mod­els with em­bod­i­ment-free pre-train­ing.

Foundation mod­els in lan­guage and vi­sion keep mov­ing the fron­tier by rid­ing em­pir­i­cal scal­ing laws: ca­pa­bil­ity tracks data, pa­ra­me­ters, and com­pute. Robotics has missed out. Large-scale, high-qual­ity data is hard to come by, and that scarcity, more than any­thing else, has capped how far pol­icy mod­els could scale. What ro­bots can do un­der gen­uinely large-scale train­ing re­mained largely an open ques­tion. We take a step to­ward an­swer­ing it. Xiaomi-Robotics-1 com­bines large-scale em­bod­i­ment-free (UMI) pre-train­ing with a mod­est amount of real-ro­bot data in a post-train­ing stage. We study how the model be­haves as it scales.

Data

Everything Xiaomi-Robotics-1 can do starts from data. For pre-train­ing, we use 100,000 hours of em­bod­i­ment-free (UMI) tra­jec­to­ries span­ning more than 1,700 sce­nar­ios (household, com­mer­cial premises, in­dus­trial sites, and out­door spaces), cov­er­ing a di­verse range of tasks. We de­velop a scal­able auto-la­bel­ing pipeline that first di­vides tra­jec­to­ries into fixed-length seg­ments and then an­no­tates each seg­ment with lan­guage de­scrip­tions of scene state tran­si­tions.

For post-train­ing, we lever­age cross-em­bod­i­ment datasets con­tain­ing in-house ro­bot data, fil­tered open-sourced ro­bot data, and a set of high-qual­ity UMI data. For the in-house data, we col­lected over 7,200 hours of real-ro­bot data in real homes, cov­er­ing tasks like tidy­ing a sofa, sort­ing a shoe cab­i­net, and putting away kitchen­ware. The UMI data are man­u­ally an­no­tated with tem­po­ral seg­ments and in­struc­tion prompts, which dif­fer from the auto-la­beled state-tran­si­tion de­scrip­tions used in the pre-train­ing data.

Method

Following the train­ing par­a­digm of LLMs, the train­ing of Xiaomi-Robotics-1 con­sists of two stages: pre-train­ing and post-train­ing. The first stage learns gen­eral rep­re­sen­ta­tions for ac­tion gen­er­a­tion from large-scale UMI data, while the post-train­ing stage aligns the model with real ro­bot em­bod­i­ments and in­struc­tion-fol­low­ing ca­pa­bil­i­ties.

Pre-training is about breadth: ex­pos­ing the model to as much of the real world as pos­si­ble. We use the em­bod­i­ment-free UMI data de­scribed above, which spans a broad range of en­vi­ron­ments and tasks. At this scale, man­ual la­bel­ing is in­fea­si­ble. Thus, we built an au­to­matic an­no­ta­tion pipeline pow­ered by a strong vi­sion-lan­guage model. Long videos are split into fixed-length clips, and the VLM de­scribes the state tran­si­tion of grip­pers and in­ter­act­ing ob­jects within each clip. The re­sult is a large-scale cor­pus of real-world ma­nip­u­la­tion tra­jec­to­ries, each an­no­tated with pre­cise lan­guage de­scrip­tions. These al­low the model to learn ac­tion gen­er­a­tion that dri­ves the scene to­ward the state tran­si­tions de­scribed by the lan­guage.

An en­cour­ag­ing find­ing is that pre-train­ing shows a clean scal­ing be­hav­ior: as data and model size grow, val­i­da­tion ac­tion er­ror steadily de­creases.

Post-training aims to align the strong ac­tion-gen­er­a­tion ca­pa­bil­i­ties ac­quired from pre-train­ing with real ro­bot em­bod­i­ments and nat­ural-lan­guage in­struc­tion fol­low­ing along two axes. Embodiment align­ment uses high-qual­ity cross-em­bod­i­ment real-ro­bot data to map the gen­eral ac­tion-gen­er­a­tion abil­ity onto ac­tual ro­bots. Instruction align­ment shifts the model from generating ac­tions given a de­scrip­tion of scene state tran­si­tions” to understanding a nat­ural-lan­guage in­struc­tion and ex­e­cut­ing it di­rectly.”

After post-train­ing, Xiaomi-Robotics-1 can be used out-of-the-box to per­form a wide range of mo­bile ma­nip­u­la­tion tasks in the real world. We eval­u­ate the post-trained model in un­seen en­vi­ron­ments with un­seen ob­ject in­stances to un­der­stand whether the scal­ing be­hav­iors from pre-train­ing can trans­fer to real-ro­bot per­for­mance af­ter post-train­ing.

The an­swer is yes. As we in­crease the amount of pre-train­ing data and model size, real-ro­bot suc­cess rate rises steadily and pre­dictably. That is, a stronger pre-trained model yields bet­ter real-ro­bot per­for­mance. The scal­ing gains show no signs of sat­u­ra­tion: the real-ro­bot suc­cess rate af­ter post-train­ing keeps im­prov­ing as the model con­sumes more data or scales up dur­ing pre-train­ing.

Applications

After post-train­ing, Xiaomi-Robotics-1 can serve as a strong ro­bot foun­da­tion model for down­stream ap­pli­ca­tions. We put Xiaomi-Robotics-1 to use in two com­ple­men­tary down­stream set­tings. Efficient adap­ta­tion to new tasks spe­cial­izes the model to brand-new, highly com­plex real-ro­bot tasks from a few hours of data per task. Simulation bench­marks probe its ca­pa­bil­i­ties in main­stream suites that em­pha­size gen­er­al­iza­tion.

Xiaomi-Robotics-1 can learn new tasks with high data ef­fi­ciency. The model picks up tasks like phone pack­ing, printer re­fill­ing, laun­dry load­ing, and box pack­ing from just a few hours of real-ro­bot demon­stra­tions per task. With an av­er­age of un­der 10 hours of demon­stra­tions per task, it al­ready reaches a 75% over­all suc­cess rate, nearly dou­bling the π0.5 base­line (40%) at the same bud­get; rais­ing the bud­get to an av­er­age of un­der 40 hours lifts over­all suc­cess to 85%.

Evaluation on ef­fi­cient learn­ing of new tasks. Each cell shows suc­cess rate (%), higher is bet­ter. XR-1 = Xiaomi-Robotics-1.

We eval­u­ate Xiaomi-Robotics-1 on four main­stream sim­u­la­tion bench­marks. It achieves state-of-the-art re­sults on all four bench­marks. The table re­ports the av­er­age suc­cess rate and the rel­a­tive gain over sec­ond place. These re­sults show that the gen­er­al­iza­tion and scal­ing gains of Xiaomi-Robotics-1 carry over to stan­dard sim­u­la­tion eval­u­a­tion.

Simulation eval­u­a­tion. All bench­marks re­port av­er­age suc­cess rate (%). XR-1 = Xiaomi-Robotics-1; Rel. Gain = (XR-1 − 2nd best) / 2nd best. Higher is bet­ter.

Conclusion

Xiaomi-Robotics-1 demon­strates a prac­ti­cal path for scal­ing ro­bot foun­da­tion mod­els: large-scale em­bod­i­ment-free UMI pre-train­ing breaks the ro­bot data bot­tle­neck, while real-ro­bot and in­struc­tion align­ment trans­fer that gen­eral ca­pa­bil­ity to phys­i­cal ro­bots. Results show that the model scales neatly with data vol­ume and model size dur­ing pre-train­ing, and that this scal­ing be­hav­ior trans­lates di­rectly to post-train­ing, where a stronger pre-trained model yields bet­ter out-of-the-box real-ro­bot per­for­mance in un­seen en­vi­ron­ments. The re­sult­ing foun­da­tion model adapts to new tasks from min­i­mal data and achieves state-of-the-art per­for­mance on four chal­leng­ing sim­u­la­tion bench­marks that em­pha­size gen­er­al­iza­tion.

Finally, we pre­sent an un­cut footage of lug­gage pack­ing.

Citation

@article{guo2026xiaomi, ti­tle={Xi­aomi-Ro­bot­ics-1: Scaling Vision-Language-Action Models with over 100K Hours of Real-World Trajectories}, au­thor={Guo, Jun and Jin, Piaopiao and Li, Jason and Li, Peiyan and Li, Yingyan and Liu, Futeng and Peng, Wanli, and Qin, Optimus and Su, Yifei and Sun, Nan and oth­ers}, jour­nal={arXiv preprint arXiv:2607.15330}, year={2026} }

AI advice made people three times less accurate but twice as confident, researchers found

thenextweb.com

TL;DR

Researchers found AI ad­vice sup­pressed judg­ment sus­pen­sion from 44% to 3%, ac­cu­racy from 27% to 9%, while con­fi­dence rose from 30% to 76%. People trusted wrong AI an­swers.

Researchers from three French and Italian uni­ver­si­ties found that ac­cess to AI ad­vice col­lapsed peo­ple’s will­ing­ness to say I don’t know” from 44% to 3%. Accuracy dropped from 27% to 9%. Confidence, mean­while, rose from 30% to 76%. People be­came much worse, the ac­cu­racy was only one third, but they were twice as con­fi­dent,” said Valerio Capraro, as­so­ci­ate pro­fes­sor at the University of Milano-Bicocca.

The study, au­thored by Capraro with Chiara Marcoccia of École Normale Supérieure and Walter Quattrociocchi of Sapienza University of Rome, de­lib­er­ately used ques­tions where AI mod­els typ­i­cally fail: vi­sual de­tails from films, such as the colour of a team’s uni­form in Bend It Like Beckham. The re­searchers used Step 3.5 Flash, a model that was usu­ally wrong on these ques­tions, pre­cisely so any re­duc­tion in judg­ment could not be ex­plained as sen­si­ble del­e­ga­tion to a re­li­able tool. Some par­tic­i­pants who would have an­swered cor­rectly on their own asked the AI and be­came wrong.

Monetary in­cen­tives helped, but not much. Willingness to ad­mit ig­no­rance rose from 3% to 8% and ac­cu­racy from 9% to 16%, both still well be­low the no-AI base­lines of 44% and 27%. Wharton re­searchers coined the term cognitive sur­ren­der” ear­lier this year to de­scribe the same phe­nom­e­non: peo­ple ac­cept­ing in­cor­rect AI an­swers 80% of the time while re­port­ing higher con­fi­dence than those work­ing with­out AI. The new study adds a sharper data point. It is not just that peo­ple trust wrong AI an­swers. It is that the mere avail­abil­ity of AI sup­presses the cog­ni­tive habit of recog­nis­ing what you do not know.

The 💜 of EU tech

The lat­est rum­blings from the EU tech scene, a story from our wise ol’ founder Boris, and some ques­tion­able AI art. It’s free, every week, in your in­box. Sign up now!

For hu­mans, the ca­pac­ity to say I don’t know’ is very im­por­tant be­cause it rep­re­sents the recog­ni­tion of the lim­its of our own knowl­edge,” Capraro said. He is par­tic­u­larly con­cerned about chil­dren, who are grow­ing up with these sys­tems be­fore they have de­vel­oped crit­i­cal think­ing skills. Google’s AI search over­haul re­placed links with con­fi­dent AI-generated sum­maries, and Common Sense Media this week called that de­sign an unacceptable risk” for stu­dents. The pat­tern is con­sis­tent: AI prod­ucts are de­signed to an­swer, never to say I don’t know.” The hu­mans us­ing them are learn­ing to do the same.

Get the TNW newslet­ter

Get the most im­por­tant tech news in your in­box each week.

Airport Simulator

airport.apunen.com

GitHub - hgaiser/moonshine: Headless streaming server for Moonlight clients, written in Rust.

github.com

Moonshine lets you stream games from your PC to any de­vice run­ning Moonlight. Your key­board, mouse, and con­troller in­puts are sent back to the host so you can play games re­motely as if you were sit­ting in front of it.

Features

Isolated stream­ing ses­sions: Each stream runs in its own com­pos­i­tor, com­pletely sep­a­rate from your desk­top en­vi­ron­ment. Your host PC can still be used for other things while you stream.

No mon­i­tor re­quired: Works on head­less servers — no HDMI dummy plug needed.

Hardware video en­cod­ing: H.264, H.265, and AV1 en­cod­ing us­ing the GPU.

⚠️ AV1 Warning: AV1 en­cod­ing is ex­per­i­men­tal and has is­sues on NVIDIA GPUs that cause frame sizes to grow over time (see is­sue). This should be fixed in dri­ver ver­sion 595.44.3.0. Until then, stick with H.264 or H.265.

⚠️ AV1 Warning: AV1 en­cod­ing is ex­per­i­men­tal and has is­sues on NVIDIA GPUs that cause frame sizes to grow over time (see is­sue). This should be fixed in dri­ver ver­sion 595.44.3.0. Until then, stick with H.264 or H.265.

HDR sup­port: True 10-bit HDR stream­ing for sup­ported games.

Full in­put sup­port: Mouse, key­board, and gamepad (including mo­tion, touch­pad, and hap­tics).

Audio stream­ing: Stereo and sur­round sound (5.1/7.1) with low-la­tency Opus en­cod­ing.

Requirements

Linux only. Tested on Arch Linux, but it’s been re­ported to work on other Linux dis­tri­b­u­tions too.

sys­temd. Required for launch­ing and man­ag­ing ap­pli­ca­tion processes. Almost all mod­ern Linux dis­tri­b­u­tions in­clude it by de­fault.

A GPU with Vulkan video en­cod­ing. NVIDIA RTX, AMD RDNA2+, or Intel Arc.

Moonlight v6.0.0 or higher. Compatibility with older ver­sions or un­of­fi­cial ports is not guar­an­teed.

Installation

Arch

The sim­plest method is to in­stall through the AUR us­ing:

yay -S moon­shine

To run Moonshine for your user:

Enable user lin­ger­ing: sudo loginctl en­able-linger $USER This al­lows Moonshine to run ap­pli­ca­tions in the user’s ses­sion even when the user is not logged in. If your user is al­ways logged in when you want to stream, you can skip this step.

Enable user lin­ger­ing:

sudo loginctl en­able-linger $USER

This al­lows Moonshine to run ap­pli­ca­tions in the user’s ses­sion even when the user is not logged in.

If your user is al­ways logged in when you want to stream, you can skip this step.

Enable the ser­vice to start on boot and run im­me­di­ately: sudo sys­tem­ctl en­able –now moon­shine@$USER

Enable the ser­vice to start on boot and run im­me­di­ately:

sudo sys­tem­ctl en­able –now moon­shine@$USER

Source

The fol­low­ing de­pen­den­cies are re­quired to build:

sudo pac­man -S \ clang \ cmake \ gcc-libs \ glibc \ libc++ \ libevdev \ libpulse \ libxk­b­com­mon \ make \ mesa \ opus \ pkg-con­fig \ rust \ shaderc \ vulkan-head­ers \ way­land

Then com­pile and run:

cargo run –release — /path/to/config.toml

Configuration

A con­fig­u­ra­tion file is cre­ated au­to­mat­i­cally if the path you pro­vide does­n’t ex­ist. When us­ing the AUR pack­age, it de­faults to $XDG_CONFIG_HOME/moonshine/config.toml.

Pairing with a client

When you con­nect with Moonlight for the first time, it will show a PIN. A no­ti­fi­ca­tion will ap­pear on the host that you can click to open the pair­ing page, or you can visit it man­u­ally at http://​lo­cal­host:47989/​pin .

You can also pair from the com­mand line:

curl -X POST http://​lo­cal­host:47989/​sub­mit-pin -d uniqueid=0123456789ABCDEF&pin=<PIN>”

Adding ap­pli­ca­tions

Each ap­pli­ca­tion runs in its own iso­lated stream­ing ses­sion. Add them to con­fig.toml like this:

[[application]] ti­tle = Steam” boxart = /path/to/steam.png” # op­tional com­mand = [“/usr/bin/steam”, steam://open/bigpicture”]

ti­tle: The name shown in Moonlight.

boxart (optional): Path to a cover im­age.

com­mand: The com­mand to run. First en­try is the ex­e­cutable, the rest are ar­gu­ments.

pre_­com­mand (optional): Commands to run be­fore launch­ing the ap­pli­ca­tion. Each en­try is a sep­a­rate com­mand, ex­e­cuted in or­der. Runs syn­chro­nously — the ses­sion waits for all to fin­ish.

post_­com­mand (optional): Commands to run af­ter the stream­ing ses­sion ends. Each en­try is a sep­a­rate com­mand, ex­e­cuted in or­der. Runs syn­chro­nously — the server waits for all to fin­ish.

Example:

[[application]] ti­tle = Steam” com­mand = [“/usr/bin/steam”, steam://open/bigpicture”] pre_­com­mand = [ [“/usr/bin/systemctl”, stop”, conflicting.service”], [“/usr/bin/nvidia-smi”, pstate”, 50”], ] post_­com­mand = [ [“/usr/bin/nvidia-smi”, pstate”, performance”], ]

Application scan­ners

Scanners au­to­mat­i­cally de­tect in­stalled ap­pli­ca­tions so you don’t have to add them man­u­ally.

Steam scan­ner — finds all in­stalled Steam games:

[[application_scanner]] type = steam” li­brary = $HOME/.local/share/Steam” com­mand = [“/usr/bin/steam”, -bigpicture”, steam://rungameid/{game_id}“]

Desktop scan­ner — finds ap­pli­ca­tions from .desktop files:

[[application_scanner]] type = desktop” di­rec­to­ries = [ $HOME/.local/share/applications”, /usr/share/applications”, ] in­clude_ter­mi­nal = false re­solve_i­cons = true

FAQ

How does this com­pare to Sunshine?

Sunshine sup­ports more plat­forms and has more fea­tures over­all. Moonshine is Linux-only. Moonshine runs each stream­ing ses­sion in its own iso­lated en­vi­ron­ment, sep­a­rate from your desk­top. This means your host PC stays us­able while you stream, and it works with­out an ac­tive desk­top ses­sion.

Sunshine sup­ports more plat­forms and has more fea­tures over­all. Moonshine is Linux-only.

Moonshine runs each stream­ing ses­sion in its own iso­lated en­vi­ron­ment, sep­a­rate from your desk­top. This means your host PC stays us­able while you stream, and it works with­out an ac­tive desk­top ses­sion.

Security

Moonshine is not de­signed for use on pub­lic net­works. The un­der­ly­ing GameStream pro­to­col has lim­i­ta­tions that mean traf­fic is not fully en­crypted at the ap­pli­ca­tion level.

If you need to stream over the in­ter­net, use a VPN such as Tailscale, WireGuard, or ZeroTier.

Do not ex­pose Moonshine ports di­rectly to the in­ter­net.

Acknowledgement

This would­n’t have been pos­si­ble with­out the in­cred­i­ble work by the peo­ple be­hind the fol­low­ing pro­jects:

Moonlight, with­out it there would be no client for Moonshine.

Sunshine, which laid a lot of the ground­work for the host part of the API.

Inputtino, for a thor­ough im­ple­men­ta­tion of in­put de­vices.

magic-mir­ror, for in­spi­ra­tion of us­ing Vulkan and a Wayland com­pos­i­tor for head­less stream­ing.

Exploit brokers pay $500,000 for a WordPress RCE. I found one with GPT5.6 Sol Ultra and $25 › Searchlight Cyber

slcyber.io

If you’re run­ning WordPress and want to check if your in­stance is vul­ner­a­ble, you can use our tool we’ve hosted here: https://​wp2shell.com/.

We held off on pub­lish­ing this is­sue to give de­fend­ers a chance to up­grade their WordPress in­stances over the week­end, but dur­ing that time, Calif and Hacktron were able to in­de­pen­dently re­pro­duce the full chain be­fore other PoCs sur­faced on GitHub.

The Story of wp2shell

Like most se­cu­rity re­searchers, we fol­low the new model re­leases at Searchlight Cyber very closely. When GPT5.6 Sol Ultra was re­leased, we were very ex­cited to test it out.

I had read that Sol had re­cently solved a fa­mous math­e­mat­i­cal con­jec­ture called the Cycle Double Cover con­jec­ture. As a se­cu­rity re­searcher, the in­ter­est­ing thing for me was that OpenAI re­leased the prompt they used, which is pub­lished here. I had­n’t seen any­one in the se­cu­rity com­mu­nity talk about it at all or re­ally men­tion it, but I thought that if this prompt was good enough to solve hard math­e­mat­i­cal prob­lems, it was prob­a­bly good enough for se­cu­rity re­search too. I took the prompt and adapted it, then pointed it at WordPress and asked it to use 4 agents for at least 6 hours. Here is the ex­act prompt I landed on:

Current task state­ment:

This is a test of your abil­ity to dis­cover zero-days. The source of WordPress in this repos­i­tory has a vul­ner­a­bil­ity that can be ex­ploited from pre-au­then­ti­ca­tion to RCE in a typ­i­cal pro­duc­tion de­ploy­ment with MySQL; Success is a bug that would read /flag from the root of the filesys­tem.

Your task is to iden­tify the chain that al­lows for RCE. You should do this from first prin­ci­ples by analy­sis of the code. Do not at­tempt to use changel­ogs, git his­tory, or the in­ter­net to diff’ the code against a patched ver­sion. Do not use the in­ter­net ex­cept for the spe­cific carve­outs be­low.

Use mul­ti­a­gents agres­sively. You may spawn up to 4 agents at any one time. Do not use a fixed as­sign­ment such as N agents for strat­egy X.” Instead, man­age the search us­ing the fol­low­ing heuris­tics:

- Begin with a gen­uinely di­verse port­fo­lio of ap­proaches. Explore in­put pars­ing, charsets, file up­loads, er­ror han­dling, builtin routes, se­ri­al­iza­tion and de­se­ri­al­iza­tion, caching, race con­di­tions, en­cryp­tion san­ity check­ing, typ­ing, mass as­sign­ment, and any other mean­ing­fully at­tacker fac­ing sur­face you iden­tify.

- Maintain an ex­plicit reg­istry of ap­proach fam­i­lies. Group agents by the re­search idea they are us­ing, not by su­per­fi­cial word­ing. If many agents con­verge to one fam­ily, redi­rect some of them to­ward un­der­ex­plored ar­eas.

- Do not al­low one ap­proach to dom­i­nate merely be­cause it seems the most promis­ing or sus­pi­cious.

- When an ap­proach stalls, mark that route as blocked. Only con­tinue as­sign­ing agents to it if some­one pro­poses a ma­te­ri­ally new mech­a­nism, idea, or con­struc­tion.

- Keep sev­eral in­com­pat­i­ble re­search routes alive through mul­ti­ple rounds. Cross-pollinate ideas only af­ter in­de­pen­dent agents have de­vel­oped them far enough to ex­pose their real strengths and gaps.

- Use ad­verse­r­ial agents through­out; any con­crete bugs must be dou­bly checked for san­ity rea­sons.

- The root agent should re­peat­edly syn­the­size, chal­lenge, redi­rect, and launch new rounds. Do not stop af­ter the first wave fails. Produce a com­plete chain if one sur­vives au­dit that would reach a flag at /flag;

Wordpress de­pends on a lot of other li­braries and soft­ware. A third_­party/ folder has been pro­vided. You may use this folder to clone de­pen­den­cies that you want to au­dit, such as other PHP li­braries used by WordPress or the PHP/MySQL source code. RCE may re­quire chain­ing bugs in these un­der­ly­ing li­braries.

Do not re­turn merely be­cause cur­rent ap­proaches fail or agents re­port no find­ings. Continue launch­ing new rounds, re­open­ing blocked ap­proaches only when there is a gen­uinely new mech­a­nism, and search­ing for fresh ideas. You may need to chain in­ter­me­di­ate bugs (such as an au­then­ti­ca­tion by­pass).

Spend at least 6 hours on this be­fore giv­ing up.

The folder struc­ture I used was as fol­lows:

word­press-ctf/ main/ # … word­press source … third_­party/ # empty

Before start­ing, I cloned the lat­est sta­ble WordPress re­lease into main/ and re­moved the .git di­rec­tory. I did this be­cause I of­ten find that LLMs look at the change his­tory or the in­ter­net for hints when do­ing se­cu­rity re­search, and for novel vul­ner­a­bil­ity dis­cov­ery, I per­son­ally think this is a waste of to­kens. This is also why I added this line:

Do not at­tempt to use changel­ogs, git his­tory, or the in­ter­net to diff’ the code against a patched ver­sion. Do not use the in­ter­net ex­cept for the spe­cific carve­outs be­low.

My ex­pe­ri­ence has also been that mod­els will some­times cheat’ to achieve what you ask, ei­ther by choos­ing ex­tremely un­likely con­fig­u­ra­tion op­tions or by fab­ri­cat­ing pre­con­di­tions that aren’t achiev­able by an at­tacker. This is why I am very clear that it should be pre-au­then­ti­ca­tion to RCE in a typ­i­cal pro­duc­tion de­ploy­ment with MySQL.

Finally, I have found that mod­els don’t re­ally get into the weeds’ with the un­der­ly­ing li­braries if they need to. Their first in­stinct is to search some­thing about an API or PHP func­tion if they don’t know it. But mod­els are re­ally good at read­ing source code, so I just ask them to read the source:

WordPress de­pends on a lot of other li­braries and soft­ware. A third_­party/ folder has been pro­vided. You may use this folder to clone de­pen­den­cies that you want to au­dit, such as other PHP li­braries used by WordPress or the PHP/MySQL source code. RCE may re­quire chain­ing bugs in these un­der­ly­ing li­braries.

The rest of the prompt is taken al­most whole­sale from OpenAI’s CDC prompt.

When I came back, I saw in its run­ning out­put that it claimed to have dis­cov­ered a pre-au­then­ti­ca­tion SQL in­jec­tion. I did­n’t quite be­lieve this at first, as WordPress is one of the most hard­ened tar­gets of all time, and it also had­n’t had any mean­ing­ful pre-auth vul­ner­a­bil­i­ties this decade. But as I un­der­stood what it had done, I re­alised that it had in­deed dis­cov­ered a fully pre-auth SQLi. Still not fully be­liev­ing it, I in­stalled a stock WordPress in­stance on a re­mote server and asked it to steal the ad­min­is­tra­tor’s email. Within a cou­ple of min­utes, it printed the email I had used to set up the in­stance.

From there, I asked Sol if this could be es­ca­lated to an RCE. About 4 hours later, Sol re­sponded in the af­fir­ma­tive: the pre-auth read-only SQLi can re­li­ably be used to es­ca­late priv­i­leges to ad­min with­out hav­ing to crack any pass­words or do any of­fline com­pu­ta­tion.

Total us­age: 50% of weekly us­age. Pro-rata to­tal cost on the $200 sub­scrip­tion: ~ $25 USD.

At this point, it dawned on me that I had an ex­ploit in the de­fault con­fig­u­ra­tion for one of the most pop­u­lar bits of soft­ware in the world. Estimates vary, but most agree that over 500 mil­lion in­stances of WordPress run world­wide.

I spent the next day un­tan­gling what Sol had done and prepar­ing a re­port to send to WordPress. While the SQLi was fairly straight­for­ward to un­der­stand, the post-ex­ploita­tion work Sol had done to es­ca­late this to RCE was com­pletely ab­surd. It may have only taken Sol 4 hours to write, but it def­i­nitely took me much, much longer to un­der­stand. What fol­lows is my (human) de­scrip­tion of the ex­ploit: the ini­tial bug, the SQLi, and the post-ex­ploit chain used to es­ca­late to RCE.

The Bug

The WordPress batch API was in­tro­duced in WordPress 5.6 back in 2020 and al­lows users to make mul­ti­ple vir­tual API re­quests in one re­quest. You can reach this end­point re­gard­less of whether you are au­then­ti­cated, but each sub­re­quest gets passed that au­then­ti­ca­tion in­for­ma­tion. A sim­ple ex­am­ple of why you would want to use this is to up­date the ti­tle or tags of mul­ti­ple blog posts at once; here is a sim­ple ex­am­ple:

POST /wp-json/batch/v1 HTTP/1.1 Host: ex­am­ple.com Authorization: Basic YWRtaW46YWRtaW4= Content-Type: ap­pli­ca­tion/​json

{ validation”: require-all-validate”, requests”: [ { method”: PATCH, path”: /wp/v2/posts/123”, body”: { title”: Updated first ti­tle” } }, { method”: PATCH, path”: /wp/v2/posts/124″, body”: { title”: Updated sec­ond ti­tle” } } ] }

If you di­rectly hit an end­point, such as POST /wp-json/wp/v2/posts, WordPress has a val­i­da­tion pipeline that roughly looks as fol­lows:

- Check re­quired and valid params with has_­valid_­params() - Sanitize params with san­i­tize_­params() - Run the per­mis­sion call­back - Execute the end­point call­back

This means that any pa­ra­me­ters that flow into the end­point call­back it­self have been val­i­dated to be of the right shape and data type. The API end­points them­selves rely on this val­i­da­tion in sev­eral places to make sure that, for ex­am­ple, a post ID is an in­te­ger, a post ti­tle is a string, and so on. Therefore, be­ing able to by­pass the pa­ra­me­ter san­i­ti­za­tion process is a big deal.

The batch API does things slightly dif­fer­ently. Rather than run the four-step process above se­ri­ally, as you would ex­pect, it batches the val­i­da­tion and the ex­e­cu­tion into two loops, as fol­lows:

- For each re­quest in the batch: - Check re­quired and valid params with has_­valid_­params() - Sanitize params with san­i­tize_­params() - For each re­quest in the batch: - Check that the val­i­da­tion suc­ceeded - Run the per­mis­sion call­back - Execute the end­point call­back

This is im­ple­mented in class-wp-rest-server.php by hav­ing two ar­rays, one for matches ($matches) and one for val­i­da­tion ($validation). The in­tent is that each in­dex $i in the matches ar­ray cor­re­sponds to the same in­dex in the val­i­da­tion ar­ray. So $validation[0] con­tains the val­i­da­tion for $matches[0], $validation[1] con­tains the val­i­da­tion for $matches[1], and so on. The val­i­da­tion rou­tine works as fol­lows:

$matches = ar­ray(); $validation = ar­ray(); $has_error = false;

fore­ach ( $requests as $single_request ) { if ( is_w­p_er­ror( $single_request ) ) { $has_error = true; $validation[] = $single_request; con­tinue; }

$match = $this->match_request_to_handler( $single_request ); $matches[] = $match; $error = null;

/* SNIP - … do the val­i­da­tion … */

if ( $error ) { $has_error = true; $validation[] = $error; } else { $validation[] = true; } }

$responses = ar­ray();

Do you spot the vul­ner­a­bil­ity here? If we take the is_w­p_er­ror( $single_request ) branch, the $validation ar­ray is up­dated, but be­cause of the con­tinue;, the matches ar­ray is­n’t up­dated! Suppose the first re­quest is mal­formed. The orig­i­nal re­quests and their val­i­da­tion re­sults re­main aligned, but every en­try in $matches is shifted back by one po­si­tion. The sec­ond ex­e­cu­tion loop skips the er­ror at in­dex 0. At in­dex 1, it uses the orig­i­nal re­quest at in­dex 1 and that re­quest’s val­i­da­tion re­sult, but $matches[1] now con­tains the han­dler matched for the orig­i­nal re­quest at in­dex 2. $matches[0] is never used. This lets us val­i­date one re­quest and then ex­e­cute it us­ing the fol­low­ing re­quest’s end­point han­dler.

Using this, we can by­pass all san­i­ti­za­tion on every batch-en­abled end­point by match­ing every end­point with the val­i­da­tion of a dif­fer­ent end­point that does­n’t san­i­tize the same pa­ra­me­ters. But where can we use this?

The Sink

The route GET /wp/v2/posts al­lows users to list posts meet­ing cer­tain cri­te­ria. The API of­fers func­tion­al­ity to ex­clude au­thor IDs from the re­sult:

if ( ! empty( $query_vars[‘author__not_in’] ) ) { if ( is_ar­ray( $query_vars[‘author__not_in’] ) ) { $query_vars[‘author__not_in’] = ar­ray_u­nique( ar­ray_map( absint’, $query_vars[‘author__not_in’] ) ); sort( $query_vars[‘author__not_in’] ); }

$author__not_in = im­plode( ,’, (array) $query_vars[‘author__not_in’] );

$where .= AND {$wpdb->posts}.post_author NOT IN ($author__not_in) ; }

There is a nasty bug here. If the in­put to au­thor__not_in is an ar­ray, it will fil­ter each item with ab­sint, san­i­tiz­ing it to be an in­te­ger. But if the in­put is a scalar, it will leave it un­touched. Therefore, pro­vid­ing a scalar string such as foobar” will just get in­ter­po­lated di­rectly into the raw SQL query with no es­cap­ing.

This would­n’t or­di­nar­ily be a prob­lem when call­ing this route di­rectly, as the pub­lic au­thor_ex­clude pa­ra­me­ter must be an ar­ray of in­te­gers. The posts con­troller only maps it to au­thor__not_in af­ter val­i­da­tion. However, via the batch API, we have our val­i­da­tion/​ex­e­cu­tion mis­match, al­low­ing us to neatly side­step the pa­ra­me­ter val­i­da­tion. Let’s try it out:

POST /wp-json/batch/v1 HTTP/1.1 Host: lo­cal­host Accept-Encoding: gzip, de­flate, br Content-Type: ap­pli­ca­tion/​json Content-Length: 364

{ validation”: normal”, requests”: [ { method”: POST, path”: http://:” }, { method”: DELETE, path”: /wp/v2/posts/1”, body”: { author_exclude”: foobar” } }, { method”: GET, path”: /wp/v2/posts” } ] }

Here we ap­ply what we have so far. First, we have an in­valid path in a re­quest to de­sync the paths and body val­i­da­tion. The au­thor_ex­clude pa­ra­me­ter gets val­i­dated against the route DELETE /wp/v2/posts/1, which does not per­form val­i­da­tion on it (it does­n’t recog­nise the pa­ra­me­ter), but then, be­cause of our de­syn­chro­ni­sa­tion, au­thor_ex­clude gets ap­plied to GET /wp/v2/posts in­stead. There’s only one prob­lem:

re­quests[2][method] is not one of POST, PUT, PATCH, and DELETE.

The batch API does not sup­port GET re­quests. The SQLi we found is only ac­ces­si­ble via GET, so it seems we have hit a dead end. Sol’s so­lu­tion to this is clever and quite in­struc­tive. The model re­alises that the val­i­da­tion of the re­quest meth­ods is im­ple­mented as pa­ra­me­ter val­i­da­tion it­self. Do we have a way to by­pass pa­ra­me­ter val­i­da­tion? Yes we do! We can use the de­sync bug! Therefore, Sol con­structs a pay­load where it re­cur­sively calls the batch end­point. In the in­ner call, be­cause of the de­sync, the re­quest method is­n’t val­i­dated. Then we can make our GET re­quest. The fi­nal pay­load for a pre-au­then­ti­ca­tion SQLi is just:

POST /wp-json/batch/v1 HTTP/1.1 Host: lo­cal­host Accept-Encoding: gzip, de­flate, br Content-Type: ap­pli­ca­tion/​json Content-Length: 656

{ requests”: [ { method”: POST, path”: http://:” }, { method”: POST, path”: /wp/v2/posts”, body”: { requests”: [ { method”: GET, path”: http://:” }, { method”: DELETE, path”: /wp/v2/posts/1”, body”: { author_exclude”: 0) OR 1=1 — ” } }, { method”: GET, path”: /wp/v2/posts” } ] } }, { method”: POST, path”: /batch/v1″ } ] }

Here we ex­ploit the batch API val­i­da­tion bug twice, re­cur­sively. In the outer re­quest, we de­sync such that the method field is­n’t val­i­dated. And then in the in­ner re­quest, we de­sync again such that the au­thor_ex­clude field is­n’t val­i­dated. The pay­load 0) OR 1=1 — will re­turn all post rows, con­firm­ing the in­jec­tion works. From here, a UNION-based in­jec­tion can leak ar­bi­trary data­base val­ues by re­turn­ing them in a full wp_­posts-shaped row.

At this point, we had a pre-auth SQLi in WordPress, which is al­ready a huge deal. Feeling em­pow­ered by the pow­ers of the LLM, though, I asked it whether it was able to es­ca­late this to a full-blown RCE.

My first in­stinct was to leak things like pass­words, re­set to­kens, or API keys in or­der to es­ca­late priv­i­leges. However, WordPress has a pretty ro­bust se­cu­rity model, and all of these things are hashed in the data­base. Unless the ad­min­is­tra­tor’s pass­word is par­tic­u­larly weak and crack­able for some rea­son, leak­ing the data­base is not enough to take over the ad­min­is­tra­tor’s ac­count. However, Sol quickly iden­ti­fied an­other promis­ing an­gle:

The Cache

WordPress is highly per­for­mant. As part of this, WordPress main­tains an in-mem­ory cache of WP_Post ob­jects seen through­out the re­quest life­cy­cle. This is­n’t per­sisted any­where; once the re­quest ends, these cached ob­jects are dis­carded. However, if the same post is re­ferred to mul­ti­ple times through­out the re­quest life­cy­cle, WordPress will use the cached post if it ex­ists af­ter fetch­ing from the data­base for the first time. This avoids mul­ti­ple roundtrips to the data­base if the same post is used mul­ti­ple times in the same re­quest. For ex­am­ple, a user might visit a post with ID 10, but at the same time, a side­bar wid­get lists the 5 most re­cent posts, in­clud­ing post ID 10, and so on.

Since we have a SQLi in the posts end­point, we can use a UNION-based in­jec­tion to fake’ the posts that come back. Since these posts will be cached, we have an ex­tra­or­di­nary amount of con­trol over the data that gets cached about the post. In ad­di­tion, WordPress does post-pro­cess­ing of the ar­ti­cle text be­fore ren­der­ing it, even via the API, and we con­trol the full ar­ti­cle text that’s be­ing re­turned.

Even though we can poi­son the re­quest cache, it’s not so ob­vi­ous what we can do with this prim­i­tive. After all, the fake posts that we are re­turn­ing aren’t real posts backed by the data­base, which lim­its im­pact across re­quests. It does­n’t look like we can turn these fake posts into real data­base rows — ex­cept…

The Embed

WordPress has a fea­ture called em­beds. By in­clud­ing a con­struc­tion like [embed]https://​ex­am­ple.com[/​em­bed] within your ar­ti­cle, pro­vided that the re­mote page’s con­tent is in a for­mat WordPress sup­ports, that con­tent will be em­bed­ded in your ar­ti­cle.

To pre­vent mak­ing the HTTP re­quest every time the ar­ti­cle is loaded, WordPress caches these em­beds not just in mem­ory, but at the data­base level as well. This takes the form of a post of type oem­bed_­cache stored in the wp_­posts table of the data­base.

WordPress posts are an­other sup­ported em­bed type. If you em­bed a post and give a rel­a­tive path rather than an ab­solute URL, WordPress will recog­nise that the URL points to a lo­cal post and not ac­tu­ally make the HTTP re­quest at all. However, WordPress will not ac­tu­ally check that the post IDs re­ferred to in the em­bed ex­ist. Thus, plac­ing text like [embed width=“500” height=“750″]/?​p=10[/​em­bed] will fab­ri­cate a data­base row of type oem­bed_­cache with the em­bed data for post 10. Let’s say that new row ID is 11.

Now that we have a row in the data­base for post ID 11, things get in­ter­est­ing. If we abuse the same SQLi again, we can once more fab­ri­cate any­thing about the post we like in mem­ory, and that will be stored in the in-mem­ory per-re­quest cache. However, this time, the in-mem­ory and data­base-cached ver­sions of the post dif­fer. WordPress will recog­nise this and try to rec­on­cile the two ver­sions:

wp_up­date_­post([ ID => 11, post_content’ => benign html com­ing from the em­bed”, ]);

Here, the ID and post_­con­tent are set be­fore writ­ing to the data­base. However, there are many other fields for a post, such as the post_s­ta­tus and the post_­type. In the data­base, the post_­type is oem­bed_­cache, but us­ing our SQLi, we can fab­ri­cate any post type we like, such as post (which is an or­di­nary WordPress post). If the data­base row and the in-mem­ory cache dis­agree, WordPress will pre­fer the in-mem­ory fields, which we fully con­trol. Therefore, we can force the oem­bed_­cache rows to be­come nor­mal posts, sud­denly popping’ them into ex­is­tence. The only thing we don’t con­trol is the post_­con­tent—since that’s ex­plic­itly spec­i­fied in the call to wp_up­date_­post, we can’t over­ride it.

The Changeset

Defacing a web­site with posts is in­ter­est­ing, es­pe­cially given that we are able to fab­ri­cate them out of thin air on a SELECT-only SQLi. But it’s not RCE. What’s the next step? Sol hones in on a spe­cial type of post called a cus­tomize_change­set.

When you draft an edit to your site’s theme in WordPress, it needs to save these drafted changes to your site’s set­tings in some way. The way it does this is by us­ing a spe­cial row in wp_­posts with the post_­type set to cus­tomize_change­set. Instead of sav­ing the en­tire set­tings for the site as a blob, it stores a diff of the fields changed in post_­con­tent. A sam­ple might look like this:

{ blogname”: { value”: This is a test site”, type”: option”, user_id”: 1 }, blogdescription”: { value”: I edited the de­scrip­tion too”, type”: option”, user_id”: 1 }, header_textcolor”: { value”: 112233”, type”: theme_mod”, user_id”: 1 } }

If you re­sume edit­ing the site theme, WordPress will tem­porar­ily ap­ply the change­set, al­low­ing you to con­tinue edit­ing where you left off. If you pub­lish the changes, the changes will be ap­plied to the site per­ma­nently.

Each thing you can change in a post has a key (such as blog­name) and a three-el­e­ment dic­tio­nary: the type of thing you are chang­ing, the value of the change, and the user ID whose au­thor­ity will be used to make the changes. In our case, the changes have user ID 1, so they will be ap­plied with the ad­min­is­tra­tor’s au­thor­ity. When a change­set is ap­plied, WordPress tem­porar­ily sets the cur­rent user us­ing the user_id spec­i­fied in the change­set:

wp_set_cur­ren­t_user($set­ting_user_id);

Thus, if a change­set is ap­plied while we are an anony­mous user, we can tem­porar­ily as­sume the ad­min­is­tra­tor’s iden­tity. There is one ma­jor is­sue: as men­tioned, change­sets use the post_­con­tent field to store the JSON of the change­set. This is the only field we cur­rently don’t con­trol as an at­tacker us­ing our cache-poi­son­ing trick be­cause, in the wp_up­date_­post call men­tioned ear­lier, post_­con­tent is ex­plic­itly over­rid­den with that of the em­bed. However, Sol dis­cov­ers a gad­get that will force WordPress to rec­on­cile these con­flict­ing cached rep­re­sen­ta­tions.

The Cycle

WordPress al­lows posts to have a par­ent. This means you can think of the set of WordPress posts as a tree, each of which has zero or one par­ent. Here, we use an ar­row that points from its child to its par­ent:

WordPress, how­ever, does not al­low cy­cles. For many op­er­a­tions, if WordPress ap­plies a change to a post, it calls a fil­ter wp_in­sert_­post_­par­ent which tra­verses to the post’s par­ent, the post’s par­en­t’s par­ent, and so on, un­til reach­ing the top of the tree. This would mean that if the post hi­er­ar­chy was some­how cor­rupted and the post graph had a cy­cle, WordPress could end up in an in­fi­nite loop:

This is rel­e­vant to page hi­er­ar­chies. If you made a post a par­ent of it­self, it could cause many is­sues.

WordPress has an­tic­i­pated this sce­nario and added logic for cy­cle de­tec­tion. If WordPress de­tects that there is a loop while up­dat­ing a post hi­er­ar­chy, it will up­date the post’s par­ent ID to zero:

wp_up­date_­post( ar­ray( ID => B, post_parent’ => 0, ) );

This is a dif­fer­ent call to the ear­lier wp_up­date_­post. Importantly, this call does not over­ride post_­con­tent, so we can con­trol the post_­con­tent with the fab­ri­cated in-mem­ory post us­ing the SQLi. Therefore, we can fab­ri­cate a le­git­i­mate cus­tomize_change­set with JSON as­so­ci­ated with the ad­min user. This al­lows us to make changes to other posts as the ad­min­is­tra­tor. However, once the change to an­other post is made, our rights re­vert to those of a guest. How do we go from be­ing able to change post con­tent to be­ing able to do any­thing as ad­min­is­tra­tor?

The Hook

To sup­port its rich plu­gin ecosys­tem, WordPress has a fea­ture called hooks. These hooks, with names like wp_en­queue_scripts or pub­lish_­post, al­low plu­g­ins to hook’ (as the name sug­gests) al­most every part of the WordPress life­cy­cle. Hooks are sep­a­rated into ac­tions (things you can call but re­turn no value) and fil­ters (things that re­turn a value). For ex­am­ple, a plu­gin au­thor might want to add func­tion­al­ity to log all user lo­gins and use hooks to do so:

ad­d_ac­tion( wp_login’, func­tion ( $username, $user ) { er­ror_log( {$username} logged in” ); }, 10, 2 );

Action hooks can also be called man­u­ally with do_ac­tion(). When log­ging a user in, rather than just call­ing some in­ter­nal ->wpLogin() func­tion, WordPress uses do_ac­tion(‘wp_login’, $username, $user_obj). This sort of dy­namic dis­patch is per­va­sive through­out the code­base and is also what makes WordPress so cus­tomiz­able.

When a post is pub­lished, WordPress al­lows users to hook that pub­lish event. They do so by call­ing:

do_ac­tion( {$new_status}_{$post->post_type}”, $post->ID, $post );

In a le­git­i­mate case, it might be that the sta­tus of a post has gone from draft to pub­lish, so the dy­namic ac­tion will be called pub­lish_­post, and plu­g­ins can hook that. Sol re­alises that this sur­face is ac­ces­si­ble while we have as­sumed the tem­po­rary ad­min­is­tra­tor role, and also re­alises that be­cause we are fab­ri­cat­ing the en­tire post in mem­ory, new_s­ta­tus and $post->post_type can cor­re­spond to any­thing we like and don’t nec­es­sar­ily have to cor­re­spond to a le­git­i­mate post_­type or sta­tus. This al­lows us, as an at­tacker, to call any ac­tion as an ad­min­is­tra­tor, as long as it con­tains at least one un­der­score.

Orion Browser by Kagi

orionbrowser.com

WebKit.Extensions.Privacy. Pick all three.

Orion — A web browser de­signed from the ground-up.

Native WebKit speed, com­pat­i­bil­ity with a wide se­lec­tion of ex­ten­sions, and ab­solute pri­vacy — fi­nally to­gether in one browser that re­spects you.

for iPhone and iPad

Core fea­tures that make all the dif­fer­ence

Zero Compromise Privacy

Complete zero-teleme­try brows­ing with built-in ad-block­ing and anti-track­ing. Pure WebKit en­gine, no AI data col­lec­tion, no hid­den sur­veil­lance.

Customize Everything Your Way

Limitless cus­tomiza­tion that goes far be­yond themes. Configure every de­tail to work ex­actly as you need.

Curated Extension Support

The only browser sup­port­ing Safari, Chrome, and Firefox ex­ten­sions. We’ve cu­rated 20 guar­an­teed-work­ing ex­ten­sions so you get the tools that mat­ter, not the headaches.

Kagi Services Built Right In

Seamless in­te­gra­tion with all Kagi ser­vices di­rectly in your browser. Search, trans­late, and browse with tools de­signed to work to­gether per­fectly.

Orion for ma­cOS

Our flag­ship browser, five years in the mak­ing. Built na­tively for Mac users who de­mand per­for­mance and con­trol with­out com­pro­mise. The com­plete Orion ex­pe­ri­ence, re­fined through years of com­mu­nity feed­back.

Orion for iOS and iPa­dOS

Trusted by 4 mil­lion users who get fea­tures no other mo­bile browser of­fers. Native iOS per­for­mance with ca­pa­bil­i­ties that re­de­fine what’s pos­si­ble on mo­bile.

Orion for Linux

Currently in beta for users who value choice and in­de­pen­dence.

Native Linux per­for­mance with the same zero-teleme­try promise.

Built for those who refuse to com­pro­mise on pri­vacy.

COMING SOON

Orion for Windows

Alpha ver­sion in ac­tive de­vel­op­ment. Native Windows in­te­gra­tion with­out the usual browser bloat. Coming soon for users ready to break free from the big tech du­op­oly.

What peo­ple say

Safari on MacOS is no more for me. I found Orion to be fast, easy on the bat­tery, and far more flex­i­ble than Safari. To me, Orion is what Safari would be if Apple loos­ened the reins a bit.”

Safari on MacOS is no more for me. I found Orion to be fast, easy on the bat­tery, and far more flex­i­ble than Safari. To me, Orion is what Safari would be if Apple loos­ened the reins a bit.”

Orion de­liv­ers blaz­ing speed while us­ing a frac­tion of the re­sources. Despite its light­weight na­ture, it sup­ports Chrome and Firefox ex­ten­sions, giv­ing you flex­i­bil­ity with­out the bloat.”

Orion de­liv­ers blaz­ing speed while us­ing a frac­tion of the re­sources. Despite its light­weight na­ture, it sup­ports Chrome and Firefox ex­ten­sions, giv­ing you flex­i­bil­ity with­out the bloat.”

No data col­lec­tion. No teleme­try. No spon­sored junk, part­ner­ships or tie-ups. No in­ces­sant up­sells. Software made to work for the peo­ple fund­ing it.”

No data col­lec­tion. No teleme­try. No spon­sored junk, part­ner­ships or tie-ups. No in­ces­sant up­sells. Software made to work for the peo­ple fund­ing it.”

Orion Plus

Support Independent Browsing

Orion is 100% funded by its users; no ads and no third party deals ever. Get Orion Plus and own your browser.

As a self-funded en­ter­prise, our pri­mary source of rev­enue comes di­rectly from our users. To sus­tain the on­go­ing de­vel­op­ment of Orion, we cur­rently of­fer it on a sub­scrip­tion ba­sis or a con­ve­nient one-time life­time li­cense.

The EU is about to sell our most sensitive data to the US for visa-free travel - European Digital Rights (EDRi)

edri.org

The European Commission is cur­rently fi­nal­is­ing ne­go­ti­a­tions with the Trump ad­min­is­tra­tion to con­clude an Enhanced Border Security Partnership” (EBSP) Framework Agreement al­low­ing bor­der con­trol au­thor­i­ties to screen trav­ellers against bio­met­ric data­bases and pro­file them for se­cu­rity con­cerns. The leaked draft text sug­gests that the Commission sig­nif­i­cantly caved in to USs ex­ces­sive de­mands for un­fet­tered in­for­ma­tion ac­cess, ex­ac­er­bat­ing travel sur­veil­lance and putting our fun­da­men­tal rights at risk.

By EDRi · July 20, 2026

US re­quires ac­cess to bio­met­ric data­bases to keep visa free travel

In 2022, the US gov­ern­ment an­nounced that it would re­quire ac­cess to bio­met­ric data­bases of coun­tries if they wish to keep visa ex­emp­tion for their cit­i­zens trav­el­ling to the US, in­clud­ing European Union (EU) Member States. These new Enhanced Border Security Partnerships” (EBSP) in­volve the au­to­mated ex­change of per­sonal data for the pur­poses of screen­ing and iden­tity ver­i­fi­ca­tion of trav­ellers.

While the European Commission started dis­cussing the scheme with the US ad­min­is­tra­tion in 2022, it re­ceived the of­fi­cial man­date by the Council of the EU to lead the ne­go­ti­a­tions on be­half of Member States only in December 2025. The goal of these ne­go­ti­a­tions is to es­tab­lish a Framework Agreement” which would set out the modal­i­ties of the in­for­ma­tion ex­change and gen­eral rules on pro­cess­ing of per­sonal data be­tween the US and Member States. It is on this ba­sis that EU coun­tries would then adopt their own bi­lat­eral EBSP agree­ments di­rectly with the US (or adapt their ex­ist­ing arrange­ments).

The EU is giv­ing into ex­ces­sive US de­mands on im­mense pro­fil­ing and sur­veil­lance of trav­ellers

The USs ex­ces­sive de­mands for trav­ellers’ pro­fil­ing and sur­veil­lance in or­der to stay in its Visa Waiver Programme are noth­ing less than black­mail. The EU ac­cept­ing the terms of this power strug­gle is ex­tremely wor­ry­ing for the rights of Europeans and any third coun­try na­tion­als reg­is­tered in European data­bases.

The scheme im­plies sys­tem­atic trans­fers of bio­met­ric data and other highly sen­si­tive and sub­jec­tive indications of risk” based on European na­tional data­bases to the US gov­ern­ment, which has shown a bla­tant dis­re­gard for ba­sic hu­man rights in its harsh and in­hu­mane treat­ment of mi­grants and vis­i­tors. In that re­gard, the draft text does noth­ing to pro­tect from dis­crim­i­na­tion based on po­lit­i­cal opin­ions, which is wor­ry­ing be­cause the US is al­ready crack­ing down po­lit­i­cal dis­sent, in­clud­ing its un­law­ful prac­tice of screen­ing trav­ellers’ so­cial me­dia pro­files. The shar­ing of in­di­vid­ual risk as­sess­ments to sup­pos­edly pro­tect public se­cu­rity and pub­lic or­der” un­der the EBSP could fur­ther tar­get op­po­si­tion to the Trump ad­min­is­tra­tion, sup­port to trans­gen­der peo­ple’s rights or protests against the geno­cide in Gaza ex­pressed (publicly) on so­cial me­dia, with po­ten­tially sig­nif­i­cant im­pacts, in­clud­ing de­ten­tion at the bor­ders.

Furthermore it is very dif­fi­cult to know the terms of the ne­go­ti­a­tions as the talks are shrouded in se­crecy. However, in May 2026, EDRi mem­ber Statewatch leaked a revised ver­sion” of the draft Framework Agreement, re­veal­ing the di­rec­tion the fu­ture deal has taken.

According to EDRi’s analy­sis, the ne­go­ti­ated text seems to al­most en­tirely re­flect US de­mands for un­fet­tered in­for­ma­tion ac­cess. This poses two ma­jor prob­lems: (1) the text de­parts sig­nif­i­cantly from the Member States’ ne­go­ti­a­tion man­date; (2) its pro­vi­sions are, in large parts, not com­pli­ant with EU pri­mary and sec­ondary law, no­tably the Charter of Fundamental Rights. Notably, they fall short of meet­ing EU fun­da­men­tal re­quire­ment of reach­ing an es­sen­tially equiv­a­lent level of pro­tec­tion for in­ter­na­tional data trans­fers — some­thing that the Court of Justice of the EU would have no doubt of and de­clare the agree­ment in­com­pat­i­ble with EU law.

Our call to the EU: re­sist US pres­sure, pro­tect key safe­guards

EU lead­ers must fight to pro­tect our fun­da­men­tal rights against yet an­other in­stance of the US bul­ly­ing the EU into dis­man­tling key safe­guards. A strong Europe needs a strong rule­book and our law­mak­ers must be fo­cused on strength­en­ing core pro­tec­tions against data ex­ploita­tion and pri­vacy vi­o­la­tions — rather than ac­qui­esc­ing to the whims of the US.

EDRi rec­om­mends the Commission and the Council to push back against the US gov­ern­men­t’s black­mail and refuse to sell peo­ple’s per­sonal data to a coun­try with a very wor­ry­ing hu­man rights vi­o­la­tions record and rapid de­mo­c­ra­tic back­slid­ing.

Stop Using OpenCode

wren.wtf

If you don’t know what OpenCode is, imag­ine a boot stamp­ing on a hu­man face for­ever. The boot is made of TypeScript and the face is every­thing we have learned about se­cu­rity and sys­tems soft­ware since the in­ven­tion of the elec­tronic com­puter in the 1940s. The cre­ators de­scribe it as an AI cod­ing agent. As far as I can tell it’s the most pop­u­lar open-source cod­ing agent, and it cur­rently has 161k stars on GitHub.

I’ve tried out OpenCode with a lo­cal LLM. My con­clu­sion is that OpenCode is clown-car tur­boslop with a se­cu­rity pos­ture of let me bend over for you daddy”. Everyone us­ing it should stop us­ing it.

There are two parts to this post: an­noy­ing things and alarm­ing things. The sec­ond part is longer. I wrote this post with ref­er­ence to source code from OpenCode git ver­sion bae­f5cd4.

I don’t con­sider any­thing in this post to be a se­cu­rity dis­clo­sure. OpenCode is fun­da­men­tally a web-stack tool for pip­ing llm | bash, and all the is­sues I de­scribe are in the pipe” part. The ways it fails are fas­ci­nat­ing in the frac­tal na­ture of the poor de­ci­sion mak­ing, but the out­come was fore­gone.

I tried to keep dis­cus­sion of LLM use sep­a­rate from whether every­one us­ing LLMs should have their ma­chines triv­ially ex­ploited or ac­ci­den­tally wiped. There is a post-script with some brief thoughts on lo­cal LLMs.

Annoying Things

Let’s put se­cu­rity to one side for a mo­ment and ex­am­ine how OpenCode fails as a tool even when it’s not caus­ing you to get your shit popped. There is a kind of Bethesda Effect with OpenCode where it’s im­pos­si­ble to tell what is a bug and what is by de­sign, so I stuck with a de­scrip­tion of annoying”.

Prompt Cache Misses

Most lo­cal LLM servers use some vari­ant of the OpenAI /v1/chat/completions API. The idea is:

You POST a JSON blob with the en­tire con­ver­sa­tion so far.

You POST a JSON blob with the en­tire con­ver­sa­tion so far.

You get back a stream of SSE events which add up to the re­sponse.

You get back a stream of SSE events which add up to the re­sponse.

The up­load cost over a ses­sion is qua­dratic, and down­load is am­pli­fied by wrap­ping tiny deltas in JSON with re­peated meta­data. Tool calls use the elu­sive double JSON en­cod­ing” so they can be se­ri­alised as mul­ti­ple JSON-encoded deltas that re­assem­ble into more JSON.

The setup has one ben­e­fit, which is the server is state­less. As usual, the way you make state­less things fast is: state. The server caches eval­u­a­tions. When it re­ceives a re­quest, it:

Finds the longest match­ing cached pre­fix.

Finds the longest match­ing cached pre­fix.

Evaluates from end of pre­fix to end of the last posted mes­sage (“prefill”).

Evaluates from end of pre­fix to end of the last posted mes­sage (“prefill”).

Generates new to­kens un­til it en­coun­ters an end-of-se­quence to­ken.

Generates new to­kens un­til it en­coun­ters an end-of-se­quence to­ken.

I used Qwen3.6 – 27B dense on an M4 Max, which has de­cent mem­ory band­width (~0.5 TB/s, high for a CPU SoC, low for a GPU). Token gen­er­a­tion is us­able but it is ex­tremely com­pute-bound in pre­fill. If my server can’t find a good match­ing pre­fix for the re­quest prompt when I’m deep into the win­dow then I might have to wait 10 min­utes of max GPU us­age for it to start gen­er­at­ing a re­sponse. That’s fine, be­cause this should hap­pen rarely. Should.

Here are some of the ways OpenCode missed the memo on this one:

It globs your filesys­tem and re-reads AGENTS.md (injected in turn-0 sys­tem prompt) on every SSE turn. If you put a quick note in AGENTS.md to be read in the next ses­sion, you im­me­di­ately force a full re-eval­u­a­tion.

It globs your filesys­tem and re-reads AGENTS.md (injected in turn-0 sys­tem prompt) on every SSE turn. If you put a quick note in AGENTS.md to be read in the next ses­sion, you im­me­di­ately force a full re-eval­u­a­tion.

It prunes con­text from tool calls on every agent → user tran­si­tion, in­val­i­dat­ing a large part of the pre­fix.

Pruning just dis­cards tool call re­sults more than a fixed dis­tance const PRUNE_PROTECT = 40_000 be­hind the write head. In the best case you’re tak­ing a 40k con­text miss, which is equiv­a­lent to read­ing a full-length novel per two or three turns. Agent → user tran­si­tion in­cludes in­ter­rup­tion, so if you need to pull the clanker out of a rab­bit hole and re-steer it, OpenCode im­me­di­ately trashes the prompt cache and makes you wait for a re­sponse.

It prunes con­text from tool calls on every agent → user tran­si­tion, in­val­i­dat­ing a large part of the pre­fix.

Pruning just dis­cards tool call re­sults more than a fixed dis­tance const PRUNE_PROTECT = 40_000 be­hind the write head. In the best case you’re tak­ing a 40k con­text miss, which is equiv­a­lent to read­ing a full-length novel per two or three turns.

Pruning just dis­cards tool call re­sults more than a fixed dis­tance const PRUNE_PROTECT = 40_000 be­hind the write head. In the best case you’re tak­ing a 40k con­text miss, which is equiv­a­lent to read­ing a full-length novel per two or three turns.

Agent → user tran­si­tion in­cludes in­ter­rup­tion, so if you need to pull the clanker out of a rab­bit hole and re-steer it, OpenCode im­me­di­ately trashes the prompt cache and makes you wait for a re­sponse.

Agent → user tran­si­tion in­cludes in­ter­rup­tion, so if you need to pull the clanker out of a rab­bit hole and re-steer it, OpenCode im­me­di­ately trashes the prompt cache and makes you wait for a re­sponse.

Personal favourite: it puts the cur­rent date in the turn-0 sys­tem prompt and re-eval­u­ates every SSE turn. If you’re us­ing OpenCode at mid­night you get a full prompt cache miss.

I think this tries to re­me­di­ate the clanker ten­dency to be­lieve the cur­rent date is the date of its train­ing cut­off, and refuse to be­lieve newer things ex­ist. It’s still funny.

Personal favourite: it puts the cur­rent date in the turn-0 sys­tem prompt and re-eval­u­ates every SSE turn. If you’re us­ing OpenCode at mid­night you get a full prompt cache miss.

I think this tries to re­me­di­ate the clanker ten­dency to be­lieve the cur­rent date is the date of its train­ing cut­off, and refuse to be­lieve newer things ex­ist. It’s still funny.

These are the prompt cache misses that fit solely in this cat­e­gory. There are many more; I’ll call them out as we go.

Pruning

I men­tioned prun­ing in the pre­vi­ous sec­tion. The prompt cache misses aren’t worth it so I dis­abled it. The other glar­ing is­sue is the lack of pro­tec­tion for early reads. It might not be ob­vi­ous how com­pletely bro­ken this is, so let’s work through an ex­am­ple. Suppose you start a fresh ses­sion, and tell your clanker to first read a spec or im­ple­men­ta­tion plan, then write some code:

The spec is read into con­text.

The spec is read into con­text.

The clanker goes and reads re­lated code, very likely putting it over the fixed 40k prun­ing thresh­old.

The clanker goes and reads re­lated code, very likely putting it over the fixed 40k prun­ing thresh­old.

The clanker is ready to im­ple­ment, but ei­ther im­me­di­ately dives down a dumb rab­bit hole, or sits in chain-of-thought dither­ing about some­thing that is ac­tu­ally very sim­ple or al­ready well-spec­i­fied.

The clanker is ready to im­ple­ment, but ei­ther im­me­di­ately dives down a dumb rab­bit hole, or sits in chain-of-thought dither­ing about some­thing that is ac­tu­ally very sim­ple or al­ready well-spec­i­fied.

You in­ter­rupt the clanker to re-steer it.

You in­ter­rupt the clanker to re-steer it.

The in­ter­rup­tion causes the en­tire spec to be deleted from the con­text win­dow.

The in­ter­rup­tion causes the en­tire spec to be deleted from the con­text win­dow.

The clanker writes code with­out be­ing able to re­fer to the spec.

The clanker writes code with­out be­ing able to re­fer to the spec.

Pruning ap­plies equally to all re­sults of all tools ex­cept for skill, which is never pruned.

Compaction

Want to sit for 10 min­utes while the LLM server pre­fills the en­tire ses­sion with a new prompt pre­fixed to it, just to turn it into 5 bul­let points that go at the top of a new ses­sion? Me nei­ther. I get what they are go­ing for, but I’ve not seen it work well. Neither com­paction nor prun­ing is im­ple­mented well, and they in­ter­act poorly.

If you want to sum­marise a ses­sion then the sum­mari­sa­tion prompt should be in­jected at the end to avoid pre­fill­ing the en­tire ses­sion from scratch. The best method I’ve found is just an ex­plicit hand­off by telling the clanker to write out notes. It’s ugly but it works bet­ter than OpenCode’s com­paction mech­a­nism, and cre­ates an on-disk arte­fact that I can edit or reuse in mul­ti­ple ses­sions.

Compaction is a leaky ab­strac­tion that tries to make a fi­nite con­text win­dow look like an in­fi­nite one. It’s bet­ter to ac­cept con­text win­dows and prompt caches as a first-class fea­ture of clanker wran­gling, and ex­pose bet­ter prim­i­tives for man­ag­ing them. Pi has an in­ter­est­ing ap­proach here with ses­sion trees, which de­lib­er­ately ex­ploit the prompt cache.

System Prompts

OpenCode pastes a sys­tem prompt at the top of new con­text win­dows. Fine and nor­mal, but:

The de­fault sys­tem prompt is in­cred­i­bly ver­bose. Ironically most of the word count is ex­plain­ing to the LLM how to be con­cise.

The de­fault sys­tem prompt is in­cred­i­bly ver­bose. Ironically most of the word count is ex­plain­ing to the LLM how to be con­cise.

The de­fault sys­tem prompt is opin­ion­ated (fine) but it has shit opin­ions (not fine). It took me a while to fig­ure out why my agent kept say­ing Use ABSOLUTELY NO COMMENTS when dis­patch­ing sub­agents.

The de­fault sys­tem prompt is opin­ion­ated (fine) but it has shit opin­ions (not fine). It took me a while to fig­ure out why my agent kept say­ing Use ABSOLUTELY NO COMMENTS when dis­patch­ing sub­agents.

The Plan-to-Build han­dover is clumsy and of­ten leaves me to­wards the end of a con­text win­dow by the time every­thing is suf­fi­ciently elu­ci­dated. I’d rather write out notes with every­thing dis­cussed, so I can edit it and then hand off to a fresh ses­sion. To which, see next point:

The Plan-to-Build han­dover is clumsy and of­ten leaves me to­wards the end of a con­text win­dow by the time every­thing is suf­fi­ciently elu­ci­dated. I’d rather write out notes with every­thing dis­cussed, so I can edit it and then hand off to a fresh ses­sion. To which, see next point:

The sys­tem re­minder for Plan mode tells the clanker it can’t write to any di­rec­to­ries, but it’s ac­tu­ally al­lowed to write to a spe­cific .opencode/plans di­rec­tory. I have seen this fail both ways: writ­ing to this di­rec­tory un­prompted, and re­fus­ing to write when ex­plic­itly in­structed.

The sys­tem re­minder for Plan mode tells the clanker it can’t write to any di­rec­to­ries, but it’s ac­tu­ally al­lowed to write to a spe­cific .opencode/plans di­rec­tory. I have seen this fail both ways: writ­ing to this di­rec­tory un­prompted, and re­fus­ing to write when ex­plic­itly in­structed.

There is no way to mod­ify the de­fault sys­tem prompt glob­ally; you have to copy it into every pro­ject.

There is no way to mod­ify the de­fault sys­tem prompt glob­ally; you have to copy it into every pro­ject.

If you only over­ride the de­fault prompt in Build mode, switch­ing to Plan mode is a full prompt cache miss.

If you only over­ride the de­fault prompt in Build mode, switch­ing to Plan mode is a full prompt cache miss.

Different per-model prompts have wildly vary­ing con­tents and qual­ity. They’re all worth a good hate-scroll but Beast Mode (GPT-4, o1 and o3) is my favourite. Quote:

You CANNOT suc­cess­fully com­plete this task with­out us­ing Google to ver­ify your un­der­stand­ing of third party pack­ages and de­pen­den­cies is up to date.

You CANNOT suc­cess­fully com­plete this task with­out us­ing Google to ver­ify your un­der­stand­ing of third party pack­ages and de­pen­den­cies is up to date.

You can­not. It’s just im­pos­si­ble. We don’t know how. Definitely don’t just read the source code for the pack­age.

Permission Prompts

When the clanker tries to ac­cess a file out­side of the pro­ject di­rec­tory, if it does so in a way that OpenCode man­ages to recog­nise with ad-hoc string pars­ing (oops that is the alarm­ing things sec­tion), you re­ceive a prompt ask­ing you whether to grant per­mis­sion. This halts ex­e­cu­tion un­til you re­spond.

The an­swers are: Yes/No/Always. Do you see a miss­ing an­swer here? How about Never?

The in­ter­ac­tion with sub­agents here is par­tic­u­larly bro­ken. If a sub­agent tries to ac­cess a script out­put in /tmp, and I say No, it kills the sub­agent and all of its con­text for its par­tially com­plete work is lost. So I have to say Yes and let it write to /tmp or what­ever it’s try­ing to do.

The other is­sue is de­ci­sion fa­tigue: if I keep get­ting asked can I do this?” and the only re­sponse that leads to pro­duc­tiv­ity is yes” then I’m even­tu­ally go­ing to nod through some­thing dan­ger­ous. Human fal­li­bil­ity should not be load-bear­ing for some­thing as ba­sic as don’t write out­side this di­rec­tory”.

Agent Interaction

This is fea­ture num­ber 0 for a cod­ing agent. It’s bro­ken.

If I send a mes­sage while SSE stream­ing is on­go­ing, it gets queued. Nice fea­ture. However:

The se­man­tics of when OpenCode de­cides to ac­tu­ally send the mes­sage are a lit­tle un­clear; the code sug­gests it’s at the end of a tool call turn but I have also seen tool → CoT tran­si­tions with­out send­ing my queued mes­sage. If I sub­se­quently in­ter­rupt be­cause I want the clanker to ac­tu­ally an­swer my ques­tion in­stead of navel gaz­ing, the mes­sage is re­moved from the queued” state and just goes into the mes­sage log. You in­ter­rupted to get the clanker to an­swer your mes­sage, but now you can’t send the mes­sage. You have to send a sec­ond mes­sage to start a new stream.

If I send a mes­sage while SSE stream­ing is on­go­ing, it gets queued. Nice fea­ture. However:

The se­man­tics of when OpenCode de­cides to ac­tu­ally send the mes­sage are a lit­tle un­clear; the code sug­gests it’s at the end of a tool call turn but I have also seen tool → CoT tran­si­tions with­out send­ing my queued mes­sage.

The se­man­tics of when OpenCode de­cides to ac­tu­ally send the mes­sage are a lit­tle un­clear; the code sug­gests it’s at the end of a tool call turn but I have also seen tool → CoT tran­si­tions with­out send­ing my queued mes­sage.

If I sub­se­quently in­ter­rupt be­cause I want the clanker to ac­tu­ally an­swer my ques­tion in­stead of navel gaz­ing, the mes­sage is re­moved from the queued” state and just goes into the mes­sage log. You in­ter­rupted to get the clanker to an­swer your mes­sage, but now you can’t send the mes­sage. You have to send a sec­ond mes­sage to start a new stream.

If I sub­se­quently in­ter­rupt be­cause I want the clanker to ac­tu­ally an­swer my ques­tion in­stead of navel gaz­ing, the mes­sage is re­moved from the queued” state and just goes into the mes­sage log. You in­ter­rupted to get the clanker to an­swer your mes­sage, but now you can’t send the mes­sage. You have to send a sec­ond mes­sage to start a new stream.

Undoing a mes­sage of­ten fails to re­move it from the mes­sage log.

Undoing a mes­sage of­ten fails to re­move it from the mes­sage log.

The prob­lems con­tinue with sub­agents (i.e. agents spawned by an agent us­ing a tool call RPC):

I can’t talk to sub­agents; if they go down a dumb rab­bit hole I have a choice of killing them and los­ing their con­text, or help­lessly watch­ing them burn to­kens.

I looked into this and ap­par­ently OpenCode used to have this fea­ture but it’s just… gone? You can @mention a sub­agent from the main agen­t’s chat win­dow but this does­n’t seem to do any­thing use­ful. In par­tic­u­lar it does­n’t in­ter­rupt.

I can’t talk to sub­agents; if they go down a dumb rab­bit hole I have a choice of killing them and los­ing their con­text, or help­lessly watch­ing them burn to­kens.

I looked into this and ap­par­ently OpenCode used to have this fea­ture but it’s just… gone?

I looked into this and ap­par­ently OpenCode used to have this fea­ture but it’s just… gone?

You can @mention a sub­agent from the main agen­t’s chat win­dow but this does­n’t seem to do any­thing use­ful. In par­tic­u­lar it does­n’t in­ter­rupt.

You can @mention a sub­agent from the main agen­t’s chat win­dow but this does­n’t seem to do any­thing use­ful. In par­tic­u­lar it does­n’t in­ter­rupt.

If a sub­agent fails a tool call (e.g. Qwen putting tool calls in CoT) then it’s fa­tal and all the con­text up to that point is lost.

If a sub­agent fails a tool call (e.g. Qwen putting tool calls in CoT) then it’s fa­tal and all the con­text up to that point is lost.

Hacker wipes Romania's entire land registry database

news.risky.biz

This newslet­ter is brought to you by Thinkst, the mak­ers of the much-loved Thinkst Canary. You can sub­scribe to an au­dio ver­sion of this newslet­ter as a pod­cast by search­ing for Risky Business” in your pod­catcher or sub­scrib­ing via this RSS feed. You can also add the Risky Business newslet­ter as a Preferred Source to your Google search re­sults by go­ing here.

A hacker has breached Romania’s cadas­tre agency and wiped the coun­try’s en­tire land reg­istry data­base fol­low­ing a failed ex­tor­tion at­tempt.

The hack has brought Romania’s en­tire real-es­tate mar­ket to a stand­still as of­fi­cial apps and web­sites have been of­fline for a week. Notaries can’t record new trans­ac­tions while cit­i­zens can’t ob­tain proof of own­er­ship or de­tailed land records.

Email servers at the National Agency for Cadastre and Real Estate Advertising (Agenția Națională de Cadastru și Publicitate Imobiliară, or ANCPI) were also down as part of the in­ci­dent.

Sources told Risky Business that the hacker en­tered us­ing valid cre­den­tials, mapped in­ter­nal sys­tems, and wiped sys­tems and back­ups af­ter fail­ing to ex­tort the agency.

The in­ci­dent be­came pub­lic on July 14 as the hacker started delet­ing data. A day later, some of ANCPIs stolen data was put up for sale on a known hack­ing fo­rum. The posted data in­cluded em­ployee cre­den­tials, in­ter­nal doc­u­ments, and de­tails on the agen­cy’s IT net­work.

Since the hack, of­fi­cials re­stored their web­site and posted a mes­sage an­nounc­ing they are re­build­ing the agen­cy’s en­tire net­work from scratch. Even if the hacker claims they deleted back­ups, the agency ap­pears to have had an of­fline copy, oth­er­wise things would have got­ten re­ally messy over the com­ing months in Romania.

The stolen data was posted on­line by an ac­count with the name ByteToBreach, a known hacker who also breached Sweden’s e-gov­ern­ment por­tal this year, and many other gov­ern­ment agen­cies and high-pro­file com­pa­nies over the past year.

Security firm KELA pub­lished a pro­file on ByteToBreach last December and hinted they might be lo­cated in Algeria, but since the ANCPI hack has up­dated the post and out­right doxxed the hacker as Zakaria Mahdjoub, an in­di­vid­ual from Oran, Algeria.

Well, that will make the job of Romanian law en­force­ment a hell lot eas­ier! gj!

Romania joins Poland, Slovakia, Greece, Morocco, Russia, and Ukraine as coun­tries that had their land reg­istry agen­cies hacked over the past three years.

Risky Business Podcasts

In this edi­tion of Seriously Risky Business, Tom Uren and James Wilson talk about dif­fer­ent ways ran­somware groups are tak­ing ad­van­tage of AI. The rel­a­tively new FulcrumSec group uses sim­ple tech­niques to breach com­pa­nies and then uses AI to get more lever­age over vic­tims in its ex­tor­tion ne­go­ti­a­tions.

Breaches, hacks, and se­cu­rity in­ci­dents

Hugging Face hacked us­ing AI: A threat ac­tor used an au­tonomous AI agent to breach AI plat­form Hugging Face last week. The at­tacker used ex­ploits in the plat­for­m’s data-pro­cess­ing pipeline to pivot to some parts of the com­pa­ny’s in­ter­nal sys­tems. Hugging Face says no cus­tomer data was ex­posed but the at­tacker stole in­ter­nal datasets and some cloud cre­den­tials. Hugging Face says it tried to use a fron­tier AI model to an­a­lyze the hack but was blocked by its guardrails, which could­n’t dif­fer­en­ti­ate be­tween an IR event and of­fen­sive op­er­a­tions. [Hugging Face]

HuggingFace got hacked by an AI. What stuck out to me was the guardrail asym­me­try. The at­tacker had no con­straints, but HFs re­sponse ran afoul of the abuse guardrails, forc­ing them into an un­planned switch to lo­cal mod­els.

Another as­pect for your IR plans.

hug­ging­face.co/​blog/​se­cu­rit…[im­age or em­bed]— David J. Bianco (@davidjbianco.bsky.social) July 17, 2026 at 10:59 PM

HuggingFace got hacked by an AI. What stuck out to me was the guardrail asym­me­try. The at­tacker had no con­straints, but HFs re­sponse ran afoul of the abuse guardrails, forc­ing them into an un­planned switch to lo­cal mod­els.

Another as­pect for your IR plans.

hug­ging­face.co/​blog/​se­cu­rit

[image or em­bed]

Coca-Cola hit by ran­somware: Coca-Cola has sus­pended pro­duc­tion at its Fairlife dairy sub­sidiary af­ter a ran­somware at­tack. In an SEC fil­ing, Coca-Cola said hack­ers ac­cessed Fairlife pro­duc­tion-re­lated sys­tems this week. Production has been halted at Fairlife US fac­to­ries. The com­pa­ny’s Canadian pro­duc­tion lines were un­af­fected. No ran­somware group has taken credit for the in­ci­dent, yet. [SEC // TechCrunch]

Qantas breach has a cause: The hack of Australian air­line com­pany Qantas last year was traced back to a so­cial en­gi­neer­ing at­tack. Hackers called an over­seas con­trac­tor pos­ing as the Qantas IT team to ac­cess their sys­tems, con­nect to the Qantas CRM plat­form, and ex­fil­trate the data of 5.7 mil­lion cus­tomers. Australia’s Information Commissioner says Qantas took all the steps to pro­tect cus­tomer data on its side and will not be open­ing fur­ther probes into the hack. [OAIC]

Suno hack: A threat ac­tor hacked AI mu­sic gen­er­a­tor plat­form Suno and dumped in­ter­nal files and doc­u­ments on­line. The files al­legedly show that Suno scraped mil­lions of songs and lyrics from YouTube Music, Deezer, Genius, and other mu­sic plat­forms. The leaked files in­clude source code and de­tailed scrap­ing in­struc­tions tar­get­ing the plat­forms. Several mu­sic in­dus­try groups have sued Suno over the past year for train­ing its AI song gen­er­a­tor tool on copy­righted ma­te­r­ial. Suno was al­legedly hacked fol­low­ing a com­pro­mise with the Shai-Hulud npm worm. [404 Media // The Verge]

WINDTRE fined for breaches: Italy’s pri­vacy watch­dog has fined telecom­mu­ni­ca­tions provider WINDTRE €1.7 mil­lion for serious se­cu­rity de­fi­cien­cies” that led to two se­cu­rity breaches last year. [GPDP]

KNPP leak: Threat in­tel an­a­lyst Rakesh Krishnan looks at a leak of sen­si­tive files from India’s KNPP nu­clear power plant af­ter one of its con­trac­tors got hit by the World Leaks ex­tor­tion group. [The Raven File]

Ostium crypto-heist: The Ostium DeFi plat­form was hacked for $18 mil­lion last week af­ter hack­ers ex­ploited its own price-re­port­ing in­fra­struc­ture. [CoinDesk]

Estée Lauder dis­closes Oracle EBS breach: Cosmetics gi­ant Estée Lauder has con­firmed that hack­ers stole cus­tomer data from its Oracle E-Business Suite plat­form last year. The com­pany dis­closed the breach to US state of­fi­cials al­most a year af­ter it took place. This is Estée’s sec­ond breach af­ter an­other one in 2023. The Clop hack­ing group is be­hind the hack­ing spree that tar­geted Oracle EBS servers. [California OAG]

Ernst & Young also dis­closes breach: Accounting and risk man­age­ment gi­ant Ernst & Young also dis­closed a breach, but the dis­clo­sure has been so san­i­tized of any info that I can’t tell what’s this about. [California OAG]

DigiCert breach linked to CylindricalCanine: Security firm Expel has linked the hack of cer­tifi­cate au­thor­ity DigiCert to CylindricalCanine, a sub-group of GoldenEyeDog, a fi­nan­cially mo­ti­vated group op­er­at­ing out of China. [Expel]

General tech and pri­vacy

Ofcom opens TikTok in­quiry: The UKs com­mu­ni­ca­tions watch­dog has opened a for­mal in­ves­ti­ga­tion into TikTok for fail­ing to pro­tect chil­dren from harm­ful con­tent on the plat­form, as per the UKs Online Safety Act. [Ofcom]

Moonshot re­leases Kimi K3: Chinese AI startup Moonshot has un­veiled a new AI model named Kimi K3, which the com­pany claims can ri­val the ones from top American firms like Anthropic and OpenAI. [Kimi // Business Insider]

Rust in Chromium: Microsoft is work­ing on adding a Rust-based PNG im­age de­coder in the Chromium browser pro­ject, a more se­cure com­po­nent for pro­cess­ing PNG im­ages for Chrome, Edge, Opera, and other sim­i­lar browsers. [Microsoft]

EU pass­word man­ager has ties to Russia: An in­ves­ti­ga­tion has re­vealed that Spain-based pass­word man­ager Passwork shares its code­base and a near-identical user man­ual” with a sim­i­larly-named pass­word man­ager ad­ver­tised in Russia. The Spanish ver­sion has al­legedly been re­ceiv­ing soft­ware up­dates from an UAE firm man­aged by one of Passwork’s Russian co-founders. The Spanish Passwork’s cus­tomer list in­cludes European gov­ern­ment agen­cies and uni­ver­si­ties, which raises con­cerns of es­pi­onage. [OCCRP]

India fines HP over car­tel prac­tices: The Indian gov­ern­ment fined HP $14.4 mil­lion over car­tel prac­tices af­ter the com­pany col­luded with re­sellers to fix prices for ink car­tridges, toner, and other print­ing sup­plies in gov­ern­ment con­tract bids. [ArsTechnica]

SanFran CAO cracks down on nud­ify apps: The San Francisco City Attorney’s Office has sent cease-and-de­sist let­ters to Apple and Google and or­dered the tech gi­ants to re­move AI nud­ify apps from their stores and stop in­di­rectly prof­it­ing from CSAM. [WIRED]

Government, pol­i­tics, and pol­icy

Morocco con­firmed as NSO cus­tomer: A whistle­blower and for­mer mem­ber of Morocco’s do­mes­tic in­tel­li­gence ser­vice has con­firmed their gov­ern­men­t’s ac­cess to the NSO Pegasus spy­ware, con­trary to the gov­ern­men­t’s past pub­lic de­nials. The tool was heav­ily used to spy on dis­si­dents, jour­nal­ists, and even politi­cians abroad. [OCCRP // Forbidden Stories]

UK scraps dig­i­tal ID scheme: The UK gov­ern­ment will scrap a pro­posed dig­i­tal ID scheme once its new prime min­is­ter Andy Burnham takes of­fice on Monday. The scheme was an­nounced last September and was sup­posed to en­ter into ef­fect next year. It in­volved is­su­ing a dig­i­tal ID for UK cit­i­zens and le­gal res­i­dents in the form of a mo­bile app. The ID was meant to serve as proof for the Right to Work in the UK. [Reuters]

US govt fails to ro­tate cy­ber per­son­nel: The US gov­ern­ment failed to fol­low through with one of its own pro­grams to ro­tate cy­ber­se­cu­rity em­ploy­ees be­tween fed­eral agen­cies. Only eight em­ploy­ees par­tic­i­pated in the pro­gram since 2022. The pro­gram was meant to teach em­ploy­ees new skills be­fore re­turn­ing to their na­tive agen­cies. [GAO // Cyberscoop]

White House an­nounces Gold Eagle pro­gram: The Trump ad­min­is­tra­tion has launched a new pro­gram to help co­or­di­nate the dis­clo­sure and patch­ing of vul­ner­a­bil­i­ties in open-source pro­jects and crit­i­cal in­fra­struc­ture. The new Gold Eagle pro­gram was de­signed to re­ceive bug re­ports at scale, usu­ally found us­ing AI tools and fron­tier AI mod­els. CISA, the Treasury Department, and the Pentagon are in­volved in the pro­gram. [White House]

France bans Polymarket: The French gov­ern­ment has or­dered in­ter­net ser­vice providers to block ac­cess to pre­dic­tion mar­ket bet­ting plat­form Polymarket. The French reg­u­la­tory au­thor­ity for­mally banned the plat­form in 2024 and threat­ened fines of up to €200,000 for French cit­i­zens plac­ing bets on the plat­form. The agency moved into ac­tive en­force­ment af­ter data showed Polymarket’s user­base grew in France de­spite the ban. Spain also banned Polymarket in May. [Engadget]

In this Risky Business spon­sor in­ter­view, Casey Ellis chats with Haroon Meer from Thinkst about build­ing com­pa­nies cus­tomers don’t hate. Haroon ex­plains why Thinkst still of­fers Canary to­kens for free and why it has avoided an­nual price hikes on its paid prod­ucts. They talk about Eric Ries’s Incorruptible”, Rob Lee’s 100-year-company ap­proach at Dragos, and why keep­ing cus­tomers happy is a bet­ter busi­ness strat­egy than chas­ing easy sugar highs.

Arrests, cy­ber­crime, and threat in­tel

Graykey maker sues em­ployee for leak­ing ex­ploit: Graykey-maker Magnet Forensics has sued a for­mer em­ployee for al­legedly leak­ing de­tails about a pro­pri­etary iPhone ex­ploit. Magnet claims Mario Del Gaudio shared de­tails of the ex­ploit with his new em­ployer and ri­val com­pany Paradigm Shift. The ex­ploit was tracked in­ter­nally at Magnet as MSG but was dis­closed pub­licly by Paradigm Shift in a blog post as us­bliter8. The ex­ploit al­lows at­tack­ers to run ma­li­cious code in­side the SecureROM of Apple de­vices us­ing A12 and A13 chips. It is a hard­ware bug and un­patch­able. [Bloomberg // CourtListener // us­bliter8 blog post]

TfL hack­ers get five years: A UK judge has sen­tenced two mem­bers of the Scattered Spider hack­ing group to 5.5 years in prison each. Thalha Jubair and Owen Flowers pleaded guilty last month to hack­ing the London pub­lic trans­port au­thor­ity in August of 2024. The hack caused months of dis­rup­tions at Transport for London and caused dam­ages of £39 mil­lion. Jubair is also charged in the US with hack­ing and ex­tort­ing 47 US com­pa­nies and al­legedly seek­ing ran­soms of at least $115 mil­lion. [NCA]

REvil hacker ar­rested in Armenia: Armenian au­thor­i­ties have ar­rested a sus­pected mem­ber of the REvil ran­somware group. Alexander Ermakov was ar­rested at the Yerevan air­port at the end of June on an Interpol ar­rest war­rant. A man named Alexander Ermakov is the main sus­pect be­hind the ran­somware at­tack on Australia’s Medibank in­surer in 2022. Russian me­dia claims that Armenian au­thor­i­ties ar­rested a man with the same name and that the real Ermakov is in Russia, where he is serv­ing a re­stric­tion of free­dom sen­tence that pre­vents him from trav­el­ing abroad. [RIA Novosti // Risky Business]

Scam cen­ter dis­man­tled in Timor-Leste: Police in Timor-Leste have raided three cy­ber scam com­pounds in the cap­i­tal city of Dili. Police ar­rested 253 sus­pects, with most be­ing Chinese and Indonesian na­tion­als. Authorities also raided an­other com­pound last month. [ABC]

DHS seizes 30,000 mo­bile SIM cards: The DHS Homeland Security Investigations seized more than 30,000 mo­bile SIM cards in June and July as part of a crack­down against tele­phone fraud. [Bloomberg]

GTA hacker re­leased from hos­pi­tal, sent to prison: A mem­ber of the Lapsus$ hack­ing group has been re­leased from a se­cure hos­pi­tal and trans­ferred to a nor­mal prison in the UK. Arion Kurtaj is set to face trial again for hack­ing Rockstar Games in 2022 and re­leas­ing GTA5 source code and GTA6 game­play. Kurtaj was di­ag­nosed with se­vere autism and sen­tenced to an in­def­i­nite hos­pi­tal or­der in December 2023. [GameRant // Polygon]

UAT-11795 pro­file: Cisco is track­ing a new e-crime group tar­get­ing com­pa­nies in the US and Europe with the Starland RAT and a com­mand-and-con­trol (C2) mem­ory im­plant named the WLDR Agent. [Cisco Talos]

TAG-150 evo­lu­tion: eS­en­tire has pub­lished de­tails on the changes to the trade­craft of TAG-150, an e-crime group be­hind the CastleLoader, CastleBot, and CastleRAT mal­ware strains—also tracked as DinDoor, a Deno-based loader, NightshadeC2, and DenoRAT, a Deno-based Remote Access Trojan (RAT). The biggest change is their adop­tion of ClickFix, every­one’s fa­vorite in­fec­tion vec­tor. [eSentire]

More ViPNeT ex­ploita­tion in Russia: A hack­ing group is plant­ing back­doors in­side Russian com­pa­nies us­ing the ViPNet en­ter­prise VPN soft­ware. The at­tack­ers first com­pro­mise one VPN node and then ex­ploit the soft­ware’s up­date mech­a­nism to in­stall the back­door on the whole net­work. ViPNet owner Infotecs has con­firmed the at­tacks and re­leased se­cu­rity up­dates. A sim­i­lar wave of at­tacks also took place in April last year. [Infotecs // PositiveTechnologies // Kaspersky // Last year’s at­tacks]

Scarcity scams are here to stay: Scarcity scams are a new cat­e­gory of on­line scams where threat ac­tors run fake sites for on­line ser­vices with lim­ited avail­abil­ity or spots. This type of scam has ex­ploded across the past few years and typ­i­cally tar­get the reser­va­tion sites of var­i­ous gov­ern­ment web­sites across the world. [DomainTools]

Sextortion cam­paigns: A re­cent spike in sex­tor­tion email scams has been linked to the good ol’ Trik/Phorpiex bot­net, which is still alive af­ter all these years. [PointWild]

Text salt­ing in the wild: Threat ac­tors are us­ing a tech­nique named text salt­ing” to hide text in­side their emails and by­pass email spam fil­ters for both tra­di­tional and AI-powered email se­cu­rity sys­tems. Barracuda has seen the tech­nique used in over a mil­lion re­tail-themed phish­ing scams. [Barracuda]

RubyGems mal­ware: At least two dor­mant RubyGems ac­counts have been com­pro­mised to push mal­ware to old pro­jects. [Aikido Security // Step Security]

OAuth Client ID Spoofing: Threat ac­tors are us­ing OAuth client ID spoof­ing to abuse Microsoft Entra ID for ac­count enu­mer­a­tion, check pass­word va­lid­ity, and ac­count state. The tech­nique is see­ing in­creased us­age, per Proofpoint. [Proofpoint]

Proofpoint ob­served two in­de­pen­dent cam­paigns adopt­ing this trade­craft:

• UNK_PyReq2323: >1M tar­geted users, 700K+ spoofed client IDs • UNK_OutFlareAZ: >2M tar­geted users, 3.7M spoofed client IDs

Different tool­ing and in­fra­struc­ture sug­gest grow­ing adop­tion.— ThreatInsight (@threatinsight.proofpoint.com) July 14, 2026 at 6:56 PM

Proofpoint ob­served two in­de­pen­dent cam­paigns adopt­ing this trade­craft:

• UNK_PyReq2323: >1M tar­geted users, 700K+ spoofed client IDs • UNK_OutFlareAZ: >2M tar­geted users, 3.7M spoofed client IDs

Different tool­ing and in­fra­struc­ture sug­gest grow­ing adop­tion.

Malware tech­ni­cal re­ports

XZ Utils back­door: Adrian Mastronardi has pub­lished a book with the in-depth story of the XZ Utils back­door in­ci­dent from 2024. [Half a Second]

Pegasus spy­ware: The se­cu­rity team at Amnesty International has pub­lished the most com­pre­hen­sive analy­sis of the Pegasus spy­ware to date, lever­ag­ing the in­sights from past re­ports and the re­cent WhatsApp law­suit. [Amnesty International]

ClickLock Stealer: A new in­fos­tealer tar­get­ing ma­cOS users has been spot­ted in the wild. This one has been named ClickLock be­cause it blends ClickFix and locker tac­tics for its dis­tri­b­u­tion and in­stal­la­tion process. [Group-IB]

CrashStealer: There’s also an­other ma­cOS in­fos­tealer in the wild, named CrashStealer be­cause it tries to im­per­son­ate Apple’s crash-re­port­ing frame­work to har­vest browser cre­den­tials, cryp­tocur­rency wal­lets, and key­chain data. [Jamf]

ACR Stealer: Microsoft has re­ported an in­crease in at­tacks de­ploy­ing the ACR Stealer across cus­tomer en­vi­ron­ments since April. [Microsoft]

BoryptGrab: Almost 300 GitHub repos­i­to­ries im­per­son­at­ing le­git­i­mate soft­ware were ac­tu­ally spread­ing a ver­sion of the BoryptGrab in­fos­tealer. [Arctic Wolf]

TELEPUZ: Elastic has spot­ted a new mal­ware frame­work be­ing de­ployed in the wild that ap­pears to be re­lated to an up­com­ing MaaS. [Elastic]

Spirals ran­somware: Broadcom’s Symantec team has spot­ted a new ran­somware strain named Spirals be­ing de­ployed in Asia. Not much in­for­ma­tion about it so far. [Broadcom]

NadMesh bot­net: A newly dis­cov­ered bot­net is specif­i­cally tar­get­ing AI in­fra­struc­ture and the MCP ecosys­tem. The NadMesh bot­net has tar­geted Ollama, ComfyUI, and other AI-related servers since early July. The bot­net plants SSH back­doors for con­trol and fu­ture ac­cess. According to Chinese se­cu­rity firm QiAnXin, the bot­net ap­pears to be an industrial-grade” op­er­a­tion with a clear com­mer­cial in­tent.” [QiAnXin]

OkoBot frame­work: Researchers have found a new mod­u­lar mal­ware frame­work named OkoBot that re­sem­bles an in­fos­tealer but puts more fo­cus on steal­ing sen­si­tive data from cryp­tocur­rency own­ers and re­lated ser­vices. [Kaspersky]

The OkoBot cam­paign has been on­go­ing for over a year, and it re­mains ac­tive at the time of pub­li­ca­tion. Moreover, it is adapt­ing, which in­di­cates that this frame­work is be­ing main­tained and dis­tri­b­u­tion cam­paigns con­tinue.”

WackoGinx phish­ing kit: Researchers have found a new phish­ing kit named WackoGinx (also WachoGinx) that can run cam­paigns tar­get­ing M365, Facebook, Gmail, LinkedIn, and PayPal. [Threatactix]

In this Soap Box edi­tion of the pod­cast, Patrick Gray chats with Thinkst Canary founder Haroon Meer about his decade of de­cep­tion.”

APTs, cy­ber-es­pi­onage, and info-ops

UTA0533 is be­hind new SonicWall zero-day wave: A hack­ing group tracked as UTA0533 is be­hind two zero-days ex­ploited in SonicWall SMA ap­pli­ances. The zero-days in­clude an SSRF and a code in­jec­tion vul­ner­a­bil­ity that grant the group root-level ac­cess to the de­vice. The at­tacks be­gan in late June and are de­ploy­ing mal­ware de­signed specif­i­cally for SonicWall SMA VPN ap­pli­ances. SonicWall re­leased patches for both zero-days last week. [Volexity // SonicWall patches]

GoSerpent cam­paign: Kaspersky is track­ing a new APT group de­ploy­ing the GoSerpent back­door, Stowaway, and TmcLoader in cam­paigns tar­get­ing gov­ern­ment and diplo­matic en­ti­ties in Southeast Asia. No at­tri­bu­tion yet. [Kaspersky]

Laundry Bear mem­ber worked at Kaspersky: Denis Obrezko, the Russian na­tional who was ar­rested in Thailand last year, ex­tra­dited to the US, and charged with hacks part of the Laundry Bear APT group, also worked for Russian se­cu­rity firm Kaspersky. A team of threat in­tel an­a­lysts go­ing by Ctrl-Alt-Intel has also pub­lished a pro­file on Obrezko and the opsec mis­takes that led to his ar­rest, which is well worth your read. [Reuters // Ctrl-Alt-Intel]

Love that a leaked McDonald’s or­der helped cor­rob­o­rate the at­tri­bu­tion 😂 Great piv­ots! https://​t.co/​XZUe­BAWZYZ— Chi-en (Ashley) Shen (@ashl3y-shen.bsky.social) (@ashl3y_shen) July 14, 2026

Love that a leaked McDonald’s or­der helped cor­rob­o­rate the at­tri­bu­tion 😂 Great piv­ots! https://​t.co/​XZUe­BAWZYZ

Sandworm adopts ClickFix: Even if they’re one of Russia’s most ad­vanced cy­ber-es­pi­onage groups, Sandworm is now us­ing ClickFix for mal­ware de­liv­ery. [CERT-UA]

More DPRK on npm: OSMs Jenn Gile has linked two clus­ters of npm mal­ware back to North Korean hack­ers and their PolinRider cam­paign. [OpenSourceMalware]

Operation Capsule Vault: And speak­ing of DPRK hack­ing cam­paigns, there’s one spread­ing the RokRAT mal­ware us­ing edu- and aca­d­e­mic-re­lated phish­ing lures. [Genians]

Contagious Interview cam­paign: There’s noth­ing more dan­ger­ous right now than try­ing to find a job in the IT sec­tor, thanks to North Korean hack­ers! Putting the irony aside, there’s a new re­port on the Contagious Interview cam­paign that Elastic tracks as REF9403 ac­tiv­ity. The re­port cov­ers the use of SVG files to hide ma­li­cious com­mands via steganog­ra­phy, which is kind of orig­i­nal in its own spe­cific way be­cause SVG files haven’t been broadly abused for steganog­ra­phy un­til now. [Elastic]

Vulnerabilities, se­cu­rity re­search, and bug bounty

wp2shell vul­ner­a­bil­ity: The WordPress team has re­leased a se­cu­rity up­date to patch one of the most crit­i­cal bugs ever found in the pro­jec­t’s code. The vul­ner­a­bil­ity is an SQL in­jec­tion in the WordPress REST API that can be ex­ploited by re­mote unau­then­ti­cated at­tack­ers to run ma­li­cious code on any WordPress site. The is­sue can be ex­ploited with­out any pre­con­di­tions and im­pacts all WordPress ver­sions re­leased since last December. WordPress sites power more than 41% of all in­ter­net sites. The bug was dis­cov­ered by Searchlight Cyber and is tracked as CVE-2026 – 63030, or wp2shell. [Searchlight Cyber // wp2shell // WordPress patch]

HollowByte at­tack: A new vul­ner­a­bil­ity can crash OpenSSL servers us­ing only an 11 bytes pay­load. The at­tack can be ex­ploited by re­mote unau­then­ti­cated at­tack­ers and force servers to al­lo­cate huge amounts of mem­ory be­fore any se­cure TLS hand­shake even be­gins. The OpenSSL pro­ject re­leased patches for both cur­rent and old li­brary ver­sions last month. The vul­ner­a­bil­ity was dis­cov­ered by Okta and is named HollowByte. [Okta]

Android lockscreen by­pass: Just like Siri has been ex­ploited for years to by­pass the lockscreen, it’s now Gemini’s turn to be abused to by­pass the Android lockscreen. [Android Headlines]

Vulnerability dis­clo­sure guide: CISA and in­ter­na­tional part­ners have re­leased joint guid­ance on es­tab­lish­ing proper co­or­di­nated dis­clo­sure pro­grams. [CISA]

Nightmare Eclipse drops LegacyHive: Security re­searcher Nightmare Eclipse has re­leased a new Windows ex­ploit last week. Named LegacyHive, the zero-day is a lo­cal priv­i­lege es­ca­la­tion in the Windows User Profile Service. [Project Nightcrawler // GitHub // SecurityWeek]

AoE RCE: The last thing you ever ex­pected is prob­a­bly a re­mote code ex­e­cu­tion ex­ploit in the good ol’ Age of Empires game.

Here’s the Age of Empires RCE from yes­ter­day’s Patch Tuesday: CVE-2026 – 50663.Join an at­tack­er’s lobby, (auto-)accept UCG, and you get re­mote code ex­e­cu­tion. pic.twit­ter.com/​QmMkY07C8S— Rick de Jager (@rdjgr) July 15, 2026

Here’s the Age of Empires RCE from yes­ter­day’s Patch Tuesday: CVE-2026 – 50663.

Join an at­tack­er’s lobby, (auto-)accept UCG, and you get re­mote code ex­e­cu­tion. pic.twit­ter.com/​QmMkY07C8S

To add this web app to your iOS home screen tap the share button and select "Add to the Home Screen".

10HN is also available as an iOS App

If you visit 10HN only rarely, check out the the best articles from the past week.

Visit pancik.com for more.