我刚毕业,希望尽快找到工作,但我对代码安全性有疑问。
在Java中没有超出范围的数组,并且没有指示Java比C更安全的指针。在C中,这些超出范围的数组可能导致堆栈损坏或缓冲区溢出和悬空指针。在一次采访中,我可能会被问到“黑客如何利用这些优势?”
我该如何回答这个问题?
欢迎举例。
答案 0 :(得分:1)
好吧,你可以专门谈谈Heartbleed(那里有大量的文档)。
基本上,有一个api调用可以通过网络进行,你的代码会请求通信保持活动状态,通常看起来像:
You: send me "potato", which is 6 chars.
Response: potato
但是,没有检查所请求的文本,并且发送的文本的长度是对齐的,所以你可以这样做:
You: send me "potato", which is 512 chars.
Response potato&&&�#((#(#)%#(((#%)password:1234#(%()#%((#%#(#%)(#)(%)(##()JFJFEOIJF#)J(JF)(#J)(#J#)(J#))J#....
响应计算机将发送马铃薯,加上506字节本质上是围绕该局部变量的堆栈,其中几乎可以包含任何内容,包括密码等。
这是一个足够好的例子吗?
答案 1 :(得分:0)
除非你的工作涉及安全,否则我认为你不会被问到这个问题。识别漏洞相对容易,但利用它们很难,而且我认为大多数雇主都非常高兴知道你知道你需要检查你的界限。
billjamesdev explains通过溢出来披露信息,这基本上就是Heartbleed的错误。但是,有更糟糕的可能结果,其中最着名的是任意的远程代码执行:基本上,由于软件漏洞,其他人在您的程序中运行他们喜欢的任何代码。基于堆栈的缓冲区溢出更容易被利用来获取RCE,但基于堆的缓冲区也可以在某种程度上被利用。
在这个微小的功能中:
int foo() {
char bar[4];
}
数组bar
存在于堆栈中。但是,存在于堆栈中的另一个有趣的事情是函数的返回地址。如果覆盖该地址,当函数返回时,它将跳转到您选择的位置,而不是开发人员认为它会返回的位置。假设你有这两个功能:
int main() {
char bar[4];
scanf("Enter no more than 4 characters: %s\n", bar);
}
int bar() {
system("sh"); // starts a shell on Linux
}
并假设bar
位于内存地址0x41414141(这种情况极不可能,但它使演示更容易,因为0x41是'A'的ASCII代码)。如果您在Linux上运行该程序并输入:
Enter no more than 4 characters: 00001111AAAA
在x86上,这将用ASCII零填充字符缓冲区,用ASCII格式覆盖一个称为父节点的基本帧指针的特殊值(对你的程序非常重要,但黑客通常不需要关心它) ,并使用0x41414141
覆盖返回地址。当main
返回时,它将启动一个shell而不是终止程序。
攻击有很多变化,但基本思路是缓冲区溢出可能会让黑客劫持程序的执行流程。
答案 2 :(得分:0)
这是我最近看到的一个很好的例子。这有点做作,但很容易理解。
我们有一个包含一些函数的调度表,由于错误,我们可以调用错误的函数。
#include <assert.h>
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SETBIT(X,Y) X|=(1ULL<<(Y))
typedef void (*method)(void);
void unsafe();
void safe();
void safe(void) {
printf("anyone can call this\n");
}
void unsafe(void) {
fprintf(stderr, "wtf, how did this get called\n");
abort();
}
static method dispatch_table[2] = {0};
typedef struct node {
union container {
float fdata;
int idata;
} data;
uint32_t func;
} Node;
int main(void) {
size_t nsize1 = sizeof(Node);
assert(nsize1 == 8);
assert(sizeof(int) == 4);
assert(sizeof(float) == 4);
assert(sizeof(double) == 8);
dispatch_table[0] = &safe;
dispatch_table[1] = &unsafe;
Node n = {.data.fdata = 10.0};
n.func = 0;
//The problem starts here
void *bad = (void*)&n;
uint64_t *badder = (uint64_t*)bad;
SETBIT(*badder, 32);// Off by one error
printf("%d\n", n.func);
dispatch_table[n.func]();
return 0;
}