• Uncategorized

About c : Unable-to-execute-code-in-non-text-segments-of-memory-even-with-execstack-enabled-duplicate

Question Detail

I’m trying to understand the basics of stack smashing attacks but I’m finding that the code seg faults when jumping to the overwritten return address. To isolate the issue, I wrote a small snippet of code exhibiting the same behaviour, using a single NOP instruction stored in the data segment of memory:

char nop[] = "\x90";
int main(){
   ((void(*)(void))nop)();
}

Which I’m then compiling as follows:

gcc -fno-stack-protector -z execstack nop.c -g -o nop

Executing the code step-wise from the function call in GDB gives the same results:

foo@bar:~/path/to/file$ gdb -q nop
/home/foo/.gdbinit:1: Error in sourced command file:
Ambiguous set command "dis intel": disable-randomization, disassemble-next-line, disassembler-options, disassembly-flavor...
Reading symbols from nop...
(gdb) list
1   char nop[] = "\x90";
2   int main(){ 
3      ((void(*)(void))nop)();
4   }
(gdb) break 3
Breakpoint 1 at 0x1131: file nop.c, line 3.
(gdb) run
Starting program: /home/foo/path/to/file/nop 

Breakpoint 1, main () at nop.c:3
3      ((void(*)(void))nop)();
(gdb) x/xi $rip
=> 0x555555555131 <main+8>: lea    0x2ed8(%rip),%rax        # 0x555555558010 <nop>
(gdb) step

Program received signal SIGSEGV, Segmentation fault.
0x0000555555558010 in nop ()
(gdb) x/xi $rip
=> 0x555555558010 <nop>:    nop

In similar questions, the issue seems to stem from the data segment being non-executable, which appears to be the case here too. Most answers seem to suggest using the execstack flag to disable this though, which clearly isn’t working here.

Any help would be much appreciated!


Edit 1: Quick verification that the execstack flag is being set:

foo@bar:~/path/to/file$ execstack nop
X nop

Edit 2: Rerunning GDB using stepi rather than step suggests that the seg-fault happens the instruction after the code jumps to the NOP:

foo@bar:~/path/to/file$ gdb -q nop
/home/foo/.gdbinit:1: Error in sourced command file:
Ambiguous set command "dis intel": disable-randomization, disassemble-next-line, disassembler-options, disassembly-flavor...
Reading symbols from nop...
(gdb) break 3
Breakpoint 1 at 0x1131: file nop.c, line 3.
(gdb) run
Starting program: /home/foo/path/to/file/nop 

Breakpoint 1, main () at nop.c:3
3      ((void(*)(void))nop)();
(gdb) stepi
0x0000555555555138  3      ((void(*)(void))nop)();
(gdb) x/xi $rip
=> 0x555555555138 <main+15>:    callq  *%rax
(gdb) stepi
0x0000555555558010 in nop ()
(gdb) x/xi $rip
=> 0x555555558010 <nop>:    nop
(gdb) stepi

Program received signal SIGSEGV, Segmentation fault.
0x0000555555558010 in nop ()
(gdb) x/xi $rip
=> 0x555555558010 <nop>:    nop

Question Answer

Just solved the problem, which was a combination of two issues:

  1. I assumed that the execstack flag would make all non-text segments of memory executable, however it seems this is not the case. To fix this, I made the raw hex code a local variable:
int main(){
   char nop[] = "\x90";
   ((void(*)(void))nop)();
}
  1. The next issue seems to have been that, with no return instruction, the code continues executing after the nop until it hits a bad area of memory. To fix this, I wrote a simple function performing the same task…
void nop() {
   return;
}

int main(){
   nop();
}

Extracted the hex from the disassembled code…

nop:
   \xf3\x0f\x1e\xfa  endbr64
   \x55              push %rbp
   \x48\x89\xe5      mov %rsp,%rbp
   \x90              nop
   \x5d              pop %rbp
   \xc3              retq

Then used these bytes as the basis for my character array:

int main(){
   char nop[] = "\xf3\x0f\x1e\xfa\x55\x48\x89\xe5\x90\x5d\xc3";
   ((void(*)(void))nop)();
}

This led to a successful execution:

foo@bar:~/path/to/file$ gdb -q nop
/home/foo/.gdbinit:1: Error in sourced command file:
Ambiguous set command "dis intel": disable-randomization, disassemble-next-line, disassembler-options, disassembly-flavor...
Reading symbols from nop...
(gdb) list
warning: Source file is more recent than executable.
1   int main(){
2      char nop[] = "\xf3\x0f\x1e\xfa\x55\x48\x89\xe5\x90\x5d\xc3";
3      ((void(*)(void))nop)();
4   }
(gdb) break 3
Breakpoint 1 at 0x114a: file nop.c, line 3.
(gdb) run
Starting program: /home/foo/path/to/file/nop 

Breakpoint 1, main () at nop.c:3
3      ((void(*)(void))nop)();
(gdb) x/xi $rip
=> 0x55555555514a <main+33>:    lea    -0xc(%rbp),%rax
(gdb) stepi
0x000055555555514e  3      ((void(*)(void))nop)();
(gdb) x/xi $rip
=> 0x55555555514e <main+37>:    callq  *%rax
(gdb) stepi
0x00007fffffffe204 in ?? ()
(gdb) x/xi $rip
=> 0x7fffffffe204:  endbr64 
(gdb) stepi
0x00007fffffffe208 in ?? ()
(gdb) x/xi $rip
=> 0x7fffffffe208:  push   %rbp
(gdb) stepi
0x00007fffffffe209 in ?? ()
(gdb) x/xi $rip
=> 0x7fffffffe209:  mov    %rsp,%rbp
(gdb) stepi
0x00007fffffffe20c in ?? ()
(gdb) x/xi $rip
=> 0x7fffffffe20c:  nop
(gdb) stepi
0x00007fffffffe20d in ?? ()
(gdb) x/xi $rip
=> 0x7fffffffe20d:  pop    %rbp
(gdb) stepi
0x00007fffffffe20e in ?? ()
(gdb) x/xi $rip
=> 0x7fffffffe20e:  retq   
(gdb) stepi
0x0000555555555150 in main () at nop.c:3
3      ((void(*)(void))nop)();
(gdb) x/xi $rip
=> 0x555555555150 <main+39>:    mov    $0x0,%eax
(gdb) continue
Continuing.
[Inferior 1 (process 68215) exited normally]

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.