黑客如何利用数组或指针

时间:2016-04-19 03:51:52

标签: c security

我刚毕业,希望尽快找到工作,但我对代码安全性有疑问。

在Java中没有超出范围的数组,并且没有指示Java比C更安全的指针。在C中,这些超出范围的数组可能导致堆栈损坏或缓冲区溢出和悬空指针。在一次采访中,我可能会被问到“黑客如何利用这些优势?”

我该如何回答这个问题?

欢迎举例。

3 个答案:

答案 0 :(得分:1)

好吧,你可以专门谈谈Heartbleed(那里有大量的文档)。

基本上,有一个api调用可以通过网络进行,你的代码会请求通信保持活动状态,通常看起来像:

You: send me "potato", which is 6 chars.
Response: potato

但是,没有检查所请求的文本,并且发送的文本的长度是对齐的,所以你可以这样做:

You: send me "potato", which is 512 chars.
Response potato&&&&#8388325099#((#(#)%#(((#%)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;
}