Headline
CVE-2023-28488: poc_exploits/CVE-2023-28488 at master · moehw/poc_exploits
client.c in gdhcp in ConnMan through 1.41 could be used by network-adjacent attackers (operating a crafted DHCP server) to cause a stack-based buffer overflow and denial of service, terminating the connman process.
Suggested description
gdhcp in ConnMan through 1.41 could be used by network-adjacent attackers to cause a denial of service, terminating the connman process.
Vulnerability Type
Buffer Overflow
Vendor of the product
ConnMan
Affected products
- ConnMan - Versions from 0.55 to 1.41 are affected (all versions up to the current moment of connman with gdhcp).
Affected components
Affected module: gdhcp Affected function: https://kernel.googlesource.com/pub/scm/network/connman/connman/+/refs/tags/1.41/gdhcp/client.c#1319
Attack type
- Remote
Impact
- Denial of Service
Attack vector
The listener_event function in the ./gdhcp/client.c file is called to accept DHCP packets from the DHCP server. When dhcp_client->listen_mode == L2 (the value at the start of DHCP requests), the dhcp_recv_l2_packet function will be called. This function reads IP, UDP and DHCP headers into a packet buffer, and writes the number of bytes read into a bytes variable. But then the bytes variable is overwritten with the controlled value ntohs(packet.ip.tot_len) from the packet:
static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd, struct sockaddr_in *dst_addr) { int bytes; struct ip_udp_dhcp_packet packet; uint16_t check;
memset(&packet, 0, sizeof(packet));
bytes = read(fd, &packet, sizeof(packet));
if (bytes < 0)
return -1;
if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp)))
return -1;
if (bytes < ntohs(packet.ip.tot\_len))
/\* packet is bigger than sizeof(packet), we did partial read \*/
return -1;
/\* ignore any extra garbage bytes \*/
bytes = ntohs(packet.ip.tot\_len); // <-- ASSIGNMENT OF CONTROLLED DATA
// \[ ... \]
}
Pre and post checks in the current code are not enough. For example, the value ntohs(packet.ip.tot_len) could be 0, so the pre-checks would pass, and the value 0 would then be assigned to the variable bytes.
In the sanity_check function, all checks can be passed, since all values are controlled. For the last check it is necessary to precalculate the value of packet->udp.len.
static bool sanity_check(struct ip_udp_dhcp_packet *packet, int bytes) { // [ … ] if (ntohs(packet->udp.len) != (uint16_t)(bytes - sizeof(packet->ip))) return false; // [ … ] }
In the case of the binaries I had and the value 0 in the variable bytes, the value for ntohs(packet->udp.len) was 0xffec.
Then in the dhcp_recv_l2_packet function checksums are checked. The checksum value for the IP header can be precomputed. And for the UDP header is skipped by setting the value of 0 as a checksum value.
static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd, struct sockaddr_in *dst_addr) { // [ … ] check = packet.ip.check; packet.ip.check = 0; if (check != dhcp_checksum(&packet.ip, sizeof(packet.ip))) return -1;
/\* verify UDP checksum. IP header has to be modified for this \*/
memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
/\* ip.xx fields which are not memset: protocol, check, saddr, daddr \*/
packet.ip.tot\_len = packet.udp.len; /\* yes, this is needed \*/
check = packet.udp.check;
packet.udp.check = 0;
if (check && check != dhcp\_checksum(&packet, bytes))
return -1;
// \[ ... \]
}
Then, in the dhcp_recv_l2_packet function, a call to the memcpy function follows, where the underflow occurs in the argument for the copy size:
static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd, struct sockaddr_in *dst_addr) { // [ … ] memcpy(dhcp_pkt, &packet.data, bytes - (sizeof(packet.ip) + sizeof(packet.udp))); // <-- STACK BUFFER OVERFLOW // [ … ] }
Because of this, stack corruption (stack buffer overflow) occurs, which leads to access to an invalid address, overwriting the value of the stack canary, or returning to an incorrect address when exiting the function, depending on the compilation settings. As a result, the process is terminated, resulting in a Denial of Service.
This vulnerability can be triggered by response to a connman DHCP client. For example, it can be triggered on DHCP Offer or DHCP ACK responses from the server to the client. Vulnerability is presented on all versions up to the current moment of connman with gdhcp (from 0.55 to 1.41).
References
- https://kernel.googlesource.com/pub/scm/network/connman/connman/+/99e2c16ea1cced34a5dc450d76287a1c3e762138
Related news
Ubuntu Security Notice 6236-1 - It was discovered that ConnMan could be made to write out of bounds. A remote attacker could possibly use this issue to cause ConnMan to crash, resulting in a denial of service, or possibly execute arbitrary code. This issue only affected Ubuntu 18.04 LTS and Ubuntu 20.04 LTS. It was discovered that ConnMan could be made to leak sensitive information via the gdhcp component. A remote attacker could possibly use this issue to obtain information for further exploitation. This issue only affected Ubuntu 16.04 LTS, Ubuntu 18.04 LTS, and Ubuntu 20.04 LTS.
Debian Linux Security Advisory 5416-1 - It was discovered that there was a potential buffer overflow and denial of service vulnerability in the gdhcp client implementation of connman, a command-line network manager designed for use on embedded devices.