Sec

网络安全研究员,专注于工业互联网安全领域。求职中。

导航

[IOT安全][原创]钉钉智能指纹考勤机M1智能硬件漏洞挖掘(二)

mailto: wangkai0351@gmail.com
【未经同意禁止转载】
strings工具得到该固件编译过程中include 的一些c语言代码文件的路径和文件名如下

esp-idf/components/esp32/./heap_alloc_caps.c
esp-idf/components/esp32/./ipc.c
esp-idf/components/esp32/./intr_alloc.c
ieee80211_crypto.c
ieee80211_hostap.c
ieee80211_ht.c
ieee80211_input.c
ieee80211_ioctl.c
ieee80211_output.c
ieee80211_phy.c
ieee80211_scan.c
ieee80211_sta.c
wl_chm.c
wl_cnx.c
ieee80211_action.c
pm.c
esp-idf/components/newlib/./locks.c
esp-idf/components/nvs_flash/src/nvs_storage.cpp
esp-idf/components/nvs_flash/src/nvs_item_hash_list.cpp
esp-idf/components/nvs_flash/src/nvs_page.cpp
esp-idf/components/nvs_flash/src/nvs_pagemanager.cpp
esp-idf/components/spi_flash/./partition.c
esp-idf/components/spi_flash/./flash_mmap.c
esp-idf/components/tcpip_adapter/./tcpip_adapter_lwip.c
esp-idf/components/vfs/./vfs.c
esp-idf/components/vfs/./vfs_uart.c
main/./bravo.c
main/../embedded/dingtalk/base/dt_log.c
main/../embedded/dingtalk/base/dt_string.c
esp-idf/components/driver/./rtc_module.c
esp-idf/components/esp32/./crosscore_int.c
esp-idf/components/esp32/./phy_init.c
components/fingerprint/./fingerprint.c
components/fingerprint/./fingerprint_helper.c
components/fingerprint/./userIdpool.c
components/hardware/./alc5660.c
components/hardware/./fd650b.c
components/hardware/./gpio_helper..c
components/hardware/./pcf8563.c
components/logcache/./dt_log_fireeye.c
components/logcache/./dt_log_flash.c
components/ota/./ota.c
components/ota/./ota_downloader.c
components/root/./dt_device.c
components/root/./dt_fingerprint.c
components/root/./dt_root.c
components/root/./dt_lightvoice.c
components/root/./dt_login.c
components/root/./dt_coredump_upload.c
components/wifi/./wifi.c
main/../embedded/dingtalk/attend/dt_atdevice_service.c
main/../embedded/dingtalk/attend/dt_atdevice_statesync.c
main/../embedded/dingtalk/attend/dt_atdevice_uploadcrash.c
main/../embedded/dingtalk/attend/dt_atdevice_uploadresp.c
main/../embedded/dingtalk/attend/dt_atdevice_usercheck.c
main/../embedded/dingtalk/attend/dt_atdevice_usercontext.c
main/../embedded/dingtalk/attend/dt_atdevice_userinfo.c
main/../embedded/dingtalk/attend/dt_atdevice_userstampcontext.c
main/../embedded/dingtalk/attend/dt_atdevice_usertimestamp.c
main/../embedded/dingtalk/auth/dt_auth_service.c
main/../embedded/dingtalk/base/dt_list.c
main/../embedded/dingtalk/base/dt_queue.c
main/../embedded/dingtalk/device/dt_device_environment.c
main/../embedded/dingtalk/device/dt_device_service.c
main/../embedded/dingtalk/lwp/dt_lwp_delivery_reg.c
main/../embedded/dingtalk/lwp/dt_lwp_error.c
main/../embedded/dingtalk/lwp/dt_lwp_header.c
main/../embedded/dingtalk/lwp/dt_lwp_pushlistener.c
main/../embedded/dingtalk/lwp/dt_lwp_response.c
main/../embedded/dingtalk/lwp/dt_lwp_setting.c
tls.dingtalk.com
main/../embedded/dingtalk/lwp/dt_lwp_useragent.c
main/../embedded/dingtalk/wukong/dt_wukong_noticelist.c
main/../embedded/dingtalk/wukong/dt_wukong_service.c
main/../embedded/dingtalk/net/dt_net_loop.c
main/../embedded/dingtalk/file/dt_file_service.c
main/../embedded/dingtalk/file/dt_file_upload_context.c
main/../embedded/dingtalk/file/dt_file_upload_service.c
main/../embedded/dingtalk/fireeye/dt_fireeye_service.c
main/../embedded/dingtalk/fireeye/dt_fireeye_upload_mediaid.c
main/../embedded/dingtalk/attend/dt_atdevice_atcmd.c
main/../embedded/dingtalk/attend/dt_atdevice_checkresp.c
main/../embedded/dingtalk/auth/dt_auth_login_result.c
main/../embedded/dingtalk/auth/dt_auth_refresh_token_response.c
main/../embedded/dingtalk/base/dt_asyncjob.c
main/../embedded/dingtalk/base/dt_asyncqueue.c
main/../embedded/dingtalk/base/dt_thread.c
main/../embedded/dingtalk/base/dt_timer.c
main/../embedded/dingtalk/lwp/dt_lwp_mid.c
main/../embedded/dingtalk/lwp/dt_lwp_transaction.c
main/../embedded/dingtalk/lwp/dt_lwp_transport.c
main/../embedded/dingtalk/wukong/dt_wukong_kickout.c
main/../embedded/dingtalk/wukong/dt_wukong_notice.c
main/../embedded/dingtalk/fireeye/dt_fireeye_logupcmd.c
main/../embedded/dingtalk/lwp/dt_lwp_ask.c
main/../embedded/dingtalk/lwp/dt_lwp_connection.c
main/../embedded/dingtalk/lwp/dt_lwp_parser.c
main/../embedded/dingtalk/net/dt_net_connection.c
main/../embedded/dingtalk/net/dt_net_openssl.c
esp-idf/components/app_update/./esp_ota_ops.c
components/ble/./dt_ble.c
components/ble/./dt_npc.c
esp-idf/components/bt/bluedroid/device/controller.c
esp-idf/components/bt/bluedroid/hci/hci_layer.c
esp-idf/components/bt/bluedroid/hci/hci_packet_factory.c
esp-idf/components/bt/bluedroid/hci/hci_packet_parser.c
esp-idf/components/bt/bluedroid/hci/packet_fragmenter.c
esp-idf/components/bt/bluedroid/osi/fixed_queue.c
esp-idf/components/bt/bluedroid/osi/future.c
esp-idf/components/bt/bluedroid/osi/hash_map.c
esp-idf/components/bt/bluedroid/osi/list.c
esp-idf/components/bt/bluedroid/stack/btu/btu_task.c
esp-idf/components/bt/bluedroid/stack/l2cap/l2c_api.c
esp-idf/components/bt/bluedroid/stack/l2cap/l2c_fcr.c
esp-idf/components/bt/bluedroid/bta/dm/bta_dm_pm.c
esp-idf/components/bt/bluedroid/bta/sys/bta_sys_main.c
esp-idf/components/bt/bluedroid/btcore/bdaddr.c
esp-idf/components/bt/bluedroid/gki/gki_buffer.c
esp-idf/components/bt/bluedroid/hci/hci_hal_h4.c
esp-idf/components/bt/bluedroid/stack/btm/btm_ble_bgconn.c
esp-idf/components/bt/bluedroid/device/interop.c
arch_main.c
ea.c
ld_fm.c
lld.c
rwble.c
rwbt.c
vhci.c
intc.c
esp-idf/components/driver/./i2c.c
esp-idf/components/driver/./i2s.c
esp-idf/components/driver/./uart.c
esp-idf/components/freertos/./heap_regions.c
esp-idf/components/freertos/./queue.c
esp-idf/components/freertos/./tasks.c
esp-idf/components/freertos/./timers.c
esp-idf/components/freertos/./ringbuf.c
pp.c
lmac.c
wdev.c
ieee80211_misc.c

从源文件命名来看,我们从中挑出一些我们最感兴趣的源文件;比如esp-idf/components/路径下的文件大多属于官方SDK中的文件,源代码是公开的,因此这部分代码很难称得上使我们感兴趣的。反而,main/../embedded/dingtalk/路径的文件是钉钉自己开发的,肯定是涉及到打卡器业务和服务器通信的代码,因此我们着重从这些源文件汇中挑选我们初探信息。

比如,我们挑选这样一个源文件

components/root/./dt_login.c

我们就先用strings的结果来看这个源文件造成的信息泄露。

on_connect_fail wifi is not connected!
connect fail,times=%d
device_not_exist! 
fp num:%d != enrollednum:%d
get users version fail. status=%d, code=%.*s, reason=%.*s
fingerprint_sync_timestamp, local: %d, remote:%d
start full fp sync!
dt_get_users_version is requesting, ignore
atm_state_sync fail. status=%d, code=%.*s, reason=%.*s
atm_state_sync timestamp=%d, code=%.*s, firmware=%.*s, system_key.fwversion:%s, 
md5=%.*s
emit_ota_task
{"maxUsers":1000,"maxFP":1000,"maxFA":0,"dev_mac_addr":"%s","dev_local_ip":"%s","router_name":"%s"}
kaoqinji
dt_update_machine_info do report request
on_reg_success failed, invalid parameter, reg=%p
reg success, timestamp=%lld (aka "%.*s"), get_rtc_timer: %ld, time(): %ld //ESP_LOGE
connect fail long time, restart wifi
login_task wifi is not connected!
login_task device is not bind!
85A09F60A599F5E1867EAB915A8BB07F
9_%d
dingboxM1
dingtalk
/s/biz/atcmd
/s/logup
/push/kickout
login_task call login_by_device
device_key=%s,sn=%s,active_code=%s,secret=%s
received logup cmd! begining to upload logs!
atmGetUserInfo
atmUploadUserInfo
atmEnterFP
atmESC
atmStateSync
atmUploadLog
atmEnterMenu
atmEnterFA
atmEnterFPCancel
atmOTA
rollback
datastr
cJSON_Parse succeed, action: %d!
recv atcmd, cmd=%.*s, data len=%d
retry_login wifi is not connected!
login long time fail, restart wifi
retry login,times=%d
device_not_exist
device_secret_invalid
login failed, status=%d, code=%.*s, reason=%.*s
LOGIN FAIL. should not happen. login ok without login_result
login success, uid=%lld
dt_get_users_version is requesting, ignore 2
need_auth(), is_wifi_connected: %d
parse_cmd
on_async_atcmd_rsp
on_async_atcmd_rsp
on_logupcmd_rsp
on_logupcmd_rsp
on_async_kickout_rsp
on_async_kickout_rsp
login_task
retry_login
on_connect_fail
on_discon nected
on_disconnected
on_connected
need_auth
set_systemtime_on_reg_success
on_async_login_rsp
on_async_login_rsp
dt_update_machine_info
on_atm_state_sync
dt_get_users_version
on_atm_get_users_version
dt_on_device_not_exist
upload coredump error!

我们看到上面包含了一些debug打印信息的流出,因此我们考虑这些debug信息应该是打印到串口的。我们当然非常想看到这些debug信息了。

上面这句话说的不准确,上述strings工具输出的敏感信息字符串主要分为几类

1. esp32 SDK本身自带的调试信息,有
#ifndef BOOTLOADER_BUILD
#define ESP_LOGE //error错误
#define ESP_LOGW //warning警告
#define ESP_LOGI //info信息
#define ESP_LOGD //debug模式
#define ESP_LOGV //
这几类
这些调试信息可能会由串口输出,这类调试信息格式如
[0;32mI (%d) %s: SPI Speed      : %s
[0;32mI (%d) %s: SPI Mode       : %s
[0;32mI (%d) %s: SPI Flash Size : %s
bravo_1.0.2-90-gcfa4912
[0;32mI (%d) %s: ESP-IDF %s 2nd stage bootloader
2. 钉钉自己开发日志记录功能,比如
device_key=%s,sn=%s,active_code=%s,secret=%s
这些日志记录是都要写到flash中存储起来,以便于在联网的时候能够upload到服务器
3. 函数名
4. 断言
5. 宏定义

ESP32 有 3 个 UART 接口,即 UART0、UART1 和 UART2。

查阅《ESP32 技术规格书》版本2.1可知

U0RXD 40 号引脚

U0TXD 41 号引脚

U1RXD 28 号引脚

U1TXD 29 号引脚

U2RXD 25 号引脚

U2TXD 27 号引脚

到PCB上看看,这三对引脚有没有露出来,如果有任意一对引脚引到了PCB的焊盘上,那么很可能就是这个PCB的串口调试端口。

下面,尝试猜测复原一下ESP32(xtensa)+FreeRTOS的符号表(symbol table),我在固件的bin文件中发现了以下的感兴趣段落。根据我的经验,符号表就应该长成一个表格的样子,规规矩矩,对齐工整,所以我猜测以下段落很像是符号表。

00000060  01 00 00 00 36 2b 0d 40  79 28 0d 40 7e 28 0d 40  |....6+.@y(.@~(.@|
00000070  d5 28 0d 40 07 2b 0d 40  05 2b 0d 40 b0 2a 0d 40  |.(.@.+.@.+.@.*.@|
00000080  d4 2a 0d 40 60 2c 0d 40  09 29 0d 40 4a 2c 0d 40  |.*.@`,.@.).@J,.@|
00000090  55 2c 0d 40 60 2c 0d 40  60 2c 0d 40 60 2c 0d 40  |U,.@`,.@`,.@`,.@|
000000a0  09 29 0d 40 09 29 0d 40  09 29 0d 40 09 29 0d 40  |.).@.).@.).@.).@|
000000b0  09 29 0d 40 09 29 0d 40  09 29 0d 40 55 2c 0d 40  |.).@.).@.).@U,.@|
000000c0  09 29 0d 40 09 29 0d 40  09 29 0d 40 3f 2c 0d 40  |.).@.).@.).@?,.@|
000000d0  09 29 0d 40 55 2c 0d 40  09 29 0d 40 09 29 0d 40  |.).@U,.@.).@.).@|
000000e0  ee 28 0d 40 09 29 0d 40  09 29 0d 40 09 29 0d 40  |.(.@.).@.).@.).@|
000000f0  09 29 0d 40 09 29 0d 40  09 29 0d 40 09 29 0d 40  |.).@.).@.).@.).@|

符号表可以简单理解为函数的入口地址函数名的存储起始地址一一对应的关系,如下表

函数名起始地址1 函数地址1
函数名起始地址2 函数地址2
函数名起始地址3 函数地址3

挑出了固件bin文件的一段

0002bf10  73 79 6e 63 5f 67 65 74  5f 61 6c 6c 5f 75 73 65  |sync_get_all_use|
0002bf20  72 69 6e 66 6f 5f 72 73  70 00 00 00 6f 6e 5f 61  |rinfo_rsp...on_a|
0002bf30  73 79 6e 63 5f 67 65 74  5f 61 6c 6c 5f 75 73 65  |sync_get_all_use|
0002bf40  72 69 6e 66 6f 5f 72 73  70 00 00 00 64 74 5f 72  |rinfo_rsp...dt_r|
0002bf50  65 73 79 6e 63 5f 66 61  69 6c 65 64 5f 75 73 65  |esync_failed_use|
0002bf60  72 73 00 00 64 74 5f 72  65 73 79 6e 63 5f 66 61  |rs..dt_resync_fa|
0002bf70  69 6c 65 64 5f 75 73 65  72 73 00 00 6f 6e 5f 61  |iled_users..on_a|
0002bf80  73 79 6e 63 5f 61 74 6d  5f 67 65 74 75 73 65 72  |sync_atm_getuser|
0002bf90  69 6e 66 6f 5f 61 63 74  69 6f 6e 00 6f 6e 5f 67  |info_action.on_g|
0002bfa0  65 74 5f 75 73 65 72 69  6e 66 6f 5f 62 79 5f 69  |et_userinfo_by_i|
0002bfb0  64 73 5f 72 73 70 00 00  64 74 5f 66 69 6e 67 65  |ds_rsp..dt_finge|

on_async_get_all_users是一个函数名,它的起始存储地址是0x0002bf20+c,当然该地址是在固件中的绝对地址(带有偏移)。

dt_resync_failed_users也是一个函数名,它的起始存储地址是0x0002bf40+c。

on_async_atm_getuserinfo_action也是一个函数名,它的起始存储地址是0x0002bf70+c。

on_get_userinfo_by_ids_rsp也是一个函数名,它的起始存储地址是0x0002bf90+c。

从flash的绝对地址上可以看出,

addr(dt_resync_failed_users)-addr(on_async_get_all_users)=0x20

addr(on_async_atm_getuserinfo_action)-addr(dt_resync_failed_users)=0x30

addr(on_get_userinfo_by_ids_rsp)-addr(on_async_atm_getuserinfo_action)=0x20

这样相减,就减掉了它们都包含的偏移地址常数。

所以符号表中,

addr(函数名起始地址2)-addr(函数名起始地址1)=0x20

addr(函数名起始地址3)-addr(函数名起始地址2)=0x30

addr(函数名起始地址4)-addr(函数名起始地址3)=0x20

或者,我们再来看一簇gpio配置相关的函数

00026ff0  6f 5f 6e 75 6d 3a 25 75  1b 5b 30 6d 0a 00 00 00  |o_num:%u.[0m....|
00027000  67 70 69 6f 5f 69 73 72  5f 72 65 67 69 73 74 65  |gpio_isr_registe|
00027010  72 00 00 00 67 70 69 6f  5f 69 6e 73 74 61 6c 6c  |r...gpio_install|
00027020  5f 69 73 72 5f 73 65 72  76 69 63 65 00 00 00 00  |_isr_service....|
00027030  67 70 69 6f 5f 69 73 72  5f 68 61 6e 64 6c 65 72  |gpio_isr_handler|
00027040  5f 61 64 64 00 00 00 00  67 70 69 6f 5f 6f 75 74  |_add....gpio_out|
00027050  70 75 74 5f 64 69 73 61  62 6c 65 00 67 70 69 6f  |put_disable.gpio|
00027060  5f 6f 75 74 70 75 74 5f  65 6e 61 62 6c 65 00 00  |_output_enable..|
00027070  67 70 69 6f 5f 73 65 74  5f 64 69 72 65 63 74 69  |gpio_set_directi|
00027080  6f 6e 00 00 67 70 69 6f  5f 73 65 74 5f 70 75 6c  |on..gpio_set_pul|
00027090  6c 5f 6d 6f 64 65 00 00  67 70 69 6f 5f 73 65 74  |l_mode..gpio_set|
000270a0  5f 6c 65 76 65 6c 00 00  67 70 69 6f 5f 69 6e 74  |_level..gpio_int|
000270b0  72 5f 64 69 73 61 62 6c  65 00 00 00 67 70 69 6f  |r_disable...gpio|
000270c0  5f 69 6e 74 72 5f 65 6e  61 62 6c 65 5f 6f 6e 5f  |_intr_enable_on_|
000270d0  63 6f 72 65 00 00 00 00  67 70 69 6f 5f 73 65 74  |core....gpio_set|
000270e0  5f 69 6e 74 72 5f 74 79  70 65 00 00 67 70 69 6f  |_intr_type..gpio|
000270f0  5f 70 75 6c 6c 64 6f 77  6e 5f 64 69 73 00 00 00  |_pulldown_dis...|
00027100  67 70 69 6f 5f 70 75 6c  6c 64 6f 77 6e 5f 65 6e  |gpio_pulldown_en|
00027110  00 00 00 00 67 70 69 6f  5f 70 75 6c 6c 75 70 5f  |....gpio_pullup_|
00027120  64 69 73 00 67 70 69 6f  5f 70 75 6c 6c 75 70 5f  |dis.gpio_pullup_|
编号 函数名 函数名的起始存储地址 和前一个函数名地址的偏移
1 gpio_isr_register 0x00027000
2 gpio_install_isr_service 0x00027010+4 0x14
3 gpio_isr_handler_add 0x00027030 0x1c
4 gpio_output_disable 0x00027040+8 0x12
5 gpio_output_enable 0x00027050+c 0x14
6 gpio_set_direction 0x00027070 0x14

这簇函数给了我们5条证据来复原符号表,应该干扰更小了,结果更准确了。

posted on 2019-06-17 18:16  大单GreatDane  阅读(1364)  评论(0编辑  收藏  举报