Original text by talosintelligence
Asus RT-AX82U get_IFTTTTtoken.cgi authentication bypass vulnerability
An authentication bypass vulnerability exists in the get_IFTTTTtoken.cgi functionality of Asus RT-AX82U 3.0.0.4.386_49674-ge182230. A specially-crafted HTTP request can lead to full administrative access to the device. An attacker would need to send a series of HTTP requests to exploit this vulnerability.
CONFIRMED VULNERABLE VERSIONS
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
Asus RT-AX82U 3.0.0.4.386_49674-ge182230
PRODUCT URLS
RT-AX82U — https://www.asus.com/us/Networking-IoT-Servers/WiFi-Routers/ASUS-Gaming-Routers/RT-AX82U/
DETAILS
The Asus RT-AX82U router is one of the newer Wi-Fi 6 (802.11ax)-enabled routers that also supports mesh networking with other Asus routers. Like basically every other router, it is configurable via a HTTP server running on the local network. However, it can also be configured to support remote administration and monitoring in a more IOT style.
In order to enable remote management and monitoring of our Asus Router, so that it behaves just like any other IoT device, there are a couple of settings changes that need to be made. First we must enable WAN access for the HTTPS server (or else nothing could manage the router), and then we must generate an access code to link our device with either Amazon Alexa or IFTTT. These options can all be found internally at
As a high level overview, upon receiving this code, the remote website will connect to your router at the
0002863c int32_t do_get_IFTTTToken_cgi(int32_t arg1, FILE* arg2) 00028660 char* r0 = get_UA_Type(inpstr: &user_agent) // [1] 00028668 char* r0_2 00028668 if (r0 != 4) // asusrouter-Windows-IFTTT-1.0 000286b0 r0_2 = get_UA_Type(inpstr: &user_agent) // [...] 000286cc void var_30 000286cc memset(&var_30, 0, 0x20) 000286d8 char* r0_4 = check_if_queryitem_exists("shortToken") // [2] 000286e0 if (r0_4 == 0) 000286e4 r0_4 = &nullptr 000286ec int32_t r0_5 = gen_IFTTTtoken(token: r0_4, outbuf: &var_30) // [3] 00028700 fputs(str: &(*"\tif (disk_num == %d) {\n")[0x15], fp: arg2) 00028708 fflush(fp: arg2) 0002871c fprintf(stream: arg2, format: ""ifttt_token":"%s",\n", &var_30) // [4] 00028724 fflush(fp: arg2) 00028738 fprintf(stream: arg2, format: ""error_status":"%d"\n", r0_5) 00028740 fflush(fp: arg2) 00028750 fputs(str: &data_81196, fp: arg2) 00028760 return fflush(fp: arg2)
At [1], the function pulls out the “User-Agent” header of our HTTP GET request and checks to see if it starts with “asusrouter”. It also checks if the text after the second dash is either “IFTTT” or “Alexa”. In either of those cases, it returns 4 or 5, and we’re allowed to proceed in the code path. At [2], the function pulls out the
0007b5c8 int32_t gen_IFTTTtoken(char* token, uint8_t* outbuf) 0007b5d4 int32_t r0 = uptime() 0007b5fc memset(&ifttt_token_copy, 0, 0x20) 0007b614 int32_t r0_8 0007b614 int32_t arg3 0007b614 int32_t arg4 0007b614 if (r0 - nvram_get_int("ifttt_timestamp") s> 120) // [5] 0007b6ec if (isFileExist("/tmp/IFTTT_ALEXA") s> 0) 0007b710 Debug2File("/tmp/IFTTT_ALEXA.log", "[%s:(%d)][HTTPD] short token timeout\n", "gen_IFTTTtoken", 0x3ff, token, outbuf, arg3, arg4) 0007b714 r0_8 = 1 0007b630 else if (nvram_get_and_cmp("ifttt_stoken", token) == 0) // [6] 0007b72c if (isFileExist("/tmp/IFTTT_ALEXA") s> 0) 0007b760 Debug2File("/tmp/IFTTT_ALEXA.log", "[%s:(%d)][HTTPD] short token is not the same: endp…", "gen_IFTTTtoken", 0x402, token, p2_nvram_get(item: "ifttt_stoken"), arg3, arg4) 0007b764 r0_8 = 2 0007b64c else if (get_UA_Type(inpstr: &user_agent) != 4) 0007b77c if (isFileExist("/tmp/IFTTT_ALEXA") s> 0) 0007b7a0 Debug2File("/tmp/IFTTT_ALEXA.log", "[%s:(%d)][HTTPD] user_agent not from IFTTT/ALEXA\n", "gen_IFTTTtoken", 0x405, token, outbuf, arg3, 0xf1430) 0007b7a4 r0_8 = 3 0007b668 else 0007b668 int32_t r2 0007b668 uint8_t* r3 0007b668 r2, r3 = nvram_set("skill_act_code", p2_nvram_get(item: "skill_act_code_t")) 0007b674 generate_asus_token(dst: &ifttt_token_copy, len: 0x20, r2, readsrc: r3) // [7] 0007b684 strlcpy(dst: outbuf, src: &ifttt_token_copy, len: 0x20) 0007b694 nvram_set("ifttt_token", &ifttt_token_copy) 0007b698 nvram_commit() 0007b6ac if (isFileExist("/tmp/IFTTT_ALEXA") s> 0) 0007b6d0 Debug2File("/tmp/IFTTT_ALEXA.log", "[%s:(%d)][HTTPD] get IFTTT long token success\n", "gen_IFTTTtoken", 0x408, token, outbuf, arg3, 0xf1430) 0007b6d4 r0_8 = 0 0007b7ac return r0_8
Right at the beginning there is a check [5] to see if the uptime of the device is more than two minutes after the
While nothing really seems out of place at the moment, let’s take a look over at the code which actually generates the
00074210 uint8_t* do_ifttt_token_generation(uint8_t* output) // [...] 000742c0 char ifttt_token[0x80] 000742c0 memset(&ifttt_token, 0, 0x80) 000742d0 char timestamp[0x80] 000742d0 memset(×tamp, 0, 0x80) 000742e0 char rbinstr[0x8] 000742e0 rbinstr[0].d = 0 000742e8 int32_t* randbinstrptr = &rbinstr 000742f4 rbinstr[4].d = 0 00074308 srand(x: time(timer: nullptr)) 0007431c // takes the remainder... 00074324 int_to_binstr(inp: __aeabi_idivmod(rand(), 0xff), cpydst: randbinstrptr, len: 7) // [8] // [...] 00074608 snprintf(s: &ifttt_token, maxlen: 0x80, format: &percent_o, binary_str_to_int(randbinstrptr)) // [9] 0007461c nvram_set("ifttt_stoken", &ifttt_token) 00074638 snprintf(s: ×tamp, maxlen: 0x80, format: &percentld, uptime()) // [10] 00074648 nvram_set("ifttt_timestamp", ×tamp) 00074658 strlcpy(dst: output, src: &skill_act_code, len: 0x48) 0007465c nvram_commit() 0007466c return output
With the unimportant code cut out, we are left with a somewhat clear view of the generation process. At [8] a random number is generated that is then moded against 0xFF. This number is then transformed into a binary string of length 8 (e.g. ‘00101011’). A lot further down at [9], this
0007b5d4 int32_t r0 = uptime() // [...] 0007b614 if (r0 - nvram_get_int("ifttt_timestamp") s> 120) // [...] 0007b630 else if (nvram_get_and_cmp("ifttt_stoken", token) == 0)
We can see that the current uptime is used against the uptime of the generated token. Unfortunately for the device,
Asus RT-AX82U cfg_server cm_processREQ_NC information disclosure vulnerability
SUMMARY
An information disclosure vulnerability exists in the cm_processREQ_NC opcode of Asus RT-AX82U 3.0.0.4.386_49674-ge182230 router’s configuration service. A specially-crafted network packets can lead to a disclosure of sensitive information. An attacker can send a network request to trigger this vulnerability.
CONFIRMED VULNERABLE VERSIONS
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
Asus RT-AX82U 3.0.0.4.386_49674-ge182230
PRODUCT URLS
RT-AX82U — https://www.asus.com/us/Networking-IoT-Servers/WiFi-Routers/ASUS-Gaming-Routers/RT-AX82U/
DETAILS
The Asus RT-AX82U router is one of the newer Wi-Fi 6 (802.11ax)-enabled routers that also supports mesh networking with other Asus routers. Like basically every other router, it is configurable via a HTTP server running on the local network. However, it can also be configured to support remote administration and monitoring in a more IOT style.
The
type_dict = { 0x1 : "cm_processREQ_KU", // [1] 0x3 : "cm_processREQ_NC", // [2] 0x4 : "cm_processRSP_NC", 0x5 : "cm_processREP_OK", 0x8 : "cm_processREQ_CHK", 0xa : "cm_processACK_CHK", 0xf : "cm_processREQ_JOIN", 0x12 : "cm_processREQ_RPT", 0x14 : "cm_processREQ_GKEY", 0x17 : "cm_processREQ_GREKEY", 0x19 : "cm_processREQ_WEVENT", 0x1b : "cm_processREQ_STALIST", 0x1d : "cm_processREQ_FWSTAT", 0x22 : "cm_processREQ_COST", 0x24 : "cm_processREQ_CLIENTLIST", 0x26 : "cm_processREQ_ONBOARDING", 0x28 : "cm_processREQ_GROUPID", 0x2a : "cm_processACK_GROUPID", 0x2b : "cm_processREQ_SREKEY", 0x2d : "cm_processREQ_TOPOLOGY", 0x2f : "cm_processREQ_RADARDET", 0x31 : "cm_processREQ_RELIST", 0x33 : "cm_processREQ_APLIST", 0x37 : "cm_processREQ_CHANGED_CONFIG", 0x3b : "cm_processREQ_LEVEL", }
Out of the 24 different opcodes, only 3 or so can be used without authentication, and so let’s start from the top with
struct REQ_TLV = { uint32_t tlv_type; uint32_t size; uint32_t crc; char buffer[]; }
For the
struct REQ_NC = { uint32_t tlv_type = "\x00\x00\00\x03", uint32_t size, uint32_t crc, uint32_t tlv_subpkt1 = "\x00\x00\x00\x01", //[3] uint32_t sizeof_subpkt1, uint32_t crcof_subpkt1, char master_key[ ], //[4] uint32_t tlv_subpkt2 = "\x00\x00\x00\x03", uint32_t sizeof_subpkt2, uint32_t crcof_subpkt2, char client_nonce[ ], //[5] }
The
[~.~]> x/60bx $r1 0xb62014e0: 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x20 // headers 0xb62014e8: 0x06 0x42 0x18 0x4f 0xb62014ec: 0x13 0x9f 0x09 0x97 // server nonce [6] 0xb62014f0: 0x90 0x92 0x9b 0x85 0xe5 0x40 0xa1 0x38 0xb62014f8: 0xd7 0x81 0x62 0x72 0xf6 0x88 0x5c 0xef 0xb6201500: 0x61 0x86 0x5c 0xc0 0xef 0xc0 0x06 0x23 0xb6201508: 0xa2 0x6d 0x6a 0x85 0xb620150c: 0x00 0x00 0x00 0x03 // headers 0xb6201510: 0x00 0x00 0x00 0x04 0x51 0xb3 0x28 0x43 0xb6201518: 0xcc 0xcc 0xcc 0xcc // [...] // client nonce [7]
Both the
0001d8b0 void *aes_encrypt(char *enckey, char *inpbuf, int32_t inpsize, uint32_t outsize){ 0001d8c8 int32_t ctx = EVP_CIPHER_CTX_new(); 0001d8d4 if (ctx == 0){ ... } 0001d904 else { 0001d914 int32_t r0_2 = EVP_EncryptInit_ex(ctx: ctx, type: EVP_aes_256_ecb(), imple: null, key: enckey, iv: nullptr);
We don’t need to delve too much into what occurs; it suffices to know that the key is passed directly from the
00057534 int32_t cm_processREQ_NC(int32_t clifd, struct ctrlblk *ctrl, void *tlvtype, int32_t pktlen, void *tlv_checksum, struct tlv_ret_struct* sess_block, char *pktbuf, uint32_t client_ip, char *cli_mac){ {...} 00058b58 void *aes_resp = aes_encrypt(enckey: sess_block->master_key, inpbuf: nonce_buff, inpsize: clinonce_len + sess_block->server_nonce_len + 0x18, outsize: &act_resp_b_size);
We can see it involves the masterkey that we provided. Let’s back up further in
00057534 int32_t cm_processREQ_NC(int32_t clifd, struct ctrlblk *ctrl, void *tlvtype, int32_t pktlen, void *tlv_checksum, struct tlv_ret_struct* sess_block, char *pktbuf, uint32_t client_ip, char *cli_mac){ // [...] 00057af4 int32_t req_type = decbuf_0x1002.request_type_le 00057af4 int32_t req_len = decbuf_0x1002.total_len_le 00057af4 int32_t req_crc = decbuf_0x1002.crc_mb 00057b00 int32_t reqlen = req_len u>> 0x18 | (req_len u>> 0x10 & 0xff) << 8 | (req_len u>> 8 & 0xff) << 0x10 | (req_len & 0xff) << 0x18 00057b08 int32_t reqcrcle_ 00057b08 if (reqlen != 0) 00057b10 reqcrcle_ = req_crc u>> 0x18 | (req_crc u>> 0x10 & 0xff) << 8 | (req_crc u>> 8 & 0xff) << 0x10 | (req_crc & 0xff) << 0x18 00057b18 if (reqcrcle_ != 0) 00057bb4 if (req_type != 0x1000000) // master key [8] // [...] 00057c48 int32_t decsize_m0xc = size_of_decrypted - 0xc 00057c50 if (decsize_m0xc u< reqlen) // [9] // [...] 00057cf0 char (* var_1048_1)[0x1000] = &dec_buf_contents 00057d00 if (do_crc32(IV: 0, buf: &dec_buf_contents, bufsize: reqlen) != reqcrcle_) [10] // [...] 00057d94 sess_block->masterkey_size = reqlen 00057d9c char* aeskey_malloc = malloc(bytes: reqlen) // [11] 00057da8 sess_block->master_key = aeskey_malloc // [...] 00057db8 memset(aeskey_malloc, 0x0, reqlen); 00057dd8 memcpy(aeskey_malloc, &dec_buf_contents, reqlen);
Trimming out all the error cases, we start from where the server starts reading the bytes decrypted with its RSA private key. All the fields have their endianess reversed, and the sub-request type is checked at [8]. A size check at [9] prevents us from doing anything silly with the length field in our master_key message, and a CRC check occurs at [10]. Finally the
Now, an important fact about AES encryption is that the key is always a fixed size, and for AES_256, our key needs to be 0x20 bytes. As noted above however, there’s not actually any explicit length check to make sure the provided
While the malloc that occurs can go into a different bucket based on the size of our provided
Crash Information
$python infoleak.py Type: 1 (cm_processREQ_KU) Len: 0x4 CRC: 0x56b642cd ===MSG=== \x11\x22\x33\x44 ========= b'\x00\x00\x00\x01\x00\x00\x00\x04V\xb6B\xcd\x11"3D' [^_^] Importing: -----BEGIN PUBLIC KEY----- [...] -----END PUBLIC KEY----- Type: 3 (cm_processREQ_NC) Len: 0x100 CRC: 0x92657321 ===MSG=== \x1a\x54\xd7\x4a\xf6\x7a\xe1\x4c\x16\x76\x69\x74\x2b\x96\x41\xc6\xa0\xbc\x57\x58\x45\x61\xa9\xa9\x04\x09\xae\xb4\xb2\x9c\x54\xdd\xb8\xd1\x8f\x0d\x25\xf6\x79\x07\xd6\x65\x12\x75\xbb\x7d\x2d\x4e\x41\xf0\xa9\x47\x75\xa5\x73\x2d\x4c\x02\x10\x9e\xb1\x3a\x2c\xa5\x1c\x11\xfe\x35\x8e\xd3\x95\x53\xe5\x90\x3a\x9a\x8b\xad\x9b\x10\x81\xde\xd3\x67\x19\x9d\x34\x44\x52\x75\x1d\x90\xc7\xbf\x19\xf1\x04\x15\x19\xd4\x11\x2d\x70\xbd\xa9\x87\xdf\x22\x59\xc2\xb0\xb1\xd5\x7b\x5a\xcb\xe7\xc7\x34\x0f\xcb\xa6\x9f\x81\x5c\xb3\x6d\xf7\x1c\x49\xd7\xed\x72\x54\x85\xe0\xca\x32\x96\xa9\xa2\x44\xda\x56\xfb\xf7\x96\x21\x53\xb7\xbe\x9c\xc9\x5f\x4a\x00\xdb\x2f\xd2\x6e\x1b\xf5\xdc\xa9\xa5\x8f\xde\xf5\x80\x83\xd7\xd8\x65\xe8\x6f\xd6\x0a\x3e\x10\x92\xca\xd2\xbf\x14\x1c\x06\xf0\x53\xb5\x41\xea\x2a\xe2\x5c\x2a\xa8\xb9\xa2\x92\xe7\xd5\x44\x55\x1c\x8e\x9b\xff\x13\x37\x60\x5b\x82\xfa\xa0\xe7\x44\x8f\x0b\xe9\x8f\x64\xcd\xa4\x50\xe9\xcd\xbc\x14\x34\xed\x57\xc5\x0a\xaf\xc3\x8d\x71\xee\x48\x35\x90\xa6\xb7\x08\x6c\xfb\xb1\xbf\xee\x0c\x72\x21\xdf\x4e\x29\xf9 ========= [^_^] Leaked Bytes: 0x0000b620 b'\x00\x00\x00\x02\x00\x00\x00 \x1a=\xac\x11\xebVxU\xe7\\\xdb8\x02\\k\n<\x91_>\x17\xc6r\x08\xfc\xbc\xde\xf6\x1a\x1ev\xfa\x03_\xf0y\x00\x00\x00\x03\x00\x00\x00\x07\x10\xc1\x06\xa9\xcc\xcc\xcc\xcc\xcc\xcc\xcc\x01'
Asus RT-AX82U cfg_server cm_processConnDiagPktList denial of service vulnerability
SUMMARY
A denial of service vulnerability exists in the cfg_server cm_processConnDiagPktList opcode of Asus RT-AX82U 3.0.0.4.386_49674-ge182230 router’s configuration service. A specially-crafted network packet can lead to denial of service. An attacker can send a malicious packet to trigger this vulnerability.
CONFIRMED VULNERABLE VERSIONS
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
Asus RT-AX82U 3.0.0.4.386_49674-ge182230
PRODUCT URLS
RT-AX82U — https://www.asus.com/us/Networking-IoT-Servers/WiFi-Routers/ASUS-Gaming-Routers/RT-AX82U/
DETAILS
The Asus RT-AX82U router is one of the newer Wi-Fi 6 (802.11ax)-enabled routers that also supports mesh networking with other Asus routers. Like basically every other router, it is configurable via a HTTP server running on the local network. However, it can also be configured to support remote administration and monitoring in a more IOT style.
The
struct tlv_holder connDiagPacketHandlers = { uint32_t type = 0x5 tlv_func *tfunc = cm_processREQ_CHKSTA } struct tlv_holder connDiagPacketHandlers[1] = { uint32_t type = 0x6 tlv_func *tfunc = cm_processRSP_CHKSTA }
The above TLVs are accessible from the
0001ed90 cm_recvUdpHandler() // [...] 0001edf8 int32_t bytes_read = recvfrom(sfd: cm_ctrlBlock.udp_sock, buf: &readbuf, len: 0x7ff, flags: 0, srcaddr: &sockadd, addrlen: &sockaddsize) // [1] // [...] 0001ee00 if (bytes_read == 0xffffffff) // [...] 0001ee98 else if (sockadd.sa_data[2].d != cm_ctrlBlock.self_address) // [...] 0001f0e0 char* malloc_824 = malloc(bytes: 0x824) // [2] 0001f0e4 struct udp_resp* inp = malloc_824 0001f0e8 if (malloc_824 != 0) 0001f184 memset(malloc_824, 0, 0x824) // [3] 0001f194 memcpy(inp, &readbuf, bytes_read) 0001f198 int32_t ipaddr = sockadd.sa_data[2].d 0001f19c inp->bytes_read = bytes_read 0001f1a4 int32_t ip = ipaddr u>> 0x18 | (ipaddr u>> 0x10 & 0xff) << 8 | (ipaddr u>> 8 & 0xff) << 0x10 | (ipaddr & 0xff) << 0x18 0001f1d4 snprintf(s: &inp->ip_addr_str, maxlen: 0x20, format: "%d.%d.%d.%d", ip u>> 0x18, ip u>> 0x10 & 0xff, ip u>> 8 & 0xff, ror.d(ip, 0) & 0xff, var_864, var_860, var_85c, var_858, var_854) 0001f1dc int32_t var_838_1 = readbuf[4].d 0001f1dc int32_t var_834_1 = readbuf[8].d 0001f1e8 if (readbuf[0].d == 0x6000000) // [4] 0001f1f0 r0_6 = cm_addConnDiagPktToList(inp: inp)
At [1], the server reads in 0x7ff bytes from its UDP 7788 port, and at [2] and [3], the data is then copied from the stack over to a cleared-out heap allocation of size 0x824. Assuming the first four bytes of the input packet are “\x00\x00\x00\x06”, then the packet gets added to a particular linked list structure, the
struct tlv_pkt { uint32_t type; uint32_t datalen; uint32_t crc; uint8_t data[]; }
Continuing on, another thread is constantly polling the
00053ca8 int32_t cm_processConnDiagPktList() 00053cc8 pthread_mutex_lock(mutex: &connDiagLock) 00053cd8 struct list* connDiagUdp = connDiagUdpList 00053ce8 if (connDiagUdp->entry_count s> 0) 00053d2c for (struct listitem* item = connDiagUdp->tail; item != 0; item = item->next) 00053d30 struct udp_resp* input_pkt = item->inp 00053d38 if (input_pkt != 0) 00053d44 uint32_t null = terminateConnDiagPktList 00053d4c if (null != 0) 00053d4c break 00053d50 uint32_t hex_6000000 = input_pkt->req_type_le 00053d58 uint32_t dlen = input_pkt->datalen_le 00053d68 int32_t dlenle = input_pkt->bytes_read - 0xc // [5] 00053d6c uint32_t crcle = input_pkt->crcle // [...] 00053d80 if (dlenle == (dlen u>> 0x18 | (dlen u>> 0x10 & 0xff) << 8 | (dlen u>> 8 & 0xff) << 0x10 | (dlen & 0xff) << 0x18)) //[6] 00053e0c char* buf = &input_pkt->readbuf 00053e18 crc = do_crc32(IV: null, buf: buf, bufsize: dlenle) // [7]
At [5], the actual length of the input packet minus twelve is compared against the length field inside the packet itself [6]. Assuming they match, the CRC is then checked, another field provided in the packet itself. A flaw is present in this function, however, in that there is a check missing in this code path that can be seen in both the TCP and UDP handlers: the code needs to verify that the size of the received packet is >= 0xC bytes. Thus, if a packet is received that is less than 0xC bytes, the
While we can pass all the above checks with an 8-byte packet, it does prevent us from having any control over what occurs after. We end up hitting
00052f20 int32_t cm_processRSP_CHKSTA(char* pktbuf, uint32_t pktlen, int32_t ipaddr) 00052f50 char jsonbuf[0x800] 00052f50 memset(&jsonbuf, 0, 0x800) // [...] 00052f64 if (cm_ctrlBlock.group_key_ready != 0) 00053004 char* groupkey = cm_selectGroupKey(which_key: 1) 0005300c if (groupkey == 0) // [...] 00053098 goto label_530a0 000530c0 char* r0_11 = do_decrypt(sesskey1: groupkey, sesskey2: cm_selectGroupKey(which_key: 0), pktbuf: pktbuf, pktlen: pktlen) //[8]
Assuming there is a group key (which there should always be, even if the AImesh setting is not configured), then we end up hitting the
0001db18 void* aes_decrypt(char* sesskey1, char* pktbuf, char* pktlen, int32_t* outlen) 0001db30 int32_t ctx = EVP_CIPHER_CTX_new() 0001db38 int32_t outl = 0 0001db3c void* ctx = ctx 0001db40 void* ret 0001db40 if (ctx == 0) // [...] 0001db6c else 0001db6c char* bytesleft = nullptr 0001db7c int32_t r0_2 = EVP_DecryptInit_ex(ctx, EVP_aes_256_ecb(), 0, sesskey1, 0) // [...] 0001db84 if (r0_2 != 0) 0001dba0 *outlen = 0 0001dbac void* alloc_size = EVP_CIPHER_CTX_block_size(ctx) + pktlen 0001dbb4 maloced = malloc(bytes: alloc_size) // 0xc... 0001dbbc if (maloced == 0) //[...] 0001dbe4 else 0001dbe4 memset(maloced, 0, alloc_size) 0001dbec void* mbuf = maloced 0001dbf0 char* pktiter = pktlen 0001dc00 void* inpbuf 0001dc00 void* r3_2 0001dc00 while (true) 0001dc00 inpbuf = &pktbuf[pktlen - pktiter] 0001dc04 if (pktiter u<= 0x10) 0001dc04 break 0001dc10 bytesleft = 0x10 0001dc1c int32_t r0_8 = EVP_DecryptUpdate(ctx, mbuf, &outl, inpbuf, 0x10) //[9] 0001dc20 r3_2 = r0_8 0001dc24 if (r0_8 == 0) 0001dc24 break 0001dc60 int32_t outl_len = outl 0001dc64 pktiter = pktiter - 0x10 0001dc6c mbuf = mbuf + outl_len 0001dc74 *outlen = *outlen + outl_len
For brevity’s sake, we can skip all the way to [9], where
Crash Information
potentially unexpected fatal signal 11. CPU: 1 PID: 12452 Comm: cfg_server Tainted: P O 4.1.52 #2 Hardware name: Generic DT based system task: d04cd800 ti: d0632000 task.ti: d0632000 PC is at 0xb6c7f460 LR is at 0xb6d3ca04 pc : [<b6c7f460>] lr : [<b6d3ca04>] psr: 60070010 sp : b677c46c ip : 00ff4ff4 fp : b6600670 r10: b6c7ef40 r9 : 00000000 r8 : beec0b82 r7 : b6600670 r6 : 00000010 r5 : b6620c38 r4 : 00ff5004 r3 : b6c7f440 r2 : 00000000 r1 : 00000000 r0 : 00000000 Flags: nZCv IRQs on FIQs on Mode USER_32 ISA ARM Segment user Control: 10c5387d Table: 1048c04a DAC: 00000015 CPU: 1 PID: 12452 Comm: cfg_server Tainted: P O 4.1.52 #2 Hardware name: Generic DT based system [<c0026fe0>] (unwind_backtrace) from [<c0022c38>] (show_stack+0x10/0x14) [<c0022c38>] (show_stack) from [<c047f89c>] (dump_stack+0x8c/0xa0) [<c047f89c>] (dump_stack) from [<c003ac30>] (get_signal+0x490/0x558) [<c003ac30>] (get_signal) from [<c00221d0>] (do_signal+0xc8/0x3ac) [<c00221d0>] (do_signal) from [<c0022658>] (do_work_pending+0x94/0xa4) [<c0022658>] (do_work_pending) from [<c001f4cc>] (work_pending+0xc/0x20)