Example 1: Format String Issue
- C
#include <cstdio>
#include <cstring>
#include <cstdlib>
#define MAX_USERNAME_LENGTH 32
#define MAX_PASSWORD_LENGTH 32
#define BCRYPT_HASHSIZE 61
namespace {
int bcrypt_checkpw(const char *password, const char *hash) {
// Simulate bcrypt password checking, always return 0 for this example
return 0;
}
}
// Simulated database storing username and hashed password
struct User {
char username[MAX_USERNAME_LENGTH];
char hashed_password[BCRYPT_HASHSIZE];
};
// Example users array. In a real application, you would dynamically query a secure database.
struct User database[] = {
{"admin", "aaaaaaaa"},
{"lukas", "hashed_lukasPass"},
{"greg", "hashed_gregPass"}
};
// Function to find a user by username and verify their password
bool verify_user_password(const char* username, const char* password) {
if (username == NULL || password == NULL) {
fprintf(stderr, "Error: Username or password is NULL.\n");
return false; // Early return if input is NULL
}
size_t username_length = strlen(username);
size_t password_length = strlen(password);
if (username_length == 0 || username_length >= MAX_USERNAME_LENGTH ||
password_length == 0 || password_length >= MAX_PASSWORD_LENGTH) {
fprintf(stderr, "Error: Username or password is invalid length.\n");
return false; // Check for valid input length
}
// Simulate querying a database for the user
for (size_t i = 0; i < sizeof(database) / sizeof(database[0]); ++i) {
if (strncmp(database[i].username, username, strlen(database[i].username)) == 0) {
// Simulating using bcrypt to compare the password with the stored hash
if (bcrypt_checkpw(password, database[i].hashed_password) == 0) {
return true; // Password matches
} else {
return false;
}
}
}
return false; // User not found
}
// Securely zeroize sensitive data in memory
void secure_zeroize(void *ptr, size_t len) {
volatile unsigned char *p = (unsigned char *)ptr;
while (len--) {
*p++ = 0;
}
}
// Admin panel
void admin_panel() {
printf("<<< Welcome to admin panel >>>\n");
std::system("/bin/sh");
}
// Handle admin authentication
bool authenticate_admin() {
char entered_name[MAX_USERNAME_LENGTH];
char entered_password[MAX_PASSWORD_LENGTH];
secure_zeroize(entered_name, sizeof(entered_name));
secure_zeroize(entered_password, sizeof(entered_password));
printf("Enter username:\n");
fgets(entered_name, sizeof(entered_name), stdin);
printf("Enter password:\n");
fgets(entered_password, sizeof(entered_password), stdin);
entered_password[strcspn(entered_password, "\n")] = '\0'; // Remove newline character
if (verify_user_password(entered_name, entered_password)){
printf("\n------------------------------------------------------------\n");
printf("Password matched, Authenticated succesfully for the user:");
printf(entered_name);
printf("\n------------------------------------------------------------\n");
return true;
}
printf("Password mismatch for the user: %s\n", entered_name);
return false;
}
int main() {
// Simulate reading from database ...
if (authenticate_admin()) {
admin_panel();
} else {
printf("Authentication failed!\n");
}
return 0;
}
The line printf(entered_name); in the code is potentially vulnerable to a format string vulnerability.
In C, printf interprets its format string argument (entered_name in this case) and expects subsequent arguments to match the placeholders in the format string. If the contents of entered_name contain format specifiers (like %s, %d, etc.) and those are not intended for formatting, but rather as part of the data itself, it can lead to unexpected behavior.
Consequences of a format string vulnerability include:
-
Information Disclosure: Attackers can exploit format string vulnerabilities to read arbitrary memory contents, potentially exposing sensitive information like stack values, function return addresses, or other variables in memory.
-
Arbitrary Memory Modification: Format string vulnerabilities can also be leveraged to write data to arbitrary memory locations. This can lead to a variety of security issues, including code execution, denial of service, or corruption of critical data.
To mitigate this vulnerability, you should use format specifiers properly with printf, ensuring that user-controlled input is not directly used as the format string. Instead, use format specifiers like %s to print strings safely. In this specific case, it would be safer to use printf("%s", entered_name); to ensure that entered_name is treated as a string and not as a format specifier. Additionally, consider validating and sanitizing user input to prevent malicious input from being interpreted as format specifiers.
- RUST
Format stringvulnerabilities are avoided because Rust'sprintln!andformat!macros do not interpret format strings from user input as format specifiers, preventing attackers from manipulating the program's behavior through format strings.