Hello, everyone.
Welcome to the concluding lecture of our Hello World Shellcode Series.
Throughout this series, we have embarked on a journey to explore the world of shell coding.
Taking our first steps toward understanding the intricacies of low level programming.
Today, we are going to delve into the art of debugging and fixing.
One of the most common uses that Shell coders encounter the segmentation fault.
I hate this word.
So in our previous sessions we have learned how to craft shell code to print Hello world to the console
using various techniques and assembler language concepts.
However, as we delve into the realm of shell coding, we have discovered that things don't go always
as smoothly as planned, and it's not uncommon to encounter errors, especially when working with delicate
balance between the low level memory management and system calls.
And today we will equip ourselves with the skills needed to identify and rectify one of the most vexing
errors in shell coding the segmentation fault.
So now let's actually compile our program again.
And as you can see, we have Main.c and hello dot ASM.
So we actually are we have actually already got this here.
But firstly, I have copied this Objdump command here because it's actually takes pretty time from our
lectures to write it down.
And I have also shared this comment with you in our attachment sections here.
Now what we're going to do here is we will firstly compile this, assemble this here Elf 64 and Hello
dot ASM and we will create an object file here.
Hello Dot no object here.
And we had the warning here label alone in line with other column might be an error here.
Let's see what it.
Yeah we need to adjust the colon here and compile it again.
This is this is not an error here.
This was just a warning and that's it.
We our code is compiled, assembled without errors here.
And as you can see, we have the file here, object file here.
And now what we're going to do is we will use the LD here linking for linking here.
Hello.
Dot org here and output file is going to be.
Hello.
World.
And as you can see, we have this executable file now.
So when we run this and as you can see, we are printing the Hello world now.
And we will use that command here, this long command here, which we will produce this shellcode from
the output here of our executable program.
Now let's actually press enter and no such file because we changed our executable file here to Hello
world and this is our shellcode.
And you might see that this bad characters here.
But don't worry, these are not actually bad characters now.
And you will see why.
Now, now what we're going to do is we will again.
Let's actually objdump d m Intel.
Hello world.
Right?
And as you can see here, we have not bad characters here at all.
But even though we have that bad characters in our shellcode but they are not actually bad characters
and you will learn that why in next lectures.
Now here, as you can see, there are.
Okay, so this was the same code in previous lecture, so we don't have any errors here, right?
So that's okay.
But what we had here is the error with the main key here, which we got the segmentation fault from
it.
So since we have compiled it, let's actually sorry for this here.
So let's now copy this here.
And paste to our here.
That's it.
This was the same shell code here from our previous lecture.
Now, what we're going to do is we will compile this C program here and see what happens.
C program and see what happens here.
And what we're going to do here is.
We will firstly run our GCC and there's a new flag here we will use.
So this flag is used to specify the optimization level for the compilation process and in this case,
the all zero uppercase all zero option indicates no optimization.
So optimization is the process of transforming the code to improve its performance or reduce its size.
So by using this, you are requesting no optimizations to be applied which can be useful for debugging
purposes as it keeps the code closer to its original form, making making it easier to analyze and we
will use F no stack protector.
So this flag instructs the compiler to disable the stack protector feature.
So stack protection protection.
Also stack protection, also known as Stack Smashing Protection or Stack Canaries is a security mechanism
designed to detect and prevent buffer overflow attacks by monitoring changes to the stack.
Disabling stack protection might be done initially and intentionally for certain reasons, but it can
be potentially make the code more vulnerable to attacks.
And here we will also use the Z exec stack.
This flag modifies the executables stack permissions to allow execution.
And in some systems, the stack memory is marked as non-executable by default to prevent certain types
of attacks.
And using the Z exec stack really relaxes this restriction and allows the stack to be executed and which
can be useful in some specific scenarios but might also be introduced security risk if not handled carefully.
And this is why what we want from this code here and what we have here and main last see, this is the
name of the source file that we will want to compile and it should be a valid C source with the file,
the C extension here.
And as you can see, our program is compiled here.
So in summary, the provided compilation command is used to compile the C source code here with Shellcode.
And the command specifies no optimization, disables stack protection and modify stack permissions to
a low execution here.
So that's it.
And now what we're going to do is we will run this here, which is it should be a dot out.
Yeah, a dot out.
And as you can see, we got the segmentation fault.
Same fault here.
And there's an easy fix for that.
But before that.
I want to introduce you to some useful tool for debugging here, which is.
Valgrind.
And as you've seen, debugging can be quite challenging, especially when dealing with issues like segmentation,
fault and memory errors.
And fortunately we have the valgrind here is to streamline the process and provide us with detailed
insights into program's behavior.
So it's actually it has very, very easy installation process.
Just write sudo apt get install valgrind and that's it.
Here, enter password and that's it.
The valgrind is installed as easy as that.
And what we're going to do here is now and before we delve into the realm of valgrind, let's ensure
we have um, installed in our system.
In order to do that, we will use valgrind v that's it.
And as you can see here, we have valgrind installed here and what we're going to do is we will just
use valgrind.
Leak check equals full.
And now we will enter our output.
That's it.
And as you can see here, valgrind.
Actually gave us pretty useful features here.
Information.
So you might be wondering why we should opt valgrind instead of other debugging methods.
So.
Well, here's the deal here.
So valgrind.
Valgrind is a remarkably faster and more user friendly when it comes to spotting memory errors, particularly
when we are working with non-optimized builds that include debugging symbols valgrind becomes a powerful
ally and it not only pinpoints where segmentation fault occurred but also sheds light on why it happened.
And this level of insight is invaluable in diagnosing and fixing user uses promptly and in addition
to tracking down memory uses, Valgrind helps us to understand how our program interacts with memory
and system calls, providing us with deeper understanding of its behavior.
And this knowledge not only makes the debugging smoother, but also makes us better programmers by exposing
us to the nitty gritty of memory management and execution flow.
So now what we're going to do is we are seeing the errors here, the note errors, the information here,
and at first we have the valgrind version copyright and the fact that it's using the lib vex and essentially
it's this is just an introduction here and we have this line of command shows that was run in this case
A.out, which is our Shellcode program.
And also what we have here is the my Shellcode length.
This is the output or the print section.
So this this part appears to be custom output that we did here and it indicates the length of our shellcode
as we wrote it.
It should do that.
So the from there it's all okay here.
Our program executes right and what we have need to do it.
And as you can see here, this this is the 55th of one bytes, right?
So the length is actually meaning bytes in our code, right?
So.
The critical part here is this.
Which indicates the segmentation fault error.
Right.
So we have the processor with default action of signal 11.
So.
This tells us that the program terminated due to a segmentation fault.
Sigsegv Right.
Which is a memory access violation.
Right.
So and we also have bad permissions for mapped region at address zero.
So at address ten 040.
That this suggests that there was an attempt to access memory at this address, but the permissions
for that memory region were not valid.
And we also have this question marks here.
And this in-home typhoon, source Shellcode out indicates that the problem occurred at this address
but is unable to provide specific information about what code or operation led to the user here.
And we also this below main information here we have this and 58.
So this shows the call stack at the time of the error.
So it appears that the user occurred below main function in some system library code.
And we also have the heap summary here.
This section provides information about heap memory usage and is in use at exit here.
The 1024 bytes in one blocks.
Let me actually check.
Is my voice recording here?
Yeah.
Perfect.
So 1024 bytes in one blocks tells us that there were 1024 bytes of heap memory in use at the time the
program exited.
And we also have total heap usage, which is 103 and 1024 bytes allocated indicates that the during
the program's execution we had these logs frees and bytes allocated and obviously we have the leak summary
here which we did not have any leaks definitely lost and in the indirect loss, possibly lost.
So this definitely loss obviously refers to memory that was allocated but not freed during the program's
execution.
In this case, there were zero bytes lost and in zero blocks and indirect lost.
So and indirect lost and possible errors are similar but refer to more complex scenarios of lost memory.
And in this case, there were zero bytes indirectly or possibly lost.
And we also have the still reachable 1024 bytes in one block.
So this indicates that indicates that was allocated but still reachable at the end of the program.
So in this case, 1024 bytes were still reachable, which may not necessarily be a problem depending
on the program's design and suppressed.
Here we have the refers to the errors valgrind has suppressed or ignored.
In this case there were no suppressed errors.
And also we have the reachable blocks which is and to see them return the leak check full show leak
kinds all and obviously in order to do that you will need to use rerun with this command with both of
them.
But in this case, this actually doesn't we don't need that information in this program here.
And error summary zero errors from zero contexts here.
And finally, this section provides a summary of detected errors.
In this case, it reports that there were zero errors from zero contexts and no errors were suppressed.
So the program then executed with a segmentation fault message, as you can see here.
Now, the problem is that, as you can see here.
The problem seems like we are not reaching the chord array here.
Now I will give you 10s to find out why we are not reaching to this array.
So I will start explaining that now.
So the segmentation fault, usually in the original code arises from the fact that the shell code,
which is code is defined at the global scope.
This means that it's stored in a section of memory that may have certain protections or permissions
which can cause problems when attempting to execute shell code from such a region.
So in this code here, we need to fix that.
So we will move that inside the main function.
But before the printf, that's it.
And in this fixed code.
The shellcode is moved inside the main function resulting in it.
Being stored in the stack frame of the main function.
So the stack is typically executable, making it suitable location to store and execute shellcode.
By doing so, the shellcode will have appropriate permissions and executed.
It will not trigger a segmentation fault.
And now what we're going to do is let's actually save this and let's go to our GCC and compile this
actually.
Use that here.
So GCC Oa0 f No.
Stack protector.
The exec stack.
And that's it.
Now we have that out here and that's it.
We have the successfully executed shellcode.
Perfect.
Now what we're going to do is this is the last lecture.
And to summarize, the original segmentation fault usually occurred because the shellcode was stored
in a region of memory that might not have had the necessary permissions for execution, and the fix
involved moving the shellcode into the local scope of the main function, ensuring that it stored in
a memory region where the execution is allowed.
So the simple change prevents a segmentation fault and allows the shellcode to be executed successfully.