As we can already see from the source, it compares the input with a predetermined value 5276
to run a shell. This means we should just pass in the password in the stdin and it should already work. The password is uSq2ehEGT6c9S24zbshexZQBXUGrncxn5sD5QfGL
Looking at the surface, it looks like guessing the password wont help us acheive our goal. In the buffer to store the password, it looks like fgets is reading more characters than the buf1 can handle, which might be vulnerable to a buffer overflow.
I have found this python program below to help us determine the buffer overflow offset with a pattern, it is like follows.
I generated a pattern with the following output and I tried running it in the program. As you can see , the saved EIP for changed after the fgets call.
I used the same program to find the position of the offset, and it is at 80 bytes.
I used a shellcode from the site https://shell-storm.org/shellcode/index.html
and I generated a payload. I also put the shellcode in the environment variable so that it can be jumped to by the saved EIP.
I saved the shellcode in the env like so ;
Then, I need to get the address of the environment variables for the EIP to jump to. I used the x/s *((char **)environ)
command in GDB and got the following output. As we can see, we have the address to our shellcode. I have added 20 to the final input because gdb has set some env vars that might affect the accuracy of this reading.
PwBLgNa8p8MTKW57S7zxVAQCxnCpV8JqTTs9XEBv
is the password.
Looking from the source, it looks like a buffer overflow may not happen on username of pass since fgets is used to read characters equals to the buffer size, but we can still try to confirm.
As we can see, the program ran without faults
I tried thinking of manipulating the user and pass input to just a null byte, but it still compares 41 characters of the open passfile. Since strcspn is new, I went to the manual to find any security warnings; no warings.
However, I noticed in the last few lines printf(username);
was called. This was a direct call to a user input, which leaves us with a string format vulnerability.
To verify this, I ran the program with some new input to test and we have a segfault.
It could be that its trying to derefrence an invalid address when I supplied the %s
parameter, so I decided to just print out a series on %p
s to get a look at the stack state at this point without the need to derefrence. As we can see, we have the stack contents displayed. And since the contents of the password from the file are pushed to the stack, we should be able to read them eventually. What we are seeing here is the function arguments that are pushed to the stack. It does not show the push
commands in the disassembly, it is pushed after we call the function by lib64
from libc since this program is compiled for 64-bit CPUs, instead, we just move them to specific registers in the program.
From the surface here, we see some addresses and their contents. Hence, I made a script to automate the parsing of the stack for a number of times.
And the script is done, now to run it with our input and we get the results below.
Now to break it down, we first need to get some visuals on the stack
We are tring to read the stuff in passfile_content
and from the input we gave, the only input which is identifyable is the contents of pass
. Which means, once we see the contents of pass
(which is 'BBBBBBBB'), we will need to go down the stack 0x70 = 112 (base10)
bytes to reach passfile_content
.
If we look at the output, we can see lines 1 to 7 contain values that are pushed by other function calls, which serve no purpose. At line 8 however, we can see BBBBBBBB, which is the start of our pass
variable. Going back to earlier, we will need to go down 112 bytes. Since %p
traverses 8 bytes at a time, and 112 / 8 = 14
, our contents of passfile_content
is at line 8 + 14 = 22
.
I used this tool online to convert the data from hex to string, and reversed it for little endianess and I got the following string Hh74RPnuQ9sa5JAEXgNWCqz7sXGnh5J5M9KfPg3H
I used Hh74RPnuQ9sa5JAEXgNWCqz7sXGnh5J5M9KfPg3H
as password for level03 and it worked
The program takes in a decimal as the password and runs some decryption methods to check for the password. Looking at the code, it seems like we are not going to be doing any overflows since there is an existence of a canary. Hence, we will need to work on the decryption itself.
We noticed that this is a XOR Cipher where every bit in the plaintext is XOR'ed against every bit in a key. It is a good fundamental encryption becase in XOR encryption, It is clear that if nothing is known about the key or plaintext, nothing can be determined from the ciphertext alone.
in a nutshell, our decrypt
function takes in init_addr
, this will be our key. The plaintext in this case is the consecutive bytes in cipher1
to cipher5
.The encrypted text is "Congratulations!"
. The program takes the key as the input an encrypts the plaintext with it to compare if the ciphertext is correct. If it is, we will be able to access shell.
If we look at this table, we can see some basic operations of the XOR cipher. If we have the ciphertext and the plaintext, to get key, we can use plaintext ^ ciphertext
As we can see, using a decoder, the key is 18
for all characters
And since the input needs to be deducted by 0x1337d00d
to get the key, our input should be 0x1337d00d - 0x12(18)
which is 0x1337cffb
or 322424827
in base 10. We are able to get the password kgv3tkEb9h2mLkRsPkXRfc2mHbjMxQzvb2FrgKkf
On the surface, this looks like a buffer overflow vulnerability with a gets()
call on a limited size buffer. The catch is, we are unable to call exec()
in the child for the buffer overflow. Which means we have to use other functions like system()
First off, I located the EIP offset with the help of Buffer Overflow EIP Offset String generator To generate the string pattern and matching.
By setting set follow-fork-mode child
in GDB, im able to trace through the fork and obtain the offset as 156
. Since we are using other functions, we have to find the addresses loaded in memory. which is the pointer to the system()
function and the string /bin/sh
. This can be acheived using print system
and find __libc_start_main,+99999999,"/bin/sh"
(find from libc main for 99999999 bytes "/bin/sh").
Our pointer to system()
is 0xf7e6aed0
, our pointer to "/bin/sh" is 0xf7f897ec
.
Hence, we can generate our shellcode like so
And we have our password 3v8QLcN5SAhPaZZfEasfmXdwyR59ktDEMAwHF3aN
In the last two lines, we see a printf call to user input, which indicates a format string vulnerability. We also noticed that the program calls exit()
and its complete and not return, which means we are enable to overwrite the EIP, and had to overwrite the GOT for exit()
instead.
We also noticed that the program decapitalizes any characters in the input buffer, so that is something to take note of. The first step would be to find the offset of the printf stack address to the contents of the first printf argument. I have made a script to go through the stack to display its contents
As we can see the contents are at the 10th %p
. This is important because we will be putting our GOT address here to be read, and overwritten using %n
. To locate the GOT address for exit()
, we can check the relocation entries using objdump -R ./level05
and we can see its 080497e0
. So our new input should be pointing to that address and printing it out for now. We also should replace %p
with %hhn
so the system will write to that address on how many bytes we have printed
As we can see, we have successfully written over the GOT. So we just need to replace this with a pointer to the shellcode, which I will put in the environment variable.
The shellcode will be a simple execve(/bin/sh)
I found here. I will be exporting this in the environment variable and we will be getting the address via GDB. As we can see, our address is 0xffffd85e
but we have a NOP sled, so let make it safer by assuming its 0xffffd87e
With this information, we can now construct our payload like so:
And we got our password h4GtNnaMs2kZFN92ymTr2DcJHAzMfzLW25Ep59mq
The program prompts the user for two inputs, where it will encrypt / hash the first one to check that if it matches the second. Buffer overflow is protected as there is a canary inside the executable. Our only way to get a shell is to get the first inputs hash correct with the second input.
We can generate a key of our own now since we know the logic, or we could just use GDB and inspect the stack instead. I used gdb and added a breakpoint at 8048866
, which is where the program checks for the output. I used a simple asdfg
input to get the correct encryption by running the program.
Since they have a debugger check, we will use a manual jump to jump over the check itself. We will add a breakpoint before and after the check so that we can manually jump over it without messing up the memory state (break at 80487b5
, jmp to 80487ba
). As we can see, its comparing to the decimal 6229077
We change our key to 6229077 and it worked, our password is GbcPDRgsFK77LNnnuh7QyFYA2942Gp8yKj9KrWD8
This program allows is to store numbers in an int buffer. During the storing process, there is no check that the index is within the buffers size, they only check if it allowed other constraints such as some mathematical properties on the idx_input
and num_input
.
After some trials, we figurerd that the first constraint in store_number(int )
checks if the index is a not modulo of 3 temp3 != 0
. The second constraint checks if the number input is not 3070230528
. If any of those constraints are true, the number is written into the buffer at meat[index] = input
.
Since they dont do index size checking, we are able to segfault the program if we provide a faulty index.
To determind the offset to the EIP, we try to input a test value and inspect the memory layout of the program.
As we can see, meat
at index 1 is at 0xffffd548
. And the EIP is at 0xffffd70c
. We are able to overwrite the EIP since the EIP address is > the buffer address. With this , we are able to find the offset which is 70c - 548 = 1C4 or 452 in base 10
.
The index we need to write to will be 452 / 4 = 113
. We can test this out
As we can see, we are 4 bytes before the EIP address. However, to actually overwrite the EIP, we need to input as index 114
, which is not allowed by the function.
To overcome this, we can use alternatives like 2 * (UINT_MAX / 4) + 114
to overflow back to 114
without actually using the number. As we can see, we have successfully overwritten the EIP with our desired value.
Since we are able to ovewrite the address now, we should be able to overwrite it to an address which points to shellcode. We will be trying ret2libc for this. We needed the address for the system()
function as well as /bin/sh
. These information should be available in GDB. The address of system()
is 0xf7e6aed0
and the address for "/bin/sh" is 0xf7f897ec
We can generate our payload like so and able to retreive the password 7WJ6jFBzrcjEYXudxnM3kdW7n3qyxR6tk2xGrkSC
The program has a stack canary implemented, so the approach to overwrite the EIP might not be the way. The program takes a file as an input, reads it character by character, creates a new file in ./backups
and writes it to their own backup files accordingly.
The exploit is quite straightforward, I will need to go to a writable directory, create a soft link to /home/users/level09/.pass
and pass that link as as argument to the binary. The program will create a backup file and write its contents in backups/<linkname>
. The password is fjAwpJNs2vvkFLRebEvAQ2hFZ4uQBWfHRsP62d8S
Looking at the code, there may be a chance at overflowing var1->msg
since strncpy(var1->message, temp, var1->msglen);
does not copy the nullbyte over. However, there are no operations after that action so I dont this it affects the program.
However upon closer inspection, I noticed that in set_username
, the while loop is ran 41 times instead of 40, which will cause an overwrite on the first byte of var1->msglen
To verify this, we can look into GDB. As we can see, the first image contains the original value in the rdx register, but the second image shows the overwritten value.
Now, if we change rdx to 0xff
or 255, we should be able to overflow the message variable later when we strncpy in set_msg
.
We did infact override the EIP, and the offset was 200. Now we add the address of secret_backdoor
and we should be finished.
The password for the end user is j4AunAPDXaJxxWjYEUxpanmvSgRDV3tpA5BEaBuE