0x1 cgi

  • cgi可以理解为最原始的动态web实现方式
  • 本质上是通过可执行文件(bash,python,二进制文件等)方式来实现来生成动态web
  • 本章本着最小化的原则利用python实现cgi服务器,使用python实现基本功能

0x2 python.cgi

  • 使用python3内置的方法开启cgi服务器(本身也提供web服务)
python -m http.server --cgi 80
  • 默认只会对/cgi-bin/目录下的文件启动cgi服务
  • 需要注意的是后缀名没有什么关系
  • 访问时一定要写cgi的全url,直接访问cgi目录会403
  • 访问http://127.0.0.1/cgi-bin/test.cgi
  • test.cgi内容如下
#!/usr/bin/python3
print("Content-Type: text/html\n")
print("""
<!doctype html>
<title>Hello</title>
<h2>hello world</h2>
"""
)
  • 使用os.environ[key]来获取header/环境变量信息,参考show_env.cgi

0x3 自定义cgi目录

  • 可以对源码http.server中的字段cgi_directories 来控制cgi目录
  • 修改的http.server版本一定要和当前python匹配
  • /cus添加到cgi解析目录
cgi_directories = ['/cgi-bin', '/htbin','/cus']
  • 将所有文件都当作cgi解析
cgi_directories = ['/cgi-bin', '/htbin','/']

0x4 GET/POST

  • 如果以key/value+get/post的方式来传递数据,可以通过FieldStorage来获取
  • get_post.cgi实现如下
#!/usr/bin/python
# -*- coding: UTF-8 -*-

# filename:test.py

# CGI处理模块
import cgi, cgitb 

# 创建 FieldStorage 的实例化
form = cgi.FieldStorage() 

# 获取GET/post数据
site_name = form.getvalue('name')
site_pwd  = form.getvalue('pwd')

print("Content-type:text/html\n")
print("name:%s\r\npwd:%s" % (site_name, site_pwd))
  • 通过get访问:curl "http://127.0.0.1/get_post.cgi?name=testname&pwd=password"

image-20211126104537658.png

  • 通过post访问:curl -d "name=testname&pwd=password" http://127.0.0.1/get_post.cgi

image-20211126104615350.png

0x5 RAW_POST

  • 某些时候数据是以raw的方式传递的
  • 这个时候需要cgi能够接收到原始的post数据
  • 服务端实现如下get_raw_post.cgi
#!/usr/bin/python
# -*- coding: UTF-8 -*-

# filename:test.py

# CGI处理模块
import os,sys
import cgi, cgitb 
import json


# http声明
print("Content-type:text/html\n")


# 获取原始data数据
content_len = int(os.environ["CONTENT_LENGTH"])
raw_data = sys.stdin.read(content_len)

print("======RAW=============")
print(raw_data)
print("======RAW=============")

# 加载json
print("======GET PHONE=============")
json_read = json.loads(raw_data)
print("phone is:"+json_read["phone"])
  • 使用curl模拟发送原始的json文件
curl \
-H "Accept: application/json" \
-H "Content-type: application/json" \
-X POST -d \
'{"phone": "12345678","password": "pwd"}' \
http://127.0.0.1/get_raw_post.cgi
  • 可以解析出json中的字段

image-20211126112558554.png

0x6 HTTPBasicAuth

  • python内置的httpserver过滤掉了自定义非200 httpstatus,因此不能通过response通知客户端登陆
  • 此外即使想要想实现接收HTTPBasicAuth也需要对httpserver稍加修改
  • 本案例以httpserver3.8为例,罗列几个核心修改点
  • 1275行处,修改默认端口为80

      parser.add_argument('port', action='store',
                          default=80, type=int,
                          nargs='?',
                          help='Specify alternate port [default: 80]')
  • 1279行处,永远开启cgi

      if 1:
          handler_class = CGIHTTPRequestHandler
      else:
          handler_class = partial(SimpleHTTPRequestHandler,
                                  directory=args.directory)
  • 1025行处,对所有目录开启cgi解析

      cgi_directories = ['/cgi-bin', '/htbin','/']
  • 1115行处增加HTTPBasicAuth中的密码传递功能

                              env['REMOTE_USER'] = authorization[0]
                              env['REMOTE_PWD'] = authorization[1]
  • 最终的的 server.py 在这里
  • cgi中实现HTTPBasicAuth的逻辑代码如下
#!/usr/bin/python
# -*- coding: UTF-8 -*-

# filename:test.py

# CGI处理模块
import os,sys
import cgi, cgitb 
import json

def authenticate():
    # 检查是否有认证
    if "REMOTE_USER" in os.environ and "REMOTE_PWD" in os.environ:
        name=os.environ["REMOTE_USER"]
        pwd=os.environ["REMOTE_PWD"]
        # 账号密码匹配
        if name=="test" and pwd=="123456":
            print("[+] great")
        else:
        # 输出错误的账号密码
            print("[+] wrong account")
            print("[!] name:"+name)
            print("[!] pwd:"+pwd)
    else:
        print("[+] 401:need auth")
    return 0


# http声明
print("Content-type:text/html\n")
authenticate()
  • 使用curl作为测试代码
curl --basic  http://127.0.0.1/http_auth.cgi
curl --basic -u test:123456 http://127.0.0.1/http_auth.cgi
curl --basic -u user:pass http://127.0.0.1/http_auth.cgi

image-20211126170537177.png

Last modification:November 26, 2021
如果觉得我的文章对你有用,请随意赞赏