fix UnTarFile #2261
已开启
dongtingchi创建于 8月22日
3 个文件变更+72-1
@@ -1355,6 +1355,11 @@ bool BackupExtExtension::CheckIsSplitTarList(const std::vector<const ExtManageIn
1355 // reset untar object1355 // reset untar object
1356 unSplitTar->Reset();1356 unSplitTar->Reset();
1357 1357 
1358+ if (!BDir::IsFilePathValid(item.hashName) || !BDir::IsFilePathValid(item.fileName)) {
1359+ HILOGE("Check split tarfile path err, path is forbidden");
1360+ break;
1361+ }
1362+ 
1358 HILOGI("check if split tar, filename: %{public}s, path: %{public}s", GetAnonyPath(item.hashName).c_str(),1363 HILOGI("check if split tar, filename: %{public}s, path: %{public}s", GetAnonyPath(item.hashName).c_str(),
1359 GetAnonyPath(item.fileName).c_str());1364 GetAnonyPath(item.fileName).c_str());
1360 // check if tar is split1365 // check if tar is split
@@ -1401,6 +1406,19 @@ ErrCode BackupExtExtension::RestoreSplitTarListForSpecialCloneCloud(const std::v
1401 // reset untar object1406 // reset untar object
1402 unSplitTar->Reset();1407 unSplitTar->Reset();
1403 1408 
1409+ if (!BDir::IsFilePathValid(item.hashName)) {
1410+ HILOGE("Check split tarfile hash path : %{public}s err, path is forbidden",
1411+ GetAnonyPath(item.hashName).c_str());
1412+ errCode = ERR_INVALID_VALUE;
1413+ break;
1414+ }
1415+ if (!BDir::IsFilePathValid(item.fileName)) {
1416+ HILOGE("Check split tarfile path : %{public}s err, path is forbidden",
1417+ GetAnonyPath(item.fileName).c_str());
1418+ errCode = ERR_INVALID_VALUE;
1419+ break;
1420+ }
1421+ 
1404 // do untar with root path1422 // do untar with root path
1405 int ret = unSplitTar->UnSplitTar(item.hashName, item.fileName);1423 int ret = unSplitTar->UnSplitTar(item.hashName, item.fileName);
1406 if (isDebug_ && ret != ERR_OK) {1424 if (isDebug_ && ret != ERR_OK) {
@@ -107,6 +107,10 @@ static int GenRealPath(const char *rootPath, const char *pathName, char* &realPa
107 return ERR_PARAM;107 return ERR_PARAM;
108 }108 }
109 realPath[allLen] = '\0';109 realPath[allLen] = '\0';
110+ if (!OHOS::FileManagement::Backup::BDir::IsFilePathValid(std::string(realPath))) {
111+ LOGE("GenRealPath path contains forbidden traversal");
112+ return ERR_PARAM;
113+ }
110 return 0;114 return 0;
111}115}
112 116 
@@ -415,6 +419,10 @@ void UnTarFile::HandleGnuLongLink(ParseTarPath *parseTarPath, bool &isSkip, TarF
415 if (nameLen != fread(parseTarPath->longLink, sizeof(char), nameLen, FilePtr)) {419 if (nameLen != fread(parseTarPath->longLink, sizeof(char), nameLen, FilePtr)) {
416 free(parseTarPath->longLink);420 free(parseTarPath->longLink);
417 parseTarPath->longLink = nullptr;421 parseTarPath->longLink = nullptr;
422+ } else if (!OHOS::FileManagement::Backup::BDir::IsFilePathValid(std::string(parseTarPath->longLink))) {
423+ LOGE("HandleGnuLongLink longLink contains forbidden traversal");
424+ free(parseTarPath->longLink);
425+ parseTarPath->longLink = nullptr;
418 }426 }
419 }427 }
420 428 
@@ -438,6 +446,10 @@ void UnTarFile::HandleGnuLongName(ParseTarPath *parseTarPath, bool &isSkip, TarF
438 if (nameLen != fread(parseTarPath->longName, sizeof(char), nameLen, FilePtr)) {446 if (nameLen != fread(parseTarPath->longName, sizeof(char), nameLen, FilePtr)) {
439 free(parseTarPath->longName);447 free(parseTarPath->longName);
440 parseTarPath->longName = nullptr;448 parseTarPath->longName = nullptr;
449+ } else if (!OHOS::FileManagement::Backup::BDir::IsFilePathValid(std::string(parseTarPath->longName))) {
450+ LOGE("HandleGnuLongName longName contains forbidden traversal");
451+ free(parseTarPath->longName);
452+ parseTarPath->longName = nullptr;
441 }453 }
442 }454 }
443 455 
@@ -591,7 +603,11 @@ int UnTarFile::ParseTarFile(const char *rootPath, EParseType type)
591 parseTarPath.realName = parseTarPath.longName;603 parseTarPath.realName = parseTarPath.longName;
592 }604 }
593 605 
594- GenRealPath(rootPath, parseTarPath.realName, parseTarPath.fullPath);606+ if (GenRealPath(rootPath, parseTarPath.realName, parseTarPath.fullPath) != 0) {
607+ LOGE("GenRealPath failed, invalid path in tar entry");
608+ FreePointer(&parseTarPath);
609+ return ERR_PARAM;
610+ }
595 parseTarPath.realLink = tarHeader->linkname;611 parseTarPath.realLink = tarHeader->linkname;
zhangchuang
zhangchuangzhangchuang16 天前

【安全检视意见|自动扫描】【中危】外部输入校验(路径穿越/符号链接) 本 PR 为 tar 解包新增了 longName/longLink/拼接后 realPath 的 IsFilePathValid 校验,但 SYMTYPE 条目的普通 linkname 字段(tarHeader->linkname,≤100 字节)仍未经校验即赋给 realLink,随后在 ProcessTarBlock 的 SYMTYPE 分支经 CreateSoftlink(realLink, fullPath) 直接创建符号链接。恶意或损坏的备份 tar 可携带指向沙箱外(绝对路径或含 ../)的 linkname:先建立越界软链,再由后续 REGTYPE 条目经 fopen 跟随该软链写文件,构成经典 tar symlink 攻击,绕过本次新增的路径校验(该校验只检查路径字符串,不解析已存在的软链)。 建议:对 realLink 同样调用 BDir::IsFilePathValid 并拒绝绝对路径目标,或在创建软链前用 realpath 确认目标仍位于 rootPath 之内;目标非法时跳过该条目(fail-closed)。

由 openharmony-security-codescan 自动扫描生成,仅供参考;若为误报请回复说明,感谢。

likedislike
596 if (parseTarPath.longLink != nullptr) {612 if (parseTarPath.longLink != nullptr) {
597 parseTarPath.realLink = parseTarPath.longLink;613 parseTarPath.realLink = parseTarPath.longLink;
@@ -366,4 +366,41 @@ HWTEST_F(InstalldUnTarFileTest, Installd_Un_Tar_File_ProcessTarBlock_0100, testi
366 }366 }
367 GTEST_LOG_(INFO) << "InstalldUnTarFileTest-end Installd_Un_Tar_File_ProcessTarBlock_0100";367 GTEST_LOG_(INFO) << "InstalldUnTarFileTest-end Installd_Un_Tar_File_ProcessTarBlock_0100";
368}368}
369+ 
370+/**
371+ * @tc.number: Installd_Un_Tar_File_GenRealPath_0100
372+ * @tc.name: Installd_Un_Tar_File_GenRealPath_0100
373+ * @tc.desc: test ParseTarFile rejects tar entry with path traversal
374+ * @tc.size: MEDIUM
375+ * @tc.type: FUNC
376+ * @tc.level Level 1
377+ * @tc.require: IC15LE
378+ */
379+HWTEST_F(InstalldUnTarFileTest, Installd_Un_Tar_File_GenRealPath_0100, testing::ext::TestSize.Level1)
380+{
381+ GTEST_LOG_(INFO) << "InstalldUnTarFileTest-begin Installd_Un_Tar_File_GenRealPath_0100";
382+ try {
383+ const TestManager tm("Installd_Un_Tar_File_GenRealPath_0100");
384+ string rootPath = tm.GetRootDirCurTest();
385+ string testDir = rootPath + "traversal";
386+ mkdir(testDir.data(), S_IRWXU);
387+ string file = testDir + "/1.txt";
388+ SaveStringToFile(file, "hello");
389+ string tarFile = rootPath + "testTraversal.tar";
390+ 
391+ string cmd = "cd " + rootPath + " && tar -cvf testTraversal.tar traversal/../../../etc/passwd 2>/dev/null";
392+ system(cmd.c_str());
393+ installd::UnTarFile unTarFile(nullptr);
394+ int ret = unTarFile.UnSplitTar(tarFile, rootPath);
395+ EXPECT_NE(ret, 0);
396+ 
397+ // cleanup
398+ std::filesystem::remove_all(testDir);
399+ std::filesystem::remove(tarFile);
400+ } catch (...) {
401+ EXPECT_TRUE(false);
402+ GTEST_LOG_(INFO) << "InstalldUnTarFileTest-an exception occurred by GenRealPath.";
403+ }
404+ GTEST_LOG_(INFO) << "InstalldUnTarFileTest-end Installd_Un_Tar_File_GenRealPath_0100";
405+}
369} // namespace OHOS::FileManagement::Backup406} // namespace OHOS::FileManagement::Backup