Sunday, October 19, 2025

Hashing Isn't Encryption — Explained Simply

 

                    I'm sure this image will be a total viewer-magnet, because Math = Sexeh

 

Everyone has heard of encryption, and most understand the basic idea. 
You have a message, apply an algorithm, and you get an encrypted version of that message. 

To go back to the original message, you use the same key used to encrypt it.

In other words, encryption allows you to move back and forth between the clear message and its encrypted form.

This is a one-to-one relation. 

 

                    Left: original message; Right: encrypted message (high-quality visuals)

---

Now, Let's Talk About Hashing, Shall We? 

Seems to be the same idea, really:

Put something in, hash it, get something out. But it behaves very differently.

Imagine: to your left, a world of infinite possible cleartext messages. In the middle, the hashing function. And to your right, the resulting hashes.


Here's the key difference:

The group to the right is smaller than the one of original messages.

Different messages can end up producing the same hash — this is called a collision.

  

In other words: there is information loss during hashing:

Your final group of hashed messages is "poorer" in terms of information than your original group.

 

                                         Left: original message; Right: hashed message (gorgeous)

 

---

A Simple Example

 

Let's clarify this with a toy example.

Imagine a small world where every message is made of 4 different digits, each one between 0 and 3.

Here's our original group of possible "cleartext" messages:

(0123)    (1023)    (2013)    (3012)

(0132)    (1032)    (2031)    (3021)

(0213)    (1203)    (2103)    (3102)

(0231)    (1230)    (2130)    (3120)

(0312)    (1302)    (2301)    (3201)

(0321)    (1320)    (2301)    (3210)

 

 And here's our toy hashing rule:

    Take the first two positions (the leftmost digits).

    Look at the indices that those digits represent.

    Swap the values at those two positions.

Example: 

(0213)

 First two digits are '0' and '2'.

(0213)

Now we swap the values at positions '0' and '2':

(1203)   

This final number is our hashed value.

---

What Happens When We Apply This To All Messages? 

- Some original values won't be found in the hashed group (information loss).

- Many of the original values will map to the same hashed result (collisions).

- Ergo, we can't always reverse the process and retrieve the original.

---

Let's Showcase This With a Particular Number

 

Suppose you pick this final (already hashed) value:

(2301)

There is only one way to get to this value, so we can actually reverse our number and get to the original

(2310) -----> (2301) 

 (check the top image or, even better, try to make the table yourself)

But now take this hashed value:

(3201) 

 

This one can actually be produced by three different original messages:

(0231) ----> (3201) 

(3102) ----> (3201) 

(3210) ----> (3201) 

Which one was the original one? Can we be sure?

 ---

Key Takeaway

 

Hashing means: 
- Information Loss

- Many-to-one relationship

- Reversibility not guaranteed

 

Even in this toy example, some hashes have several origin messages, leading to the same output. In fact, the original group has 24 unique values and the end group has only 12 unique values.

And real life examples are much, much more complex than this, making even finding collisions computationally impractical. But the principle remains.

 

---

 

And remember:

To teach is to lie (a little). 

Hashing (and encryption) can be way more complicated and interesting than this. 

For more information, check this link and this link

Also, this simple algorithm can be found in the fine (and free) "Reverse Engineering for Beginners", by Dennis Yurichev. 

Check that out too.

Have Fun!

Saturday, October 11, 2025

How Computers Think: A Liars Walk Through x86 Assembly

 

                                                                                    So, yeah. The artist found out about digital art.
 
 

"To teach is to lie."

 

A few days ago I promised to talk a bit about a simple Assembly program.

And talk I shall!

First off, let's just get out of the way what that code is doing:

It takes any number of integer positive values as parameters and adds them.

So, if you run the program with ./<program_name> 40 2 (which we shall in a bit) it will print out the answer:



If this were a Python program, it would be simple as can be. But Python is withholding from you a lot of what is happening behind the scenes. And we want none of that. We want to see stuff as it is happening.

 

First of all, a couple of disclaimers:

- This code isn't as simple as it could be. It's in fact part of a series of lessons I'm going through in order to review and solidify my ASM and debugging knowledge.  They are part of a NASM tutorial (ASMtutor), which I highly advise — it's quite carefully made, without major errors (very common in basic ASM stuff) and which guides you in a smooth manner towards greater knowledge. You can find it here.
- The code I'm using can be found here.

- To create a compiled binary of this code, in particular of the files functions_v1.asm and 13_atoi.asm, you need to download those two files and run: 

nasm -f elf 13_atoi.asm

ld -m elf_i386 13_atoi.o -o atoi

 This will create an executable named atoi, which you can then run as explained before.

- You should know a bit about binary and hexadecimal. Please take a quick gander at this. Knowing how to quickly translate between them will really help to inspect the debugger.
- And speaking of which, I'll be using GDB with GEF and I advise you to do so as well. If you already rock on GDB you probably don't need it. But then again, I highly doubt you'll be reading this if you already have mastered GDB. 

As for GEF, it will make the debugger much more pleasing to the eyes, and your code easier to follow.
- To finally start the debugger (GEF) with our program, we'll run:



- My focus isn't a 'deep dive' into Assembly. I want to give you the very basic and essential tools to be able to go through this code, instruction by instruction and be able to understand how the computer is 'thinking'. In order to do that, I'll give you some basic tools.

- Speaking of which, here is a pdf with the few commands and concepts you'll refer to in order to be able to follow through the code.
- Functions in Assembly are interesting. You can call a function or jump into a function (check the PDF), but also can just enter a function because you're going through the code flow and there is no call, no jump (or there isn't a reason to jump). So, for example, in this case:

You have here a comparison and you are checking if eax is equal to 0, when you do cmp eax, 0. Then you have a jump not zero. If eax is NOT zero, then you jump elsewhere to the function divideLoop. But if eax IS in fact zero, then you just move along that grocery shopping list to the next item on the list. Therefore, you enter the function printLoop and decrease ecx by 1. 

 

On the other hand if printLoop actually had a dot behind it (.printLoop) it would be a function inside of whatever function we are in, and couldn't be called from outside of that function. If in doubt, dig a little deeper into Assembly. 

- You might also need a quick contextual/theoretical rundown, so here it goes:

Assembly is the lowest level code you can have that is still human-readable. Below it is the world of ones and zeroes, and above it the world of different programming languages and their way to read and write programs.

In assembly, we're working in the so-called User Space, but sometimes we need to give control to the Operating System through system calls. To do that, we use interrupts (Here is a link to a nice Linux system call table. If you use Windows we can't be friends, sorry). You won't really need that table in order to follow along and read the code, but it sure helps. We only use two syscalls in this code — one for writing to the terminal, and another to exit cleanly. (look up: do the registers change after the OS is done with them?).

When the syscall happens, the OS takes control of the program, and uses the stack and your registers. After that, you cannot be 100% sure of how all registers will be, so if it matters to you, like what happens in other situations, make sure you save those registers into the stack and get them back after the OS lets go of control and returns it to User space.

 

There are a couple of bitwise operations - basically XOR ('exclusive or'). To learn more about bitwise operators, check this page. And if you like computer games and are even slightly interested in electronics or logic, then take a gander at this game. It's great fun.

But, again, you don't have to jump into bitwise operations to read this code. Just do a mental substitution. Whenever you see, for example xor eax, eax it's basically the same as taking zero and placing it in the register eax or, in other words, doing mov eax, 0

Learn boolean logic, bitwise operations, and everything else because you want to and because its fun. :) 

 

What is a register? What is the Stack? You probably heard about those two terms before. Here's a very quick and dirty analogy:

Imagine you're sitting at a desk and I'm telling you to remember things, like a name, then a number, then an address, then another name. Sure, you can do it for a while. Your short term memory will be able to memorize some stuff before it's too much stuff to handle. Those places where you're storing this easy-to-remember, short-term memory stuff are the registers. Registers are temporary containers for fast memory access. Incredibly fast memory access. A bit like short-term variables on steroids.

But what if I start giving you too much stuff to remember? Then you are allowed to write whatever information I tell you on small pieces of paper, as long as you store them properly on a receipt-spike (also known as an order-spike). You've seen them before in restaurants. Something like this:
 

                          Analogies R Us: each strip of paper a memory location, an integer, etc
 
This stack, or this order-spike, uses a LIFO system, meaning Last In First Out. Whatever last piece of paper with information you have pushed into that pile, is the last one you can pop out and examine.

Neat, no? So, you can store x amount of things for very quick access inside those registers, and you can push whatever you can't hold in quick access memory into that pile, and then you can take it our and again place it in a register if you so wish (actually, you push copies of those values — the originals remain in registers unless you clear them).

All that I've said so far is to be taken with a pinch of salt. Not only because these last bits are an analogy, but because, like I said in the beginning, to teach is to lie. I have to omit stuff in order to let you do some progress.

Imagine us teaching Math in class and instead of telling kids all over the world that you can't divide by zero, we instead said, there are several instances in mathematics where dividing by zero is perfectly sensible, and then described said instances. Can you imagine the faces of those 7th graders? Nothing would be retained.

By lying to them, they learn a useful rule and important aspect of mathematics, and when they advance in their mathematical careers, they get to learn at least of one way to actually divide by zero.

It's the same here. I'm skimming a lot of fat in order to draw an understandable picture, and to let you (if you so wish) take your first steps in Assembly, which I truly love.

Got any questions? Stuff you don't understand? Stuff you'd like to see? Shoot them in my general direction. Search on Google, use an LLM ("gulp, LLMs?! But aren't they the devil?". Look, I was here when Wikipedia first came up and academia was having a fit over it. I also survived using AltaVista back in the 90s. You'll do fine with LLMs and live to tell the tale).

As I've shown in one of the pictures above, we'll be using our program to add 40 to 2 and to find out the Answer to the Ultimate Question of Life, the Universe, and Everything.

I won't go through the whole code. It would serve little purpose and, besides, you need to walk the walk.


                                                     Ah, Jayne... you look young.
 

 So, back to business!

 

You need to:

- download the two .asm files;

- compile them into a binary file;

- install GEF (GDB probably already installed);

- download the PDF with some information;

- Run GDB and check what happens to the Registers and to the Stack as you step into the program.

 

And that's it! We got a party going.

 

Ok, let's pretend anyone is actually reading this and actually is inspecting the program with the debugger. You run the code in terminal to open the debugger, then when it opens, you write start and press enter.
What might you be seeing at this point in time? Something like this:


                                        Already regretting your very recent life decisions? Don't! This stuff is fascinating.


What a bloody mess! I know. Don't worry. The first time I started looking at this I was pretty confused as well. But let's first try to get some 'unhelpful' information out of the way (ahem, teaching is lying) and focus on the meat of this and at what we'll be focusing on exclusively. So, clean-up time!

 

                                       Some breathing room, at last!
 

Aha, so what do we have here?

We have 3 different screens:

- Registers

- Stack

- Code

 

Lucky us! Because that's exactly what we want to follow as we're moving along our code. Let's start with the registers. If you've looked at your PDF, you'll see the stars of our show there. 

Register Screen:

esp is the stack pointer and it is always pointing to the top of the stack. It's not holding the value at the top of the stack, which is the value 3, but it's holding the value 0xffffd0f0.

Huh.. interesting. If you look at the menu below, you'll see that that's the exact stack address of its topmost value. Memory position 0xffffd0f0 is the topmost piece of paper (like its identifier) and in it we have written the value 3 (more on why 3 later). Everything else we care about is (apparently) 0 for now, so let's move on.

Stack Screen:

Remember that pile of papers stuck at the order-spike? This is it.
 Here's an example of one of these lines:

0xffffd0f8│+0x0008: 0xffffd2e0  →  0x32003034 ("40"?)

Let me explain it to you. 

0xffffd0f8  memory location in the Stack. This is represented in 4 byte intervals, since we're in a 32 bit program

+0x0008  Memory offset from the top of the stack. This means that we're in a position 8 bytes above the position of the top of the stack (0xffffd0f0). As we add stuff to the stack the memory values go down. This is an important quirk that you would do good in remembering. Also note that while this has the stack facing upwards, sometimes you'll see it upside down (which makes the memory values 'growing' down appear to be a bit less strange).

0xffffd2e0  What is stored in that stack position (our piece of paper). In this case it is a pointer to a string. That string is '40'. So, in other words, there is a location in memory that is storing the string '40' and in this stack there is a pointer pointing to the first character of that string, or '4'. Read on, it will be made somewhat clearer. You don't need to know in depth what are pointers, etc to be able to read this program, but knowing these things de-mystifies them plenty and makes your life easier and more enjoyable. 

(bonus cookie points for you if you can explain why, as you go up in the stack you go from  0xffffd2e3 to 0xffffd2e0, a small 3 byte jump, and then follow with a big big jump from 0xffffd2e0 to 0xffffd2a5 a 59 byte-sized jump)

0x32003034  Well, I see a 40 and the beginning of a 2. :) Let me explain:

As 40 and 2 were used as arguments in our program, they were added to the stack. But they weren't added as integers. In fact, they were added as ASCII characters. 


 We're working with hexadecimal characters and we're looking for the value 40, right?

So, 0x32003034. You'd possibly expect to read from left to right, but we're working in little-endian here, so in fact our number will be presented right to left. And if we look at that table, we would be reading (now reversed) 4, 0, NUL, 2. That NUL is the null byte which marks the end of that string. And I'd bet you my breeches that after that 32 there will be immediately to its left another 00, since these strings are being stored next to each other. If you're paying attention, you'll know I was cheating and you won't want to bet against me as you read the next value in the stack 0x48530032 and see, in black and white that '00 32', which translates to the number 2 (in decimal). You might be tempted to continue translating that line and read 'SH..'. Interesting, no? But I'll leave it to you to find out more.

But you might be asking, why is the Stack in this initial state? And that is a perfectly valid question. As we load up our program and enter its two parameters 40 and 2, we have automatically added 4 things to the stack, in this order (from bottom to top):

- a pointer to the last parameter (our third argument, or argv[2]);

- a pointer to the first parameter (our second argument, or argv[1]);

- a pointer to the program itself, its full path, in fact (our first argument, or argv[0]);

- the number of arguments  (argc).

As you start reading the program and debugging it, you'll see the stack add more items and remove them. 

Code Screen:

This is where you follow your code. the green arrow and green letters shows you where you're at in the code, and the red dot tells you that there's a breakpoint here. If you're into learning more about GDB (please do) then you can try this link as well. This line of code is the next to be executed. And as you press si ('step into') you'll move along the code, Assembly line by Assembly line.

 

So, let's do just that and run si some 4 times, until we get to that compare (cmp). How will that code look like? That Stack, will it change? And the Registers?  Let's see:


 So, what happened? We started by popping the value on the top of the stack and placing it in ecx. So, in that next step you'll see that ecx = 3. Then we do the same and place that value, a pointer to the function name, in edx, removing it from the top of the stack. Finally, we decrease ecx, basically subtracting 1 to the value within and we xor edx, edx which is basically the same as doing mov edx, 0. ecx is holding the number of variables used in our Math and edx is emptied for future use. See how that works? If we push, we take the value in the register and copy it into the stack, and if we pop we remove the value from the stack and place it in the designated variable.

I'm stopping here with the guided code lesson. I'm leaving it up to you. You do have all the tools that you need to do so.

Remember tidbits like: if you call a function, you jump to that function, instead of continuing down the lines of code, but you place the line of code right after the call on top of the stack (the return address is pushed onto the stack). When you hit the instruction ret, on the other hand, you pop whatever value is on top of the stack and go directly to the line that is referenced in that value (a pointer to a memory value). 

GEF will be kind enough to tell you when a jump instruction isn't fulfilled (which means that instead of jumping into a function you continue reading the code below that jump).

Pay also attention to the name of the function and on how far you are away from its start. For example, in this case, I'm inside the inner function .multiplyLoop, inside the function atoi:

 


 

Here's some more stuff you might want to get into:
 

- what is the register eip doing?

-  What's with bl, BYTE PTR [esi+ecx*1]? Can you guess what's happening there?

- Pay attention to where you're going and what's happening when you run a ret instruction.

- Perhaps write down all the commands in a piece of paper and try to 'guess' how the flow should work until the very last operation. Then compare to what you see on GDB. Does it match?

And that's it! You're now ready to start discovering more and more about this wonderful world, or you're thinking that anyone that likes this must be a bit crazy.



Either way, you cannot unsee it now. 
Have fun and hack away!

Saturday, July 26, 2025

"INTs Aren't Integers and FLOATs aren't Real"

 

                            I was told this is a cat-submarine. Tail = Periscope. I believe it.
 


Over the past few weeks, I’ve been juggling two realities. On one side work: networks, and the daily exploration of security tasks in a banking environment. On the other, low-level code: free time, NASM, one instruction at a time.

 

I’ve been reviewing the basics—x86 syntax, memory layout, data definitions, etc etc etc. I'm following a series of videos, from a YouTuber that I appreciate, and although I found some information lacking or imprecise—particularly in the episode devoted to divisionit's still a good set of videos and I'd advise anyone to watch them here. Lately I've taken a gander at integers. How they’re stored, manipulated, compared, and what all those flags mean when you're moving bits around and trying to make sense of a program in GDB.

If you’ve spent more than a few hours in GDB (as I have, unreasonably so at times), you’ve probably done a CMP eax, ebx and then wondered what exactly happened to the flags. What’s the deal with CF, ZF, SF, and the rest of the alphabet soup? Why do certain jump instructions follow CMP, and not others?

So, quick refresher for the two family members that like me and still read this blog:

CMP just subtracts the second operand from the first, sets flags, and discards the result. The flags tell you the outcome, and then you pick the jump based on what you want to test.

InstructionMeaningFlags checked
JE / JZEqual (zero result)ZF = 1
JNE / JNZNot equalZF = 0
JL / JNGELess than (signed)SF != OF
JLE / JNGLess or equal (signed)ZF = 1 or SF != OF
JB / JCBelow (unsigned)CF = 1
JAAbove (unsigned)CF = 0 && ZF = 0

Notice those signed vs unsigned differences? JB isn’t "jump if smaller", it’s "jump if below"—unsigned. If you’re comparing signed ints, you should be using JL, JG, and so on.

Floats make it even more nuanced. UCOMISS xmm0, xmm1 for example, is how you compare scalar floats. That instruction sets flags similar to CMP, but works with IEEE 754 single-precision values, not integers. And yes, it’s aware of signs, NaNs, Infs, and the rest (of floating point hell).

Anyway, all that to say: this is kinda subtle, or at least it requires some study and care. IMO, totally worth learning. Most would disagree profoundly. I’ve been pushing myself to remember it, slowly but deliberately. You can check out some of the tiny experiments here. It’s not a project, more like a scratchpad that runs on opcodes and (lovely, quality) coffee.

 

                                                                 ...When in doubt, explain it to me. I'm anathema too.


 And you know what's fun? Mathematics! No, really.
I've seen it again and again: people treating floats as if they are basically the real numbers
(ℝ). They aren't!

Just take a look under the hood and you'll understand why.

"But OPQAM, I use Python/Java/Whatever. I don’t care about Assembly or floating-point registers!"

And that’s fair—until you try 0.1 + 0.2 and get 0.30000000000000004, or even 0.30000001192092895508. Then it might matter.

Can you think of a situation where such a small discrepancy could be a problem? I sure can. I can think of several, and some of them imply falling bridges.

Here’s the core issue: ints aren’t integers, and floats aren’t rationals.

 

An example

The decimal 0.1 becomes the binary: 0.0001100110011001100110011001100110011..., repeating infinitely.
But computers can’t store infinity. They cut off after some number of bits: ~23 bits for floats, ~52 bits for doubles. It’s like trying to store 1/3 in decimal — you can’t write 0.3333... forever. You round. You approximate. So do computers.

So, this means that you cannot represent 0.1 precisely.

 

The problem isn’t the mathematics — it’s representation.

How do systems and programmers deal with this?

  • Use decimal representations (decimal.Decimal) when exactness matters (e.g. money).

  • Use rational types that store fractions exactly (Fraction(1, 10)).

  • Use symbolic math when precision must be preserved throughout (SymPy, CAS software).

  • Or use fixed-point arithmetic — store cents instead of euros.

These are workarounds. The real solution? Know that floats are approximations, not truth.



                                                Accurate depiction of my two family members' faces as they read through this.


Meanwhile, back in the Real World, there was a CTF going on in the team. A colleague of mine created it and invited us all to some friendly competition for the next few weeks. Honestly? I will probably only do a couple CTFs before I turn my attention elsewhere. Having a family + hobbies + a day job does limit one's time. Still, I got to dip, and I managed to solve a challenge involving a simple—but satisfying—privilege escalation. I'm not going to give a lot of details, since it goes against the point of the 'contest' and it's actually requested by the site that we don't do it. But here's the 'trick': Classic PATH hijack. I dropped a fake ls binary into a writable dir (/tmp), positioned it early in $PATH, and executed the vulnerable binary which, instead of ls executed cat. Bang. Root shell. Dump flag. Walk away smiling.
This was, of course, allowed by purposefully using SETUID in the binary. A no-no. But there you have it. It was fun.

And yeah, sure—it’s not the most sophisticated vector ever, but the fact is, simple stuff works. You don't have to be fancy-schmancy to make something give you a 'win'. It is thus in Jiu Jitsu, and in illusionism. And so it is in hacking.

Also worth noting: a few days ago I got into a discussion with a colleague about TPM (Trusted Platform Module). I’ve blogged about the TPM issue here, but here’s the gist of it: I had to disable TPM on an old laptop (a very respectable ThinkPad x260) just to make the thing actually power off correctly. I tried everything—kernel parameters, ACPI tweaks, prayer (not really, but I totally could have!)—but nothing worked. Full shutdown always left the machine 'hot'.

Disabling TPM did the trick.

Why? Long story short: the TPM implementation on that hardware was tailored for Windows, and Linux support is... charitable at best. As for the discussion. That colleague warned me that in certain scenarios, like full power drains, I could end up with an unbootable machine if I lost TPM state. So I did what any sane people would do: I tested it again and again, simulating different scenarios.

 

Unplugged, drained, every scenario short of desoldering the CMOS battery. Result? Nothing broke. LUKS doesn’t need TPM. At least, not for the way I have this set up. In Linux, with this setting, TPM is optional unless you're deliberately tying encryption keys to it—and even then, tread carefully.

These tests were fun. They reminded me of the joy of breaking things on purpose, and the calm that comes with understanding exactly why something behaves the way it does.

So yeah, life’s been busy. I haven’t had much time to write (sentence never written by any amateur blogger ever). But I’m still here, still learning, and still hacking away.

Next up? I'll probably hit you up with some more ASM stuff as I keep on watching those videos and experimenting with stuff. Maybe some CTFs... who knows? Not me! And I'm right here. 

We’ll see.


Sunday, June 22, 2025

Playing With Bits: Of Malware Labs, Steganography and Narnia

 

 

                                    Where's mah Gibson, punk?

 

 Hi y'all!

As mentioned here, I played around with some steganography. The idea was simple and unfancy: just a .ppm file and some Python.
Why .ppm? Because it’s stupidly simple: uncompressed RGB values, no PNG compression, or arcane metadata. Just bytes (although .ppm headers can have comments, so keep that in mind).

 

The Method
                A .ppm header

 

Looking at the image above, the header is simply to get:

(0x50 0x36)  → P6: binary PPM file

(0x0a) → newline

(0x36 0x34 0x30) → 640: image width

(0x20) → space

(0x34 0x32 0x36) → 426: image height

And bam—our header info.

And then we get 3 bytes at a time, for each pixel (RGB: Red, Green, Blue), defining the color of that precise byte.

After that, pixel data: 3 bytes per pixel (RGB). Nothing else.

To hide data, I zeroed the least significant bit (LSB) of each byte, which doesn’t really alter the image in a visible way. This gives you 3 bits per pixel to encode data. Stack those bits together and slice them in blocks of 8—now you have bytes. Bytes mean ASCII. ASCII means text.

That’s the gist. You can write an entire hidden message (or image, or audio) inside another image by tampering with just the LSBs.

To make this practical, I wrote a couple of Python scripts. You can find them here. Mess with that as you see fit.

 

 

Limitations?

Sure. Quite a few:

  • .ppm headers with comments will throw things off. But you can easily code around that. Heck, have that as a homework if you'd like.
  • No encryption. Anyone with a hex editor and some free time can sniff it out. 
  • No error detection or correction. More homework.
  • Every LSB is predictably overwritten—pattern detection is trivial.
  • Any kind of compression or encryption on the container image kills the message.

 

OpSec Level: Meh

You’d probably want to:

  • Only use one LSB per pixel (maybe just Blue).

  • Randomize altered pixels with a PRNG.

  • Encrypt the payload beforehand.

     ...

But let’s be real—Chi-squared tests, histograms, stego‑detectors—if someone’s looking for it, they’ll find it.

Still, security through obscurity has its place (despite all the hate). Think authoritarian regimes where strong crypto might be illegal or where you might simply be forced to decrypt everything you own.
Just saying... it can be part of your 'onion'.


Malware Analysis Lab - The Barebones Setup

I’ve said before I’d blog about my lab setup. Never got around to it because I kept tweaking things or getting distracted by something shinier.

 Goal here: no fancy stuff, just a focus on working securely with malware.

My current setup:
- a dedicated Mini-PC.
- a managed switch which has a port isolated for the Mini-PC
- a VLAN dedicated to that host
- a Raspery Pi as Gateway/firewall/logger for that VLAN

 

A true minimal setup:

- Your laptop

- Two VMs:

  • Flare VM (Windows + analysis tools)

  • REMnux (Linux + reverse engineering toolkit)


Essentials:

  • Keep malware VM networks disabled unless strictly needed

  • Take frequent snapshots

  • Log and document everything

  • Don’t aim for perfect—just safe and functional


That's it. I won't go further into this for now. If I find the need, I'll do it later. What you want is something that will let you experiment with some safe malware samples, learn the basic tools and avoid having your home invaded by your baddies.


🧙‍♂️ Back to Narnia (OverTheWire)

Yup, I’m back at it. I paused these CTFs months ago—wasn’t getting much out of them. Realized I needed to read more about shellcode and memory handling before diving deeper.

So now I’m back. And yeah, I’m also changing my mind about avoiding walkthroughs. Everyone does them, so I might as well add my spin: fewer spoilers, more insights. You’ll find the step-by-step mess here on GitHub (don’t expect polish).

narnia0

What’s this one about? Buffer overflows and UTF-8 stuffsesses. Simple code:

                There's the Gibson <3

 

Buffer overflow by 4 bytes. The goal is to insert \xde\xad\xbe\xef in little-endian form.

First instinct was to strings, gdb, and objdump my way through. But you don’t need that. Just feed the program 20 garbage bytes (aaaaaaaa...) followed by those 4 crafted bytes.

Small snag: input is treated as UTF-8. So outputting raw 0xde didn’t work nicely.

Long story short, I learned that I couldn't easily output xde, and I did try, by looking at UTF-8/HEX lists, but it was a no go. I then decided to build a binary with printf:

printf "aaaaaaaaaaaaaaaaaaaa\xef\xbe\xad\xde" > /tmp/key
 

This solved the problem, but didn't elevate me to narnia1. A picture can explain more than a thousand words, and so a picture filled to the brim with words might be a wonder. So, here:

 

See at the top? the redirection is not returning an error message, but I am still user narnia0, and thus haven't really solved the issue. But why?
 

The issue is Linux related, to be fair. As we run the script with our payload, we're spawning a shell, but it is dying instantly since it's not connected to a tty. I needed to keep the shell opened, so I got the cat out of the bag. It forces the shell session to remain open and thus granting us permanence.
The shell is ugly, but we get elevated privileges (you can always load a nicer shell, if you really want to), and now we can search for the password. That's easy enough, so we can leave it out of this explanation.

 

That’s it for now. These blogposts are mostly memory aids and curiosity igniters. Narnia stuff goes to GitHub as I solve them, and I’ll post here only when there’s something worth rambling about.

 

Get your tools ready and keep exploring!

Tuesday, May 27, 2025

Why Won’t You Power Off?

                       

        "Beneath this mask there is more than flesh. Beneath this mask there is an idea, Mr. Creedy."

 

 I recently purchased an old-school Lenovo ThinkPad x260 - a beautiful little tank of a machine. It was in tip top shape: clean chassis, no keyboard shine, and great battery health. So, first things first: wipe Windows off the face of the earth and install Linux (not Arch, btw).

Everything was great ... except for this teeny tiny issue:

Even after shutdown, the laptop stayed warm. 

This typically means the system isn’t reaching a full ACPI S5 (soft off) or G3 (mechanical off) state.

 ...I didn't expect it to have any temperature issues, but I decided to play it safe, and installed lm-sensors, just to be sure. No worries there! The temperature was low, even after running some tests and forcing the CPU to do some work. So, the issue wasn't that it was overheating... perhaps it just wasn't shutting down properly?
After some testing, I confirmed that hypothesis: USB ports were still feeding power. The fan wasn't spinning, and there was no noise or visible lights, but something was still alive in there and refusing to go to sleep.

 

I tried to issue clean shutdown commands, with shutdown -P now, oldschool init 0, the desktop shutdown button, etc. You name it. Nothing forced the computer to turn off and the battery was still sipping away slowly. Plugging  a phone to the USB would charge the phone. You get the picture.


 

Step 1: Blame the BIOS

I quickly checked the BIOS setup (on this ThinkPad F1 does the trick) and went through the tabs:

- Disabled "Wake on LAN"

- Checked "Intel SpeedStep" and thermal profiles

- Ensured USB always-on charging was off

Note: this did not stop phone charging through USB.

- Even poked at virtualization, secure boot, and power-on behaviors

 

No luck. I all looked fine an dandy. And yet, the x260 continued to undead itself.

There was a slight mystery in that the power button worked 'strangely'. And by strangely I mean that the computer didn't connect directly when pressing the power button. But the reason was quite obvious. Since the computer was not properly shut down, I couldn't turn it on by just pressing the power button. I had to first keep the power button pressed for long enough to force a hard shut down. Only then did the computer shut down and allowed me to turn it on by pressing the power button.


 

Step 2: Linux Kernel Parameters, ACPI... huh, no?

So, I guessed I might as well go up a couple of layers and check the OS itself. Maybe the Kernel wasn't handling ACPI (Advanced Configuration and Power Interface) properly.

This led me to go and modify GRUB entries:

# In /etc/default/grub, change the following line:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash acpi=force reboot=pci"

# Then update GRUB (Debian/Ubuntu):
sudo update-grub

...And reboot=hard, reboot=efi

( `acpi=force` forces the kernel to enable ACPI even if it thinks the BIOS doesn’t support it, and `reboot=pci`, `reboot=efi`, ... tell the kernel which reboot/shutdown method to try — in some buggy firmware cases, different paths behave differently

Also, please please and pretty please make sure to back up your current GRUB configuration before modifying it. Bad kernel parameters can sometimes prevent boot. You have been warned)

I also tested power off sequences with systemctl poweroff, shutdown -h now, and halt.

Same result every time: "Bye bye, mate. I'm going into zombie mode." 

I found the emergency reset button in the back of the x260, which cleared the battery state - cute. But not the fix either.

 

 



Step 3: It's elementary TPM,  my dear Watson Reader. 

Eventually, buried in a forum post, someone suggested disabling the security chip - also known as TPM (Trusted Platform Module) - in the BIOS.

Interesting. Why would a security chip interfere with system shutdown?

But hey! Let's try it.  And try I did. BIOS > Security > Security Chip, and I set it to disabled.

There was the option to switch between intel PTT (Intel's firmware-based implementation of TPM) and discrete TPM.

Boom! Disabling both solved the problem.

No... really. No more zombies in my machine.

 

 So, erm. What was all that about?


 

Step 4: Read up

TPM and ACPI can be tightly coupled, especially on systems originally designed to work with Windows. 

Firmware behavior is often not standardized across vendors, and ACPI implementations are notoriously inconsistent. 

When Windows is installed, in this case, it plays nicely with proprietary firmware handling of secure state transitions (shutdowns/hibernation). But Linux? That OS expects firmware to follow spec, not to have it silently ignore part of the validation process. 

By disabling the TPM, we cut off whatever interaction was confusing the firmware - and Linux finally does get a say in controlling the power sequence.

 

 Anecdote du jour: did you know that Torvalds once said something nice about ACPI?

 "ACPI is a complete design disaster in every way. But we're kind of stuck with it. If any Intel people are listening to this and you had anything to do with ACPI, shoot yourself now, before you reproduce."

 

It must have been a Monday.

 

Should you, dear reader, disable TPM on your old machine?

Well, if you don't use full-disk encryption tied to TPM, yes I guess.

If you're using Linux and notice a behavior such as this, then certainly try it.

But if you do rely on TPM for encryption (why do you need to do that nowadays is beyond me) then be careful. TPM often stores or seals encryption keys. Disabling it can make your system unable to decrypt the disk unless you’ve backed up your recovery keys elsewhere.

 If you’re still facing power-off issues after all this, check if your BIOS firmware is outdated (I admit that this was my F plan, after all else didn't work... but I can be lazy like that). Lenovo occasionally issues updates that patch quirky ACPI or EC bugs, especially for Linux users.


 

Alright, that's it for today! I finally am getting my life-work-zen states figured out and aligned, so I'm getting a bit more time to do research and to document my work, which is totally awesome. So I hope to start posting a bit more frequently.

 

As always, enjoy the ride!

Hashing Isn't Encryption — Explained Simply

                           I'm sure this image will be a total viewer-magnet, because Math = Sexeh   Everyone has heard of encryption , ...