调试httpd通过fork+execute调用的cgibin程序

VSole2023-04-18 09:41:26

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

① 设置环境变量,然后直接调试cgibin。

② 了解httpd是如何调用cgibin的,然后通过设置gdb调试子进程、catch exec,调试cgibin。

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

一、httpd调用CGI上下文说明

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

int __fastcall process_cgi(_DWORD *a1){......  v77 = spawn(*filename, filename, argv, v79, v75, v9, v8, a1 + 992);......}

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

__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;}

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

二、调试原理

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

set follow-fork-mode child

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

set detach-on-fork off # 默认是on

上面两条命令结合起来就实现了同时调试父进程和子进程,那么这个时候也还有一个问题:在子进程中,execute调用cgibin是作为一个函数来实现的,单步步过该函数达不到调试目的,步进该函数更加容易陷入到函数的细节实现中。

对于调试cgibin,可以通过catch exec命令,来捕获执行新进程的事件。当进程使用execute重新执行一个程序时,gdb会中断程序的运行,到ld加载器start函数中。除此之外,还可以使用例如catch exec /bin/ls来指定需要捕获的具体进程加载程序事件。

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

① 在fork的时候,执行gdb命令:set follow-fork-mode child,使得gdb开始调试子进程。

② 同时,可以执行gdb命令:set detach-on-fork off,让在调试子进程的同时,父进程挂起。这样执行完子进程也可以返回到父进程中。

③ 执行gdb命令:catch exec,捕获子进程通过execute加载执行新程序的事件。

三、调试过程

现在回归到具体的调试过程中。

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

pwndbg> info inferiors  Num  Description       Executable* 1    process 1444      /home/utest/app/FirmAE/firmwares/_DIR850L_FW115KRb07.bin.extracted/squashfs-root/sbin/httpd

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

  0x409d2c 56>     move   $s3, $a2► 0x409d30 60>     jalr   $t9                           <fork>

此时,执行gdb命令:

set follow-fork-mode childset detach-on-fork off

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

pwndbg> info inferiors  Num  Description       Executable  1    process 1444      /home/utest/app/FirmAE/firmwares/_DIR850L_FW115KRb07.bin.extracted/squashfs-root/sbin/httpd* 2    process 31740     /home/utest/app/FirmAE/firmwares/_DIR850L_FW115KRb07.bin.extracted/squashfs-root/sbin/httpd

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

  0x409e68     addiu  $a0, $zero, 5  0x409e6c     lw     $t9, -0x7d7c($gp)  0x409e70     move   $a0, $s2► 0x409e74     jalr   $t9                                  path: 0x7ed330 ◂— '/htdocs/web/session.cgi'       argv: 0x4280f0 —▸ 0x7ed330 ◂— '/htdocs/web/session.cgi'       envp: 0x0

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

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

pwndbg> stack 3000:0000│ fp sp 0x7fe2b4a0 ◂— 0x101:0004│       0x7fe2b4a4 —▸ 0x7fe2bd23 ◂— '/htdocs/web/session.cgi'02:0008│       0x7fe2b4a8 ◂— 0x003:000c│       0x7fe2b4ac —▸ 0x7fe2bd3b ◂— 'HTTP_HOST=192.168.0.1'04:0010│       0x7fe2b4b0 —▸ 0x7fe2bd51 ◂— 'HTTP_USER_AGENT=Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0'05:0014│       0x7fe2b4b4 —▸ 0x7fe2bdb0 ◂— 'HTTP_ACCEPT=*/*'06:0018│       0x7fe2b4b8 —▸ 0x7fe2bdc0 ◂— 'HTTP_ACCEPT_LANGUAGE=en-US,en;q=0.5'07:001c│       0x7fe2b4bc —▸ 0x7fe2bde4 ◂— 'HTTP_ACCEPT_ENCODING=gzip, deflate'08:0020│       0x7fe2b4c0 —▸ 0x7fe2be07 ◂— 'HTTP_ORIGIN=http://192.168.0.1'09:0024│       0x7fe2b4c4 —▸ 0x7fe2be26 ◂— 'HTTP_REFERER=http://192.168.0.1/index.php'0a:0028│       0x7fe2b4c8 —▸ 0x7fe2be50 ◂— 'HTTP_COOKIE=uid=bjtsPYnEJz'0b:002c│       0x7fe2b4cc —▸ 0x7fe2be6b ◂— 'GATEWAY_INTERFACE=CGI/1.1'0c:0030│       0x7fe2b4d0 —▸ 0x7fe2be85 ◂— 'CONTENT_LENGTH=31'0d:0034│       0x7fe2b4d4 —▸ 0x7fe2be97 ◂— 'CONTENT_TYPE=application/x-www-form-urlencoded'0e:0038│       0x7fe2b4d8 —▸ 0x7fe2bec6 ◂— 'SCRIPT_FILENAME=/htdocs/web/session.cgi'0f:003c│       0x7fe2b4dc —▸ 0x7fe2beee ◂— 'REQUEST_URI=/session.cgi'10:0040│       0x7fe2b4e0 —▸ 0x7fe2bf07 ◂— 'REMOTE_ADDR=192.168.0.2'11:0044│       0x7fe2b4e4 —▸ 0x7fe2bf1f ◂— 'REMOTE_PORT=45050'12:0048│       0x7fe2b4e8 —▸ 0x7fe2bf31 ◂— 'REQUEST_METHOD=POST'13:004c│       0x7fe2b4ec —▸ 0x7fe2bf45 ◂— 'SCRIPT_NAME=/session.cgi'14:0050│       0x7fe2b4f0 —▸ 0x7fe2bf5e ◂— 'SERVER_NAME=192.168.0.1'15:0054│       0x7fe2b4f4 —▸ 0x7fe2bf76 ◂— 'SERVER_ADDR=192.168.0.1'16:0058│       0x7fe2b4f8 —▸ 0x7fe2bf8e ◂— 'SERVER_PORT=80'17:005c│       0x7fe2b4fc —▸ 0x7fe2bf9d ◂— 'SERVER_SOFTWARE=Mathopd/1.6b9'18:0060│       0x7fe2b500 —▸ 0x7fe2bfbb ◂— 'SERVER_ID=LAN-1'19:0064│       0x7fe2b504 —▸ 0x7fe2bfcb ◂— 'SERVER_PROTOCOL=HTTP/1.1'1a:0068│       0x7fe2b508 ◂— 0x0

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

四、小结

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

① 执行到fork函数,执行gdb命令:set follow-fork-mode child,使得gdb调试到子进程中。

② 执行gdb命令:set detach-on-fork off,使得gdb在调试子进程的时候挂起父进程,这样也方便并行调试。

③ 执行到execute函数处,执行gdb命令:catch exec,捕获子进程通过execute调用执行cgi的事件。

forkcgi
本作品采用《CC 协议》,转载必须注明作者和本文链接
在调试dlink的httpd时,漏洞可能发生在httpd通过fork+execute调用的cgibin中,其中httpd解析网络请求中的字段,并且以环境变量的形式传递给cgibin进行处理。② 了解httpd是如何调用cgibin的,然后通过设置gdb调试子进程、catch exec,调试cgibin。在dir 850l的固件中,几乎所有的cgi都是通过链接的形式到程序cgibin,cgibin根据请求的不同来采用不同的接口(函数)进行处理。
cgiHandler函数中,将用户的HTTP请求参数作为环境变量,通过诸如LD_PRELOAD即可劫持进程的动态链接库,实现远程代码执行。代码首先拼接出用户请求的cgi完整路径并赋予cgiPath,然后检查此文件是否存在以及是否为可执行文件。随后代码将cgiPath、envp、stdIn与stdOut作为参数传入launchCgi函数中。
关于MIPS架构的寄存器及指令集请自行查阅资料,这里就不多作介绍了。,则也会在prologue处保存下来,并在epilogue处取出。流水线指令集相关特性MIPS架构存在“流水线效应”,简单来说,就是本应该顺序执行的几条命令却同时执行了,其还存在缓存不一致性(cache
简介闲来没事,做一个自动化recon的工具,简化操作流程。可以对域名或ip进行whois查询,dns记录查询,ip端口扫描,http屏幕快照。最终生成markdown格式的报告,可使用Obsidian或vscode的markdown notes插件打开,借助双向链接功能,方便跳转查看。依赖的程序amass子域名查找 下载地址masscan扫描全部端口,发现开放端口。nmap服务扫描,针对masscan发现的端口进行具体的服务扫描。
nmap -PN -sS -sV --script=vulscan –script-args vulscancorrelation=1 -p80 target. nmap -PN -sS -sV --script=all –script-args vulscancorrelation=1 target. NetCat,它的实际可运行的名字叫nc,应该早很就被提供,就象另一个没有公开但是标准的Unix工具。
主要的环境准备参见上一篇文章,这里介绍本篇文章中会用的的模拟工具以及另一个静态分析工具。
微服务架构中,API网关充当着非常重要的一环,它不仅要负责外部所有的流量接入,同时还要在网关入口处根据不同类型请求提供流量控制、日志收集、性能分析、速率限制、熔断、重试等细粒度的控制行为。API网关一方面将外部访问与微服务进行了隔离,保障了后台微服务的安全,另一方面也节省了后端服务的开发成本,有益于进行应用层面的扩展。与此同时,API网关也具备解决外界访问带来的安全问题,如TLS加密、数据丢失、跨
PHP disable_functions disable_functions是php.ini中的一个设置选项。相当一个黑名单,可以用来设置PHP环境禁止使用某些函数,通常是网站管理员为了安全起见,用来禁用某些危险的命令执行函数等。
disable_functions是php.ini中的一个设置选项。相当一个黑名单,可以用来设置PHP环境禁止使用某些函数,通常是网站管理员为了安全起见,用来禁用某些危险的命令执行函数等。
相当一个黑名单,可以用来设置PHP环境禁止使用某些函数,通常是网站管理员为了安全起见,用来禁用某些危险的命令执行函数等。高ini_restore()可用于恢复 PHP 环境配置参数到其初始值
VSole
网络安全专家