Question

This program is running with root privileges on my machine and I need to perform a Stack overflow attack on the following code and get root privileges:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/sha.h>

void sha256(char *string, char outputBuffer[65])
{
    unsigned char hash[SHA256_DIGEST_LENGTH];
    int i = 0;
    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    SHA256_Update(&sha256, string, strlen(string));
    SHA256_Final(hash, &sha256);

    for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
    {
        sprintf(outputBuffer + (i * 2), "%02x", hash[i]);
    }
    outputBuffer[64] = 0;
}

int password_check(char *userpass)
{
    char text[20] = "thisisasalt";
    unsigned int password_match = 0;
    char output[65] = { 0, };
    // >>> hashlib.sha256("Hello, world!").hexdigest()
    char pass[] = "315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3"; 

    text[0] = 'a';
    text[1] = 't';
    text[2] = 'j';
    text[3] = '5';
    text[4] = '3';
    text[5] = 'k';
    text[6] = '$';
    text[7] = 'g';
    text[8] = 'f';
    text[9] = '[';
    text[10] = ']';
    text[11] = '\0';
    strcat(text, userpass);
    sha256(text, output);

    if (strcmp(output, pass) == 0)
    {
        password_match = 1;
    }

    return (password_match == 1);
}

int main(int argc, char **argv)
{
    if (argc < 3)
    {
        printf("Usage: %s <pass> <command>\n", argv[0]);
        exit(1);
    }
    if (strlen((const char *) argv[1]) > 10)
    {
        printf("Error: pasword too long\n");
        exit(1);
    }

    if (password_check(argv[1]))
    {
        printf("Running command as root: %s\n", argv[2]);
        setuid(0);
        setgid(0);
        system(argv[2]);
    }
    else
    {
        printf("Authentication failed! This activity will be logged!\n");
    }

    return 0;
}

So I try to analyse the program with IDA and I see the text segment going from the lower addresses to the higher addresses, higher than that I see the data and then the bss and finally external commands.

Now as far as I know the stack should be just above that, but I'm not certain how to view it, how exactly am I supposed to view the stack in order to know what I'm writing on? (Do I even need it or am I completely clueless?)

Second question is considering the length of the input, how do i get around this check in the code:

if (strlen((const char *) argv[1]) > 10)
    {
        printf("Error: pasword too long\n");
        exit(1);
    }

Can I somehow give the string to the program by reference? If so how do I do it? (Again, hoping I'm not completely clueless)

Was it helpful?

Solution

Now as far as I know the stack should be just above that, but I'm not certain how to view it, how exactly am I supposed to view the stack in order to know what I'm writing on? (Do I even need it or am I completely clueless?)

The stack location varies all the time - you need to look at the value of the ESP/RSP register, its value is the current address of the top of the stack. Typically, variable addressing will be based on EBP rather then ESP, but they both will point to the same general area of memory.

During analysis, IDA sets up a stack frame for each function, which acts much like a struct - you can define variables with types and names in it. This frame is summarized at the top of the function: stack summary view Double-clicking it or any local variable in the function body will open a more detailed window. That's as good as you can get without actually running your program in a debugger.

You can see that text is right next to password_match, and judging from the addresses, there are 0x14 bytes allocated for text, as one would expect. However, this is not guaranteed and the compiler can freely shuffle the variables around, pad them or optimize them into registers.

Second question is considering the length of the input, how do i get around this check in the code:

if (strlen((const char *) argv[1]) > 10)
{
    printf("Error: pasword too long\n");
    exit(1);
}

You don't need to get around this check, it's already broken enough. There's an off-by-one error.

Stop reading here if you want to figure out the overflow yourself.


The valid range of indices for text spans from text[0] through text[19]. In the code, user input is written to the memory area starting at text[11]. The maximum input length allowed by the strlen check is 10 symbols + the NULL terminator. Unfortunately, that means text[19] contains the 9th user-entered symbol, and the 10th symbol + the terminator overflow into adjacent memory space. Under certain circumstances, that allows you to overwrite the least significant byte of password_match with an arbitrary value, and the second least significant byte with a 0.
Your function accepts the password if password_match equals 1, which means the 10th character in your password needs to be '\x01' (note that this is not the same character as '1').

Here are two screenshots from IDA running as a debugger. text is highlighted in yellow, password_match is in green.

The password I entered was 123456789\x01.

  1. Stack before user entered password is strcat'd into text. stack variables before overflow

  2. Stack after strcat. Notice that password_match changed. stack variables after overflow

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top