[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) 编辑 收藏 举报