7thmagpie

Valknir: Scope

I’ve been mapping where Valknir goes next. Right now it finds malloc calls, basic double-frees, and use-after-free. Next from here are memory leaks, scope awareness, and alias tracking. Ultimately it’s a project for consolidating what I’m learning in security, and until recently that learning was pointing towards vulnerability research - but offensive security is increasingly where my interests lie. That shift made me ask whether Valknir’s original concept still fits the direction, and where it might expand if it does. Which led me to a feature I’ve since decided against - but worth writing about exactly why.

The feature was backdoor detection. I’d been reading up on the xz attack (CVE-2024-3094), and the process was fascinating to pick apart. It raised the obvious question: how do you defend against attacks that don’t come down to a memory bug at all? Binary exploitation is the floor. Above it is the whole open-ended space - build-chain compromise, dependency poisoning, abuse of the trust in a release process - where there’s no single primitive (or chain of them) to point a checker at. The problem is that a tool which flagged that kind of thing for developers would work just as well as a detection oracle for attackers.

Worth noting that this is true of any detector, Valknir’s planned ones included. Run it against your own planted double-free, see whether it flags, and you’ve learned whether the bug survives static analysis. (The current iteration is far too young to be much use that way, but the point stands.) Detection and evasion-testing aren’t two related things - they’re the same operation, read in opposite directions.

What stops that being a problem for the existing roadmap is that it gives an attacker nothing they don’t already have. Valknir isn’t competing on detection - its focus is clear explanation, not finding anything new. The best it can realistically be is as good as the strongest static analysers at spotting a double-free, not better, and any serious attacker already has those - fuzzers, sanitisers, CodeQL. So the dual-use is there, but no advantage is handed over that wasn’t already available. The real question was never “could an attacker use this” - everything clears that bar - but whether it hands them something they couldn’t already easily get.

Backdoor detection, on the other hand, is where that answer flips. A detector that scores suspicion leaks a gradient. Fewer flags each pass, so an attacker hill-climbs against it - refining the backdoor until the tool goes quiet. Valknir’s whole point is explainability. It doesn’t say suspicious, it says here is the tell and here is why. Pointed at backdoors, that hands the attacker the gradient in plain English. The property that makes it good for developers is the property that makes it dangerous in an attacker’s hands. An open, explainable map of how to find a backdoor - and by inversion, how to hide one - has no business being published.

The detectors that escape this are pass/fail on something unfakeable. Does the release tarball reproduce from the git tag - yes or no. No partial credit to optimise toward, no gradient to descend. That’s why serious supply-chain defence lives in reproducible builds and provenance rather than a spot-the-evil-code checker. The constraint isn’t avoid security features. It’s avoid building detectors with a climbable gradient.

Supply-chain backdoors are badly under-defended, and someone should definitely work on them. But explainable, open-source, source-level detection is not the way to do it, for the reason above.

None of which means a tool like that shouldn’t exist at all. It’s that it belongs to red teams and the like - sanctioned offence, working on the defender’s side - not to attackers or the general public. You don’t needlessly arm the army you’re defending against. If I ever do build backdoor detection, it stays private and scoped to that use. The crossing of the line is in the publishing, not the building.

Besides, those features would bloat Valknir into a scope-crept monolith. I want one clean, well-regarded tool, not a kitchen sink. Staying defender-advantaged and staying in scope turn out to be the same decision.

The Pattern of Transformation

I recently pushed the implementation of double free detection to the Valknir repository, followed by working on the write-up for the included docs. In particular, I was researching how double frees are exploited using some CTF writeups to gain an understanding of the process of finding and carrying out an exploit. The technical detail lives in the docs — what struck me here was something else.

Overall, the process of the exploit involved a lot of manipulating the heap so that exploitation of double free and the resulting write access to the free list allowed for transformation of data. This wasn’t something that could be done in one or even a few straightforward steps — there was instead a lot of passing back and forth, performing seemingly arbitrary steps which didn’t involve the memory in question, then more manipulation. The process from an innocuous string to shell access was the result of many small steps, and the solution a result of cumulative gains. Distilling the process down to its fundamental pattern, void of the specific technicalities, allows it to be seen more objectively within the greater scheme of things.

This is a pattern which I’ve noticed everywhere — probably the most fundamental one to the way I see the world. It’s the pattern of cycles and transformations (spirals, to be more specific) — of waiting for the opportune circumstances and taking action, followed by waiting again. The most relatable application is life itself: we aren’t constantly busy, we aren’t making giant leaps from one situation or identity to another, and life doesn’t always feel like it’s moving forward or even in the same direction consistently. It’s the same cycle of death and rebirth — something that fundamentally implies transformation — just in the figurative sense. Just like the memory dies (freed to the heap) and resurfaces in a different form, so too does each phase of our life. You’re not the same person in the same circumstances as you were 10, 20, 30 years ago, but that transformation was cumulative — many small steps, some destructive, some creative, all working together to result in the entire life and identity you have today.

Plants and animals die, are eaten and transformed into nutrients to sustain animal life, or decompose into nutrients to sustain plant life. At some point that cycle repeats. This is the fundamental cycle which allows and sustains life on the planet. On a larger scale, stars die, create nebulae, which later condense into other stars. These things aren’t binary processes — they are long sequences of steps involving alternating periods of great activity and rest. Importantly, these things don’t occur in a vacuum (well, stars literally do, but that’s beside the point I’m trying to make). Everything exists within the context of everything else. Just like the memory in the heap spends a waiting period where it’s not subject to any read or write operations, it exists within the larger context of the heap — in order to progress in a useful way, it must wait for the tcache to be filled to push it into the fastbin. Only then can the double free be triggered. Similarly, life, planets, stars — everything that exists does so within a larger context, acted on by outside forces, and able to transform only under certain conditions. Sometimes there is no choice but to wait for those circumstances.

Ultimately, this isn’t new knowledge or an extension of an already established theory, but rather another example of how pervasive this pattern is. What’s particularly interesting to me is that despite the temptation to view technology as separate from the real world — much less anything spiritual — it appears to be every bit as much a part of things as anything else. Furthermore, I find the act of noticing the same patterns across many things aids in a much deeper understanding and helps codify that understanding in a more condensed way. This isn’t a case of applying a 50-step process for a specific scenario, but of applying a known pattern. I don’t need to dig into a large memory bank — I apply already established muscle memory in a new context.

Kairos

A fairly recent realisation I’ve had is that, if I were to assign just one word to describe my interests, that word would be “systems”. A couple of years ago I took a class in systems and networking which ignited something in me more than anything in a long time — possibly ever. In hindsight that now seems so obvious. The class in question is where I was introduced to 100rabbits and uxntal, to low level programming, and to OS scheduling. Something slotted into place. Not a new interest arriving but an existing one finally finding its most precise expression. The excitement I felt was less discovery and more recognition. Other classes have interested me, but this one inspired me — I’d say it ignited a passion, but more accurately, it showed me my existing passion through the lens of an existing interest. A connection that seems quite glaringly obvious now, given I’ve always been inclined to view programming as analogous to other things.

When I encounter something that genuinely interests me I think about it deeply and analogically — how does it connect to other things, what does it illuminate outside its own domain, where else does this pattern appear? Learning about OS schedulers, the connection arose naturally —a main issue in my daily life is a scheduling problem. I’ve long made the analogy between the mind and operating systems — in many ways I think of my own mind in the same terms and try to apply learning from one to the other. OS schedulers solve a very important problem, and that problem is very similar to one I face in my own life. It stands to reason, then, that the solution may be similar too. Hence, why not take those same principles used in schedulers and, with some choice adaptations, apply them to the real world — a piece of software which serves as a practical implementation of the threshold between computer science and psychology, of the virtual world and the physical world, in a real and useful way.

Kairos is the Greek concept of the opportune moment — qualitative time, the right moment, as distinct from Chronos, chronological measured time. Most productivity tools are Chronos thinking. Fixed slots, tracked hours, measured output, the relentless pursuit of 100% utilisation. What I want to build is based on a different premise: optimal is not maximum. A system running at 70% capacity in harmony with its own rhythm will always outperform one pushed to 100% and fighting itself.

The core insight the tool is built around is energy rather than time. Time is fixed and equal for everyone. Energy is variable, personal, cyclical, and renewable — but only if the system is respected rather than exploited. The laws of thermodynamics apply to human systems as much as physical ones. Every system has a rate of energy loss. Sustainable productivity requires input, not just output. Rest is not the absence of productivity. It is part of the cycle. A rhythm which works for the individual should be honoured, not fought with for the sake of sticking to a cookie-cutter system.

This is not a new project yet. Focus is currently on Valknir. Kairos is sitting with me — being thought about, refined, allowed to develop before the first line of design begins. Some things require the right moment. The name was chosen deliberately.

Valknir

As a natural progression of my interests in systems and low level programming, I started exploring the topic of binary exploitation and software vulnerabilities in general. I’m not the sort to simply read and ingest or to practice with tutorials or tiny projects which have been done to death by every budding student and have no practical use or possibility of success. That’s not to say anything I make has to be successful — truly I don’t care much if no one uses it — but the possibility of it being something someone could feasibly reach for specifically is important to me. A better explanation of that might be that it has to be both useful and in some way innovative. I never once made a calculator (or other similar overdone project) while learning to program in the first place. If it’s something I’m going to make just to delete down the line because it adds no value, then I simply cannot muster up the motivation to do it. But I do learn best through doing. So I needed an idea which I was genuinely motivated to pursue which would also serve the purpose of teaching me what I wanted to learn. Hence, a vulnerability scanner which explains in its output what the vulnerability is and why it’s important. Not just “your code has a bug on line 32” but “your code has this specific vulnerability which can be used to do these specific things”. This seemed to tick all the boxes. As an added bonus, it will also teach me more about systems — not just what they do and how, but where the backdoors are, how to break things, how to fix things, loopholes that can be exploited. Even though it’s not the system itself being scanned, the program runs on the system, and it may well be the system that a nefarious type wants to gain access to via a vulnerable program. To make it good and genuinely useful, I’ll also need to ensure my understanding of the structure of programs is sound — bugs don’t always appear in the most obvious places or in the most textbook ways, so the tools to find them need to understand deeply, including edge cases.

I saw this as an opportunity to also kill two more birds with the same stone. Firstly, I’d been wanting to learn Rust for some time — I just hadn’t yet had the opportunity to build something in it that genuinely excited me. Secondly, I wanted to refresh and improve my skills in C (which I very much enjoy as a language) after a long break due to maternity leave. Hence, Valknir would be written in Rust to parse C code. I could go on about the benefits of using Rust, but honestly…it’s just a good excuse to learn a language that seems interesting to me.

Currently, Valknir has been started. I have, admittedly, been struggling to find time due to caring for a baby, although that should hopefully change in the next week or so. It’s not something I want to hack together or go through the motions simply to tick off development tasks — its purpose is for my own learning, so it should be given the time and attention it needs, lest I end up with something working but hollow, having failed in its most fundamental purpose. That said, the bare bones are there: it can find malloc calls and I have a branch which can detect double free which has yet to be finalised and merged to main. My hope is for the double free branch to be ready to merge by the end of next week.

The work isn’t purely development though — I plan to also include some write-ups on each vulnerability it covers. These won’t be all-inclusive encyclopaedic entries, but will focus on points of interest, real world examples, and practical importance. These will tie in with example code in the repository which I’m using as test cases during the development process. That’s not to say evidence of any kind of test driven development should be expected (or even any kind of testing suite at all) — mainly because I hate writing tests, but also because the focus is on learning about vulnerabilities, not implementing textbook perfect programming practices. Until such time as it becomes evident that testing is required, I’m going to go ahead and call it out of scope. Do I expect that to change? Yes, absolutely. But right now the software is very basic and writing tests has too much time and energy cost for almost no value — I’m not even trying to handle edge cases or anything beyond incredibly basic code: two calls to free(p) are detected as a possible double free. Deciding if they’re in scope of each other or even trying to free the same p are for later iterations. Right now the focus is on implementing detection of the very basic versions of common vulnerabilities. Valknir has to learn to walk before I can make it run (in the personified sense, that is…the program does run, I promise!).