esp32ctf_thu
项目地址:
https://github.com/xuanxuanblingbling/esp32ctf_thu
环境:ubuntu18.04
安装esp-idf:
#依赖
sudo apt-get install git wget flex bison gperf python3.7 python3.7-venv cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0
#获取esp-idf
mkdir -p ~/esp
cd ~/esp
git clone -b v4.3 --recursive https://github.com/espressif/esp-idf.git
#安装工具链
cd ~/esp/esp-idf
./install.sh all
使用esp-idf:
#在当前终端启用esp-idf
. $HOME/esp/esp-idf/export.sh
#打开esp-idf配置菜单
idf.py menuconfig
Serial flasher config ---> Flash size (4 MB)
Partition Table ---> Partition Table (Custom partition table CSV)
==》
#根据配置和源码 编译esp-idf项目
idf.py build
#将编译好的固件烧录到esp32
idf.py flash
开始挑战
题目说明
有以下四个方向的题目,方向间题目并行,方向内题目采取闯关模式串行。但由于配置冲突,采用跳帽来切换题目方向:
- 跳帽连接GND与23号引脚:硬件、网络、蓝牙
- 拔掉GND与23号引脚跳帽:MQTT
由于我的esp32的GND和23号引脚相距过远,用不了跳帽,所以直接用杜邦线连上了(一样的)
硬件
task1 -> task2 -> task3
task1
将GPIO18抬高,持续3s即可获得flag
#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);
}
}
==》使用杜邦线将18号引脚和3.3V引脚连起来,18号引脚就将处于高电平状态
==》通过CRT建立与esp32的通信
==》THUCTF{Ev3ryth1ng_st4rt_fr0m_GPIO_!!!}
task2
在GPIO18处构造出1w个上升沿==》信号从低电平(逻辑 0)变为高电平(逻辑 1)的过程
int trigger = 0;
static void IRAM_ATTR gpio_isr_handler(void* arg){
trigger++;
}
void hardware_gpio_setup(){
……
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);
}
}
==》只需要将18号引脚与TX相接,利用串口一直有数据输出,就可以自动构造上升沿
==》THUCTF{AuT0++_is_th3_r1ght_w4y_hhhhhh}
task3
在另一个串口处寻找第三个flag
#define ECHO_TEST_TXD (GPIO_NUM_4)
#define ECHO_TEST_RXD (GPIO_NUM_5)
void hardware_uart_setup(){
……
//定义UART1 TXD为4号引脚 RXD为5号引脚
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 控制器(UART1)发送flag
uart_write_bytes(UART_NUM_1, hardware_flag_3, strlen(hardware_flag_3));
vTaskDelay(1000 / portTICK_RATE_MS);
}
}
==》使用USB转TTL
==》TX对RX(5号引脚),RX对TX(4号引脚),GND对GND
网络
-> task2
task1
-> task3
task1
连接板子目标端口,尝试获得flag
//打印一个随机SSID和password的WIFI
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);
}
//根据SSID和password连接WIFI
void connect_wifi(char * ssid, char * pass){
nvs_init();
wifi_init_sta(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); //打印IP
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);
}
}
==》需要我们构造一个程序提供的随机SSID和password的WIFI热点
==》下图是我又重新运行后产出的随机WIFI
==》提供给板子连接,将会返回IP地址和3333监听端口
==》监听3333端口,并发送”getflag“即可通过验证获得flag:THUCTF{M4k3_A_w1rele55_h0t5p0ts}
task2
你知道他发给百度的flag么
void network_http()
{
char fmt[] = "GET / HTTP/1.0\r\n"
"Host: www.baidu.com:80\r\n"
"User-Agent: esp-idf/1.0 esp32\r\n"
"flag: %s\r\n"
"\r\n";
char request[200];
sprintf(request,fmt,network_flag_2);
const struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
};
struct addrinfo *res;
struct in_addr *addr;
int s;
while(1) {
if(open_next_tasks){
printf("[+] network task II : send the second flag to baidu\n");
getaddrinfo("www.baidu.com", "80", &hints, &res);
addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
ESP_LOGI("network", "DNS lookup succeeded. IP=%s", inet_ntoa(*addr));
s = socket(res->ai_family, res->ai_socktype, 0);
connect(s, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
write(s, request, strlen(request));
close(s);
}
vTaskDelay(10000 / portTICK_PERIOD_MS);
}
}
==》就是我们在通过(获取flag)第一关的基础上,程序会向baidu发送带有flag的数据包
==》通过wireshark捕获给esp32提供热点的wifi
==》得到flag:THUCTF{Sn1ffer_N3tw0rk_TrAffic_In_7h4_Main_r0aD}
task3
flag在空中
static void network_wifi()
{
static const char ds2ds_pdu[] = {
0x48, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xE8, 0x65, 0xD4, 0xCB, 0x74, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x60, 0x94, 0xE8, 0x65, 0xD4, 0xCB, 0x74, 0x1C, 0x26, 0xB9,
0x0D, 0x02, 0x7D, 0x13, 0x00, 0x00, 0x01, 0xE8, 0x65, 0xD4, 0xCB, 0x74,
0x1C, 0x00, 0x00, 0x26, 0xB9, 0x00, 0x00, 0x00, 0x00,
};
char pdu[200]={0}; // 802.11 数据包,后续将包含flag
memcpy(pdu,ds2ds_pdu,sizeof(ds2ds_pdu));
memcpy(pdu+sizeof(ds2ds_pdu),network_flag_3,sizeof(network_flag_3));
while(1) {
if(open_next_tasks){
printf("[+] network task III : send raw 802.11 package contains the third flag\n");
esp_wifi_80211_tx(ESP_IF_WIFI_STA, pdu, sizeof(ds2ds_pdu)+sizeof(network_flag_3), true);
}
vTaskDelay(5000 / portTICK_PERIOD_MS);
}
}
蓝牙
task1
修改蓝牙名称并设置可被发现即可获得flag
//启用蓝牙
void bt_app_gap_start_up(void)
{
get_random(target_name,8);
//随机蓝牙设备名称
printf("[+] bluetooth task I : Please change your bluetooth device name to %s\n",target_name);
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
esp_bt_controller_init(&bt_cfg);
esp_bt_controller_enable(ESP_BT_MODE_BTDM);
esp_bluedroid_init();
esp_bluedroid_enable();
esp_bt_dev_set_device_name("THUCTF"); //本设备蓝牙名称
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
esp_bt_gap_register_callback(bt_app_gap_cb);
esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 0x10, 0);
}
static void update_device_info(esp_bt_gap_cb_param_t *param)
{
char bda_str[18];
unsigned char device_name[256];
uint8_t device_name_len;
esp_bt_gap_dev_prop_t *p;
ESP_LOGI(GAP_TAG, "[+] bluetooth task I : Device found: %s", bda2str(param->disc_res.bda, bda_str, 18));
for (int i = 0; i < param->disc_res.num_prop; i++) {
p = param->disc_res.prop + i;
switch (p->type) {
case ESP_BT_GAP_DEV_PROP_EIR: {
get_name_from_eir(p->val, device_name, &device_name_len);
check_name(target_name,(char *)device_name); //验证,打印flag
ESP_LOGI(GAP_TAG, "[+] bluetooth task I : Found a target device, address %s, name %s", bda_str, device_name);
break;
}
default:
break;
}
}
}
void check_name(char * a,char * b){
if(!strcmp(a,b)){
printf("bluetooth task I : %s\n",bt_flag_1);
esp_bt_gap_cancel_discovery();
scan = 0;
next_task();
}
}
==》创建一个该名称的蓝牙
==》获得flag:THUCTF{b1u3t00th_n4me_a1s0_c4n_b3_An_aTT4ck_surfAce}
task2
flag在空中
//启动BLE
void next_task(){
//包含flag的数据包
unsigned char fmt[]= {0x06,0x09,0x68,0x65,0x6C,0x6C,0x6F,
sizeof(bt_flag_2),0xFD};
char client_name[10]={};
get_random(client_name,5);
printf("[+] bluetooth task II : BLE device name is %s\n",client_name);
printf("[+] bluetooth task II : Please find the second flag in the ADV package from this BLE device %s\n",client_name);
unsigned char data[100];
memcpy(data,fmt,sizeof(fmt));
memcpy(data+2,client_name,5);
memcpy(data+sizeof(fmt),bt_flag_2,sizeof(bt_flag_2));
//配置原始广播数据
esp_ble_gap_config_adv_data_raw(data,sizeof(fmt)+sizeof(bt_flag_2));
//开始广播
esp_ble_gap_start_advertising(&ble_adv_params);
//注册GATT
esp_ble_gatts_register_callback(gatts_event_handler);
esp_ble_gatts_app_register(PROFILE_A_APP_ID);
//设置MTU
esp_ble_gatt_set_local_mtu(500);
}
==》由经典蓝牙转为BLE(低功耗蓝牙)
==》只要能接收到BLE发出的广播数据包就可以拿到flag
==》通过nRF connect连接随机生成的BLE设备名,点击raw接收广播数据
==》将获取到的16进制数据转个ASCII即可获得flag:THUCTF{AdVD47a}
task3
分析GATT业务并获得flag
#define GATTS_CHAR_UUID_TEST_A 0xFF01
static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
switch (event) {
//注册GATT应用程序事件
case ESP_GATTS_REG_EVT:
ESP_LOGI(GATTS_TAG, "REGISTER_APP_EVT, status %d, app_id %d\n", param->reg.status, param->reg.app_id);
gl_profile_tab[PROFILE_A_APP_ID].service_id.is_primary = true;
gl_profile_tab[PROFILE_A_APP_ID].service_id.id.inst_id = 0x00;
gl_profile_tab[PROFILE_A_APP_ID].service_id.id.uuid.len = ESP_UUID_LEN_16;
gl_profile_tab[PROFILE_A_APP_ID].service_id.id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID_TEST_A;
esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[PROFILE_A_APP_ID].service_id, GATTS_NUM_HANDLE_TEST_A);
break;
//读取属性事件,当远程设备尝试读取属性值时触发
case ESP_GATTS_READ_EVT: {
ESP_LOGI(GATTS_TAG, "GATT_READ_EVT, conn_id %d, trans_id %d, handle %d\n", param->read.conn_id, param->read.trans_id, param->read.handle);
esp_gatt_rsp_t rsp;
memset(&rsp, 0, sizeof(esp_gatt_rsp_t));
rsp.attr_value.handle = param->read.handle;
//根据open_task3提供flag
if(open_task3){
rsp.attr_value.len = sizeof(bt_flag_3);
memcpy(rsp.attr_value.value,bt_flag_3,sizeof(bt_flag_3));
}else{
rsp.attr_value.len = 4;
rsp.attr_value.value[0] = 0xde;
rsp.attr_value.value[1] = 0xed;
rsp.attr_value.value[2] = 0xbe;
rsp.attr_value.value[3] = 0xef;
}
esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id,
ESP_GATT_OK, &rsp);
break;
}
//写入属性事件
case ESP_GATTS_WRITE_EVT: {
//打印写入的数据
ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, conn_id %d, trans_id %d, handle %d", param->write.conn_id, param->write.trans_id, param->write.handle);
if (!param->write.is_prep){
ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, value len %d, value :", param->write.len);
esp_log_buffer_hex(GATTS_TAG, param->write.value, param->write.len);
printf("[+] bluetooth task III : %s\n",param->write.value);
//如果写入的数据为bt_flag_2
if(!strncmp(bt_flag_2,(char *)param->write.value,param->write.len)){
printf("[+] bluetooth task III : you can read the third flag this time\n");
open_task3 = 1;//标志位置1
}
}
esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL);
break;
}
//执行写入属性事件
case ESP_GATTS_EXEC_WRITE_EVT:
ESP_LOGI(GATTS_TAG,"ESP_GATTS_EXEC_WRITE_EVT");
esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL);
break;
//MTU事件
case ESP_GATTS_MTU_EVT:
ESP_LOGI(GATTS_TAG, "ESP_GATTS_MTU_EVT, MTU %d", param->mtu.mtu);
break;
case ESP_GATTS_UNREG_EVT:
break;
//创建服务事件
//保存创建的服务保存,并添加一个特征
case ESP_GATTS_CREATE_EVT:
ESP_LOGI(GATTS_TAG, "CREATE_SERVICE_EVT, status %d, service_handle %d\n", param->create.status, param->create.service_handle);
gl_profile_tab[PROFILE_A_APP_ID].service_handle = param->create.service_handle;
gl_profile_tab[PROFILE_A_APP_ID].char_uuid.len = ESP_UUID_LEN_16;
gl_profile_tab[PROFILE_A_APP_ID].char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_TEST_A; //0xff01
esp_ble_gatts_start_service(gl_profile_tab[PROFILE_A_APP_ID].service_handle);
a_property = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY;
esp_err_t add_char_ret = esp_ble_gatts_add_char(gl_profile_tab[PROFILE_A_APP_ID].service_handle, &gl_profile_tab[PROFILE_A_APP_ID].char_uuid,
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
a_property,
&gatts_demo_char1_val, NULL);
if (add_char_ret){
ESP_LOGE(GATTS_TAG, "add char failed, error code =%x",add_char_ret);
}
break;
case ESP_GATTS_ADD_INCL_SRVC_EVT:
break;
//添加特征事件
case ESP_GATTS_ADD_CHAR_EVT: {
uint16_t length = 0;
const uint8_t *prf_char;
ESP_LOGI(GATTS_TAG, "ADD_CHAR_EVT, status %d, attr_handle %d, service_handle %d\n",
param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle);
gl_profile_tab[PROFILE_A_APP_ID].char_handle = param->add_char.attr_handle;
gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.len = ESP_UUID_LEN_16;
gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;
esp_err_t get_attr_ret = esp_ble_gatts_get_attr_value(param->add_char.attr_handle, &length, &prf_char);
if (get_attr_ret == ESP_FAIL){
ESP_LOGE(GATTS_TAG, "ILLEGAL HANDLE");
}
ESP_LOGI(GATTS_TAG, "the gatts demo char length = %x\n", length);
for(int i = 0; i < length; i++){
ESP_LOGI(GATTS_TAG, "prf_char[%x] =%x\n",i,prf_char[i]);
}
esp_err_t add_descr_ret = esp_ble_gatts_add_char_descr(gl_profile_tab[PROFILE_A_APP_ID].service_handle, &gl_profile_tab[PROFILE_A_APP_ID].descr_uuid,
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, NULL, NULL);
if (add_descr_ret){
ESP_LOGE(GATTS_TAG, "add char descr failed, error code =%x", add_descr_ret);
}
break;
}
//添加特征描述符事件
case ESP_GATTS_ADD_CHAR_DESCR_EVT:
gl_profile_tab[PROFILE_A_APP_ID].descr_handle = param->add_char_descr.attr_handle;
ESP_LOGI(GATTS_TAG, "ADD_DESCR_EVT, status %d, attr_handle %d, service_handle %d\n",
param->add_char_descr.status, param->add_char_descr.attr_handle, param->add_char_descr.service_handle);
break;
case ESP_GATTS_DELETE_EVT:
break;
case ESP_GATTS_START_EVT:
ESP_LOGI(GATTS_TAG, "SERVICE_START_EVT, status %d, service_handle %d\n",
param->start.status, param->start.service_handle);
break;
case ESP_GATTS_STOP_EVT:
break;
//连接事件
case ESP_GATTS_CONNECT_EVT: {
esp_ble_conn_update_params_t conn_params = {0};
memcpy(conn_params.bda, param->connect.remote_bda, sizeof(esp_bd_addr_t));
/* For the IOS system, please reference the apple official documents about the ble connection parameters restrictions. */
conn_params.latency = 0;
conn_params.max_int = 0x20; // max_int = 0x20*1.25ms = 40ms
conn_params.min_int = 0x10; // min_int = 0x10*1.25ms = 20ms
conn_params.timeout = 400; // timeout = 400*10ms = 4000ms
ESP_LOGI(GATTS_TAG, "ESP_GATTS_CONNECT_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:",
param->connect.conn_id,
param->connect.remote_bda[0], param->connect.remote_bda[1], param->connect.remote_bda[2],
param->connect.remote_bda[3], param->connect.remote_bda[4], param->connect.remote_bda[5]);
gl_profile_tab[PROFILE_A_APP_ID].conn_id = param->connect.conn_id;
//start sent the update connection parameters to the peer device.
esp_ble_gap_update_conn_params(&conn_params);
break;
}
//断开连接
case ESP_GATTS_DISCONNECT_EVT:
ESP_LOGI(GATTS_TAG, "ESP_GATTS_DISCONNECT_EVT, disconnect reason 0x%x", param->disconnect.reason);
esp_ble_gap_start_advertising(&ble_adv_params);
break;
//确认事件
case ESP_GATTS_CONF_EVT:
ESP_LOGI(GATTS_TAG, "ESP_GATTS_CONF_EVT, status %d attr_handle %d", param->conf.status, param->conf.handle);
if (param->conf.status != ESP_GATT_OK){
esp_log_buffer_hex(GATTS_TAG, param->conf.value, param->conf.len);
}
break;
case ESP_GATTS_OPEN_EVT:
case ESP_GATTS_CANCEL_OPEN_EVT:
case ESP_GATTS_CLOSE_EVT:
case ESP_GATTS_LISTEN_EVT:
case ESP_GATTS_CONGEST_EVT:
default:
break;
}
}
==》创建了一个UUID为0xff01的GATT应用程序
==》当在这个应用程序触发写入事件写入task2中获得的flag,将使得open_task3置1
==》再当触发读取事件时,再根据open_task3为1就可以获取flag的值
触发写入事件:
再触发读取事件:
最后将获得的十六进制转ASCII就获得flag:THUCTF{WrItE_4_gA7T}
MQTT
由于项目中提供的mqtt靶机我访问不到了,我就干脆在本地根据项目提供的Dockerfile搭建一个:
FROM ubuntu:16.04
RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list && \
apt-get update
RUN apt-get install -y software-properties-common && \
apt-add-repository ppa:mosquitto-dev/mosquitto-ppa && \
apt-get install -y mosquitto
CMD ["/bin/sh","-c","service mosquitto start; while true ; do sleep 10; done"]
# docker build -t mqtt:v1 .
# docker run -p 1883:1883 -d mqtt:v1
本地采用ubuntu16.04
sudo apt-get update
sudo apt-get install -y software-properties-common
sudo apt-add-repository -y ppa:mosquitto-dev/mosquitto-ppa
sudo apt-get install -y mosquitto
sudo service mosquitto start
sudo service mosquitto status
使用MQTT工具测试一下:MQTTX,注意MQTT版本
靶机服务没问题,因为我的靶机是虚拟机,还需要再做一个端口转发:
#管理员运行
#netsh interface portproxy add v4tov4 listenaddress=本地IP listenport=本地端口 connectaddress=虚拟机IP connectport=虚拟机端口
netsh interface portproxy add v4tov4 listenaddress=192.168.182.170 listenport=1883 connectaddress=192.168.121.142 connectport=1883
#关闭端口转发
netsh interface portproxy delete v4tov4 listenaddress=192.168.182.170 listenport=1883
测试完毕,修改一下代码:main.c
最后重新编译,烧录即可
task1
你知道MQTT的上帝是谁么
//MQTT事件处理器回调函数
//event包含事件的详细信息
static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
{
//从事件中获取MQTT客户端的句柄
esp_mqtt_client_handle_t client = event->client;
int msg_id;
switch (event->event_id) {
//当MQTT客户端与代理连接时
case MQTT_EVENT_CONNECTED:
ESP_LOGI("mqtt", "MQTT_EVENT_CONNECTED");
//向主题(/topic/flag1)发布消息(mqtt_flag_1)
msg_id = esp_mqtt_client_publish(client, "/topic/flag1", mqtt_flag_1, 0, 1, 0);
printf("[+] MQTT task I: publish successful, msg_id=%d\n", msg_id);
//订阅主题(topic_2)
msg_id = esp_mqtt_client_subscribe(client, topic_2, 0);
printf("[+] MQTT task II: subscribe successful, msg_id=%d\n", msg_id);
break;
//断开连接触发
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI("mqtt", "MQTT_EVENT_DISCONNECTED");
break;
//当MQTT客户端成功订阅主题时触发
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI("mqtt", "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
break;
//当MQTT客户端取消订阅主题时触发
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI("mqtt", "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
break;
//MQTT客户端成功发布消息时触发
case MQTT_EVENT_PUBLISHED:
ESP_LOGI("mqtt", "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
break;
//MQTT接收订阅的消息时触发
case MQTT_EVENT_DATA:
ESP_LOGI("mqtt", "MQTT_EVENT_DATA");
printf("[+] MQTT task II: topic -> %.*s\r\n", event->topic_len, event->topic);
printf("[+] MQTT task II: data -> %.*s\r\n", event->data_len, event->data);
mqtt_data_hander(event->data_len,event->data);
break;
//错误
case MQTT_EVENT_ERROR:
ESP_LOGI("mqtt", "MQTT_EVENT_ERROR");
break;
default:
ESP_LOGI("mqtt", "Other event id:%d", event->event_id);
break;
}
return ESP_OK;
}
==》程序将flag发布到主题/topic/flag1上,订阅主题就可以接收到flag
==》我这里使用#订阅所有主题,就可以接收到flag:
(这里我在使用hexo g生成静态网页的时候发现文章后续消失了,只剩下到THUCTF为止,但是在我使用代码块框住时又没有问题了)
THUCTF{#_1s_God_in_MQTT}
task2
你能欺骗订阅者么
static void mqtt_app_start(char * broker)
{
char r[10]={0};
get_random(r,6);
//凭借随机字符串获得主题
sprintf(topic_2,"%s%s","/topic/flag2/",r);
esp_mqtt_client_config_t mqtt_cfg = {
.uri = broker,
};
//初始化MQTT客户端
client = esp_mqtt_client_init(&mqtt_cfg);
//注册事件处理函数
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);
//启动MQTT客户端,连接MQTT代理
esp_mqtt_client_start(client);
while(1){
printf("[+] MQTT task II: I send second flag to baidu\n");
//向topic_2主题发布消息
esp_mqtt_client_publish(client, topic_2, "www.baidu.com?46", 0, 1, 0);
vTaskDelay(10000 / portTICK_RATE_MS);
}
}
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
ESP_LOGD("mqtt", "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
mqtt_event_handler_cb(event_data);
}
//执行HTTP GET请求
static void http_get_task(char * webserver,char * httpdata)
{
const struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
};
struct addrinfo *res;
struct in_addr *addr;
int s;
//使用DNS查找指定webserver的IP地址,结果存储于res
getaddrinfo(webserver, "80", &hints, &res);
//从res中获取IP地址
addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
ESP_LOGI("network", "DNS lookup succeeded. IP=%s", inet_ntoa(*addr));
//创建套接字
s = socket(res->ai_family, res->ai_socktype, 0);
ESP_LOGI("network", "... allocated socket");
//连接指定主机
connect(s, res->ai_addr, res->ai_addrlen);
ESP_LOGI("network", "... connected");
//释放res
freeaddrinfo(res);
//向套接字写入hhtp请求
write(s, httpdata, strlen(httpdata));
//关闭套接字
close(s);
}
char tag3[] = " [+] MQTT task III: ";
void mqtt_data_hander(int length,char * data){
char l[10];
char url[500] = {0};
char out[500] = {0};
char httpdata[500]={0};
char flagdata[500]={0};
char tag3[] = " [+] MQTT task III: ";
//字符串拼接,注意其中有flag2和flag3
sprintf(flagdata,"%s%s%s",mqtt_flag_2,tag3,mqtt_flag_3);
int a = 46;
//在收到的数据中查找"?"的位置
char * p = strnstr(data,"?",length);
if(p){
int data_length = p - data;
//提取查询参数
snprintf(l,length - data_length,"%s",p+1);
//转整数
a = atoi(l);
length = data_length;
}
//收到的数据(不包含查询参数)复制到url
sprintf(url,"%.*s",length, data);
char fmt[] = "GET / HTTP/1.0\r\n"
"User-Agent: esp-idf/1.0 esp32\r\n"
"flag: %s\r\n"
"\r\n";
//收到的数据长度小于 flag和tag3的总长度
if( a < (int)(sizeof(mqtt_flag_2) + sizeof(tag3) - 1 ) ){
//将flag附加到out
memcpy(out,flagdata,a & 0xff);
//将http请求头填充到httpdata
sprintf(httpdata,fmt,out);
//发送http请求
http_get_task(url,httpdata);
}
}
==》程序会向topic_2主题发布消息”www.baidu.com?46“
==》程序会根据发布的消息向目标地址(www.baidu.com)发送请求
==》但是当我们给topic_2主题发布自己VPS的地址时,程序也会向VPS的80端口发送请求,请求中包含了flag
==》我们只需要在VPS上监听80端口,等待程序自动发送请求,就可以获得flag:THUCTF{attAck_t0_th3_dev1ce_tcp_r3cV_ch4nnel}
task3
这是个内存破坏的前戏
//字符串拼接,注意其中有flag2和flag3
sprintf(flagdata,"%s%s%s",mqtt_flag_2,tag3,mqtt_flag_3);
//收到的数据长度小于 flag和tag3的总长度
if( a < (int)(sizeof(mqtt_flag_2) + sizeof(tag3) - 1 ) ){
//将flag附加到out
//长度a & 0xff
memcpy(out,flagdata,a & 0xff);
//将http请求头填充到httpdata
sprintf(httpdata,fmt,out);
//发送http请求
http_get_task(url,httpdata);
}
==》注意此处在进行判断时使用的是有符号数,但是在后续memcpy中长度参数使用a & 0xff就将变为无符号数
==》当传入-1即可绕过限制,将flagdata中的flag3也复制进去
==》监听80端口,获得flag:THUCTF{0ver_the_Air_y0u_c4n_a77ack_t0_1ntranet_d3v1ce}
固件
查看提供的源码可以看到其实还有一个flag:
==》创建了一个以flag为名称的任务==》直接通过esptools提取固件
esptool.py.exe --baud 115200 read_flash 0x10000 0x310000 dump.bin
直接在固件文件中找HUCTF字符串就可以获得flag:HUCTF{DuMp_the_b1n_by_espt00l.py_Ju5t_1n_0ne_Lin3}