CWE学习(一)

CWE(Common Weakness Enumeration,通用缺陷枚举)。是由美国国土安全部国家计算机安全部门资助的软件安全战略性项目。

CVE (Common Vulnerabilities & Exposures,常用漏洞和风险)。 CVE 是国际著名的安全漏洞库,也是对已知漏洞和安全缺陷的标准化名称的列表,它是一个由企业界、政府界和学术界综合参与的国际性组织,采取一种非盈利的组织形式,其使命是为了能更加快速而有效地鉴别、发现和修复软件产品的安全漏洞。

用面向对象的话说,CWE就好像定义了一个类(漏洞类型),而CVE属于具体的对象。

CWE-20: Improper Input Validation

官方文档

软件接收输入,但不会验证或者有效的验证输入是否安全。

输入验证会应用于:

  • raw data: strings, numbers, parameters, file contents
  • metadata:关于raw data的信息,比如headers或者大小

示例代码1

本示例要求用户获得最大尺寸为100平方的 m × n m \times n m×n 游戏板的高度和宽度。

...
#define MAX_DIM 100
...
/* board dimensions */

int m,n, error;
board_square_t *board;
printf("Please specify the board height: \n");
error = scanf("%d", &m);

if ( EOF == error ){
	die("No integer passed: Die evil hacker!\n");
}
printf("Please specify the board width: \n");
error = scanf("%d", &n);

if ( EOF == error ){
	die("No integer passed: Die evil hacker!\n");
}
if ( m > MAX_DIM || n > MAX_DIM ) {
	die("Value too large: Die evil hacker!\n");
}
board = (board_square_t*) malloc( m * n * sizeof(board_square_t));

malloc函数原型为 void* malloc (size_t size);

该示例中代码会检验输入数据是否超过上限,但没有检查是否出现负数。攻击者可以将 mn均设为负数,并且绝对值非常大,负负得正来分配更多内存。

示例代码2

public static final double price = 20.00;
int quantity = currentUser.getAttribute("quantity");
double total = price * quantity;
chargeUser(total);

代码规定了物品得价格为20。却没有对购买物品得数量进行校验,如果数量为负数,那么商家还会亏钱。

CWE-22: Improper Limitation of a Pathname to a Restricted Directory

官方文档

软件使用外部输入来构造一个路径名,该路径名用于访问受限制的父目录下的文件或目录,但软件不会正确地处理路径名中的特殊元素(比如 ../),这些元素会导致路径名解析到受限制目录外的位置。

示例代码1

String path = getInputPath();
if (path.startsWith("/safe_dir/"))
{
	File f = new File(path);
	f.delete()
}

攻击者可输入/safe_dir/../important.dat 来删除和 /safe_dir/ 同目录下的 important.dat 文件。

CWE-78: Improper Neutralization of Special Elements used in an OS Command

官方文档

当软件用外部输入构造要执行的系统命令时,没有处理外部输入中的特殊元素导致恶意命令被执行。

示例代码1

public String coordinateTransformLatLonToUTM(String coordinates)
{
	String utmCoords = null;
	try {
		String latlonCoords = coordinates;
		Runtime rt = Runtime.getRuntime();
		Process exec = rt.exec("cmd.exe /C latlon2utm.exe -" + latlonCoords);
		// process results of coordinate transform
		
		// ...
	}
	catch(Exception e) {
		...
	}
	return utmCoords;
}

示例代码中没有对coordinates的值进行检查,导致恶意用户可以输入&来执行其它的系统命令。

CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer

官方文档

软件对内存缓冲区执行操作,但它可以读取或写入缓冲区预期边界之外的内存位置。

示例代码1

void host_lookup(char *user_supplied_addr){
	struct hostent *hp;
	in_addr_t *addr;
	char hostname[64];
	in_addr_t inet_addr(const char *cp);
	
	/*routine that ensures user_supplied_addr is in the right format for conversion */
	
	validate_addr_form(user_supplied_addr);
	addr = inet_addr(user_supplied_addr);
	hp = gethostbyaddr( addr, sizeof(struct in_addr), AF_INET);
	strcpy(hostname, hp->h_name);
}

示例代码分配了一个64字节的数组来存储hostname,但是并没有校验hp->name的值是否超过64。

示例代码2

int main (int argc, char **argv) {
	char *items[] = {"boat", "car", "truck", "train"};
	int index = GetUntrustedOffset();
	printf("You selected %s\n", items[index-1]);
	return 0;
}

同样,代码并没有对index进行校验,可能导致buffer over-read。

CWE-125: Out-of-bounds Read

官方文档

软件读取数组之外的数据

示例代码1

int getValueFromArray(int *array, int len, int index) {
	int value;
	if (index < len) {
		value = array[index];
	}
	else {
		printf("Value is: %d\n", array[index]);
		value = -1;
	}
	return value;
}

代码对index长度进行了校验,不得超过数组最大长度,但是没有确保index不能为负数,这样会导致out of bounds read。

正确的做法为

// range of values for the array
if (index >= 0 && index < len) {
	...
}

CWE-190: Integer Overflow or Wraparound

官方文档

当算术运算中出现增加一个非常大的数字或其它情况时容易触发。加法运算后值变负或者变小。

示例代码1

#define JAN 1
#define FEB 2
#define MAR 3

short getMonthlySales(int month) {...}

float calculateRevenueForQuarter(short quarterSold) {...}

int determineFirstQuarterRevenue() {

	// Variable for sales revenue for the quarter
	float quarterRevenue = 0.0f;
	
	short JanSold = getMonthlySales(JAN); /* Get sales in January */
	short FebSold = getMonthlySales(FEB); /* Get sales in February */
	short MarSold = getMonthlySales(MAR); /* Get sales in March */
	
	// Calculate quarterly total
	short quarterSold = JanSold + FebSold + MarSold;
	
	// Calculate the total revenue for the quarter
	quarterRevenue = calculateRevenueForQuarter(quarterSold);
	
	saveFirstQuarterRevenue(quarterRevenue);
	
	return 0;
}

determineFirstQuarterRevenue方法用来计算第一个季度的财政收入,这个方法会接收前3个月的销售额作为输入,然后根据3个月的销售额总和来计算第一个季度的财政收入并存入数据库,然而它们均用short类型的变量来存储数据,short最大值32768。使得计算3个月的总和时容易造成整数溢出。

正确代码

float calculateRevenueForQuarter(long quarterSold) {...}

int determineFirstQuarterRevenue() {
...
	// Calculate quarterly total
	long quarterSold = JanSold + FebSold + MarSold;
	// Calculate the total revenue for the quarter
	quarterRevenue = calculateRevenueForQuarter(quarterSold);

...
}

CWE-200: Exposure of Sensitive Information to an Unauthorized Actor

官方文档

软件将敏感信息暴露给没有明确授权访问该信息的参与者。

示例代码1

public BankAccount getUserBankAccount(String username, String accountNumber) {
	BankAccount userAccount = null;
	String query = null;
	try {
		if (isAuthorizedUser(username)) {
			query = "SELECT * FROM accounts WHERE owner = "
			+ username + " AND accountID = " + accountNumber;
			DatabaseManager dbManager = new DatabaseManager();
			Connection conn = dbManager.getConnection();
			Statement stmt = conn.createStatement();
			ResultSet queryResult = stmt.executeQuery(query);
			userAccount = (BankAccount)queryResult.getObject(accountNumber);
		}
	} catch (SQLException ex) {
		String logMessage = "Unable to retrieve account information from database,\nquery: " + query;
		Logger.getLogger(BankManager.class.getName()).log(Level.SEVERE, logMessage, ex);
	}
	return userAccount;
}

getUserBankAccount 方法使用提供的用户名和帐号从数据库检索银行帐户对象以查询数据库。如果查询数据库时引发SQLException,则会创建一条错误消息并输出到日志文件。创建的错误消息包含有关数据库查询的信息,这些信息可能包含有关数据库或查询逻辑的敏感信息。在这种情况下,错误消息将公开数据库中使用的表名和列名。这些数据可以用来简化其他攻击,例如直接访问数据库的SQL注入。

CWE-287: Improper Authentication

当一个用户声称拥有一个给定的身份时,软件不能证明或者不能充分证明这个声明是正确的。

my $q = new CGI;

if ($q->cookie('loggedin') ne "true") {
	if (! AuthenticateUser($q->param('username'), $q->param('password'))) {
		ExitError("Error: you need to log in first");
	}
	else {
	# Set loggedin and user cookies.
	$q->cookie(
	-name => 'loggedin',
	-value => 'true'
	);

	$q->cookie(
	-name => 'user',
	-value => $q->param('username')
	);
	}
}

if ($q->cookie('user') eq "Administrator") {
DoAdministratorTasks();
}

CWE-400: Uncontrolled Resource Consumption

官方文档

软件不能正确地控制有限资源的分配和维护,从而使参与者能够影响所消耗的资源量,最终导致可用资源的耗尽。

资源包括:内存,系统存储空间,数据库连接等等。

示例代码1

/* process message accepts a two-dimensional character array of the form [length][body] containing the message to be processed */
int processMessage(char **message)
{
	char *body;
	int length = getMessageLength(message[0]);
	
	if (length > 0) {
		body = &message[1][0];
		processMessageBody(body);
		return(SUCCESS);
	}
	else {
		printf("Unable to process message; invalid message length");
		return(FAIL);
	}
}

这里示例代码处理一个二维数组,二维数组第一行表示数组有多少个消息,这里并没有对消息数量进行限制,如果message非常大,那么非常消耗系统资源。

正确示范

unsigned int length = getMessageLength(message[0]);
if ((length > 0) && (length < MAX_LENGTH)) {
	...
}

示例代码2

public void acceptConnections() {
	try {
		ServerSocket serverSocket = new ServerSocket(SERVER_PORT);
		int counter = 0;
		boolean hasConnections = true;
		while (hasConnections) {
			Socket client = serverSocket.accept();
			Thread t = new Thread(new ClientSocketThread(client));
			t.setName(client.getInetAddress().getHostName() + ":" + counter++);
			t.start();
		}
		serverSocket.close();
	} catch (IOException ex) {
		...
	}
}

示例代码并没有对connection作出限制。可以通过线程池来限制创建线程的数量。

正确示范

public static final int SERVER_PORT = 4444;
public static final int MAX_CONNECTIONS = 10;
...

public void acceptConnections() {
	try {
		ServerSocket serverSocket = new ServerSocket(SERVER_PORT);
		int counter = 0;
		boolean hasConnections = true;
		while (hasConnections) {
			hasConnections = checkForMoreConnections();
			Socket client = serverSocket.accept();
			Thread t = new Thread(new ClientSocketThread(client));
			t.setName(client.getInetAddress().getHostName() + ":" + counter++);
			ExecutorService pool = Executors.newFixedThreadPool(MAX_CONNECTIONS);
			pool.execute(t);
		}
		serverSocket.close();
		
	
	} catch (IOException ex) {
		...
	}
}

CWE-416: Use After Free

官方文档

释放内存后引用内存可能会导致程序崩溃、使用意外值或执行代码,这也是CTF中的热点。

示例代码1

#include <stdio.h>
#include <unistd.h>
#define BUFSIZER1 512
#define BUFSIZER2 ((BUFSIZER1/2) - 8)

int main(int argc, char **argv) {
	char *buf1R1;
	char *buf2R1;
	char *buf2R2;
	char *buf3R2;
	buf1R1 = (char *) malloc(BUFSIZER1);
	buf2R1 = (char *) malloc(BUFSIZER1);
	free(buf2R1);
	buf2R2 = (char *) malloc(BUFSIZER2);
	buf3R2 = (char *) malloc(BUFSIZER2);
	strncpy(buf2R1, argv[1], BUFSIZER1-1);
	free(buf1R1);
	free(buf2R2);
	free(buf3R2);
}

示例代码2

char* ptr = (char*)malloc (SIZE);
if (err) {
	abrt = 1;
	free(ptr);
}
...
if (abrt) {
	logError("operation aborted before commit", ptr);
}

ptrfree之后其中的内容被清空了。但是中间一系列操作过后,原ptr指向的那片堆内存可能被其它的指针指向(设为ptr1),在之后的logError操作中再次引用了ptr,实际上记录的是ptr1的值。当free(ptr);之后应有ptr=NULL;操作。

CWE-476: NULL Pointer Dereference

官方文档

当应用程序引用预期有效但为NULL的指针时,就会发生空指针引用,通常会导致崩溃或退出。

示例代码1

void host_lookup(char *user_supplied_addr){
	struct hostent *hp;
	in_addr_t *addr;
	char hostname[64];
	in_addr_t inet_addr(const char *cp);
	
	/*routine that ensures user_supplied_addr is in the right format for conversion */
	
	validate_addr_form(user_supplied_addr);
	addr = inet_addr(user_supplied_addr);
	hp = gethostbyaddr( addr, sizeof(struct in_addr), AF_INET);
	strcpy(hostname, hp->h_name);
}

gethostbyaddr( addr, sizeof(struct in_addr), AF_INET); 中,由于用户提供的host ip无效,导致 gethostbyaddr 返回 NULL,而strcpy之前没有对hp进行校验。这段代码同样有buffer overflow(CWE-119)。

CWE-787: Out-of-bounds Write

官方文档

软件在预期缓冲区的末尾或开头之前写入数据。也是CTF热点,strcpy出现的地方几乎必有它。

示例代码1

char * copy_input(char *user_supplied_string){
	int i, dst_index;
	char *dst_buf = (char*)malloc(4*sizeof(char) * MAX_SIZE);
	if ( MAX_SIZE <= strlen(user_supplied_string) ){
		die("user string too long, die evil hacker!");
	}
	
	dst_index = 0;
	for ( i = 0; i < strlen(user_supplied_string); i++ ){
		if( '&' == user_supplied_string[i] ){
			dst_buf[dst_index++] = '&';
			dst_buf[dst_index++] = 'a';
			dst_buf[dst_index++] = 'm';
			dst_buf[dst_index++] = 'p';
			dst_buf[dst_index++] = ';';
		}
		else if ('<' == user_supplied_string[i] ){
			/* encode to &lt; */
		}
		else 
			dst_buf[dst_index++] = user_supplied_string[i];
	}
	return dst_buf;
}

dst_index 的长度4倍于用户输入的字符串,而&字符的加密用了5个字符。当输入字符串全部为&时会导致缓冲区溢出。

示例代码2

char* trimTrailingWhitespace(char *strMessage, int length) {
	char *retMessage;
	char *message = malloc(sizeof(char)*(length+1));
	
	// copy input string to a temporary string
	char message[length+1];
	int index;
	for (index = 0; index < length; index++) {
		message[index] = strMessage[index];
	}
	message[index] = '\0';
	
	// trim trailing whitespace
	int len = index-1;
	while (isspace(message[len])) {
		message[len] = '\0';
		len--;
	}
	
	// return string without trailing whitespace
	retMessage = message;
	return retMessage;
}

这段代码中当输入全空格时会导致len的值小于0,从而写入到message数组以外的内存。

CWE-798: Use of Hard-coded Credentials

官方文档

示例代码1

将密码或者密钥等重要信息硬编码在代码里。

int VerifyAdmin(char *password) {
	if (strcmp(password, "Mew!")) {
		printf("Incorrect Password!\n");
		return(0)
	}
	printf("Entering Diagnostic Mode...\n");
	return(1);
}
  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值