I am stuck on the following problem. Consider this code:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
void runme() {
printf("Hello, world!\n");
exit(0);
}
int main(char argc, char **argv) {
if (argc != 2) return 1;
void (*buffer[8])(void);
buffer[(int8_t) argv[1][0]] = runme;
return 0;
}
The program reads the first argument, that will serve as an index. If you write the right number you will be able to overwrite the return address, with the address of the function runme.
I understand that if I overwrite the return address, when main finish it's execution, the program will continue executing the runme function. So with that behaviour in mind, I have calculated which number should I introduce as a first argument.
Then if I try the following command: ./program "$(echo -ne '\x0b')"
the output is segmentation fault.
I have tried to debug it in gdb and the output is the following (segfault also):
In addition, I have looked at dmesg's output, which is the second image.
Yesterday I was looking through the internet what could have caused the segmentation fault but I found nothing. What am I missing? Does libc developers have included a new protection mechanism?
My machine information
- Amd Ryzen 5700U
- Ubuntu 22.04
- Gcc 11.03
- Binutils 2.38
- Glibc 2.35
CodePudding user response:
The crash is caused by stack misalignment. See Why does the x86-64 / AMD64 System V ABI mandate a 16 byte stack alignment?
The jump-by-ret results in entering runme
with the stack misaligned, which violates the ABI, and some libc functions do in fact break when called with a misaligned stack. It doesn't happen on my system, but apparently your malloc
implementation (which printf
calls) requires stack alignment.
Disassembling the code bytes, the faulting instruction is movaps [rsp 0x10], xmm1
, whose memory operand must be aligned to 16 bytes. However, rsp
has a hex value ending in 8
, so rsp 0x10
is not aligned.
I don't off the top of my head see a simple way to have the exploit work around this.
CodePudding user response:
That version of the code works "as intended" (between quotes, because, for this kind of code, it depends on who is intending...).
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
void runme() {
printf("Hello, world!\n");
exit(0);
}
int main(char argc, char **argv) {
puts("");
if (argc != 2) return 1;
void (*buffer[8])(void);
buffer[(int8_t) argv[1][0]] = runme;
return 0;
}
The only difference is the puts
. It works also if you printf anything (but an empty string, but in this case gcc acts as if there were no printf at all).
But not for any function call. For example I've tried with a fflush(stdout)
at this place, and it is still segfaulting (I've chose this just to be sure that it wasn't a strange thing related to usage of any FILE functions).
Old answer (unaware that it was on purpose)
What a strange way to pass integer argument.
I was about to reply "argv[1]
" is a string and cannot be cast to an int, when I saw that you were using argv[1][0]
, which is an integer (a char). Then to say "you'll get the ascii code of 1st char of whatever argument you pass to your program, which is too big". But even that, you already know, since you pass a specific, non-ascii byte as an argument.
But, well, that byte is \x0b
which is 11. And you allocate an array of 8 pointer. So, still, it is normal that you have a seg fault.