nginx deny限制路径绕过

nginx deny限制路径绕过

简介

这篇文章中,分享一个技巧。解析问题来绕过路由限制

环境记录

采取docker-compose 部署

  • 部署nginx-docker

mkdir nginx_test
cd nginx_test
touch docker-compose.yml
mkdir -p nginx/conf.d
touch nginx/conf.d/default.conf
  • docker-compose.yml

version: '3'
services:
  nginx:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./html:/usr/share/nginx/html
  • nginx/conf.d default.conf

server {
    listen 80;
    server_name localhost;

    location / {
        root /usr/share/nginx/html;
        index index.html index.htm;
    }
}
  • nodejs、django会在下文说明

从nginx解析问题开始

 Nginx 是一个高性能的 HTTP 服务器和反向代理服务器,广泛应用于网站托管和负载均衡。解析问题通常指Nginx 在处理客户端请求时遇到的各种安全问题

Missing root location

  • 在 Nginx 配置中,root 指令用于指定请求静态文件的根目录。缺少 root 指令可能会导致请求无法找到正确的文件路径,root 指令通过定义提供文件的基本目录发挥着关键作用

  • 问题配置举例

server {
        root /etc/nginx;

        location /hello.txt {
                try_files $uri $uri/ =404;
                proxy_pass http://127.0.0.1:8080/;
        }
}

如上,这个nginx配置例子,/etc/nginx 被指定为根目录。此设置允许访问指定根目录中的文件,例如 /hello.txt 。但是,配置中并没有根位置 ( location / {...} ) 的配置。意味着 root 指令全局适用,允许对根路径 / 的请求访问 /etc/nginx 下的文件,攻击者可通过提供位于 /etc/nginx/nginx.conf 的 Nginx 配置文件来暴露敏感信息

攻击举例:

图片

图片

图片

Alias LFI Misconfiguration

Nginx的配置文件中,存在一类本地文件包含 (LFI) 的漏洞,如下demo配置:

location /imgs {
        alias /path/images/;
    }
  • 攻击者payload:/imgs../flag.txt会解析为/path/images/../flag.txt,从而进行目录穿越&绕过限制获取敏感文件内容

  • 修复方式

location /imgs/ {
        alias /path/images/;
    }

Unsafe path restriction 限制路径绕过

本文主要分享如下配置的绕过

location = /admin {
    deny all;
}

location = /admin/ {
    deny all;
}
  • 讲在前面:HTTP 协议在 Web 应用程序中发挥着重要作用,但是,不同框架的 HTTP 解析器的实现可能会引入细微的差异,从而导致潜在的安全漏洞。比如:nginx+uwsgi+flask 框架。也就是利用各个框架解析的差异,造成路径限制绕过

  • 说回这个配置,nginx拒绝对 /admin 的http访问,如果web用户访问此路径,则会403 error

图片

图片

trim方法

在编程中,trim 函数用于去除字符串两端的空白字符(例如空格、制表符、换行符等)是一个常用的字符串操作,对于清理和规范化输入数据非常有用。不同编程语言的 trim 函数可能名称略有不同,但功能基本相同。下面介绍常见编程语言中 trim 函数的使用示例

  • Python

Python 中的 strip 方法用于去除字符串两端的空白字符。

text = "  Hello, World!  "
trimmed_text = text.strip()
print(trimmed_text)  # 输出: "Hello, World!"
  • JavaScript

JavaScript 中的 trim 方法用于去除字符串两端的空白字符。

let text = "  Hello, World!  ";
let trimmedText = text.trim();
console.log(trimmedText);  // 输出: "Hello, World!"
  • Java

Java 中的 trim 方法用于去除字符串两端的空白字符。

public class Main {
    public static void main(String[] args) {
        String text = "  Hello, World!  ";
        String trimmedText = text.trim();
        System.out.println(trimmedText);  // 输出: "Hello, World!"
    }
}
  • C#

C# 中的 Trim 方法用于去除字符串两端的空白字符。

using System;

class Program {
    static void Main() {
        string text = "  Hello, World!  ";
        string trimmedText = text.Trim();
        Console.WriteLine(trimmedText);  // 输出: "Hello, World!"
    }
}
  • PHP

PHP 中的 trim 函数用于去除字符串两端的空白字符。

<?php
$text = "  Hello, World!  ";
$trimmed_text = trim($text);
echo $trimmed_text;  // 输出: "Hello, World!"
?>
  • Ruby

Ruby 中的 strip 方法用于去除字符串两端的空白字符。

text = "  Hello, World!  "
trimmed_text = text.strip
puts trimmed_text  # 输出: "Hello, World!"
  • Go

Go 语言中没有内置的 trim 方法,但可以使用 strings 包中的 TrimSpace 函数。

package main

import (
    "fmt"
    "strings"
)

func main() {
    text := "  Hello, World!  "
    trimmedText := strings.TrimSpace(text)
    fmt.Println(trimmedText)  // 输出: "Hello, World!"
}
  • SQL

在 SQL 中,可以使用 TRIM 函数去除字符串两端的空白字符。

SELECT TRIM('  Hello, World!  ') AS trimmed_text;

本文主要介绍python 和 nodejs作为后台此类问题绕过情况

  • 虽说每种语言都是借助trim方法做一定的格式化字符串操作,但每种语言处理逻辑又有不同,例如:python strip() 删除字符 \x85 ,而 JavaScript 则不使用 trim() 删除字符 \x85

s = "\xa0 laotie233 \x85"
print(bytes(s.encode()))
print(bytes(s.strip().encode()))

图片

s = "\xa0 laotie233 \x85"
s.trim()

图片

nodejs绕过

  • nodejs后台服务器代码

const express = require('express');
const app = express();
const port = 3000;

// 中间件示例:记录请求日志
app.use((req, res, next) => {
    console.log(`${req.method} ${req.url}`);
    next();
});

// 中间件示例:解析 JSON 请求体
app.use(express.json());


// /admin 路由
app.get('/admin', (req, res) => {
    return res.send('ADMIN');
});

// 404 错误处理
app.use((req, res, next) => {
    res.status(404).send('404 Not Found');
});

// 全局错误处理
app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).send('Something broke!');
});

app.listen(port, '0.0.0.0', () => {
    console.log(`Server is running on http://0.0.0.0:${port}`);
});

图片

  • nginx 外部访问

server {
    listen 80;
    server_name localhost;


    location = /admin {
        deny all;
    }
    location = /admin/ {
        deny all;
    }

    # location / {
    #     root /usr/share/nginx/html;
    #     index index.html index.htm;
    # }
    location / {
        proxy_pass http://192.168.56.130:3000;
    }
}

图片

  • 绕过姿势:按照 trim() 逻辑,Node.js 会去除路径名中的字符\xA0 ,但 Nginx 认为它们作为 URL 的一部分

  • 绕过poc如下

图片

  • 成功绕过

图片

python类后台绕过

  • 这里拿django举例

图片

  • 代码示例

图片

  • nginx配置同理

server {
    listen 80;
    server_name localhost;


    location = /admin {
        deny all;
    }
    location = /admin/ {
        deny all;
    }

    # location / {
    #     root /usr/share/nginx/html;
    #     index index.html index.htm;
    # }
    location / {
        proxy_pass http://192.168.56.130:8000;
    }
}

图片

  • 绕过姿势

图片

图片

修复方式

location ~* ^/admin {
    deny all;
}
  • 这块的代码~ 表达式正则匹配路径名 /admin ,不过有损的是,如果用户向 /admin_test 发送请求,该请求也将被403

server {
    listen 80;
    server_name localhost;


    location ~* ^/admin {
        deny all;
    }
    location ~* ^/admin/ {
        deny all;
    }

    # location / {
    #     root /usr/share/nginx/html;
    #     index index.html index.htm;
    # }
    location / {
        proxy_pass http://192.168.56.130:8000;
    }
}

图片

写在最后

本文介绍的只是一些初步的相关知识,后续应该会继续写较为深入的版本

参考

https://github.com/detectify/vulnerable-nginx

https://rafa.hashnode.dev/exploiting-http-parsers-inconsistencies

更多网络安全优质免费学习资料与干货教程加v

送渗透工具、技术文档、书籍,面试题、视频(基础到进阶。环境搭建,HTML,PHP,MySQL基础学习,信息收集,SQL注入,XSS,CSRF,暴力破解等等)、应急响应笔记、学习路线。

申明:本账号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,

所有渗透都需获取授权,违者后果自行承担,与本号及作者无关,请谨记守法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值