环境搭建 硬件 esp32、micro-usb、杜邦线、usb->ttl、支持嗅探的无线网卡、BLE Sniffer
软件 这是源码: esp32 的 windows 烧录环境:,直接点进来下载离线安装包 [
image.png](https://cdn.nlark.com/yuque/0/2022/png/268938/1663401939422-92048e0d-64e1-4672-8e71-5fc4562289c9.png#clientId=u5b2aaab0-268b-4&crop=0&crop=0&crop=1&crop=1&errorMessage=unknown%20error&from=paste&height=331&id=u3cf42c8c&margin=%5Bobject%20Object%5D&name=image.png&originHeight=414&originWidth=760&originalType=binary∶=1&rotation=0&showTitle=false&size=37035&status=error&style=none&taskId=u69341c37-925e-4dec-b77b-218a93422d9&title=&width=608)
我下载的是这个,无脑安装
image.png
完事之后出来了两个快捷方式
image.png
这俩用哪个都行,打开之后切换目录到源码的文件夹,cd esp32ctf_thu/thuctf/ 输入命令:idf.py menuconfig
,稍等片刻会打开一个新界面,设置 Serial flasher config 的 Flash size 为 4MB
image.png
设置 Partition Table 的 Partition Table 为 Custom partition table CSV
image.png
选完之后 Q 保存退出 然后idf.py build
编译代码
image.png
等它编译一阵,完事之后就可以idf.py flash
了,出现 Connecting….. 的时候要摁住板子上的 BOOT 键
image.png
烧写完成之就可以关掉了,随便找个串口工具,选择波特率 115200 就能看到 log 了
image.png
注意 题目其实是这里,烧录好之后拿着这个文件夹里的内容做题,里面有个 tar 包,是删掉了真实 flag 的源码,有些关卡需要分析源码才知道咋做,源码按照不同的题目方向分开了,很友好!
开搞 咱从头开始,先把 GND 和 23 号引脚连起来,如果前面已经供电了在连 GND 和 23 引脚需要断电重新供电,或者摁一下板子上的 EN 摁扭才能切换到硬件的题目这一方向
image.png
硬件题目 task1 题目:将 GPIO18 抬高,持续 3s 即可获得 flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #define GPIO_INPUT_IO_0 18 .... void hardware_task1 () { int hit = 0 ; while (1 ) { printf ("[+] hardware task I : hit %d\n" ,hit); if (gpio_get_level(GPIO_INPUT_IO_0)){ hit ++ ; }else { hit = 0 ; } if (hit>3 ){ printf ("[+] hardware task I : %s\n" ,hardware_flag_1); break ; } vTaskDelay(1000 / portTICK_RATE_MS); } }
此时日志如下
image.png
GPIO 是指板子上的一组引脚。这些引脚可以发送或接收电信号,但它们不是为任何特定目的而设计的,可以由我们通过编程来实现任意功能。这就是为什么它们被称为通用 IO(General-purpose input/output) 抬高就是给它供电,把板子上的 3.3V 或 5V 与他接起来就行了
image.png
task2 题目:在 GPIO18 处构造出 1w 个上升沿
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #define GPIO_INPUT_IO_0 18 #define GPIO_INPUT_PIN_SEL ((1ULL<<GPIO_INPUT_IO_0) ) #define ESP_INTR_FLAG_DEFAULT 0 static void IRAM_ATTR gpio_isr_handler (void * arg) { trigger++; } void hardware_gpio_setup () { gpio_config_t io_conf; io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL; io_conf.mode = GPIO_MODE_INPUT; io_conf.intr_type = GPIO_INTR_POSEDGE; io_conf.pull_up_en = 0 ; gpio_config(&io_conf); gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT); gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void *) GPIO_INPUT_IO_0); } void hardware_task2 () { trigger = 0 ; while (1 ){ printf ("[+] hardware task II : trigger %d\n" ,trigger); if (trigger > 10000 ){ printf ("[+] hardware task II : %s\n" ,hardware_flag_2); break ; } vTaskDelay(1000 / portTICK_RATE_MS); } }
上升沿指的是数字电路中数字电平从低电平(数字 0)到高电平(数字 1)的一瞬间,下降沿同理 借助一个 TX 的引脚会一直输出这一特点来与 GPIO18 连起来,这样就可以啦
image.png
补充:上下拉是给 IO 一个默认的状态,上拉和下拉是指 GPIO 输出高电位(上拉)还是低电位(下拉),从程序设计的角度讲,上拉就是如果没有输入信号则此时 I/O 状态为 1,下拉相反关于上下拉电阻看一下这个
试着理解一下代码的意思,给 GPIO18 注册了一个上升沿中断处理函数,函数的功能是 trigger+1,同时把 GPIO18 的上拉关掉,这样没有输入时候的 I/O 状态就不是 1,有输入的时候就会触发上升沿,这样 trigger 就会增加了
task3 题目:在另一个串口处寻找第三个 flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #define ECHO_TEST_TXD (GPIO_NUM_4) #define ECHO_TEST_RXD (GPIO_NUM_5) #define ECHO_TEST_RTS (UART_PIN_NO_CHANGE) #define ECHO_TEST_CTS (UART_PIN_NO_CHANGE) void hardware_uart_setup () { uart_config_t uart_config = { .baud_rate = 115200 , .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, .source_clk = UART_SCLK_APB, }; uart_driver_install(UART_NUM_1, 1024 * 2 , 0 , 0 , NULL , 0 ); uart_param_config(UART_NUM_1, &uart_config); uart_set_pin(UART_NUM_1, ECHO_TEST_TXD, ECHO_TEST_RXD, ECHO_TEST_RTS, ECHO_TEST_CTS); } void hardware_task3 () { printf ("[+] hardware task III : find the third flag in another UART\n" ); while (1 ) { uart_write_bytes(UART_NUM_1, hardware_flag_3, strlen (hardware_flag_3)); vTaskDelay(1000 / portTICK_RATE_MS); } }
被晃了呜呜呜,这个板子上有个 TX2 我以为是这个呐,结果等了半天啥也没有,这个是让你分析代码,看一下用的哪一个 GPIO 作为 TX,通过 define 可以看到,TXD 是 GPIO4,那就把 GPIO4 接到 USB->TTL 的 RX 上就可以看到了
image.png
image.png
网络题目 task1 题目:连接板子目标端口,尝试获得 flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 void network_init () { char ssid[0x10 ] = {0 }; char pass[0x10 ] = {0 }; get_random(ssid,6 ); get_random(pass,8 ); printf ("[+] network task I: I will connect a wifi -> ssid: %s , password %s \n" ,ssid,pass); connect_wifi(ssid,pass); } static void network_tcp () { char addr_str[128 ]; struct sockaddr_in dest_addr ; dest_addr.sin_addr.s_addr = htonl(INADDR_ANY); dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(3333 ); int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); ESP_LOGI(TAG, "Socket created" ); bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof (dest_addr)); ESP_LOGI(TAG, "Socket bound, port %d" , 3333 ); listen (listen_sock, 1 ); while (1 ) { ESP_LOGI(TAG, "Socket listening" ); struct sockaddr_storage source_addr ; socklen_t addr_len = sizeof (source_addr); int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len); inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof (addr_str) - 1 ); ESP_LOGI(TAG, "Socket accepted ip address: %s" , addr_str); char buffer [100 ]; while (recv(sock,buffer ,0x10 ,0 )){ if (strstr (buffer ,"getflag" )){ send(sock, network_flag_1, strlen (network_flag_1), 0 ); break ; }else { send(sock, "error\n" , strlen ("error\n" ), 0 ); } vTaskDelay(1000 / portTICK_RATE_MS); } open_next_tasks = 1 ; shutdown (sock, 0 ); close (sock); } }
这里得往上翻日志了,他随机指定了一个 wifi 名和密码,会去连接那个密码,用手机开个热点即可 ssid: kbmxet , password svtujgjb
image.png
image.png
日志里给出了 IP 和端口,用电脑也连接上开的热点
image.png
nc 一下,连上之后根据源码里的逻辑,发送 getflag 即可
image.png
task2 题目:你知道他发给百度的 flag 么
此部分代码不完善,可能会因死循环爆栈导致重启,请见谅…(确实容易重启 🤣)
改为用电脑开这个热点,然后直接抓取网卡的流量,嗯,,他好像不会切换,得重新做一遍
image.png
电脑开启热点后会有一个新的网卡,就抓这个网卡即可
image.png
追踪 HTTP 流发现 flag
image.png
task3 题目:flag 在空中 同时日志如下:
image.png
把无线网卡插到 kali 里面,得搞到无线网卡再做
蓝牙题目 task1 题目:修改蓝牙名称并设置可被发现即可获得 flag 也是刚开始的日志中随机指定了蓝牙设备的名字
image.png
直接改手机的名字就行了,现在手机好像默认不被发现?在手机上点击扫描周围设备就可以了
image.png
image.png
task2 题目:flag 在空中 那就抓包吧,开启 Hollong,直接全选广播包中就有,同时也确定了他的 MAC 地址和设备名
image.png
task3 题目:分析 GATT 业务并获得 flag 用 nRF Connect 连接,一开始读,只有 DEEDBEEF
image.png
搜索源码里的 [+] bluetooth task III 定位到这里,我们写入的值与 flag2 进行了对比,通过则 open_task3 = 1
image.png
只有当 open_task3 = 1 时才会把真正的 flag 拷贝过去
image.png
发送 task2 的 flag
image.png
再次读取,转成 ASCII 码即可 THUCTF{WrItE_4_gA7T}
image.png