* fix: CVE-2022-48579 in clamav

* convert std::wstring to wchar*

* update changelog
This commit is contained in:
Tobias Brick 2023-08-29 08:38:36 -07:00 коммит произвёл GitHub
Родитель 0f63c57325
Коммит bb0a3a807a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 451 добавлений и 2 удалений

Просмотреть файл

@ -0,0 +1,445 @@
From 0c606d48b8e5b1e0c178b47cbbaee60c57cc5d82 Mon Sep 17 00:00:00 2001
From: Tobias Brick <tobiasb@microsoft.com>
Date: Mon, 21 Aug 2023 19:26:18 +0000
Subject: [PATCH] Port CVE-2022-48579 from unrar to clamav/libclamunrar. CVE
Details: https://nvd.nist.gov/vuln/detail/CVE-2022-48579 Original Patch:
https://github.com/pmachapman/unrar/commit/2ecab6bb5ac4f3b88f270218445496662020205f
Change from original patch:
The original patch added a member field to LastCheckedSymlink CmdExtract, defined as a std::wstring. However, the fork we're patching doesn't use std::wstring and uses wchar* everywhere. So I had to manually change that field and everywhere that uses it. This touched:
* extinfo.cpp
* extinfo.hpp
* extract.cpp
* extract.hpp
Rejected some files in the initial patch:
* crypt.hpp: Patch changed CRYPT5_KDF_LG2_COUNT from 16 to 15 but clamav already had it at 15.
* dll.rc: Patch changed a bunch of dll information for the windows build but we don't build for windows.
* version.hpp: Patch bumps some version constants but in the clamav version, they don't always seem to take those version bumps.
* win32stm.cpp: Patch changes windows code, which we don't use.
Original Commit Header:
From 2ecab6bb5ac4f3b88f270218445496662020205f Mon Sep 17 00:00:00 2001
From: Peter Chapman <peter@conglomo.co.nz>
Date: Tue, 20 Dec 2022 20:03:01 +1300
Subject: [PATCH] Updated to 6.2.3
arcread.cpp | 4 ++-
crypt.hpp | 5 +--
dll.rc | 8 ++---
extinfo.cpp | 89 +++++++++++++++++++++++++++++++++++++++++++++++----
extinfo.hpp | 3 +-
extract.cpp | 47 ++++++++++++++++++++++++---
extract.hpp | 6 ++++
hardlinks.cpp | 2 --
model.cpp | 6 ++--
pathfn.cpp | 14 +++++---
timefn.hpp | 11 +++++++
ulinks.cpp | 6 ++--
version.hpp | 6 ++--
win32stm.cpp | 8 +++--
14 files changed, 180 insertions(+), 35 deletions(-)
---
libclamunrar/arcread.cpp | 4 +-
libclamunrar/extinfo.cpp | 90 +++++++++++++++++++++++++++++++++++---
libclamunrar/extinfo.hpp | 3 +-
libclamunrar/extract.cpp | 47 ++++++++++++++++++--
libclamunrar/extract.hpp | 6 +++
libclamunrar/hardlinks.cpp | 2 -
libclamunrar/model.cpp | 6 ++-
libclamunrar/pathfn.cpp | 14 ++++--
libclamunrar/timefn.hpp | 11 +++++
libclamunrar/ulinks.cpp | 6 ++-
10 files changed, 167 insertions(+), 22 deletions(-)
diff --git a/libclamunrar/arcread.cpp b/libclamunrar/arcread.cpp
index 1a401f4..73954c7 100644
--- a/libclamunrar/arcread.cpp
+++ b/libclamunrar/arcread.cpp
@@ -1453,7 +1453,9 @@ bool Archive::ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode)
{
if (SubHead.UnpSize>0x1000000)
{
- // So huge allocation must never happen in valid archives.
+ // Prevent the excessive allocation. When reading to memory, normally
+ // this function operates with reasonably small blocks, such as
+ // the archive comment, NTFS ACL or "Zone.Identifier" NTFS stream.
uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName);
return false;
}
diff --git a/libclamunrar/extinfo.cpp b/libclamunrar/extinfo.cpp
index 5cb90a4..1cfe1c4 100644
--- a/libclamunrar/extinfo.cpp
+++ b/libclamunrar/extinfo.cpp
@@ -112,6 +112,69 @@ static bool LinkInPath(const wchar *Name)
}
+// Delete symbolic links in file path, if any, and replace them by directories.
+// Prevents extracting files outside of destination folder with symlink chains.
+bool LinksToDirs(const wchar *SrcName,const wchar *SkipPart,wchar *LastChecked,const size_t LastCheckedSize)
+{
+ // Unlike Unix, Windows doesn't expand lnk1 in symlink targets like
+ // "lnk1/../dir", but converts the path to "dir". In Unix we need to call
+ // this function to prevent placing unpacked files outside of destination
+ // folder if previously we unpacked "dir/lnk1" -> "..",
+ // "dir/lnk2" -> "lnk1/.." and "dir/lnk2/anypath/poc.txt".
+ // We may still need this function to prevent abusing symlink chains
+ // in link source path if we remove detection of such chains
+ // in IsRelativeSymlinkSafe. This function seems to make other symlink
+ // related safety checks redundant, but for now we prefer to keep them too.
+ //
+ // 2022.12.01: the performance impact is minimized after adding the check
+ // against the previous path and enabling this verification only after
+ // extracting a symlink with ".." in target. So we enabled it for Windows
+ // as well for extra safety.
+//#ifdef _UNIX
+ wchar Path[NM];
+ if (wcslen(SrcName)>=ASIZE(Path))
+ return false; // It should not be that long, skip.
+ wcsncpyz(Path,SrcName,ASIZE(Path));
+
+ size_t SkipLength=wcslen(SkipPart);
+
+ if (SkipLength>0 && wcsncmp(Path,SkipPart,SkipLength)!=0)
+ SkipLength=0; // Parameter validation, not really needed now.
+
+ // Do not check parts already checked in previous path to improve performance.
+ size_t LastCheckedLength=wcsnlen(LastChecked, LastCheckedSize);
+ for (uint I=0;Path[I]!=0 && I<LastCheckedLength && Path[I]==LastChecked[I];I++)
+ if (IsPathDiv(Path[I]) && I>SkipLength)
+ SkipLength=I;
+
+ wchar *Name=Path;
+ if (SkipLength>0)
+ {
+ // Avoid converting symlinks in destination path part specified by user.
+ Name+=SkipLength;
+ while (IsPathDiv(*Name))
+ Name++;
+ }
+
+ for (wchar *s=Path+wcslen(Path)-1;s>Name;s--)
+ if (IsPathDiv(*s))
+ {
+ *s=0;
+ FindData FD;
+ if (FindFile::FastFind(Path,&FD,true) && FD.IsLink)
+#ifdef _WIN_ALL
+ if (!DelDir(Path))
+#else
+ if (!DelFile(Path))
+#endif
+ return false; // Couldn't delete the symlink to replace it with directory.
+ }
+ wcsncpyz(LastChecked,SrcName,LastCheckedSize);
+//#endif
+ return true;
+}
+
+
bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName)
{
// Catch root dir based /path/file paths also as stuff like \\?\.
@@ -131,10 +194,14 @@ bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *Pr
UpLevels++;
TargetName++;
}
- // If link target includes "..", it must not have another links
- // in the path, because they can bypass our safety check. For example,
+ // If link target includes "..", it must not have another links in its
+ // source path, because they can bypass our safety check. For example,
// suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next
- // or "dir/lnk1" -> ".." first and "dir/lnk1/lnk2" -> ".." next.
+ // or "dir/lnk1" -> ".." first, "dir/lnk1/lnk2" -> ".." next and
+ // file "dir/lnk1/lnk2/poc.txt" last.
+ // Do not confuse with link chains in target, this is in link source path.
+ // It is important for Windows too, though this check can be omitted
+ // if LinksToDirs is invoked in Windows as well.
if (UpLevels>0 && LinkInPath(PrepSrcName))
return false;
@@ -160,15 +227,26 @@ bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *Pr
}
-bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName)
+bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName,bool &UpLink)
{
+ // Returning true in Uplink indicates that link target might include ".."
+ // and enables additional checks. It is ok to falsely return true here,
+ // as it implies only the minor performance penalty. But we shall always
+ // return true for links with ".." in target for security reason.
+
+ UpLink=true; // Assume the target might include potentially unsafe "..".
+#if defined(SAVE_LINKS) && defined(_UNIX) || defined(_WIN_ALL)
+ if (Arc.Format==RARFMT50) // For RAR5 archives we can check RedirName for both Unix and Windows.
+ UpLink=wcsstr(Arc.FileHead.RedirName,L"..")!=NULL;
+#endif
+
#if defined(SAVE_LINKS) && defined(_UNIX)
// For RAR 3.x archives we process links even in test mode to skip link data.
if (Arc.Format==RARFMT15)
- return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName);
+ return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName,UpLink);
if (Arc.Format==RARFMT50)
return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead);
-#elif defined _WIN_ALL
+#elif defined(_WIN_ALL)
// RAR 5.0 archives store link information in file header, so there is
// no need to additionally test it if we do not create a file.
if (Arc.Format==RARFMT50)
diff --git a/libclamunrar/extinfo.hpp b/libclamunrar/extinfo.hpp
index f3c7511..a77fac8 100644
--- a/libclamunrar/extinfo.hpp
+++ b/libclamunrar/extinfo.hpp
@@ -1,8 +1,9 @@
#ifndef _RAR_EXTINFO_
#define _RAR_EXTINFO_
+bool LinksToDirs(const wchar *SrcName,const wchar *SkipPart,wchar *LastChecked,const size_t LastCheckedSize);
bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName);
-bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName);
+bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName,bool &UpLink);
#ifdef _UNIX
void SetUnixOwner(Archive &Arc,const wchar *FileName);
#endif
diff --git a/libclamunrar/extract.cpp b/libclamunrar/extract.cpp
index dc109b9..e133770 100644
--- a/libclamunrar/extract.cpp
+++ b/libclamunrar/extract.cpp
@@ -9,6 +9,12 @@ CmdExtract::CmdExtract(CommandData *Cmd)
*DestFileName=0;
TotalFileCount=0;
+
+ // Common for all archives involved. Set here instead of DoExtract()
+ // to use in unrar.dll too. Allows to avoid LinksToDirs() calls
+ // and save CPU time in no symlinks including ".." in target were extracted.
+ UpLinkExtracted=false;
+
Unp=new Unpack(&DataIO);
#ifdef RAR_SMP
Unp->SetThreads(Cmd->Threads);
@@ -98,6 +104,8 @@ void CmdExtract::ExtractArchiveInit(Archive &Arc)
AnySolidDataUnpackedWell=false;
StartTime.SetCurrentTime();
+
+ *LastCheckedSymlink=0;
}
@@ -539,6 +547,10 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName));
#endif
+ if (ExtrFile && Command!='P' && !Cmd->Test && !Cmd->AbsoluteLinks &&
+ UpLinkExtracted)
+ ExtrFile=LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink, ASIZE(LastCheckedSymlink));
+
File CurFile;
bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE;
@@ -667,7 +679,17 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY)
{
wchar RedirName[NM];
- ConvertPath(Arc.FileHead.RedirName,RedirName,ASIZE(RedirName));
+
+ // 2022.11.15: Might be needed when unpacking WinRAR 5.0 links with
+ // Unix RAR. WinRAR 5.0 used \ path separators here, when beginning
+ // from 5.10 even Windows version uses / internally and converts
+ // them to \ when reading FHEXTRA_REDIR.
+ // We must perform this conversion before ConvertPath call,
+ // so paths mixing different slashes like \dir1/dir2\file are
+ // processed correctly.
+ SlashToNative(Arc.FileHead.RedirName,RedirName,ASIZE(RedirName));
+
+ ConvertPath(RedirName,RedirName,ASIZE(RedirName));
wchar NameExisting[NM];
ExtrPrepareName(Arc,RedirName,NameExisting,ASIZE(NameExisting));
@@ -681,7 +703,22 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION)
{
if (FileCreateMode)
- LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName);
+ {
+ bool UpLink;
+ LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName,UpLink);
+ UpLinkExtracted|=LinkSuccess && UpLink;
+
+ // We do not actually need to reset the cache here if we cache
+ // only the single last checked path, because at this point
+ // it will always contain the link own path and link can't
+ // overwrite its parent folder. But if we ever decide to cache
+ // several already checked paths, we'll need to reset them here.
+ // Otherwise if no files were created in one of such paths,
+ // let's say because of file create error, it might be possible
+ // to overwrite the path with link and avoid checks. We keep this
+ // code here as a reminder in case of possible modifications.
+ *LastCheckedSymlink=0; // Reset cache for safety reason.
+ }
}
else
{
@@ -868,8 +905,6 @@ void CmdExtract::UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize)
bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize)
{
- SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives.
-
File Existing;
if (!Existing.WOpen(NameExisting))
{
@@ -1131,6 +1166,8 @@ void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName)
DirExist=FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName));
if (!DirExist)
{
+ if (!Cmd->AbsoluteLinks && UpLinkExtracted)
+ LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink, ASIZE(LastCheckedSymlink));
CreatePath(DestFileName,true,Cmd->DisableNames);
MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr);
}
@@ -1212,6 +1249,8 @@ bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile)
MakeNameUsable(DestFileName,true);
+ if (!Cmd->AbsoluteLinks && UpLinkExtracted)
+ LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink, ASIZE(LastCheckedSymlink));
CreatePath(DestFileName,true,Cmd->DisableNames);
if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true))
{
diff --git a/libclamunrar/extract.hpp b/libclamunrar/extract.hpp
index 159759b..6de575f 100644
--- a/libclamunrar/extract.hpp
+++ b/libclamunrar/extract.hpp
@@ -52,6 +52,12 @@ class CmdExtract
bool PrevProcessed; // If previous file was successfully extracted or tested.
wchar DestFileName[NM];
bool PasswordCancelled;
+ bool UpLinkExtracted; // At least one symlink with ".." in target was extracted.
+
+ // Last path checked for symlinks. We use it to improve the performance,
+ // so we do not check recently checked folders again.
+ wchar LastCheckedSymlink[NM];
+
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
bool Fat32,NotFat32;
#endif
diff --git a/libclamunrar/hardlinks.cpp b/libclamunrar/hardlinks.cpp
index 40cc0aa..171b5fa 100644
--- a/libclamunrar/hardlinks.cpp
+++ b/libclamunrar/hardlinks.cpp
@@ -1,7 +1,5 @@
bool ExtractHardlink(CommandData *Cmd,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize)
{
- SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives.
-
if (!FileExist(NameExisting))
{
uiMsg(UIERROR_HLINKCREATE,NameNew);
diff --git a/libclamunrar/model.cpp b/libclamunrar/model.cpp
index 83391c5..e4f9e3c 100644
--- a/libclamunrar/model.cpp
+++ b/libclamunrar/model.cpp
@@ -532,13 +532,15 @@ inline bool RARPPM_CONTEXT::decodeSymbol2(ModelPPM *Model)
Model->Coder.SubRange.LowCount=HiCnt;
Model->Coder.SubRange.HighCount=Model->Coder.SubRange.scale;
i=NumStats-Model->NumMasked;
- pps--;
+
+ // 2022.12.02: we removed pps-- here and changed the code below to avoid
+ // "array subscript -1 is outside array bounds" warning in some compilers.
do
{
- pps++;
if (pps>=ps+ASIZE(ps)) // Extra safety check.
return false;
Model->CharMask[(*pps)->Symbol]=Model->EscCount;
+ pps++;
} while ( --i );
psee2c->Summ += Model->Coder.SubRange.scale;
Model->NumMasked = NumStats;
diff --git a/libclamunrar/pathfn.cpp b/libclamunrar/pathfn.cpp
index 983bd74..162eda2 100644
--- a/libclamunrar/pathfn.cpp
+++ b/libclamunrar/pathfn.cpp
@@ -31,11 +31,17 @@ wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath,size_t DestSize)
const wchar *s=DestPtr;
if (s[0]!=0 && IsDriveDiv(s[1]))
s+=2;
- if (s[0]=='\\' && s[1]=='\\')
+
+ // Skip UNC Windows \\server\share\ or Unix //server/share/
+ if (IsPathDiv(s[0]) && IsPathDiv(s[1]))
{
- const wchar *Slash=wcschr(s+2,'\\');
- if (Slash!=NULL && (Slash=wcschr(Slash+1,'\\'))!=NULL)
- s=Slash+1;
+ uint SlashCount=0;
+ for (const wchar *t=s+2;*t!=0;t++)
+ if (IsPathDiv(*t) && ++SlashCount==2)
+ {
+ s=t+1; // Found two more path separators after leading two.
+ break;
+ }
}
for (const wchar *t=s;*t!=0;t++)
if (IsPathDiv(*t))
diff --git a/libclamunrar/timefn.hpp b/libclamunrar/timefn.hpp
index 5271361..49b61e8 100644
--- a/libclamunrar/timefn.hpp
+++ b/libclamunrar/timefn.hpp
@@ -22,6 +22,17 @@ class RarTime
// Internal time representation in 1/TICKS_PER_SECOND since 01.01.1601.
// We use nanoseconds here to handle the high precision Unix time.
+ // It allows dates up to July 2185.
+ //
+ // If we'll ever need to extend the date range, we can define a lower
+ // precision Windows version of TICKS_PER_SECOND. But then Unix and Windows
+ // versions can differ in least significant digits of "lt" time output
+ // for Unix archives.
+ // Alternatively we can introduce 'bool HighPrecision' set to true
+ // in SetUnixNS() and TicksPerSecond() instead of constant above.
+ // It might be more reliable than defining TicksPerSecond variable,
+ // which wouldn't survive memset of any structure hosting RarTime.
+ // We would need to eliminate all such memsets in the entire code first.
uint64 itime;
public:
// RarLocalTime::Reminder precision. Must be equal to TICKS_PER_SECOND.
diff --git a/libclamunrar/ulinks.cpp b/libclamunrar/ulinks.cpp
index af6ef36..cd93628 100644
--- a/libclamunrar/ulinks.cpp
+++ b/libclamunrar/ulinks.cpp
@@ -70,7 +70,8 @@ static bool SafeCharToWide(const char *Src,wchar *Dest,size_t DestSize)
}
-bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName)
+static bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,
+ const wchar *LinkName,bool &UpLink)
{
char Target[NM];
if (IsLink(Arc.FileHead.FileAttr))
@@ -100,13 +101,14 @@ bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const w
if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) ||
!IsRelativeSymlinkSafe(Cmd,Arc.FileHead.FileName,LinkName,TargetW)))
return false;
+ UpLink=strstr(Target,"..")!=NULL;
return UnixSymlink(Cmd,Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime);
}
return false;
}
-bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd)
+static bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd)
{
char Target[NM];
WideToChar(hd->RedirName,Target,ASIZE(Target));
--
2.34.1

Просмотреть файл

@ -1,7 +1,7 @@
Summary: Open source antivirus engine
Name: clamav
Version: 0.105.2
Release: 1%{?dist}
Release: 2%{?dist}
License: ASL 2.0 AND BSD AND bzip2-1.0.4 AND GPLv2 AND LGPLv2+ AND MIT AND Public Domain AND UnRar
Vendor: Microsoft Corporation
Distribution: Mariner
@ -12,6 +12,7 @@ Source0: https://github.com/Cisco-Talos/clamav/archive/refs/tags/%{name}-
# To update the cache run:
# [repo_root]/toolkit/scripts/build_cargo_cache.sh %%{name}-%%{version}.tar.gz %%{name}-%%{name}-%%{version}
Source1: %{name}-%{name}-%{version}-cargo.tar.gz
Patch0: CVE-2022-48579.patch
BuildRequires: bzip2-devel
BuildRequires: check-devel
BuildRequires: cmake
@ -51,7 +52,7 @@ mkdir -p $HOME
pushd $HOME
tar xf %{SOURCE1} --no-same-owner
popd
%autosetup -n clamav-clamav-%{version}
%autosetup -p1 -n clamav-clamav-%{version}
%build
export CARGO_NET_OFFLINE=true
@ -128,6 +129,9 @@ fi
%dir %attr(-,clamav,clamav) %{_sharedstatedir}/clamav
%changelog
* Tue Aug 29 2023 Tobias Brick <tobiasb@microsoft.com> - 0.105.2-2
- Patch CVE-2022-48579
* Fri Feb 17 2023 corvus-callidus <108946721+corvus-callidus@users.noreply.github.com> - 0.105.2-1
- Upgrade to 0.105.2 to fix CVE-2023-20032 and CVE-2023-20052