[ 嵌入式通信技术 ] [ 2 ] CGI

CGI 程序运行原理

CGI 是 Web 服务器运行时外部程序的规范,按照 CGI 编写的程序可扩展服务器功能。CGI 程序运行在 HTTP 服务器中,为 HTTP 服务器与其他第三方应用程序之间提供数据交换手段。程序的大部分采用 C 语言编写,通过系统函数调用来控制设备,以此达到灵活高效又能控制设备的目的。

image.png

如上图所示,Web 服务器发送 HTTP 请求 Request 到 CGI 进程,CGI 进程把 HTTP 请求的 Header 设置为进程的环境变量。

pass.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char* getcgidata(FILE* fp, char* requestmethod);

int main() {
  char* input;
  char* req_method;
  char name[64];
  char pass[64];
  int i = 0;
  int j = 0;

  //     printf("Content-type: text/plain; charset=iso-8859-1\n\n");
  printf("Content-type: text/html\n\n");
  printf("The following is query reuslt:<br><br>");

  req_method = getenv("REQUEST_METHOD");
  input = getcgidata(stdin, req_method);

  // 我们获取的input字符串可能像如下的形式
  // Username="admin"&Password="aaaaa"
  // 其中"Username="和"&Password="都是固定的
  // 而"admin"和"aaaaa"都是变化的,也是我们要获取的

  // 前面9个字符是UserName=
  // 在"UserName="和"&"之间的是我们要取出来的用户名
  for (i = 9; i < (int)strlen(input); i++) {
    if (input[i] == '&') {
      name[j] = '\0';
      break;
    }
    name[j++] = input[i];
  }

  // 前面9个字符 + "&Password="10个字符 + Username的字符数
  // 是我们不要的,故省略掉,不拷贝
  for (i = 19 + strlen(name), j = 0; i < (int)strlen(input); i++) {
    pass[j++] = input[i];
  }
  pass[j] = '\0';

  printf("Your Username is %s<br>Your Password is %s<br> \n", name, pass);

  return 0;
}

char* getcgidata(FILE* fp, char* requestmethod) {
  char* input;
  int len;
  int size = 1024;
  int i = 0;

  if (!strcmp(requestmethod, "GET")) {
    input = getenv("QUERY_STRING");
    return input;
  } else if (!strcmp(requestmethod, "POST")) {
    len = atoi(getenv("CONTENT_LENGTH"));
    input = (char*)malloc(sizeof(char) * (size + 1));

    if (len == 0) {
      input[0] = '\0';
      return input;
    }

    while (1) {
      input[i] = (char)fgetc(fp);
      if (i == size) {
        input[i + 1] = '\0';
        return input;
      }

      --len;
      if (feof(fp) || (!(len))) {
        i++;
        input[i] = '\0';
        return input;
      }
      i++;
    }
  }
  return NULL;
}

HTTP 请求的 Body 正文设置为进程的标准输入,CGI 进程的标准输出作为 HTTP 数据包响应 Web 服务器的请求。

test.c

#include <stdio.h>  
int main()  
{  
  printf("Content-type:text/html\n\n"); //这句一定要加上  
  printf("<html><body>");  
  printf("<font style=\"color:red; font-size:30px;\">Hello, CGI!</font><br/>");  
  printf("<a href=\"/index.html\">return index.html</a>");  
  printf("</body></html>");  
  return 0;  
}  

环境变量是指 CGI 程序定义的一组环境变量,通过环境变量可以传递数据。常用 CGI 程序环境变量如下表所示:

image.png

CGI 程序通过函数 environ 可以获取所有的环境变量及其值,通过函数 getenv 可以获得指定环境变量的对应值。

QUERY_STRING:获取 CGI 程序 URL 中 '?' 之后的数据。

REQUEST_METHOD:判断 CGI 请求所采用的请求方法,以决定是通过 stdin 还是通过环境变量 QUERY_STRING 获取客户端传输数据。


通过如下命令,可以将 c 文件,生成 cgi 程序:

gcc -static -o test.cgi test.c

参考

[1]常天佑,朱涛,宋健,等.面向嵌入式CGI的命令注入漏洞挖掘研究[J].计算机时代,2023,(04):1-4+10.DOI:10.16644/j.cnki.cn33-1094/tp.2023.04.001.