How to Secure Device Access to AWS IoT

Imagine a scenario where you can fake as many WiFi hotspots as you want and then wander through the crowd. When people a...
1. Introduction
2. Device access using Amazon Cognito authentication
3. Resource Cleanup (Optional)
Imagine a scenario where you can fake as many WiFi hotspots as you want and then wander through the crowd. When people around you search for WiFi hotspots, the screen is full of your fake WiFi hotspots, like this:

(In the picture above,'1. This advertising space rental','2. Silly upstairs','3. The three hotspots of marrying a programmer'were fake by me.)

You can use this technology to broadcast information such as a statement or a cough from a gunman. Of course, these access points can't be connected because they are false!

Want to know how this is done? Then follow along step by step.

Yes, first of all, this technology can only be used on linux, and there is also some pickiness about wireless network cards, which will be described below.

============================Stage One: Basic Principles==============

First, it needs to be clear how WiFi-enabled devices, such as mobile phones and computers, know about the existence of wireless hotspots around them. Wireless hotspots (usually wireless routers) periodically send Beacon frames outward, with the Chinese name beacon frames. One of the most important functions of beacon frames is to advertise the existence of wireless networks (but not just this).

The beacon frame contains some basic information about this wireless hotspot, such as ESSID (also known as the network name, such as "1. This advertising space is rented" in the image above), BSSID (MAC address of access point), encryption method (such as open no password, WEP encryption or WPA/WPA2 encryption), supported transmission rate, and so on.

When a wireless device receives a beacon frame, it knows that the surrounding access point exists. So, if I can construct a Beacon frame and send it out, then the wireless device will also think there is such a hot spot when it receives it. So when I construct many Beacon frames, each frame announces an access point and periodically sends these frames, the wireless device assumes there are many access points around it.

This is the basic principle of forging any WiFi hotspot.

=========================Stage 2: Familiar with Beacon frame formats============

Then the next goal is clear -- to construct Beacon frames. To construct a Beacon frame, you know the format of a Beacon frame. Before giving the Beacon frame format, you need to explain the general format of 802.11 managed frames:

The number above each field in the format refers to the number of bytes the field occupies, such as 2 bytes for Frame Control, 6 bytes for Destination Address, and so on.

1. Frame Control, the Chinese name is Frame Control Field, which takes up 2 bytes. Each bit is defined as follows:

(1) The Protocol field consists of two bits to display the version number used by the frame. There is currently only one version of 802.11, numbered 0. So the value of Protocol is 00.

(2) The Type field is composed of two bits, and the Sub type field is composed of four bits. There are three types of frames for 802.11 frames: management frame, control frame and data frame. Each type can be divided into several subtypes. Beacon frames have a Type field value of 00 (administrative frames) and Sub type field value of 1000.

(3) To DS bits and From DS bits are used to indicate whether the frame's destination is a distributed system, as defined below:

1. Introduction

The previous article in this series describes all the protocols supported by AWS IoT, the principles and details of how authentication and authorization work, and the scenarios in which they fit. The reference steps and codes for the device to access AWS IoT using IAM authentication are also described. Next, the reference steps and codes for the device to access AWS IoT using Amazon Cognito authentication are described.

Before proceeding to the next step, make sure that you have completed Chapter 4 of the first article in this series of documents and are ready to proceed.

2. Device access using Amazon Cognito authentication

You can use third-party identities, such as Google, Facebook, OIDC, SAML, or user-developed custom identities to exchange Cognito identities in the Cognito identity pool and use Cognito identities to authenticate devices. Cognito identity authentication is more complex. First, the Cognito identity pool configures a role for the authenticated identity, then Role's IAM Policy is used to authenticate the request. In addition, Cognito identity binds an IoT Policy in AWS IoT, which also authenticates the request. In fact, the ultimate permission for a Cognito identity is the intersection of the IAM policy of the identity pool Role and the IoT policy of the Cognito identity. Cognito identity can be used for mobile-side authentication for mobile scenarios.

Note: Since IoT Policy supports a variety of policy variables in the IoT context, it is generally recommended to give IAM Policy a larger privilege and then use policy variables in IoT Policy to achieve fine-grained privilege management.

Flow diagram of the device using Amazon Cognito authentication:

2.1 Create Amazon Cognito Identity Pool and Related Permissions

First, create a Cognito identity pool

IdentityPoolId=`aws cognito-identity create-identity-pool \ --identity-pool-name IoTDevicesPool \ --no-allow-unauthenticated-identities \ --developer-provider-name login.IoTDemo.dev | jq .IdentityPoolId| sed 's/"//g'`

Configure IdentityPoolId to an environment variable.

echo "export IdentityPoolId=$IdentityPoolId" >> ~/.bashrc

Create a role for Certified ognito identity substitution.

IoTDeviceRoleInCognitoArn=`aws iam create-role \ --role-name IoTDeviceRoleInCognito --assume-role-policy-document "{ \"Version\": \"2012-10-17\", \"Statement\": [ { \"Effect\": \"Allow\", \"Principal\": { \"Federated\": \"cognito-identity.amazonaws.com\" }, \"Action\": \"sts:AssumeRoleWithWebIdentity\", \"Condition\": { \"StringEquals\": { \"cognito-identity.amazonaws.com:aud\": \"$\" }, \"ForAnyValue:StringLike\": { \"cognito-identity.amazonaws.com:amr\": \"authenticated\" } } } ] }" | jq .Role.Arn | sed 's/"//g'`

Bind roles to Cognito identity pool

aws cognito-identity set-identity-pool-roles \ --identity-pool-id $ \ --roles authenticated=$

Bind permissions to roles that can Attach IoT Policy

When a device accesses an IoT using a Cognito identity, it also needs to have an IoT Policy bound to it. Usually, this step needs to be performed by the back-end service, considering the security of permissions. To simplify this, the device binds the IoT Policy to itself, which should be prohibited in the production system.

IoTPolicyManagerArn=`aws iam create-policy \ --policy-name IoTPolicyManager \ --policy-document "{ \"Version\": \"2012-10-17\", \"Statement\": [ { \"Sid\": \"VisualEditor0\", \"Effect\": \"Allow\", \"Action\": \"iot:AttachPolicy\", \"Resource\": \"*\" } ] }" | jq .Policy.Arn| sed 's/"//g'`

Bind IAM Policy to role.

aws iam attach-role-policy --role-name IoTDeviceRoleInCognito \ --policy-arn $

The Cognito identity pool supports a variety of authentication methods, such as Google, Facebook, Amazon, OID C, where developer-authenticated identities are used to obtain identities.

2.2 Create and deploy developer-authenticated identity programs

Create an IAM user, developerIdpUser. This user is used to provide permissions to the developer provider program.

The developer provider program assigns identities to devices and calls Cognito's GetOpenIdTokenForDeveloperIdentity interface to return the identity and token of the Cognito identity pool.

aws iam create-user --user-name developerIdpUser

Create a policy and bind it to the developerIdpUser user

developerIdpPolicy_arn=`aws iam create-policy \ --policy-name developerIdpPolicy \ --policy-document "{ \"Version\": \"2012-10-17\", \"Statement\": [ { \"Sid\": \"VisualEditor0\", \"Effect\": \"Allow\", \"Action\": \"cognito-identity:GetOpenIdTokenForDeveloperIdentity\", \"Resource\": \"arn:aws-cn:cognito-identity:cn-north-1:$:identitypool/$\" } ] }" | jq .Policy.Arn | sed 's/"//g'` aws iam attach-user-policy --user-name developerIdpUser \ --policy-arn $

Create Access Key for developerIdpUser user

aws iam create-access-key \ --user-name developerIdpUser > /tmp/IoT_demo_access_key2

Record AccessKeyId and SecretAccessKey

AccessKeyId=`cat /tmp/IoT_demo_access_key2 | jq .AccessKey.AccessKeyId| sed 's/"//g'` SecretAccessKey=`cat /tmp/IoT_demo_access_key2 | jq .AccessKey.SecretAccessKey| sed 's/"//g'`

Generate developer provider program.

cat <<EOF > ~/awsIoTAccessDemo/developer_provider.py #!/usr/bin/env python # -*- coding: utf-8 -*- import boto3 import argparse import json from flask import Flask, abort, request, jsonify #Get parameters parser = argparse.ArgumentParser(description='developer idp provider.') parser.add_argument('--developer_provider_name', default='login.IoTDemo.dev', help='developer_provider_name') parser.add_argument('--identityPoolId', required=True, help='identityPoolId') parser.add_argument('--AccessKeyId', required=True, help='AccessKeyId') parser.add_argument('--SecretAccessKey', required=True, help='SecretAccessKey') args = parser.parse_args() developer_provider_name = args.developer_provider_name identityPoolId = args.identityPoolId access_key_id = args.AccessKeyId secret_access_key = args.SecretAccessKey app = Flask(__name__) region = 'cn-north-1' tokenDuration = 3600 cognito_client = boto3.client('cognito-identity',region_name=region,aws_access_key_id=access_key_id,aws_secret_access_key=secret_access_key) #cognito_client = boto3.client('cognito-identity',region_name=region) @app.route('/login/', methods=['POST']) def login(): if not request.json or 'device_id' not in request.json: abort(400) device_id = request.json['device_id'] try: response = cognito_client.get_open_id_token_for_developer_identity(IdentityPoolId=identityPoolId,Logins=,TokenDuration=tokenDuration) token = response['Token'] identityId = response['IdentityId'] return jsonify({'result': 'success','identityId':identityId,'token':token}) except Exception as e: return jsonify({'result': 'failed'}) if __name__ == "__main__": # Setting host to 0.0.0.0 will also allow users outside the network to access the service app.run(host="0.0.0.0", port=8383, debug=True) EOF

Run developer_provider.py

python developer_provider.py --identityPoolId $ \ --AccessKeyId $ --SecretAccessKey $

developer_provider.py in   http://0.0.0.0:8383/   Accept the request and return the identity information of the Cognito identity pool.

Open a new SSH window and navigate to the awsIoTAccessDemo directory.

cd ~/awsIoTAccessDemo

2.3 Device Access Using HTTP Protocol

Devices certified by Cognito require both IAM Policy and IoT Policy authorization.

Create an IAM Policy for the device and bind it to the role of the Cognito identity pool.

IoTDeviceCognitoHttpPolicyArn=`aws iam create-policy \ --policy-name IoTDeviceCognitoHttpPolicy \ --policy-document "{ \"Version\": \"2012-10-17\", \"Statement\": [ { \"Sid\": \"VisualEditor0\", \"Effect\": \"Allow\", \"Action\": \"iot:Publish\", \"Resource\": [ \"arn:aws-cn:iot:cn-north-1:$:topic/IoTDemo/device_cognito_http\" ] } ] }" | jq .Policy.Arn | sed 's/"//g'` aws iam attach-role-policy --role-name IoTDeviceRoleInCognito \ --policy-arn $

Create IoT Policy

aws iot create-policy --policy-name IoTPolicyForDeviceCognitohttp \ --policy-document "{ \"Version\": \"2012-10-17\", \"Statement\": [ { \"Effect\": \"Allow\", \"Action\": \"iot:Publish\", \"Resource\": [ \"arn:aws-cn:iot:cn-north-1:$:topic/IoTDemo/device_cognito_http\" ] } ] }"

Generate device emulator.

cat <<EOF > ~/awsIoTAccessDemo/device_cognito_http.py #!/usr/bin/env python # -*- coding: utf-8 -*- import boto3 import argparse import json import requests #Get parameters parser = argparse.ArgumentParser(description='Send data to IoT Core') parser.add_argument('--data', default="data from device_cognito_http", help='data to IoT') parser.add_argument('--iot_policy_name', default="IoTPolicyForDeviceCognitohttp", help='iot policy name for device cognito http.') parser.add_argument('--developer_provicer_endpoint', default="http://127.0.0.1:8383/login/", help='developer ID provider endpoint') args = parser.parse_args() data = args.data policy_name = args.iot_policy_name developer_provicer_endpoint = args.developer_provicer_endpoint device_name = 'device_cognito_http' region = 'cn-north-1' topic = "IoTDemo/"+device_name headers = {"Content-Type": "application/json"} body = {"device_id":device_name} r1 = requests.post(developer_provicer_endpoint, data=json.dumps(body), headers = headers) token = json.loads(r1.text)['token'] identityId = json.loads(r1.text)['identityId'] cognito_client = boto3.client('cognito-identity', region_name=region) response = cognito_client.get_credentials_for_identity(IdentityId=identityId, Logins={'cognito-identity.cn-north-1.amazonaws.com.cn':token}) sessionToken=response['Credentials']['SessionToken'] accessKeyId=response['Credentials']['AccessKeyId'] secretKey=response['Credentials']['SecretKey'] iot_client = boto3.client('iot',region_name=region,aws_access_key_id=accessKeyId,aws_secret_access_key=secretKey,aws_session_token=sessionToken) response = iot_client.attach_policy( policyName=policy_name, target=identityId ) iot_data_client = boto3.client('iot-data',region_name=region,aws_access_key_id=accessKeyId,aws_secret_access_key=secretKey,aws_session_token=sessionToken) response = iot_data_client.publish( topic=topic, qos=0, payload=json.dumps({"source":device_name, "data":data}) ) EOF

Run the device emulator to send a message.

python device_cognito_http.py --data "data from device cognito http." \ --developer_provicer_endpoint "http://127.0.0.1:8383/login/" \ --iot_policy_name IoTPolicyForDeviceCognitohttp

View messages received by AWS IoT in the console opened in Chapter 4.3 of the first article in this series.

2.4 Device Access Using MQTT OVER WEBSOCKET Protocol

Devices certified by Cognito require both IAM Policy and IoT Policy authorization.

Create an IAM Policy for the device and bind it to the Cognito role.

IoTDeviceCognitoWebsocketPolicyArn=`aws iam create-policy \ --policy-name IoTDeviceCognitoWebsocketPolicy \ --policy-document "{ \"Version\": \"2012-10-17\", \"Statement\": [ { \"Sid\": \"VisualEditor0\", \"Effect\": \"Allow\", \"Action\": [ \"iot:Publish\", \"iot:Receive\" ], \"Resource\": \"arn:aws-cn:iot:cn-north-1:$:topic/IoTDemo/device_cognito_websocket\" }, { \"Sid\": \"VisualEditor1\", \"Effect\": \"Allow\", \"Action\": \"iot:Connect\", \"Resource\": \"arn:aws-cn:iot:cn-north-1:$:client/device_cognito_websocket\" }, { \"Sid\": \"VisualEditor2\", \"Effect\": \"Allow\", \"Action\": \"iot:Subscribe\", \"Resource\": \"arn:aws-cn:iot:cn-north-1:$:topicfilter/IoTDemo/device_cognito_websocket\" } ] }" | jq .Policy.Arn | sed 's/"//g'` aws iam attach-role-policy --role-name IoTDeviceRoleInCognito \ --policy-arn $

Create an IoT Policy.

aws iot create-policy --policy-name IoTPolicyForDeviceCognitoWebsocket \ --policy-document "{ \"Version\": \"2012-10-17\", \"Statement\": [ { \"Sid\": \"VisualEditor0\", \"Effect\": \"Allow\", \"Action\": [ \"iot:Publish\", \"iot:Receive\" ], \"Resource\": \"arn:aws-cn:iot:cn-north-1:$:topic/IoTDemo/device_cognito_websocket\" }, { \"Sid\": \"VisualEditor1\", \"Effect\": \"Allow\", \"Action\": \"iot:Connect\", \"Resource\": \"arn:aws-cn:iot:cn-north-1:$:client/device_cognito_websocket\" }, { \"Sid\": \"VisualEditor2\", \"Effect\": \"Allow\", \"Action\": \"iot:Subscribe\", \"Resource\": \"arn:aws-cn:iot:cn-north-1:$:topicfilter/IoTDemo/device_cognito_websocket\" } ] }"

Generate device emulator.

cat <<EOF > ~/awsIoTAccessDemo/device_cognito_websocket.py #!/usr/bin/env python # -*- coding: utf-8 -*- from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient import json import time import sys import boto3 import requests import argparse #Get parameters parser = argparse.ArgumentParser(description='Send data to IoT Core') parser.add_argument('--iot_policy_name', default="IoTPolicyForDeviceCognitoWebsocket", help='iot policy name for device_cognito_websocket.') parser.add_argument('--developer_provicer_endpoint', default="http://127.0.0.1:8383/login/", help='developer ID provider endpoint') parser.add_argument('--endpoint_prefix', required=True, help='your iot endpoint prefix.') args = parser.parse_args() policy_name = args.iot_policy_name developer_provicer_endpoint = args.developer_provicer_endpoint endpoint_prefix = args.endpoint_prefix device_name = 'device_cognito_websocket' region = 'cn-north-1' private_topic = "IoTDemo/"+device_name server_root_ca_file = "./AmazonRootCA1.pem" endpoint = "%s.ats.iot.cn-north-1.amazonaws.com.cn" % endpoint_prefix port = 443 headers = {"Content-Type": "application/json"} body = {"device_id":device_name} r1 = requests.post(developer_provicer_endpoint, data=json.dumps(body), headers = headers) token = json.loads(r1.text)['token'] identityId = json.loads(r1.text)['identityId'] cognito_client = boto3.client('cognito-identity', region_name=region) response = cognito_client.get_credentials_for_identity(IdentityId=identityId, Logins={'cognito-identity.cn-north-1.amazonaws.com.cn':token}) sessionToken=response['Credentials']['SessionToken'] accessKeyId=response['Credentials']['AccessKeyId'] secretKey=response['Credentials']['SecretKey'] iot_client = boto3.client('iot',region_name=region,aws_access_key_id=accessKeyId,aws_secret_access_key=secretKey,aws_session_token=sessionToken) response = iot_client.attach_policy( policyName=policy_name, target=identityId ) # Custom MQTT message callback def customCallback(client, userdata, message): print("Received a new message: ") print(message.payload) print("from topic: ") print(message.topic) print("--------------\n\n") def pub_msg(): try: pri_loopCount = 0 while True: print 'please input:', msg = raw_input() private_data = msg message = {} message['message'] = json.dumps({"source":device_name, "data":private_data}) message['sequence'] = pri_loopCount messageJson = json.dumps(message) myAWSIoTMQTTClient.publish(private_topic, messageJson, 1) pri_loopCount += 1 time.sleep(2) except: sys.exit() if __name__ == '__main__': # Init AWSIoTMQTTClient myAWSIoTMQTTClient = None myAWSIoTMQTTClient = AWSIoTMQTTClient(device_name, useWebsocket=True) myAWSIoTMQTTClient.configureEndpoint(endpoint, port) myAWSIoTMQTTClient.configureCredentials(server_root_ca_file) myAWSIoTMQTTClient.configureIAMCredentials(accessKeyId, secretKey, sessionToken) # AWSIoTMQTTClient connection configuration myAWSIoTMQTTClient.configureAutoReconnectBackoffTime(1, 32, 20) myAWSIoTMQTTClient.configureOfflinePublishQueueing(-1) # Infinite offline Publish queueing myAWSIoTMQTTClient.configureDrainingFrequency(2) # Draining: 2 Hz myAWSIoTMQTTClient.configureConnectDisconnectTimeout(10) # 10 sec myAWSIoTMQTTClient.configureMQTTOperationTimeout(5) # 5 sec # Connect and subscribe to AWS IoT myAWSIoTMQTTClient.connect() myAWSIoTMQTTClient.subscribe(private_topic, 1, customCallback) time.sleep(2) pub_msg() EOF

Run the device emulator.

python device_cognito_websocket.py \ --developer_provicer_endpoint "http://127.0.0.1:8383/login/" \ --iot_policy_name IoTPolicyForDeviceCognitoWebsocket \ --endpoint_prefix $

The device emulator runs continuously, accepts input data, sends it to AWS IoT Core, and subscribes to topic s that it sends messages to.

Enter a message to be sent to AWS IoT, such as "data from device cognito websocket." The device receives this message from itself. You can also see this message in the console opened in Chapter 4.3 of the first article in this series.

Execute Ctrl+C stop program.

3. Resource Cleanup (Optional)

aws iam detach-role-policy --role-name IoTDeviceRoleInCognito --policy-arn arn:aws-cn:iam::$:policy/IoTPolicyManager aws iam detach-role-policy --role-name IoTDeviceRoleInCognito --policy-arn arn:aws-cn:iam::$:policy/IoTDeviceCognitoHttpPolicy aws iam detach-role-policy --role-name IoTDeviceRoleInCognito --policy-arn arn:aws-cn:iam::$:policy/IoTDeviceCognitoWebsocketPolicy aws iam delete-policy --policy-arn arn:aws-cn:iam::$:policy/IoTDeviceCognitoHttpPolicy aws iam delete-policy --policy-arn arn:aws-cn:iam::$:policy/IoTPolicyManager aws iam delete-policy --policy-arn arn:aws-cn:iam::$:policy/IoTDeviceCognitoWebsocketPolicy aws iam delete-role --role-name IoTDeviceRoleInCognito aws cognito-identity delete-identity-pool --identity-pool-id $IdentityPoolId sed -i '/IdentityPoolId/d' ~/.bashrc aws iam detach-user-policy --user-name developerIdpUser --policy-arn arn:aws-cn:iam::$:policy/developerIdpPolicy aws iam delete-policy --policy-arn arn:aws-cn:iam::$:policy/developerIdpPolicy AccessKeyId=`cat /tmp/IoT_demo_access_key2 | jq .AccessKey.AccessKeyId| sed 's/"//g'` aws iam delete-access-key --access-key-id $AccessKeyId --user-name developerIdpUser aws iam delete-user --user-name developerIdpUser rm -f /tmp/IoT_demo_access_key2 target1=`aws iot list-targets-for-policy --policy-name IoTPolicyForDeviceCognitohttp | jq .targets[0] | sed 's/"//g'` aws iot detach-policy --policy-name IoTPolicyForDeviceCognitohttp --target $target1 aws iot delete-policy --policy-name IoTPolicyForDeviceCognitohttp target2=`aws iot list-targets-for-policy --policy-name IoTPolicyForDeviceCognitoWebsocket | jq .targets[0] | sed 's/"//g'`

Since Beacon frames are administrative frames, To DS and From DS are both 0.

(4) The Mor Fragments bit, similar to the more fragments bit of an IP packet. Because 802.11 frames have a length limitation on the load, when a large amount of data is passed in from the upper layer, it needs to be transmitted in segments. Beacon frames do not need to be segmented, so the bit is 0.

(5) Retry bit. Sometimes a frame needs to be retransmitted, and any retransmitted frame should have this position of 1, otherwise 0. Beacon frames do not have retransmissions, so the bit is 0.

(6) Power Management bit. Many wireless devices are powered by batteries, such as mobile phones. Turning off the wireless transmitter can extend the battery life when there is no data flow. If the wireless device wants the position to be 1, that means the wireless device will enter power-saving mode after the frame (or this data exchange) transmission is complete. Since access points are not allowed to enter power-saving mode, this bit of the Beacon frame is 0.

(7) More data bits. In order to service wireless devices in power-saving mode, access points cache the frames to be passed to the wireless devices. If the wireless device wakes up from power-saving mode and finds position 1 in a frame, the access point has more cached data to send to the wireless device. If you're just faking Beacon frames, this bit will always be 0.

(8) If the Protected Frame bit is set to 1, then the frame is protected by link-layer security protocols, such as WEP and WPA/WPA2. Beacon management frames do not require encryption, so 0.

(9) If the Order bit is positioned, then the frame enters a strict sequential transmission mode, but the sender and receiver must pay an extra price. This bit of Beacon frame is 0.

Thus, the Frame Control field of the Beacon frame we constructed is 0 × 800 × 00 (in the order shown, 00000001,00000000, after which the highest is considered, then 10000000,00000, or 0, for binary × 800 × 00).

2. The Duration field is used to reserve media usage time in 802.11 frames. Simply put, each frame tells all wireless devices through the Duration field:'How long do I still need to use the medium!' The Duration field guarantees that a series of atomic operations will not be interrupted, provided, of course, that everyone follows 802.11 ~and Beacon frames are broadcast and have no subsequent data interaction, so their Duration is 0, or 0 × 00 0 × 00.

3. Destination Address is the destination address and the MAC address of the receiver. Since Beacon frames are broadcast frames, the destination address is the broadcast address, which is FF:FF:FF:FF:FF:FF:FF.

4. Source Address is the source address and the MAC address of the sender. The sender address is usually the MAC address of the access point, but there are exceptions. For example, if a repeater is added in the middle, the MAC address of the sender is the address of the repeater.

5. BSSID is the MAC address of the access point.

6. Seq-ID (Sequence Control) field is named Sequence Control field. Its lower 4 digits are Segment Number, while the higher 12 digits are Sequence Number. The difference between the frame fragments is the segment number. The first segment is numbered 0, followed by each segment in the same order. Except for the last segment, the More data bits for all segments are positioned. Since Beacon frames are usually not segmented, the low 4-bit number is 0000 and the high 12-bit number is sequential.

7. Frame body is the frame body. If the frame is a data frame, then the frame theme is the payload of the data, and if the frame is managed, it is usually a variety of information elements (explained below).

8. FCS, the Chinese name frame check sequence, is usually the cyclic redundancy check code CRC.

=========================Stage 3: Familiarize with Beacon Frame Body===========

From the above analysis, you can see that the most important content is contained in the Frame body. The Frame body for managing frames consists of several information elements. Information elements consist of fixed length information elements and variable length information elements. Fixed-length information elements take up a fixed number of bytes, such as Timestamp, which takes up 8 bytes. The number of bytes consumed by variable-length information elements is uncertain, such as the ESSID representing the network name in a Beacon frame. Information elements can also be divided into required and optional information elements. The Beacon frame format is detailed in the following figure (referenced from 802.11 Wireless Network Authoritative Guide, 2nd Edition):

Complex, isn't it? Since we're just going to construct one that meets the criteria, we can simply construct the simplest Frame body from the simplest process, which only needs to contain the four required information elements: Timestamp, Beacon interval, Capability info, and SSID.

1. Timestamp, an 8-byte timestamp, can be used to synchronize wireless devices in BSS. The BSS master timer periodically transmits the number of microseconds that are currently running. When the counter reaches its maximum value, it starts counting from 0. For 64-bit counters that can count more than 580,000 years, count them from scratch.

2. Beacon interval, which takes up 2 bytes, is used to set the unit of time between Beacon signals. Time units are usually abbreviated as TU and represent 1024 microseconds. Beacon interval is usually set to 100 TUs, sending Beacon signals approximately every 0.1 seconds.

3. Capability info has 16 bits to tell the network what performance it has. Each bit represents a tag corresponding to a particular function of the network. The workstation uses these announcement data to determine if it supports all of the BSS's functions. Workstations that do not implement all the features in the performance announcement cannot join the BSS. Your definitions are as follows (referenced from 802.11 Wireless Network Authoritative Guide, 2nd Edition):

(1) The ESS location indicates that the network is the basic structure of an extended set of services, that is, the network that access points usually create. IBSS and ESS are mutually exclusive. If IBSS is in place, the network is a stand-alone basic server network, also known as a direct connection to a wireless network card.

(2) CF-Pollable and CF-Poll request are non-competitive-polling bits, representing functions related to power saving mode. When the workstation wakes up from power-saving mode, it can poll the workstation for cached frames. Poll means polling. For an access point, the combination of the two represents the following table:

CF-POLLABLECF-POLL REQUESTMeaning00Access point does not support point coordination function01Access point uses PCF for delivery, but does not support polling10Access Points use PCF for delivery and polling11Reserved, not yet used

(3) Privacy, confidentiality. If the Privacy bit is set to zero and there is no WPA information element next, the wireless network is open without a password. Setting this to 1 means you need to use WEP to maintain confidentiality.

(4) Short Preamble, Short Leader, 802.11b specification added this field to support the High Speed Direct Sequence Spread Spectrum Physical Layer. Set to 1 to indicate that this network is currently using a short preamble. 0 means that this selection is not used and short preambles are prohibited in this BSS. 802.11g specifies a short preamble, so this field must be 1 in a network built according to the 802.11g standard.

(5) PBCC, enveloped binary cyclocode, 802.11b specification adds this field to support high-speed direct sequence spread spectrum physical layer. Set to 1 to indicate that this network currently uses a packet binary cyclocode modulation mechanism, 0 to indicate that this option is not used and that the use of packet binary cyclocodes is prohibited in this BSS.

(6) Channel Agility, Mobile Channel Conversion, this field is added to the 802.11b specification to support the High Speed Direct Sequence Spread Spectrum Physical Layer. Set to 1 to indicate that this network uses the Mobile Channel Conversion option, 0 to indicate that this option is not used and that Mobile Channel Conversion is prohibited in this BSS.

(7) Short Slot Time, which was added to the 802.11g specification and set to 1 to represent a shorter time slot.

(8) DSSS-OFDM, which is new to 802.11g specification and is set to 1 to represent frame construction using DSSS-OFDM.

If we want to construct a simplest Beacon frame, the Capability info field can be set to zero × 01 0 × 00, if you want to become WEP encrypted, you can set it to 0 × 11 0 × 00, of course, many options don't matter.

4. SSID, the service set identifier, is a variable-length information element, commonly referred to as the network name. The common format for variable-length information elements is:

The Element ID of the SSID is 0. Some documents treat SSIDs as network names because network administrators typically specify SSIDs as strings. In fact, an SSID is simply a string of bytes that identifies the BSSID of the network to which it belongs. Some products require that this string must be an ASCII string that ends with null (that is, 0), although the standard does not have a special specification for this. The length of the SSID is between 0 and 32 bytes. If the name of the hot spot to be forged is "h e l l o", then this element should be 0,5,'h','e','l','l','o'.

=======================Stage 4: Write code to construct Beacon frames===========

Thus, we have worked out every detail of a simplest Beacon frame, except for the final check code FCS. Let's summarize how the final Beacon frame looks - for example, I want to fake an open one called "hello,carrot!" Access point with MAC address ec:17:2f:2d:b6:b8, then the simplest Beacon frame should look like this:

fieldvalueFrame Control0×80 0×00Duration0×00 0×00Destination Address0xFF 0xFF 0xFF 0xFF 0xFF 0xFFSource Address0xEC 0×17 0x2F 0x2D 0xB6 0xB8BSSID0xEC 0×17 0x2F 0x2D 0xB6 0xB8Seq-IDLow 4:0 × 0, 12 bits high: frame numberTimestampNumber of microseconds currently runningBeacon interval0×64 0×00(100)Capability info0×01 0×00SSID0×00 0x0D 'h' 'e' 'l' 'l' 'o' ',' 'c' 'a' 'r' 'r' 'o' 't' '!'FCSCyclic redundancy checkcodes, fortunately the driver calculates itself

Very concise,

In order to be dynamic, it is necessary to define a structure, so let's see how the code is implemented.

First is the definition of an access point structure:

struct ap { uint8 bssid[6]; uint16 seq_id; uint8 essid_len; char essid[32]; };

Then the initialization function for struct ap:

void init_ap(struct ap* p_ap,uint8* p_bssid,char* p_essid) { memcpy(p_ap->bssid,p_bssid,6); p_ap->seq_id=0; uint32 t_len=strlen(p_essid); if(t_len>32) t_len=32; p_ap->essid_len=t_len; memcpy(p_ap->essid,p_essid,t_len); }

Finally, a function to construct beacon frames from struct ap:

uint16 create_beacon_frame(uint8* p_buffer,struct ap* p_ap) { memcpy(p_buffer,"\x80\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF",10); memcpy(p_buffer+10,p_ap->bssid,6); memcpy(p_buffer+16,p_ap->bssid,6); p_buffer[22]=(uint8)(p_ap->seq_id&0xFF); p_buffer[23]=(uint8)((p_ap->seq_id>>8)&0xFF); p_ap->seq_id+=0x10; struct timeval t_time; gettimeofday(&t_time,0); uint64 t_timestamp=((uint64)t_time.tv_sec)*1000000+t_time.tv_usec; uint8 t_i; for(t_i=0;t_i<8;t_i++) p_buffer[24+t_i]=(uint8)((t_timestamp>>(t_i<<3))&0xFF); memcpy(p_buffer+32,"\x64\x00\x01\x00",4); p_buffer[36]=0; p_buffer[37]=p_ap->essid_len; memcpy(p_buffer+38,p_ap->essid,p_ap->essid_len); return 38+p_ap->essid_len; }

As for how to use it, here are the following:

struct ap t_ap; init_ap(&t_ap,(uint8*)"\xEC\x17\x2F\x2D\xB6\xB8","zjs"); uint8 t_buffer[256]; uint16 t_len=create_beacon_frame(t_buffer,&t_ap);

This will enable us to do this at t_ A beacon frame named zjs with a MAC address of ec:17:2f:2d:b6:b8 was generated in the buffer.

===========================Phase 5: Send Beacon Frame================

Constructed beacon frame, almost sent. Do you think you can just create a socket and send it? How naive! Sending this is not easy. I also read the source code in the aircrack-ng suite to know how to send it. To get the wireless network card to send the original 802.11 frames, you first need to set the wireless network card to monitor mode. Setting the wireless network card to monitor mode can use ifconfig and iwconfig commands, of course, if you want to implement your own code, you can refer to the source code of ifconfig and iwconfig commands.

ifconfig wlan0 down iwconfig wlan0 mode monitor ifconfig wlan0 up

The three commands above, turn off wlan0, set wlan0 to monitor mode, and turn on wlan0, remember to have administrator privileges.

After setting to monitor mode, you need to create link-layer raw sockets bound to wlan0 in your code, in order to create link-layer sockets, find out the network card number of wlan0, bind the original sockets to wlan0, and set the original sockets to mixed mode, as follows:

int32 create_raw_socket(char* p_iface) { /* new raw socket */ int32 t_socket=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL)); if(t_socket<0) { perror("<create_raw_socket> socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL)) failed!"); return -1; } /* get the index of the interface */ struct ifreq t_ifr; memset(&t_ifr,0,sizeof(t_ifr)); strncpy(t_ifr.ifr_name,p_iface,sizeof(t_ifr.ifr_name)-1); if(ioctl(t_socket,SIOCGIFINDEX,&t_ifr)<0) { perror("<create_raw_socket> ioctl(SIOCGIFINDEX) failed!"); return -1; } /* bind the raw socket to the interface */ struct sockaddr_ll t_sll; memset(&t_sll,0,sizeof(t_sll)); t_sll.sll_family=AF_PACKET; t_sll.sll_ifindex=t_ifr.ifr_ifindex; t_sll.sll_protocol=htons(ETH_P_ALL); if(bind(t_socket,(struct sockaddr*)&t_sll,sizeof(t_sll))<0) { perror("<create_raw_socket> bind(ETH_P_ALL) failed!"); return -1; } /* open promisc */ struct packet_mreq t_mr; memset(&t_mr,0,sizeof(t_mr)); t_mr.mr_ifindex=t_sll.sll_ifindex; t_mr.mr_type=PACKET_MR_PROMISC; if(setsockopt(t_socket,SOL_PACKET,PACKET_ADD_MEMBERSHIP,&t_mr,sizeof(t_mr))<0) { perror("<create_raw_socket> setsockopt(PACKET_MR_PROMISC) failed!"); return -1; } return t_socket; }

P_in parameter Iface is usually wlan0.

If monitor mode is turned on and the original socket is created, can t_be sent directly? Buffer? too young too simple! One more radiotap head! I was not lightly impressed by this pit at that time. Later, I saw the source code of aireply-ng, grabbed bags, and Baidu, then I could roughly understand the role of radiotap. During packet capture, a radiotap header is attached to the wireless network card to display information related to the physical layer, such as power, rate. In the process of sending the package, radiotap gives the wireless network card some reference information.

The function to attach a radiotap header and send a packet is as follows:

int32 send_80211_frame(int32 p_socket,uint8* p_buffer,uint32 p_size) { uint8 t_buffer[4096]; uint8* t_radiotap=(uint8*)"\x00\x00\x0d\x00\x04\x80\x02\x00\x02\x00\x00\x00\x00"; memcpy(t_buffer,t_radiotap,13); memcpy(t_buffer+13,p_buffer,p_size); p_size+=13; int32 t_size=write(p_socket,t_buffer,p_size); if(t_size<0) { perror("<send_80211_frame> write() failed!"); return -1; } return t_size; }

You can see that 13 bytes are added before the beacon frame is generated.

=====================Stage 6: Integration, Testing Results==================

Now that all the code is complete, here is a complete code:

#include <unistd.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <netpacket/packet.h> #include <linux/if_ether.h> #include <sys/ioctl.h> #include <arpa/inet.h> #include <sys/time.h> #include <net/if.h> typedef unsigned char bool; typedef signed char int8; typedef unsigned char uint8; typedef signed short int16; typedef unsigned short uint16; typedef signed int int32; typedef unsigned int uint32; typedef signed long long int64; typedef unsigned long long uint64; struct ap { uint8 bssid[6]; uint16 seq_id; uint8 essid_len; char essid[32]; }; void init_ap(struct ap* p_ap,uint8* p_bssid,char* p_essid) { memcpy(p_ap->bssid,p_bssid,6); p_ap->seq_id=0; uint32 t_len=strlen(p_essid); if(t_len>32) t_len=32; p_ap->essid_len=t_len; memcpy(p_ap->essid,p_essid,t_len); } uint16 create_beacon_frame(uint8* p_buffer,struct ap* p_ap) { memcpy(p_buffer,"\x80\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF",10); memcpy(p_buffer+10,p_ap->bssid,6); memcpy(p_buffer+16,p_ap->bssid,6); p_buffer[22]=(uint8)(p_ap->seq_id&0xFF); p_buffer[23]=(uint8)((p_ap->seq_id>>8)&0xFF); p_ap->seq_id+=0x10; struct timeval t_time; gettimeofday(&t_time,0); uint64 t_timestamp=((uint64)t_time.tv_sec)*1000000+t_time.tv_usec; uint8 t_i; for(t_i=0;t_i<8;t_i++) p_buffer[24+t_i]=(uint8)((t_timestamp>>(t_i<<3))&0xFF); memcpy(p_buffer+32,"\x64\x00\x01\x00",4); p_buffer[36]=0; p_buffer[37]=p_ap->essid_len; memcpy(p_buffer+38,p_ap->essid,p_ap->essid_len); return 38+p_ap->essid_len; } int32 create_raw_socket(char* p_iface) { /* new raw socket */ int32 t_socket=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL)); if(t_socket<0) { perror("<create_raw_socket> socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL)) failed!"); return -1; } /* get the index of the interface */ struct ifreq t_ifr; memset(&t_ifr,0,sizeof(t_ifr)); strncpy(t_ifr.ifr_name,p_iface,sizeof(t_ifr.ifr_name)-1); if(ioctl(t_socket,SIOCGIFINDEX,&t_ifr)<0) { perror("<create_raw_socket> ioctl(SIOCGIFINDEX) failed!"); return -1; } /* bind the raw socket to the interface */ struct sockaddr_ll t_sll; memset(&t_sll,0,sizeof(t_sll)); t_sll.sll_family=AF_PACKET; t_sll.sll_ifindex=t_ifr.ifr_ifindex; t_sll.sll_protocol=htons(ETH_P_ALL); if(bind(t_socket,(struct sockaddr*)&t_sll,sizeof(t_sll))<0) { perror("<create_raw_socket> bind(ETH_P_ALL) failed!"); return -1; } /* open promisc */ struct packet_mreq t_mr; memset(&t_mr,0,sizeof(t_mr)); t_mr.mr_ifindex=t_sll.sll_ifindex; t_mr.mr_type=PACKET_MR_PROMISC; if(setsockopt(t_socket,SOL_PACKET,PACKET_ADD_MEMBERSHIP,&t_mr,sizeof(t_mr))<0) { perror("<create_raw_socket> setsockopt(PACKET_MR_PROMISC) failed!"); return -1; } return t_socket; } int32 send_80211_frame(int32 p_socket,uint8* p_buffer,uint32 p_size) { uint8 t_buffer[4096]; uint8* t_radiotap=(uint8*)"\x00\x00\x0d\x00\x04\x80\x02\x00\x02\x00\x00\x00\x00"; memcpy(t_buffer,t_radiotap,13); memcpy(t_buffer+13,p_buffer,p_size); p_size+=13; int32 t_size=write(p_socket,t_buffer,p_size); if(t_size<0) { perror("<send_80211_frame> write() failed!"); return -1; } return t_size; } int32 main() { struct ap t_ap1,t_ap2; init_ap(&t_ap1,(uint8*)"\xEC\x17\x2F\x2D\xB6\xB8","zjs ap 1"); init_ap(&t_ap2,(uint8*)"\xEC\x17\x2F\x2D\xB6\xB9","zjs ap 2"); uint8 t_buffer[1024]; int32 t_socket=create_raw_socket("wlan0"); while(1) { uint16 t_len=create_beacon_frame(t_buffer,&t_ap1); printf("%dn",send_80211_frame(t_socket,t_buffer,t_len)); t_len=create_beacon_frame(t_buffer,&t_ap2); printf("%dn",send_80211_frame(t_socket,t_buffer,t_len)); usleep(100000); } return 0; }

Save as ~/beacon.c

Compile:

gcc beacon.c -o beacon

Execute (requires administrator privileges):

ifconfig wlan0 down iwconfig wlan0 mode monitor ifconfig wlan0 up ./beacon

After execution, the console keeps outputting the actual writing length of the frame, which should be 59. If you change the name of a hot spot, it should change accordingly.

At this point, by turning on the phone, you can search for the wifi hotspots "zjs ap 1" and "zjs ap 2". If not, please be patient for a moment. Currently, hot spots can be found on K-Touch W68a (Android 4.2.2), iPhone 6, vivo Y22L (Android 4.3), as illustrated by the following:

Of course, you want to play harder, like this:

#include <unistd.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <netpacket/packet.h> #include <linux/if_ether.h> #include <sys/ioctl.h> #include <arpa/inet.h> #include <sys/time.h> #include <net/if.h> typedef unsigned char bool; typedef signed char int8; typedef unsigned char uint8; typedef signed short int16; typedef unsigned short uint16; typedef signed int int32; typedef unsigned int uint32; typedef signed long long int64; typedef unsigned long long uint64; #define AP_COUNT 8 struct ap { uint8 bssid[6]; uint16 seq_id; uint8 essid_len; char essid[32]; }; void init_ap(struct ap* p_ap,uint8* p_bssid,char* p_essid) { memcpy(p_ap->bssid,p_bssid,6); p_ap->seq_id=0; uint32 t_len=strlen(p_essid); if(t_len>32) t_len=32; p_ap->essid_len=t_len; memcpy(p_ap->essid,p_essid,t_len); } uint16 create_beacon_frame(uint8* p_buffer,struct ap* p_ap) { memcpy(p_buffer,"\x80\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF",10); memcpy(p_buffer+10,p_ap->bssid,6); memcpy(p_buffer+16,p_ap->bssid,6); p_buffer[22]=(uint8)(p_ap->seq_id&0xFF); p_buffer[23]=(uint8)((p_ap->seq_id>>8)&0xFF); p_ap->seq_id+=0x10; struct timeval t_time; gettimeofday(&t_time,0); uint64 t_timestamp=((uint64)t_time.tv_sec)*1000000+t_time.tv_usec; uint8 t_i; for(t_i=0;t_i<8;t_i++) p_buffer[24+t_i]=(uint8)((t_timestamp>>(t_i<<3))&0xFF); memcpy(p_buffer+32,"\x64\x00\x01\x00",4); p_buffer[36]=0; p_buffer[37]=p_ap->essid_len; memcpy(p_buffer+38,p_ap->essid,p_ap->essid_len); return 38+p_ap->essid_len; } int32 create_raw_socket(char* p_iface) { /* new raw socket */ int32 t_socket=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL)); if(t_socket<0) { perror("<create_raw_socket> socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL)) failed!"); return -1; } /* get the index of the interface */ struct ifreq t_ifr; memset(&t_ifr,0,sizeof(t_ifr)); strncpy(t_ifr.ifr_name,p_iface,sizeof(t_ifr.ifr_name)-1); if(ioctl(t_socket,SIOCGIFINDEX,&t_ifr)<0) { perror("<create_raw_socket> ioctl(SIOCGIFINDEX) failed!"); return -1; } /* bind the raw socket to the interface */ struct sockaddr_ll t_sll; memset(&t_sll,0,sizeof(t_sll)); t_sll.sll_family=AF_PACKET; t_sll.sll_ifindex=t_ifr.ifr_ifindex; t_sll.sll_protocol=htons(ETH_P_ALL); if(bind(t_socket,(struct sockaddr*)&t_sll,sizeof(t_sll))<0) { perror("<create_raw_socket> bind(ETH_P_ALL) failed!"); return -1; } /* open promisc */ struct packet_mreq t_mr; memset(&t_mr,0,sizeof(t_mr)); t_mr.mr_ifindex=t_sll.sll_ifindex; t_mr.mr_type=PACKET_MR_PROMISC; if(setsockopt(t_socket,SOL_PACKET,PACKET_ADD_MEMBERSHIP,&t_mr,sizeof(t_mr))<0) { perror("<create_raw_socket> setsockopt(PACKET_MR_PROMISC) failed!"); return -1; } return t_socket; } int32 send_80211_frame(int32 p_socket,uint8* p_buffer,uint32 p_size) { uint8 t_buffer[4096]; uint8* t_radiotap=(uint8*)"\x00\x00\x0d\x00\x04\x80\x02\x00\x02\x00\x00\x00\x00"; memcpy(t_buffer,t_radiotap,13); memcpy(t_buffer+13,p_buffer,p_size); p_size+=13; int32 t_size=write(p_socket,t_buffer,p_size); if(t_size<0) { perror("<send_80211_frame> write() failed!"); return -1; } return t_size; } int32 main() { struct ap t_aps[AP_COUNT]; uint32 t_i; for(t_i=0;t_i<AP_COUNT;t_i++) { uint8 t_mac[6]; char t_essid[32]; memcpy(t_mac,"\xEC\x17\x2F\x2D\xB6\xB0",6); memcpy(t_essid,"zjs ap 0",9); t_mac[5]+=t_i; t_essid[7]+=t_i; init_ap(&t_aps[t_i],t_mac,t_essid); } int32 t_socket=create_raw_socket("wlan0"); while(1) { for(t_i=0;t_i<AP_COUNT;t_i++) { uint8 t_buffer[1024]; uint16 t_len=create_beacon_frame(t_buffer,t_aps+t_i); printf("%dn",send_80211_frame(t_socket,t_buffer,t_len)); } usleep(100000); } return 0; }

As you can imagine, it is brushed screen:

Zhou Yuxin from Nanjing University to Intel Asia Pacific Research and Development Co., Ltd.
[email protected]

1 December 2021, 19:06 | Views: 1862

Add new comment

For adding a comment, please log in
or create account

0 comments