首页
社区
课程
招聘
[原创] 调试httpd通过fork+execute调用的cgibin程序
发表于: 2023-3-14 10:55 13025

[原创] 调试httpd通过fork+execute调用的cgibin程序

2023-3-14 10:55
13025

在调试dlink的httpd时,漏洞可能发生在httpd通过fork+execute调用的cgibin中,其中httpd解析网络请求中的字段,并且以环境变量的形式传递给cgibin进行处理。那么要调试cgibin就有两种方式:

方法1的优点是简单直接,缺点是需要了解httpd是如何处理、传递数据到环境变量,以免使用了实际上通不过httpd校验的环境变量;方法2的优点是直观,但是需要熟练掌握gdb调试子进程相关的知识。

和许多典型的httpdserver一样,传入到函数process_cgi中的参数a1是httpd所定义的数据结构,其中包含了一个网络请求的数据集合,例如请求方式REQUEST_METHOD、URI、SESSION等等。a1传递到process_cgi函数中进行处理,获取到需要调用的cgi,以及将需要处理的数据转化成环境变量集合到cgi中。在dir 850l的固件中,几乎所有的cgi都是通过链接的形式到程序cgibin,cgibin根据请求的不同来采用不同的接口(函数)进行处理。

spawn函数则是一个典型的封装了fork+execve的函数,通过fork函数创建子进程,设置子进程的进程组、资源限制、重定向输入输出等。

那么到此,回想之前的标题:如何调试httpd通过execute调用的cgibin,该问题就可以抽象为:如何调试子进程中通过execute调用的可执行文件。

默认情况下,gdb在调试多进程的时候,只会追踪父进程,例如执行完fork函数,fork的返回值是子进程的pid,gdb中实际上在调试的是父进程。如果要调试子进程,则需要在gdb中使用如下的命令:

上面命令解决了让gdb调试到子进程,但是有时候还需要同时调试父进程和子进程,如果仅仅是gdb在子进程中,父进程依旧会正常运行。那么,就可以使用如下的命令,使得在调试子进程的时候,父进程也暂停处于挂起的状态:

上面两条命令结合起来就实现了同时调试父进程和子进程,那么这个时候也还有一个问题:在子进程中,execute调用cgibin是作为一个函数来实现的,单步步过该函数达不到调试目的,步进该函数更加容易陷入到函数的细节实现中。
对于调试cgibin,可以通过catch exec命令,来捕获执行新进程的事件。当进程使用execute重新执行一个程序时,gdb会中断程序的运行,到ld加载器start函数中。除此之外,还可以使用例如catch exec /bin/ls来指定需要捕获的具体进程加载程序事件。

综上所述,调试通过fork+execute调用的程序,可以使用如下步骤:

现在回归到具体的调试过程中。
当httpd执行到调用cgi的spawn函数中,此时准备执行fork函数,可以看到只有httpd一个进程,也就是父进程PID=1444:

第一步需要保证在执行到spawn的时候,fork之后是进入了子进程,

此时,执行gdb命令:


再查看进程信息,也可以确认此时gdb处于子进程中。此时也可以通过inferiors Num切换到父进程。

继续在子进程中执行到execute函数,可以看到此时正准备加载执行/htdocs/web/session.cgi,该cgi实际上是一个链接到cgibin。

那么此时就应该执行gdb命令:catch exec,用来捕获子进程加载cgi的事件:

这个时候可以查看栈,来看httpd传递给cgi哪些环境变量(也就是需要处理的数据):

以前做毕设的时候,要实现对cgi的模糊测试,就是将AFL生产的数据通过设计的数据结构转换成环境变量到cgi 中去执行。

如何调试httpd使用fork+execute执行的cgibin,本质上可以抽象为:如何调试通过fork+execute调用的程序,办法是:

int __fastcall process_cgi(_DWORD *a1)
{
......
  v77 = spawn(*filename, filename, argv, v79, v75, v9, v8, a1 + 992);
......
}
int __fastcall process_cgi(_DWORD *a1)
{
......
  v77 = spawn(*filename, filename, argv, v79, v75, v9, v8, a1 + 992);
......
}
__pid_t __fastcall spawn(const char *filename, char *const *argv, char *const *envp, int a4, int a5, int a6, int a7, char *path)
{
  __pid_t result; // $v0
  int v13; // $a0
  int v14[4]; // [sp+18h] [-18h] BYREF
  __pid_t v15; // [sp+28h] [-8h]
 
  result = fork();  // 创建子进程,此时的子进程依旧是httpd的程序镜像
  if ( result == -1 )
  {
    v15 = -1;
    lerror("spawn: failed to create child process");
    goto LABEL_6;
  }
  if ( !result )
  {
    setpgid(0, 0);  // 改变子进程的进程组
    sub_409C9C(13, 0);
    if ( coredir )
    {
      v14[3] = 0;
      v14[2] = 0;
      v14[1] = 0;
      v14[0] = 0;
      setrlimit64(4, v14);  // 设置资源限制
    }
    dup2(a4, 0);  // 复制文件描述符:标准输入和标准输出
    dup2(a4, 1);
    if ( a5 != -1 )
      dup2(a5, 2);
    if ( chdir(path) == -1 )
    {
      v13 = 5;
    }
    else
    {
      execve(filename, argv, envp);  // 执行cgibin,将cgibin加载并替换掉子进程的httpd
      v13 = 6;
    }
    exit(v13);
  }
  ++dword_42350C;
  if ( debug )
  {
    v15 = result;
    log_d("child process %d created", result);
LABEL_6:
    result = v15;
  }
  return result;
}
__pid_t __fastcall spawn(const char *filename, char *const *argv, char *const *envp, int a4, int a5, int a6, int a7, char *path)
{
  __pid_t result; // $v0
  int v13; // $a0
  int v14[4]; // [sp+18h] [-18h] BYREF
  __pid_t v15; // [sp+28h] [-8h]
 
  result = fork();  // 创建子进程,此时的子进程依旧是httpd的程序镜像
  if ( result == -1 )
  {
    v15 = -1;
    lerror("spawn: failed to create child process");
    goto LABEL_6;
  }
  if ( !result )
  {
    setpgid(0, 0);  // 改变子进程的进程组
    sub_409C9C(13, 0);
    if ( coredir )
    {
      v14[3] = 0;
      v14[2] = 0;
      v14[1] = 0;
      v14[0] = 0;
      setrlimit64(4, v14);  // 设置资源限制
    }
    dup2(a4, 0);  // 复制文件描述符:标准输入和标准输出
    dup2(a4, 1);
    if ( a5 != -1 )
      dup2(a5, 2);
    if ( chdir(path) == -1 )
    {
      v13 = 5;
    }
    else
    {
      execve(filename, argv, envp);  // 执行cgibin,将cgibin加载并替换掉子进程的httpd
      v13 = 6;
    }
    exit(v13);
  }
  ++dword_42350C;
  if ( debug )
  {
    v15 = result;
    log_d("child process %d created", result);
LABEL_6:
    result = v15;
  }
  return result;
}
set follow-fork-mode child

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 7
支持
分享
最新回复 (2)
雪    币: 1452
活跃值: (1441)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享,学到了
2023-12-28 13:31
0
雪    币: 124
活跃值: (374)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
谢谢大神分享!
2023-12-28 20:16
0
游客
登录 | 注册 方可回帖
返回
//